OKHttp源码解读

99封情书 提交于 2019-12-29 12:27:15

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

OKHttp源码解读

本次分享主要目标

  • 1.大概流程解读,同步流程,异步流程
  • 2.Interceptor简读

一、OKHttp的简单使用

    private String get(String url) throws IOException {

        Request request = new Request.Builder()
                .url(url)
                .build();
        Response response = client.newCall(request).execute();

        return response.body().string();
    }
    

 
private void useOkHttp() throws IOException {
        //1.创建OkHttpClient对象
        OkHttpClient okhttp = new OkHttpClient.Builder()
                              .readTimeout(15, TimeUnit.SECONDS)
                              .writeTimeout(15, TimeUnit.SECONDS)
                              .build();
        //2.创建请求对象Request
        Request request = new Request.Builder()
                              .url("url")
                              .addHeader("key","value")                     
                              .get()//设置请求方式是get
                              .build();
 
 
        Call call = okhttp.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ResponseBody body = response.body();
                String string = body.string();
            }
        });
    }

以上是OKHttp很简单的两个用法,一个是同步的,一个是异步的。

二、回顾Retrofit源码与OKhttp的初始化

  • Rerofit的初始化
  • ServiceMethon的初始化
  • 调用OKHttp
  • 装饰器 adapt的使用

OKHttp与Retrofit都是同一帮人写的,所以写法是类似的。

Builder重度使用,OKHttpCient与Retrofit的作用类似。

public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {


  final Dispatcher dispatcher;
  final @Nullable Proxy proxy;
  final List<protocol> protocols;
  final List<connectionspec> connectionSpecs;
  final List<interceptor> interceptors;
  final List<interceptor> networkInterceptors;
  final EventListener.Factory eventListenerFactory;
  final ProxySelector proxySelector;
  final CookieJar cookieJar;
  final @Nullable Cache cache;
  final @Nullable InternalCache internalCache;
  final SocketFactory socketFactory;
  final SSLSocketFactory sslSocketFactory;
  final CertificateChainCleaner certificateChainCleaner;
  final HostnameVerifier hostnameVerifier;
  final CertificatePinner certificatePinner;
  final Authenticator proxyAuthenticator;
  final Authenticator authenticator;
  final ConnectionPool connectionPool;
  final Dns dns;
  final boolean followSslRedirects;
  final boolean followRedirects;
  final boolean retryOnConnectionFailure;
  final int callTimeout;
  final int connectTimeout;
  final int readTimeout;
  final int writeTimeout;
  final int pingInterval;

请求过程中用到的各种设置都可以从这里拿到,Retrofit的next方法是不是还有印象,后面的各种方法都是有retrofit的参数传进去,获取初始化参数。

三、执行过程简析

 Call call = okhttp.newCall(request);
 call.enqueue
 call.execute

这个是先拿到一个request,然后excute或者enqueue

1.我们先分析同步的代码流程


@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    transmitter.timeoutEnter();
    transmitter.callStart();
    try {
      client.dispatcher().executed(this);
      return getResponseWithInterceptorChain();
    } finally {
      client.dispatcher().finished(this);
    }
  }

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

  client.dispatcher().executed(this);
  
  /** Used by {@code Call#execute} to signal it is in-flight. */
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
  
  /** Used by {@code Call#execute} to signal completion. */
  void finished(RealCall call) {
    finished(runningSyncCalls, call);
  }
  

我们总结总结同步的流程

  1. RealCall.excute
  2. client.dispatcher().executed
  3. RealCall.getResponseWithInterceptorChain
  4. client.dispatcher().finished;

同步执行的时候,runningSyncCalls 进行了添加删除用作记录,目前没有数量控制,也没有线程调度什么。

看到这个Call,有没有想到Retrofit里的Call?

  • Retrofit Call接口,OKHttpCall
  • OKHttp里也是有个Call接口,RealCall,作为他的实现类,还有一个AsyncCall,但是这个不是Call的实现类,复制RealCall做异步使用的。

2.异步流程解析

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

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.promoteAndExecute

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

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

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

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

    for (int i = 0, size = executableCalls.size(); i &lt; size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

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->RealCall.getResponseWithInterceptorChain()->AsyncCall.responseCallback

  @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);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

我们总结总结异步的流程

> 1. RealCall.enqueue > 2. Dispatcher.enqueue > 3. Dispatcher.promoteAndExecute > 4. AsyncCall.executeOn > 5. AsyncCall.execute > 6. RealCall.getResponseWithInterceptorChain > 7. AsyncCall.responseCallback

从流程上来看的话,不管是同步异步实际请求都是调用

RealCall.getResponseWithInterceptorChain

只是异步请求Dispatcher 做了线程管理,这块内容下次再做分享

三、Interceptor简析

getResponseWithInterceptorChain的实现

 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<interceptor> interceptors = new ArrayList&lt;&gt;();
    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);
      }
    }
  }

这里的核心是

  1. interceptors.add
  2. Response response = chain.proceed(originalRequest);

添加拦截器,执行拦截器。


拦截器的执行采用的是递归原理,理解了递归原理的话,拦截器这块就没什么内容了,就是解读各个拦截器的实现。


我们看下一个简单的递归例子

function Recursion(depth) {
    console.log('抱着');
    if (!depth) {
        console.log('我的小鲤鱼')
    } else {
        Recursion(--depth);  // 递归调用
    }
    console.log('的我');
}

console.log('吓得我抱起了');
Recursion(2)

日志打印出来

  1. 吓得我抱起了
  2. 抱着
  3. 抱着
  4. 抱着
  5. 我的小鲤鱼
  6. 的我
  7. 的我
  8. 的我

递归的特点

  • 递归结束的条件
  • 执行顺序,前置的按照顺序从上往下执行,结果是从下往上

这里是不是可以理解,每个interceptor都能拿到网络请求后的response结果了,因为最后一个拿到的response,然后从下往上每个都能拿到了。

avatar

Interceptor的原理明白了吗?

也就是开始被执行的类request的信息是做少的,越执行reqeust信息越多,response,则是最后被执行的的是最原始的,然后被修改添加的越多。

我们再来学习下Interceptor的1个知识点

  1. interceptor与networkInterceptor的区别
  2. RetryAndFollowUpInterceptor代码解读

通过 addInterceptor() 方法添加的拦截器是放在最前面的。而通过 addNetworkInterceptor() 方法添加的网络拦截器,则是在非 WebSocket 请求时,添加在 ConnectInterceptor 和 CallServerInterceptor 之间的。

addInterceptor(应用拦截器):

  1. 不需要担心中间过程的响应,如重定向和重试.
  2. 总是只调用一次,即使HTTP响应是从缓存中获取.
  3. 观察应用程序的初衷. 不关心OkHttp注入的头信息如: If-None-Match.
  4. 允许短路而不调用 Chain.proceed(),即中止调用.
  5. 允许重试,使 Chain.proceed()调用多次 > +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ addNetworkInterceptor(网络拦截器):
  6. 能够操作中间过程的响应,如重定向和重试.
  7. 当网络短路而返回缓存响应时不被调用.
  8. 只观察在网络上传输的数据.
  9. 携带请求来访问连接.

RetryAndFollowUpInterceptor

  @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Transmitter transmitter = realChain.transmitter();

    int followUpCount = 0;
    Response priorResponse = null;
    while (true) {
      transmitter.prepareToConnect(request);

      if (transmitter.isCanceled()) {
        throw new IOException("Canceled");
      }

      Response response;
      boolean success = false;
      try {
        response = realChain.proceed(request, transmitter, null);
        success = true;
      } catch (RouteException e) {
        // The attempt to connect via a route failed. The request will not have been sent.
        if (!recover(e.getLastConnectException(), transmitter, false, request)) {
          throw e.getFirstConnectException();
        }
        continue;
      } catch (IOException e) {
        // An attempt to communicate with a server failed. The request may have been sent.
        boolean requestSendStarted = !(e instanceof ConnectionShutdownException);
        if (!recover(e, transmitter, requestSendStarted, request)) throw e;
        continue;
      } finally {
        // The network call threw an exception. Release any resources.
        if (!success) {
          transmitter.exchangeDoneDueToException();
        }
      }

      // Attach the prior response if it exists. Such responses never have a body.
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

      Exchange exchange = Internal.instance.exchange(response);
      Route route = exchange != null ? exchange.connection().route() : null;
      Request followUp = followUpRequest(response, route);

      if (followUp == null) {
        if (exchange != null &amp;&amp; exchange.isDuplex()) {
          transmitter.timeoutEarlyExit();
        }
        return response;
      }

      RequestBody followUpBody = followUp.body();
      if (followUpBody != null &amp;&amp; followUpBody.isOneShot()) {
        return response;
      }

      closeQuietly(response.body());
      if (transmitter.hasExchange()) {
        exchange.detachWithViolence();
      }

      if (++followUpCount &gt; MAX_FOLLOW_UPS) {
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      request = followUp;
      priorResponse = response;
    }
  }

这里有个比较关关键的一点

  while (true) {
      transmitter.prepareToConnect(request);

      if (transmitter.isCanceled()) {
        throw new IOException("Canceled");
      }

      Response response;
      boolean success = false;
      try {
        response = realChain.proceed(request, transmitter, null);
        

while(true) 这里是跟其他interceptor不一样的地方,对于重定向大家有没有了解?

退出while的条件

     if (followUp == null) {
        if (exchange != null &amp;&amp; exchange.isDuplex()) {
          transmitter.timeoutEarlyExit();
        }
        return response;
      }

      RequestBody followUpBody = followUp.body();
      if (followUpBody != null &amp;&amp; followUpBody.isOneShot()) {
        return response;
      }
      
      if (++followUpCount &gt; MAX_FOLLOW_UPS) {
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }
      

一个是返回后没有再重定向了,另外一个则是重定向次数超过了阈值

注意点

重定向执行的时候,是没有再调用Interceptor,但是netInterceptor还是被调用的。

本次分享到此结束,下次详解,dispatch与其他interceptor </interceptor></asynccall></asynccall></interceptor></interceptor></connectionspec></protocol>

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!