拆轮子系列---OkHttp3源码分析

作者: a49f87ef5d4f | 来源:发表于2018-08-08 20:49 被阅读1次

    0.前言

    okhttp是一个有square开发网络请求框架,square出品,必属精品。以前也看过这个框架的源码,但是时间久了,好多都记不清了,悔当初没有好好记录下来,今天有时间,又看了一遍,把思路分析记录下来。

    1.基本使用

    从最简单的Get请求分析,okhttp请求分为同步请求和异步请求:

    同步请求:

     private final OkHttpClient client = new OkHttpClient();
    
      public void run() throws Exception {
        Request request = new Request.Builder()
            .url("https://publicobject.com/helloworld.txt")
            .build();
    
        try (Response response = client.newCall(request).execute()) {
          if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    
          Headers responseHeaders = response.headers();
          for (int i = 0; i < responseHeaders.size(); i++) {
            System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
          }
    
          System.out.println(response.body().string());
        }
      }
    

    异步请求:

    private final OkHttpClient client = new OkHttpClient();
    
      public void run() throws Exception {
        Request request = new Request.Builder()
            .url("http://publicobject.com/helloworld.txt")
            .build();
    
        client.newCall(request).enqueue(new Callback() {
          @Override public void onFailure(Call call, IOException e) {
            e.printStackTrace();
          }
    
          @Override public void onResponse(Call call, Response response) throws IOException {
            try (ResponseBody responseBody = response.body()) {
              if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    
              Headers responseHeaders = response.headers();
              for (int i = 0, size = responseHeaders.size(); i < size; i++) {
                System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
              }
    
              System.out.println(responseBody.string());
            }
          }
        });
      }
    

    代码分析就从这最基本的开始。

    2.基本流程

    先看同步请求,首先创建了一个Request,Request采用了建造者模式,可以很好的避免构造函数参数的数量爆棚,Request代表了一个http请求,包含了请求方法,请求头,和请求体。

    接着是调用了OkHttpClient的newCall(request)

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

    这个方法返回一个RealCall,接着看RealCall的newRealCall这个静态方法

    static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
        // Safely publish the Call instance to the EventListener.
        RealCall call = new RealCall(client, originalRequest, forWebSocket);
        call.eventListener = client.eventListenerFactory().create(call);
        return call;
      }
    

    可以看到这里是真正RealCall的创建,里面包含了okhttpclient还有request,同时为这个RealCall注册了一个事件监听,这个eventListenerFactory就是在构建okhttp的时候传入的

    new OkHttpClient.Builder()
          .eventListenerFactory(PrintingEventListener.FACTORY)
          .build();
    

    网络请求的结果被封装在Response里,通过调用RealCall的execute方法,得到最终结果,所以可想而知,真正的网络请求也是在这一个过程进行的。接着就看一下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);
        }
      }
    

    这个方法先判断是否已经执行该Call了,如果executed=true,那么就抛出异常,如果为false,就调用client.dispatcher().executed(this);Dispatcher根据注释来看,是用来决定何时执行异步策略的,看一下它的executed方法

    synchronized void executed(RealCall call) {
        runningSyncCalls.add(call);
      }
    

    runningSyncCalls是一个双向列表,这个方法就是单纯的将该RealCall添加进列表,所以网络请求不是在这里发起的,那么回到RealCall的execute方法,在dispatcher的executed方法调用后,接着调用了
    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);
      }
    

    这个方法的前办部分构建了一个拦截器列表,包括我们在构建okhttpclient时添的interceptor和networkInterceptor,接着又生成了一个RealInterceptorChain,调用它的proceed方法返回最终的Response。
    这个proceed方法,就是我们要关注的重点:

    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
          RealConnection connection) throws IOException {
        if (index >= interceptors.size()) throw new AssertionError();
    
        calls++;
    
        // If we already have a stream, confirm that the incoming request will use it.
        if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must retain the same host and port");
        }
    
        // If we already have a stream, confirm that this is the only call to chain.proceed().
        if (this.httpCodec != null && calls > 1) {
          throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
              + " must call proceed() exactly once");
        }
    
        // Call the next interceptor in the chain.
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
            connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
            writeTimeout);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
    
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
          throw new IllegalStateException("network interceptor " + interceptor
              + " must call proceed() exactly once");
        }
    
        // Confirm that the intercepted response isn't null.
        if (response == null) {
          throw new NullPointerException("interceptor " + interceptor + " returned null");
        }
    
        if (response.body() == null) {
          throw new IllegalStateException(
              "interceptor " + interceptor + " returned a response with no body");
        }
    
        return response;
      }
    

    注重看这两行

     RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
            connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
            writeTimeout);
        Interceptor interceptor = interceptors.get(index);
        Response response = interceptor.intercept(next);
    

    可以看到每次调用proceed方法,都会生成一个新的RealInterceptorChain,其他参数都一样,只是index增加了1,接着就是根据index在拦截器列表找到一个拦截器,然后调用拦截器的intercept方法,并将刚才生成的RealInterceptorChain传入,所以现在大家应该清楚了吧,在自定义拦截器时复写的intercept方法传入Chain,其实是RealInterceptorChain,在复写拦截器的时候,经常会写chain.processed(request),之后就会重复之前的过程,如果你不调用chain.processed(request)方法,你会发现没有网络请求发出(没有http缓存的情况下),这是因为整个网络请求过程采用的是责任链模式,不调用这个方法,就相当于断掉了这个链条的传递,因为最终的请求的发出和Response的构建是在CallServerInterceptor里完成的,是链条的最后一环,这里注意的是resposne是由下向上返回的,类似于递归。得到response后,我们再回到Realcall的execute方法,看最后有一个finally

     client.dispatcher().finished(this);
    

    调用dispatcher的finished方法

        private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
        int runningCallsCount;
        Runnable idleCallback;
        synchronized (this) {
          if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
          if (promoteCalls) promoteCalls();
          runningCallsCount = runningCallsCount();
          idleCallback = this.idleCallback;
        }
    
        if (runningCallsCount == 0 && idleCallback != null) {
          idleCallback.run();
        }
      }
    

    将call从dispatcher的列表中移除。并重置RunningCount的数量,当runningCallsCount的数量为0时,会回调idleCallback。
    接下来看一下异步的get请求, client.newCall(request).enqueue,同样是生成了一个RealCall,只不过这次调用的是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));
      }
    

    给方法还是调用了Dispatcher的enqueue方法,,但是你发现这里生成了一个新对象AsyncCall。

    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);
          }
        }
      }
    

    它有一个父类NamedRunnable,先看他的父类

    public abstract class NamedRunnable implements Runnable {
      protected final String name;
    
      public NamedRunnable(String format, Object... args) {
        this.name = Util.format(format, args);
      }
    
      @Override public final void run() {
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
          execute();
        } finally {
          Thread.currentThread().setName(oldName);
        }
      }
    
      protected abstract void execute();
    }
    
    

    NamedRunnable是一个抽象类,并且实现了runnable接口,在run方法里调用了execute这个抽象方法。
    我们在回到AsyncCall,看他的execute方法

    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);
          }
    

    getResponseWithInterceptorChain(),熟悉吧,还是原来的配方,还是原来的味道。再看Dispatcher的enqueu方法

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

    很显然,现将这个call添加到一个双向列表,然后启用了一个线程池,调用了这个AsyncCall。最后也是调用dispatcher的finish方法。

    盗张图

    image

    3.拦截器

    从刚才分析的流程来看,拦截器是重点,okhttp允许我们自定义拦截器,也有一些默认的拦截器,下面分析一下这些拦截器的作用:

    RetroyAndFollowUpInterceptor:处理重定向和失败重连

    BridgeInterceptor:请求前添加一些http header,保存cookie,处理zip

    CacheInterceptor:处理http响应缓存

    ConnectInterceptor:建立与服务器的链接

    CallServerInterceptor:发送request并解析response

    这里只是简单说一下其作用,有时间单拿出一篇文章详细分析。

    image

    关注我的公众号

    相关文章

      网友评论

        本文标题:拆轮子系列---OkHttp3源码分析

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