天天看点

Android网络框架:OKHttp源码简单解析(一)

这是第一次解析源码并把它写出来,在之前,我一直以为只要会用别人的轮子就好,做出实际的效果就行,对看源码对自己的能力提升不以为然。后来偶然听到一句话:看别人的DEMO,你就可以会用轮子,但是要想用好轮子,还是得看源码。我觉得看源码有两个方面的好处:

1. 从本质上去理解所学框架的原理和流程;
 2. 可以看到软件开发里常见的思维方法和设计模式;这些在编程里都是相通的。
           

下面这篇文章,我们队OkHttp框架做一个简单的分析。

使用OkHttp访问网络,通用的做法就是下面四步:

步骤: 
 //1 创建okHttpClient对象
 //2 创建网络Request对象
 //3 创建与Request对应的返回Call对象
 //4 请求加入调度(直接执行)
           

在第四步中,访问网络有两种方式,一种是同步请求访问网络。一种是异步请求访问网络,我们先看看同步访问网络是怎么实现的。

同步访问网络实现源码解析:它是通过okHttpClient.newCall(re)创建一个Call对象。如下:

OkHttpClient okHttpClient =new OkHttpClient();
 Response response = okHttpClient.newCall(request).execute();
 Request request = new Request.Builder()
                .url("http://publicobject.com/helloworld.txt")
                .build();
 Call call=okHttpClient.newCall(request);
 Response response = call.execute();
 if(response.isSuccessful()){             Log.i("TAG","response.code()="+response.code());//200           Log.i("TAG","response.body().string()="+response.body().string());
        }
           

这里我们先看看Call对象到底是什么鬼?跟踪源码我们可以看到:

/**
   * Prepares the {@code request} to be executed at some point in the future.//准备一个在未来某个点会被执行的网络请求
   */
  @Override public Call newCall(Request request) {
    return new RealCall(this, request);
  }
           

可以看到真正起作用的是RealCall,并非我们看到的Call;我们再看看RealCall是什么?

final class RealCall implements Call {//RealCall是一个继承了Call接口的网络真正请求类,
  private final OkHttpClient client;
 ....
  protected RealCall(OkHttpClient client, Request originalRequest) {
    this.client = client;
    this.originalRequest = originalRequest;
  }
//真正执行execute的地方;
  @Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain(false);
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
      client.dispatcher().finished(this);
    }
  }
           

上面代码中,client.dispatcher().executed(this);是将当前网络请求,从OkHttpClient获得的Dispatcher然后把它加入到分发器里面的队列 executedCalls中,在完成的时候会remove掉,源码如下:

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

  synchronized void finished(Call call) {
    if (!runningSyncCalls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
  }
           

上面中的runningSyncCalls是指正在执行的同步任务队列,在OkHttp框架中对网络请求进行分发任务的是Dispatcher类,Dispatcher维护了如下变量,用于控制并发的请求

private int maxRequests = ;//最大并发请求数为64
  private int maxRequestsPerHost = ;//每个主机最大请求数为5
  private ExecutorService executorService;//线程池
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//维护准备执行的异步请求队列,双端队列缓存(用数组实现,可自动扩容,无大小限制);
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//正在执行的异步任务队列,包括已经取消但是并未finish的请求
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//正在执行的同步任务队列,包括已经取消但是并未finish的请求
           

Dispatcher: 分发者,也就是生产者(默认在主线程)

AsyncCall: 队列中需要处理的Runnable(包装了异步回调接口)

ExecutorService:消费者池(也就是线程池)

根据生产者消费者模型的模型理论,当入队(enqueue)请求时,如果满足(runningRequests<64 && runningRequestsPerHost<5),那么就直接把AsyncCall直接加到runningCalls的队列中,并在线程池中执行。如果消费者缓存满了,就放入readyAsyncCalls进行缓存等待。