美文网首页开发者联盟Android技术知识
okhttp 源码学习(二)基本流程

okhttp 源码学习(二)基本流程

作者: iyifei | 来源:发表于2018-03-01 18:32 被阅读14次

    本节将对okhttp中的关键的类和方法进行简单介绍,并梳理出执行的基本流程进行,让大家对源码有一个全局的认识。

    Class Call

    回顾我们上节中出现的Call对象,这个Call是什么呢?

    Call对象其实就是一个准备好执行网络请求的request,是对request进行了封装,支持取消。由于一个Call对象表示的是请求和响应流,因此同一个Call不能执行两次。

    Class Dispatcher

    在讲Dispatcher之前我们还是来回顾一下如何我们在执行同步或异步请求,分别是执行了Call的execute()和enqueue()方法。下面我们就来看一下这两个方法的源码。

    首先,我们来看一下execute():

    @Override public Response execute() throws IOException {
      synchronized (this) {
        if (executed) throw new IllegalStateException("Already Executed");
        executed = true;
      }
      captureCallStackTrace();
      eventListener.callStart(this);
      try {
        client.dispatcher().executed(this);
        Response result = getResponseWithInterceptorChain();
        if (result == null) throw new IOException("Canceled");
        return result;
      } catch (IOException e) {
        eventListener.callFailed(this, e);
        throw e;
      } finally {
        client.dispatcher().finished(this);
      }
    }
    

    其中client.dispatcher()返回就是Dispatcher对象,跟踪源码我们发现这个Dispatcher其实是client的成员变量。接下来我们再看看enqueue():

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

    这里同样使用了client.dispatcher(),看到这里大家就知道了,同步和异步请求其实都是通过这个Dispatcher来完成的。那么我们就来了解一下Dispatcher是什么?

    Dispatcher负责okhttp的任务调度,管理同步/异步的请求状态,并维护一个线程池,用于执行请求,是Okhttp的核心类。

    Method getResponseWithInterceptorChain()

    接下来我将带大家了解一下这个关键的方法,顾名思义,这个方法是用于获取请求的Response的。
    回到上面同步请求execute()方法中,我们可以发现该方法内部就是通过调用这个getResponseWithInterceptorChain()来获取请求的Response。其实异步请求enqueue()方法最终也是通过调用这个方法来获取请求的Response的。这里我带大家了解一下enqueue()方法如何调用这个方法的。
    回到enqueue()方法中,我们可以发现异步请求是通过client.dispatcher().enqueue(new AsyncCall(responseCallback))来实现的。我们先看一下enqueue()方法中参数new AsyncCall(responseCallback)的源码。

     final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;
    
        AsyncCall(Callback responseCallback) {
          super("OkHttp %s", redactedUrl());
          this.responseCallback = responseCallback;
        }
    
        String host() {
          return originalRequest.url().host();
        }
    
        Request request() {
          return originalRequest;
        }
    
        RealCall get() {
          return RealCall.this;
        }
    
        @Override protected void execute() {
          boolean signalledCallback = false;
          try {
            Response response = getResponseWithInterceptorChain();
            if (retryAndFollowUpInterceptor.isCanceled()) {
              signalledCallback = true;
              responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
              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 {
              eventListener.callFailed(RealCall.this, e);
              responseCallback.onFailure(RealCall.this, e);
            }
          } finally {
            client.dispatcher().finished(this);
          }
        }
      }
    

    我们发现这个AsyncCall内部类其实是一个Runnable。我们再来看一下Dispatcher的enqueue()方法的源码。

    synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
          runningAsyncCalls.add(call);
          executorService().execute(call);
        } else {
          readyAsyncCalls.add(call);
        }
      }
    

    我们发现executorService().execute(call)其实就是通过Dispatcher中的线程池来执行这个AsyncCall Runnable的。这样我们再回过头来看看这个AsyncCallexecute ()方法,其实就是在这里调用了getResponseWithInterceptorChain()来获取请求的Response。
    讲到这里,大家是不是开始对这个getResponseWithInterceptorChain()方法十分好奇了,这个方法是如何获取请求的Response。当然我们还是一起来看一下源码。

      Response getResponseWithInterceptorChain() throws IOException {
        // Build a full stack of interceptors.
        List<Interceptor> interceptors = new ArrayList<>();
        interceptors.addAll(client.interceptors());
        interceptors.add(retryAndFollowUpInterceptor);
        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, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());
    
        return chain.proceed(originalRequest);
      }
    

    这段代码大家看起来可能会很奇怪,这里怎么出现这么多interceptor,大伙也没看到请求,为啥这里就直接返回了response了。其实,这里采用了责任链模式,来实现最终的请求response。具体如何实现的我会在后面的文章中给大家一一说明,这里就不展开了说。

    Interceptor

    Interceptor也是okhttp的核心之一,它把实际的网络重试、重定向、缓存、透明压缩、网络连接等功能拆分到独立的interceptor中,再通过Interceptor.Chain将这些功能环环相扣起来,最终完美实现网络请求。
    我将在接下来的文章中讲述以上方法中的5个重要的拦截器interceptor:

    • RetryAndFollowUpInterceptor: 主要用于重试和重定向请求
    • CacheInterceptor: 处理缓存拦截器
    • BridgeInterceptor: 负责okhttp请求和响应对象与实际http协议中请求和响应对象之间的转换。同时处理cookies相关内容
    • ConnectionInterceptor: 负责建立连接和流对象
    • CallServerInterceptor: 负责完成最终的网络请求,发送请求和读取响应

    基本流程图

    本节最后放一张流程图,将以上几个重要的概念串联起来,便于大家从全局来认识一下okhttp。


    okhttp基本流程图

    在接下来的章节中,我将结合以上的基本流程图带着大家深入分析源码。
    上一节 okhttp 源码学习(一)基本用法
    下一节 okhttp 源码学习(三)Dispatcher 深入解析

    相关文章

      网友评论

        本文标题:okhttp 源码学习(二)基本流程

        本文链接:https://www.haomeiwen.com/subject/apcnxftx.html