okHttp拦截器那些事

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.
        }
    }
}

参考https://blog.csdn.net/ethanco/article/details/51908479

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值