OkHttp源码解析

参考:

源码版本:3.14.9
源码地址:https://github.com/square/okhttp

此篇文章主要讲解OkHttp的请求过程,以及源码中一些比较难懂的知识点的使用,并把这些知识点抽取成一个Demo,可以比较深刻的去理解源码的执行过程。Demo 包含 :

  1. 源码中 Dispatcher类的调度过程
  2. 以及RealCall类中getResponseWithInterceptorChain()方法中责任链模式的调度过程。
image.png

简单调用

       String url = "www.baidu.com";
       OkHttpClient client = new OkHttpClient.Builder().build();
       Request request = new Request.Builder().url(url).build();

       Call call = client.newCall(request);
       call.enqueue(new Callback() {
           @Override
           public void onFailure(Call call, IOException e) {

           }

           @Override
           public void onResponse(Call call, Response response) throws IOException {

           }
       });

源码解析流程

此处不讲解OkHttpClient的初始化过程,以及Request的构建过程,用到的是 Builder设计模式,直接从enqueue()方法开始解析。

1. 请求发送过程

  • 注意:call.execute()是同步方法,call.enqueue()是异步方法,一般调用call.enqueue()

点击查看call.enqueue()方法,进入到 RealCall类中的call.enqueue()方法,如下:

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.callStart();
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

这个 RealCall里面执行的方法,首先会把这个Call加入到Dispatcher这个调度器里面,进入Dispatcher类中的 enqueue() 方法,如下:

void enqueue(AsyncCall call) {
  synchronized (this) {
    //将此次请求加入准备请求队列中
    readyAsyncCalls.add(call);

    // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
    // the same host.
    if (!call.get().forWebSocket) {
      AsyncCall existingCall = findExistingCallWithHost(call.host());
      if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
    }
  }
  promoteAndExecute();
}

Dispatcher类中包含了三个队列,一个线程池:
为了管理所有的请求,Dispatcher采用了队列+生产+消费的模式。

  • 为同步执行提供了runningSyncCalls来管理所有的同步请求;
  • 为异步执行提供了runningAsyncCalls和readyAsyncCalls来管理所有的异步请求。其中readyAsyncCalls是在当前可用资源不足时,用于缓存请求的。
  • ExecutorService线程池

查看 promoteAndExecute()方法,

  private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
    //调用 AsyncCall 类 executeOn的方法,在executeOn方法中开启线程池
      asyncCall.executeOn(executorService());//1
    }

    return isRunning;
  }

在1处会调用AsyncCall中的executeOn()方法

    void executeOn(ExecutorService executorService) {
      assert (!Thread.holdsLock(client.dispatcher()));
      boolean success = false;
      try {
        executorService.execute(this);//开启线程池
        success = true;
      } catch (RejectedExecutionException e) {
        InterruptedIOException ioException = new InterruptedIOException("executor rejected");
        ioException.initCause(e);
        transmitter.noMoreExchanges(ioException);
        responseCallback.onFailure(RealCall.this, ioException);
      } finally {
        if (!success) {
          client.dispatcher().finished(this); // This call is no longer running!
        }
      }
    }

此时会调用AsyncCall中的execute()方法,

    @Override protected void execute() {
      boolean signalledCallback = false;
      transmitter.timeoutEnter();
      try {
        //核心函数
        Response response = getResponseWithInterceptorChain();
        signalledCallback = true;
        responseCallback.onResponse(RealCall.this, response);
      } catch (IOException e) {
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } catch (Throwable t) {
        cancel();
        if (!signalledCallback) {
          IOException canceledException = new IOException("canceled due to " + t);
          canceledException.addSuppressed(t);
          responseCallback.onFailure(RealCall.this, canceledException);
        }
        throw t;
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }

getResponseWithInterceptorChain()方法就是执行拦截器链,直到返回 Response。通过责任链模式循环调用所有拦截器,每个拦截器可以根据Request和Reponse前后执行相应的逻辑。

  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(new RetryAndFollowUpInterceptor(client));
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, transmitter, null, 0,
        originalRequest, this, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    boolean calledNoMoreExchanges = false;
    try {
      Response response = chain.proceed(originalRequest);
      if (transmitter.isCanceled()) {
        closeQuietly(response);
        throw new IOException("Canceled");
      }
      return response;
    } catch (IOException e) {
      calledNoMoreExchanges = true;
      throw transmitter.noMoreExchanges(e);
    } finally {
      if (!calledNoMoreExchanges) {
        transmitter.noMoreExchanges(null);
      }
    }
  }

这里把自定义的拦截器,还有内置的必要的拦截器都添加到一个List里面了,最后用List的拦截器创建一个拦截器的链—— RealInterceptorChain。而这个RealInterceptorChain 其实是实现了Interceptor 里面的一个Chain 接口。

我们先了解一下RealInterceptorChain 这个类是如何进行工作的。

首先RealInterceptorChain,是实现了Interceptor.Chain 接口的一个类,这个接口有是三个方法,后面会介绍到,其中一个就是前面调用的proceed方法。

public Response proceed(Request request, Transmitter transmitter, @Nullable Exchange exchange)
      throws IOException {
      ...
    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(interceptors, transmitter, exchange,
        index + 1, request, call, connectTimeout, readTimeout, writeTimeout);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

省略了前后的一些代码,发现,其实这里又新建一个 RealInterceptorChain,然后调用了这个拦截器的 intercept 的方法。那么在每个拦截器里面做了什么,这个拦截的方法又是做了什么呢?

首先看一下拦截器这个接口:

public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;
    ...
  }
}

很明显,每个实现Interceptor的拦截器。都会实现了intercept,这样一个拦截的方法。而传入的就是一个Chain。符合前面RealInterceptorChain的调用。

以一个拦截器的代码为例:BridgeInterceptor,这个类实现的intercept方法,而这个方法里面,除了那些前置或后续的操作之外,在中间你会发现 下面一句代码:

Response networkResponse = chain.proceed(requestBuilder.build());

又通过传进来的Chain ,重新调用了proceed的方法。好吧,到这里应该可以了解到这些拦截器,是如何一个个调用被调用的了,原理都是通过了中间的一个RealInterceptorChain,一个接一个的向下调用。然后得到的结果又一个一个向上传。

总结一下,这些拦截器的调用:

刚开始getResponseWithInterceptorChain的最后开始调用了 chain.proceed,然后在RealInterceptorChain的里面,又新建一个 RealInterceptorChain,并且调用当前的拦截器的 intercept,并且把新建的Chain传递进去,然后拦截器的 intercept方法里面,除了一些自身的操作外,又调用Chain的proceed方法。就这样一直调用新建Chian, 一直调用到最后一个拦截器。

这里的调用其实是一个迭代,递归的结构。请求做了前一步的处理才能到下一级,一级级下去,到最后真正发出去请求,相对做了拦截。而得到结果,只有当最后一个拦截器完全了请求,拿到结果之后,才会把结果往上一级,上一级拿到结果,做了相应的处理之后,再到上一级,这就是对结果的拦截处理。

Demo

结构:

image.png

调用:

    public static void main(String[] args) throws IOException {
        //Dispatcher类的调度过程
        new Dispatcher().enqueue(new AsyncCall("22","33"));

        //demo02调用RealCall类中getResponseWithInterceptorChain()
        //方法中责任链模式的调度过程
        Request request = new Request();
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.add(new RetryAndFollowUpInterceptor());
        interceptors.add(new BridgeInterceptor());
        interceptors.add(new CacheInterceptor());
        interceptors.add(new ConnectInterceptor());

        Interceptor.Chain chain = new RealInterceptorChain(interceptors,0,request);

        Response response = chain.proceed(request);
    }

运行结果:

---Dispatcher---enqueue-
--AsyncCall执行execute方法--
1.失败重试拦截器
2.请求和响应转换拦截器
3.缓存拦截器
4.与服务器建立连接拦截器
 RealInterceptorChain 第 3 次执行
 RealInterceptorChain 第 2 次执行
 RealInterceptorChain 第 1 次执行
 RealInterceptorChain 第 0 次执行

Demo挺简单的,可以自行下载:
https://github.com/Ityang/OKHttpDemo

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