OkHttp源码探索

来源:互联网 发布:mac mini怎么接显示器 编辑:程序博客网 时间:2024/05/17 03:44

之前一直在使用volley作为网络请求模块,但发现volley已经过时(源码还是很值得反复品味的),现在基本是Retrofit和OkHttp的天下了,于是把OkHttp拿来学习一下。
首先,看一个OkHttp的简单示例:

        Request.Builder builder = new Request.Builder().url(url).get();        Request okRequest = builder.build();        Call call = mOkHttpClient.newCall(okRequest);        call.enqueue(new Callback() {            @Override            public void onFailure(Call call, IOException e) {                e.printStackTrace();            }            @Override            public void onResponse(Call call, final Response response) throws IOException {                mHandler.post(new Runnable() {                    @Override                    public void run() {                        listener.onFetchFinished(response.body().string());                    }                });            }        });

该类框架都比较类似,创建一个请求及回调,然后加入请求队列,后面就由框架来进行任务调度,和结果回调。具体的细节,后面使用时再慢慢看,现在先探索一下OkHttp的主路线。

首先, 把请求加入到任务队列:

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

此方法就为避免单个请求多次添加,

// Dispatcher.java  synchronized void enqueue(AsyncCall call) {    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {      runningAsyncCalls.add(call);      executorService().execute(call);    } else {      readyAsyncCalls.add(call);    }  }  /** Returns the number of running calls that share a host with {@code call}. */  private int runningCallsForHost(AsyncCall call) {    int result = 0;    for (AsyncCall c : runningAsyncCalls) {      if (c.host().equals(call.host())) result++;    }    return result;  }  public synchronized ExecutorService executorService() {    if (executorService == null) {      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));    }    return executorService;  }

这个方法比较有意思,很明显有两个任务队列,一个是正在执行的队列(runningAsyncCalls)和一个等待队列(readyAsyncCalls);不仅限制了最大并发请求的数量,还对同一个域名的并发请求做了限制,当然单个应用可能请求的域名都是一样的,所以这个就得看实际情况了。
当前并发请求数量小于限制时,就把请求直接丢到线程池中执行;否则就把请求加入到等待队列。在代码中搜索readyAsyncCalls,看看等待队列中的任务是什么时候被执行的。看到下面的方法:

// Dispatcher.java  private void promoteCalls() {    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {      AsyncCall call = i.next();      if (runningCallsForHost(call) < maxRequestsPerHost) {        i.remove();        runningAsyncCalls.add(call);        executorService().execute(call);      }      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.    }  }

搜索这个方法是在哪里被调用的:
这里写图片描述
可以看到,在任务结束、改变最大并发请求和改变域名最大并发请求时会调用该方法,这些肯定是在意料之中的。

接下来就跟踪一下任务是如何执行的,我们就从队列中的请求任务入手。
AsyncCall的继承关系如下:

abstract class NamedRunnable implements Runnablefinal class AsyncCall extends NamedRunnable
/** * Runnable implementation which always sets its thread name. */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();}

任务的执行放在了execute()中执行。我们来看看AsyncCall的execute()方法:

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

可以看出来,主要的内容是在getResponseWithInterceptorChain()里面,因为这个方法直接返回了Response,也就是请求结果。

  private 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 (!retryAndFollowUpInterceptor.isForWebSocket()) {      interceptors.addAll(client.networkInterceptors());    }    interceptors.add(new CallServerInterceptor(        retryAndFollowUpInterceptor.isForWebSocket()));    Interceptor.Chain chain = new RealInterceptorChain(        interceptors, null, null, null, 0, originalRequest);    return chain.proceed(originalRequest);  }

这个方法的主要内容是得到一个列表interceptors,从字面上看,这个列表是用于请求拦截。通过断点跟踪后发现,这个列表确实重要,并且顺序也是非常的重要,从下面的代码慢慢分析。

//RealInterceptorChain.java  @Override public Response proceed(Request request) throws IOException {    return proceed(request, streamAllocation, httpStream, connection);  }  public Response proceed(Request request, StreamAllocation streamAllocation, HttpStream httpStream, Connection connection) throws IOException {    if (index >= interceptors.size()) throw new AssertionError();    、、、    // Call the next interceptor in the chain.     //注意这里的index+1了    RealInterceptorChain next = new RealInterceptorChain(        interceptors, streamAllocation, httpStream, connection, index + 1, request);    Interceptor interceptor = interceptors.get(index);    Response response = interceptor.intercept(next);    、、、    return response;  }

省略一些异常情况处理后,很清晰的看到,从interceptors列表的第一项(index初始化为0)开始执行interceptor.intercept(next),并且参数是RealInterceptorChain链条的下一个节点。

  @Override public Response intercept(Chain chain) throws IOException {    Request request = chain.request();    streamAllocation = new StreamAllocation(        client.connectionPool(), createAddress(request.url()));    int followUpCount = 0;    Response priorResponse = null;    while (true) {      if (canceled) {        streamAllocation.release();        throw new IOException("Canceled");      }      Response response = null;      boolean releaseConnection = true;      try {        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);        releaseConnection = false;      } catch (RouteException e) {        、、、      } finally {        // We're throwing an unchecked exception. Release any resources.        if (releaseConnection) {          streamAllocation.streamFailed(null);          streamAllocation.release();        }      }      // 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();      }      Request followUp = followUpRequest(response);      if (followUp == null) {        if (!forWebSocket) {          streamAllocation.release();        }        return response;      }      closeQuietly(response.body());      、、、      request = followUp;      priorResponse = response;    } // end while  }

这个方法的主体是一个死循环,有两种方式退出循环:一个是throw new IOException(“Canceled”),另一个是return response。也就是说,当请求被取消,或者得到结果了就结束返回了。再看看response是什么时候被赋值的:

response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);

又回到刚才的proceed(request)里面了,而proceed(request)里面用到了index+1,也就是这时会使用interceptors列表中的第二个interceptor来进行intercept(next),酱紫看来,这是一个递归的过程。只有当进行到chain中的某一个节点,能够得response而不是继续向下传递时,递归开始反转退栈。
跟踪代码看到interceptor的顺序为RetryAndFollowUpInterceptor——>BridgeInterceptor——>CacheInterceptor——>ConnectInterceptor——>CallServerInterceptor. 至于为何需要这么几层,还不是很清楚,还需要继续学习。

至此,okhttp的请求主流程基本介绍完毕,后面还有大量的东西需要去学习。

0 0