OkHttp中的拦截器是一种强大机制,请求前处理(请求入参,添加请求头,打印请求参数等)、请求处理(请求log打印,回调处理,错误重试,响应处理等),极其方便了我们的网络请求逻辑。
拦截器支持我们自己的各种自定义逻辑 继承Interceptor就可随心所欲的玩耍,需要注意一点的坑就是”Response.body.string()“默认只能调用一次,先看一下string()方法里面干了点啥:
/**
以字符串形式返回响应,该字符串由Content-Type头部的字符集解码。如果这
报头不存在或缺少字符集,这将尝试按照<a href="https://en.wikipedia.org wiki/Byte_order_mark"来解码响应体,其BOM</a>或UTF-8。
自动关闭{@link ResponseBody}。
此方法将整个响应体加载到内存中。如果响应体非常大,则可能触发{@link OutOfMemoryError}。如果这是您的响应的可能性,请选择流响应体。*/
public final String string() throws IOException {
BufferedSource source = source();
try {
Charset charset = Util.bomAwareCharset(source, charset());
return source.readString(charset);
} finally {
Util.closeQuietly(source);
}
}
原因就是:
OkHttp 将响应体的缓冲资源返回的同时,调用 closeQuietly() 方法默默释放了资源。
okhttp官方提供了log打印的拦截器,我们可以参考其流程解决string一次的问题。
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
源码分析的文章比较多,源码不贴了。贴一下如何优雅的打印请求log日志
如图:
代码如下:
/**
* desc 请求日志打印拦截器
*
* @author zhangcx
* 2018/11/5 下午1:35
*/
public class HttpLogger implements HttpLoggingInterceptor.Logger {
private StringBuilder mMessage = new StringBuilder();
@Override
public void log(String message) {
// 请求或者响应开始
if (message.startsWith("--> POST") || message.startsWith("--> GET")) {
mMessage.setLength(0);
}
// 以{}或者[]形式的说明是响应结果的json数据,需要进行格式化
if ((message.startsWith("{") && message.endsWith("}"))
|| (message.startsWith("[") && message.endsWith("]"))) {
message = JsonUtil.formatJson(message);
}
mMessage.append(message.concat("\n"));
// 请求或者响应结束,打印整条日志
if (message.startsWith("<-- END HTTP")) {
LogUtils.out(mMessage.toString());
}
}
/**
* 格式化json字符串
*
* @param jsonStr 需要格式化的json串
* @return 格式化后的json串
*/
public static String formatJson(String jsonStr) {
if (null == jsonStr || "".equals(jsonStr)) return "";
StringBuilder sb = new StringBuilder();
char last;
char current = '\0';
int indent = 0;
for (int i = 0; i < jsonStr.length(); i++) {
last = current;
current = jsonStr.charAt(i);
//遇到{ [换行,且下一行缩进
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]换行,当前行缩进
case '}':
case ']':
sb.append('\n');
indent--;
addIndentBlank(sb, indent);
sb.append(current);
break;
//遇到,换行
case ',':
sb.append(current);
if (last != '\\') {
sb.append('\n');
addIndentBlank(sb, indent);
}
break;
default:
sb.append(current);
}
}
return sb.toString();
}
/**
* 添加space
*
*/
private static void addIndentBlank(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
}
}
根据HttpLoggingInterceptor,获取responsebody的json内容
/**
* desc token过期退出登录
*
* @author zhangcx
* 2018/11/5 下午1:40
*/
public class TokenInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
ResponseBody responseBody = response.body();
long contentLength = responseBody.contentLength();
if (!bodyEncoded(response.headers())) {
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // Buffer the entire body.
Buffer buffer = source.buffer();
Charset charset = UTF8;
MediaType contentType = responseBody.contentType();
if (contentType != null) {
try {
charset = contentType.charset(UTF8);
} catch (UnsupportedCharsetException e) {
return response;
}
}
if (!isStringText(buffer)) {
return response;
}
if (contentLength != 0) {
String result = buffer.clone().readString(charset);
//TODO 请求结果
// LogUtils.d(result);
if (response.isSuccessful()) {
}
}
}
return response;
}
private static final Charset UTF8 = Charset.forName("UTF-8");
private boolean bodyEncoded(Headers headers) {
String contentEncoding = headers.get("Content-Encoding");
return contentEncoding != null && !contentEncoding.equalsIgnoreCase("identity");
}
/**
* 是否为纯文本
*/
private static boolean isStringText(Buffer buffer) {
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;
}
int codePoint = prefix.readUtf8CodePoint();
if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
return false;
}
}
return true;
} catch (EOFException e) {
return false; // Truncated UTF-8 sequence.
}
}
}