转载

OkHttp源码解析

Okhhtp是square公司出品的一款非常优秀的网络框架。可以说,目前市面上大部分Android开发人员在网络请求事,都在使用这款优秀的框架。下面,我就结合源码来分析下这款网络框架内部的执行原理,做到知其然也之气所以然的目的。

使用

OkHttp的使用非常的简单,我下面写一个大概的使用方法提供给大家查看。

//创建okhttpclient对象
        OkHttpClient okHttpClient = new OkHttpClient.Builder().build();
        
        //创建请求,里面可以设置get、post请求和请求地址和请求头等一系列操作
        Request request = new Request.Builder()
                .url("www.baidu.com")
                .build();

        //获取到一个call,这其实是一个线程请求任务,由其发起真正的网络请求
        Call call = okHttpClient.newCall(request);

        //同步请求
        try {
            call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //TODO 异步请求失败的操作
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //TODO 同步请求成功的操作
            }
        });

复制代码

可以看到,OkHttp的使用并不复杂,按照上面的一套流程下来就可以执行一次请求。由于这篇文章重点在于分析源码,所以对于OkHttp的使用并不做过多的描述。

下面开始进入分析源码状态。

Dispatcher分发器

dispatcher分发器可以说是OkHttp的重要部分之一,用一句话来说明他的用途,就是他是用来管理同步和异步请求的。

我们点击OkhttpClient的Builder()进去看下源码

public Builder() {
   1   dispatcher = new Dispatcher();
   2   protocols = DEFAULT_PROTOCOLS;
   3   connectionSpecs = DEFAULT_CONNECTION_SPECS;
   4   eventListenerFactory = EventListener.factory(EventListener.NONE);
   5   proxySelector = ProxySelector.getDefault();
   6   cookieJar = CookieJar.NO_COOKIES;
   7   socketFactory = SocketFactory.getDefault();
   8   hostnameVerifier = OkHostnameVerifier.INSTANCE;
   9   certificatePinner = CertificatePinner.DEFAULT;
   10   proxyAuthenticator = Authenticator.NONE;
   11   authenticator = Authenticator.NONE;
   12   connectionPool = new ConnectionPool();
   13   dns = Dns.SYSTEM;
   14   followSslRedirects = true;
   15   followRedirects = true;
   16   retryOnConnectionFailure = true;
   17   connectTimeout = 10_000;
   18   readTimeout = 10_000;
   19   writeTimeout = 10_000;
   20   pingInterval = 0;
    }

复制代码

可以看到在标记为1的地方有一个 dispatcher = new Dispatcher() ,原来分发器Dispatcher是在OkHttpClient的Builder中创建的。

然后点击Dispatcher进去看下Dispatcher类的内部代码

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  private Runnable idleCallback;

  /** 线程池 */
  private ExecutorService executorService;

  /** 准备执行任务的异步队列 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** 正在执行任务的异步队列 */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** 正在执行任务的同步队列*/
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
}

复制代码

删减了一些代码,我们只看他的内部的声明的一些变量。可以看到,在Dispatcher的内部其实声明了四个东西,三个任务队列,分为是:

准备执行任务的异步队列 readyAsyncCalls,

正在执行任务的异步队列 runningAsyncCalls,

正在执行任务的同步队列 runningSyncCalls;

和一个线程池 executorService;

那么Dispatcher是如何在执行同步和异步请求的呐?

同步请求

我们点击dispatcher.execute()的execute()方法进去查看源码

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

复制代码

1.这段代码首先用一个同步锁synchronize来修饰代码块,代表这段代码只能是串行状态并不能支持并行。

2.其次再进行一次if判断,判断任务是否在执行,如果正在执行的任务再次进行执行,那么便会抛出异常"Already Executed",可见OkHttp不能重复进行任务的执行。

3.紧接着调用了dispatcher.execute(this)来执行同步任务

4.接着调用一个连接器链getResponseWithInterceptorChain(),将遍历所有的拦截器,最后将结果result返回。(关于拦截器后面再讲,这里不做论述)

5.最后再次调用dispatcher.finished(this)来结束所有的请求。

那么这个流程我们重点分析第3步和第5步

我们点击第3步的execute(this)方法里面查看里面做了什么

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

复制代码

可以看到,这个方法仍然用synchronize来进行了修饰使得多线程也只能串行。之后将RealCall这个任务添加放入runningSyncCalls这个运行时同步任务队列中。

可以看出,execute()主要作用就是将任务添加到任务队列当中去,那么添加进去之后在哪里执行呐?这就要到第5步的dispatcher.finish(this)方法中去看了。

点击finish(this)进入里面查看代码

void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
   1  int runningCallsCount;
   2  Runnable idleCallback;
   3  synchronized (this) {
   4   if (!calls.remove(call)) throw new AssertionError("Call wasn't   in-flight!");
   5   if (promoteCalls) promoteCalls();
   6   runningCallsCount = runningCallsCount();
   7   idleCallback = this.idleCallback;
   8  }

   9  if (runningCallsCount == 0 && idleCallback != null) {
   10   idleCallback.run();
    }
  }

复制代码

具体我们需要看的是 finished(Deque calls, T call, boolean promoteCalls)

前面几行都只是做一些判断,我们重点看第5行的代码,里面有一个判断if(promoteCalls) promoteCalls();

如果promoteCalls为true,那么就去执行promoteCalls()方法。那么重点再promoteCalls()方法里面到底做了什么。我们再点击进去看看

private void promoteCalls() {
   1  if (runningAsyncCalls.size() >= maxRequests) return; 
   2  if (readyAsyncCalls.isEmpty()) return; 

   3  for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
   4   AsyncCall call = i.next();

   5   if (runningCallsForHost(call) < maxRequestsPerHost) {
   6     i.remove();
   7     runningAsyncCalls.add(call);
   8     executorService().execute(call);
   9   }

   10   if (runningAsyncCalls.size() >= maxRequests) return; 
    }
  }

复制代码

可以看到前面1,2两步都是在判断,判断运行时异步队列的最大值是否超过64,超过则return结束掉。如果异步准备队列是空(isEmpty()),那么也return结束掉。

第3步开始进行for循环,从等待队列中取出任务,添加到运行队列runningAsyncCalls中去(runningAsyncCalls.add(call);),然后再把call任务交给线程池去执行(executorService().execute(call)),从而完成一次请求。

那么来总结下同步请求:

通过dispatcher分发器,将一次网络请求call添加到运行时同步队列runningSynCall中去,然后再通过一个promoteCall()方法,把任务队列中的任务交给线程池去执行,从而完成一次请求。

异步请求

我们再来看下异步请求

//异步请求
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //TODO 异步请求失败的操作
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //TODO 同步请求成功的操作
            }
        });

复制代码

我们点击enqueue()方法进去看下代码

void enqueue(Callback responseCallback);
复制代码

看到只有一行代码,很简洁

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

复制代码

我们再次跳到 client.dispatcher().enqueue(new AsyncCall(responseCallback));的enqueue()方法中去看下

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

复制代码

可以看到,在这里面我们进行了一次判断,如果正在运行的异步队列里面的任务小于maxRequests并且runningCallsForHost(call) < maxRequestsPerHost则往runningAsyncCalls 中添加任务,然后交给线程池去执行,不然则往异步准备队列中添加任务,作为等待。

那么maxRequests和maxRequestsPerHost是啥?我们在分析Dispatcher的时候看到在这个类初始化了几个变量其中就有这两个变量

public final class Dispatcher {
  private int maxRequests = 64;
  private int maxRequestsPerHost = 5;
  
  }

复制代码

看到maxRequests为64,代表最大异步请求数量为64,最大的host数为5个,如果运行任务队列中超过了这两个数量,则不能把任务交给线程池去执行,而是直接放到readyAsyncCalls队列中去等待。

@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 {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }
  }
复制代码

可以看到这里仍然通过一次连接器链将结果返回;

然后有几次判断,判断是否是需要重定向重连等,如果retryAndFollowUpInterceptor.isCanceled()代表连接取消失败,则回调responseCallback.onFailure(RealCall.this, new IOException("Canceled"));如果成功则回调responseCallback.onResponse(RealCall.this, response);

最后又回到了finish(this)方法,这个之前说过了,就是把运行队列中的内容拿出来交给线程池去执行。

总结一下异步请求流程

异步请求

1.一开始会去执行dispatcher.enqueue(),里面会有两个判断,判断运行任务列队runningAsyncCalls中的任务总数是否超过64个以及网络请求的host是否超过5个,如果没超过这两个数,那么往运行任务队列中添加任务,然后把任务交给线程池去执行。如果超过了,则直接放到准备等待任务队列中readyAsyncCalls中去等待。

2.之后执行一次拦截器链getResponseIntecepterChain()把结果返回返回

3.在进行一次判断,这个任务是否需要重新连接,是否被取消retryAndFollowUpInterceptor.isCanceled(),如果取消了则证明这个任务执行失败了,则在线程中回调onFailure()方法,如果成功则回调onResponse()方法。

4.最后调用dispatcher.finish(this)方法,里面有一个promoteCalls()方法,用来调整队列中的任务。通过把刚才准备队列中(readyAsyncCalls)中的任务再次添加到异步运行任务队列中去,然后交给线程池去执行。

这就是一次异步请求的流程。

后面我们再来分析拦截器和连接池这两个OkHttp的重点功能

未完待续。。。

原文  https://juejin.im/post/5ec23b7e6fb9a04342682c70
正文到此结束
Loading...