OkHttp源码分析系列--整体的工作流程

OkHttp作为时下最受欢迎的网络请求框架之一,它有着自己的优点:

  • 使用了众多的设计模式(如:Builder模式、责任链模式等),尤其是在Interceptor中使用的责任链模式,将整个网络请求串联起来最为经典。
  • 基于Http请求头、DiskLruCache等缓存策略实现Respose的缓存。
  • 内部维护了队列线程池,可以轻松实现并发任务。
  • 拥有自动维护的socket连接池,减少握手次数。

除了上面的优点,其实还有很多,这里就不一一详述了。

大家都应该知道OkHttp执行网络请求有两种方式:

  • 1、同步网络请求--Call.execute();
  • 2.异步网络请求--Call.enqueue(Callback responseCallback)。

那么接下来就从这两个方面进行分析。

1.Call对象的真实面貌——RealCall

  • OkHttpClient#newCall():

      @Override 
      public Call newCall(Request request) {
          return new RealCall(this, request, false /* for web socket */);
      }
    

    从上述代码,我们发现Call的实现类原来是RealCall,并且创建RealCall对象时,会需要OkHttpClient、Request以及forWebSocket。

  • RealCall详细分析:

RealCall.png
  • RealCall构造方法
    构造方法中除了会保存之前的OkHttpClient、Request、boolean forWebSocket之外,还会创建RetryAndFollowUpIntercptor对象,这个对象主要作用是在网络请求是出现异常情况进行重新连接的,具体的分析放在后面。

  • execute()同步请求方法:
    既然是同步网络请求,那么就意味着不需要为它去执行分配线程的操作了。并且它应该会直接进入网络请求的操作。

          @Override public Response execute() throws IOException {
                ...
                // 通过Dispatcher管理网络请求——对于同步请求只是将其添加到runningSyncCalls队列中。
                client.dispatcher().executed(this);
                // 构建一堆的网络请求拦截器
                Response result = getResponseWithInterceptorChain();
                return result;
                ...
            }
    
  • getResponseWithInterceptorChain() 分析:

          Response getResponseWithInterceptorChain() throws IOException {
              // Build a full stack of interceptors.
              // 1.创建interceptors集合。
              List<Interceptor> interceptors = new ArrayList<>();
              // 2.添加用户设置的所有的interceptors。
              interceptors.addAll(client.interceptors());
              // 3.添加错误重连的Interceptor,此对象在构造方法中被创建。
              interceptors.add(retryAndFollowUpInterceptor);
              // 4.添加BridgeInterceptor拦截器,将用户设置的Request进行高度封装(请求头、关于响应体的解gzip)。
              interceptors.add(new BridgeInterceptor(client.cookieJar()));
              // 5.添加处理网络请求对应缓存的拦截器
              interceptors.add(new CacheInterceptor(client.internalCache()));
              // 6.添加进行Socket连接的拦截器
              interceptors.add(new ConnectInterceptor(client));
              // 7.添加用户定义的网络拦截器
              if (!forWebSocket) {
                interceptors.addAll(client.networkInterceptors());
              }
              // 8.添加Socket下与服务器进行数据读写操作的拦截器。
              interceptors.add(new CallServerInterceptor(forWebSocket));
          
              // 9.责任链模式中保存了下一拦截器的链条对象(递归执行的关键)。
              Interceptor.Chain chain = new RealInterceptorChain(
                  interceptors, null, null, null, 0, originalRequest);
              return chain.proceed(originalRequest);
          }
    

    构建一堆的Interceptor之后,执行RealInterceptorChain中的proceed()方法,开启整个网络请求责任链工作。

    RealInterceptorChain # proceed()源码分析:

          public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
                RealConnection connection) throws IOException {
              ...
              // Call the next interceptor in the chain.
              // 1.创建保存了下一个interceptor的链条对象。
              RealInterceptorChain next = new RealInterceptorChain(
                  interceptors, streamAllocation, httpCodec, connection, index + 1, request);
              // 2.执行当前当前interceptor的intercept()方法。
              Interceptor interceptor = interceptors.get(index);
              Response response = interceptor.intercept(next);
              ...
              return response;
            }
    
  • 再来分析enqueue()异步网络请求:

    既然是异步网络请求,那么肯定需要两个条件:
    1、网络请求的任务体;
    2、执行具体任务的工作线程。

          @Override 
          public void enqueue(Callback responseCallback) {
              ...
              client.dispatcher().enqueue(new AsyncCall(responseCallback));
              ...
          }
    
  • AsyncCall.execute()源码分析:

          protected void execute() {
            ...
            // 我们发现其实AsyncCall最后也是做了这件事情
            Response response = getResponseWithInterceptorChain();
            ...  
          }
    

2.管理网络请求任务的策略器——Dispatcher

Dispatcher主要的工作就是给异步网络请求任务(AsyncCall)分配工作线程并执行。


Dispatcher.png
  • Dispatcher.enqueue()源码分析:

      synchronized void enqueue(AsyncCall call) {
          // 1.检测正在执行的异步网络请求数是否小于maxRequests
          // &&相同主机的异步网络请求数是否小于maxRequestsPerHost
          if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            // 2.满足条件,添加到runningAsyncCalls队列并且执行。
            runningAsyncCalls.add(call);
            executorService().execute(call);
          } else {
            // 3.不满足条件,添加到readyAsyncCalls。
            readyAsyncCalls.add(call);
          }
       }
    
  • Dispatcher.executed()源码分析:

      synchronized void executed(RealCall call) {
          // 直接添加到runningSyncCalls队列
          runningSyncCalls.add(call);
       }
    

关于Okhttp的整体工作流程,基本上就是这个样子。至于Interceptor的源码分析,会在后续的文章中给出。最后把整体流程图贴出来:

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

推荐阅读更多精彩内容