什么是Interceptor:
Interceptor翻譯過來就是攔截器竟趾,它是OkHttp網絡請求中抓取請求和響應必須的一個全能王漾峡。
你如果用過okhttp定欧,一定對HttpLoggingInterceptor不陌生信殊,這個是squareup公司寫的一個樣板挽绩,其實它呢也就是告訴你了任何你想拿到的數據⊙咚看源碼么赵刑,go。场刑。般此。
public final class HttpLoggingInterceptor implements Interceptor {
private static final Charset UTF8 = Charset.forName("UTF-8");
//設置攔截級別,枚舉4種
public enum Level {
NONE,
BASIC,
HEADERS,
BODY
}
牵现。铐懊。。瞎疼。科乎。。贼急。茅茂。捏萍。。
空闲。令杈。。碴倾。逗噩。。影斑。。机打。矫户。
。残邀。皆辽。。芥挣。驱闷。。空免。空另。。
@Override public Response intercept(Chain chain) throws IOException {
Level level = this.level;
Request request = chain.request();
if (level == Level.NONE) {
//不攔截蹋砚,直接返回
return chain.proceed(request);
}
boolean logBody = level == Level.BODY;
boolean logHeaders = logBody || level == Level.HEADERS;
RequestBody requestBody = request.body();
boolean hasRequestBody = requestBody != null;
//建立連接
Connection connection = chain.connection();
//拿到連接協議扼菠,如果連接不存在就直接用http_1_1協議
Protocol protocol = connection != null ? connection.protocol() : Protocol.HTTP_1_1;
String requestStartMessage = "--> " + request.method() + ' ' + request.url() + ' ' + protocol;
//如果設置的級別是base,就打印頭部分
if (!logHeaders && hasRequestBody) {
requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
}
logger.log(requestStartMessage);
//如果設置的級別是頭坝咐,就打印頭部分
if (logHeaders) {
//如果有請求體循榆,就將請求體的長度和類型打印
if (hasRequestBody) {
//請求頭的值,存在就攔截
if (requestBody.contentType() != null) {
logger.log("Content-Type: " + requestBody.contentType());
}
//-1代表請求數據長度0
if (requestBody.contentLength() != -1) {
logger.log("Content-Length: " + requestBody.contentLength());
}
}
//請求頭部分墨坚,遍歷打印
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
// 這里因為上面已經打印了秧饮,所以就過濾一下
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
logger.log(name + ": " + headers.value(i));
}
}
//沒有請求體,或者等級沒有設置為打印請求體泽篮,結束打印
if (!logBody || !hasRequestBody) {
logger.log("--> END " + request.method())盗尸;
} else if (bodyEncoded(request.headers())) {
//有請求體或者log等級設置為body,打印請求頭中設置的編碼
logger.log("--> END " + request.method() + " (encoded body omitted)");
} else {
//將請求體數據給寫進緩存流
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
//設置編碼為utf-8
Charset charset = UTF8;
MediaType contentType = requestBody.contentType();
if (contentType != null) {
charset = contentType.charset(UTF8);
}
logger.log("");
//判斷是否是人類可讀的字符帽撑,是就打印
if (isPlaintext(buffer)) {
logger.log(buffer.readString(charset));
logger.log("--> END " + request.method()
+ " (" + requestBody.contentLength() + "-byte body)");
} else {
//人類不懂振劳,就用二進制讀出來
logger.log("--> END " + request.method() + " (binary "
+ requestBody.contentLength() + "-byte body omitted)");
}
}
}
long startNs = System.nanoTime();
Response response;
try {
//請求開始
response = chain.proceed(request);
} catch (Exception e) {
//請求異常
logger.log("<-- HTTP FAILED: " + e);
throw e;
}
//請求花費時間
long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
//響應體了
ResponseBody responseBody = response.body();
long contentLength = responseBody.contentLength();
String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
//打印長度和響應碼,響應信息油狂,響應體對應請求體(這里要考慮重定向url)历恐,請求耗費時間寸癌,響應體長度
logger.log("<-- " + response.code() + ' ' + response.message() + ' '
+ response.request().url() + " (" + tookMs + "ms" + (!logHeaders ? ", "
+ bodySize + " body" : "") + ')');
//打印響應頭
if (logHeaders) {
Headers headers = response.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
logger.log(headers.name(i) + ": " + headers.value(i));
}
//響應體不存在,等級不為body
if (!logBody || !HttpHeaders.hasBody(response)) {
logger.log("<-- END HTTP");
} else if (bodyEncoded(response.headers())) {
//響應體編碼不對稱
logger.log("<-- END HTTP (encoded body omitted)");
} else {
//source弱贼,響應體來了
BufferedSource source = responseBody.source();
//設置最大緩存大小蒸苇,當然是緩存整個body嘍,全吃
source.request(Long.MAX_VALUE);
Buffer buffer = source.buffer()吮旅;
Charset charset = UTF8;
//拿到media類型對應的字符集
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
logger.log("");
logger.log("Couldn't decode the response body; charset is likely malformed.");
logger.log("<-- END HTTP");
return response;
}
}
//如果不是人類能看懂的溪烤,就不打印string形式的嘍
if (!isPlaintext(buffer)) {
logger.log("");
logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
return response;
}
//是人類懂的,就開始讀string嘍
if (contentLength != 0) {
logger.log("");
logger.log(buffer.clone().readString(charset));
}
//最后打印body的長度
logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
}
}
return response;
}
/**
*判斷緩存流的數據是否是人類能讀懂的庇勃,哈哈檬嘀,也就是abc123唄
*/
static boolean isPlaintext(Buffer buffer) {
//先判斷是不是123abc能看懂的
try {
Buffer prefix = new Buffer();
long byteCount = buffer.size() < 64 ? buffer.size() : 64;
buffer.copyTo(prefix, 0, byteCount);
for (int i = 0; i < 16; i++) {
if (prefix.exhausted()) {
break;
}
//再判斷是不是iso8859-1之類的,當然不是能懂的啊
int codePoint = prefix.readUtf8CodePoint();
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false;
}
}
return true;
} catch (EOFException e) {
return false; // Truncated UTF-8 sequence.
}
}
//返回頭中的編碼是否存在并且不是identity责嚷,一般都是gzip,deflate,compress之中一個
private boolean bodyEncoded(Headers headers) {
String contentEncoding = headers.get("Content-Encoding");
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
}
}
好了終于擼了一遍源碼鸳兽,相信還有很多朋友沒有看懂,解釋這些源碼干嘛啊罕拂,另外推薦一篇文章揍异,大家踴躍去看吧(感謝ychongjie的攔截器細致分析):
鏈接直通車
沒有太多時間更新和維護,有什么不妥的請指出爆班,匆忙的成果衷掷,畢竟公司團隊項目你不做其他人多做,多少有點坑隊友柿菩,dota骨灰級玩家怎么能做出這種事情呢戚嗅,不定期更新中。枢舶。渡处。