关于Interceptor

什么是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骨灰级玩家怎么能做出这种事情呢,不定期更新中。。。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容