欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android之OkHttp框架学习:Dispatcher和RealCall

程序员文章站 2022-07-03 08:31:45
个人感觉,Dispatcher和RealCall算是OkHttp中两个很重要且比较好理解的部分了。RealCall继承于Call,主要是用于执行我们的请求,当我们调用client....

个人感觉,Dispatcher和RealCall算是OkHttp中两个很重要且比较好理解的部分了。RealCall继承于Call,主要是用于执行我们的请求,当我们调用client.newCall(request)的时候就会生成一个RealCall实例,我们用它进行同步或异步请求。Dispatcher中主要是对我们的所有请求进行管理,方便我们执行一些类似于cancelAll()这种取消所有请求的操作。

第一次写源码分析,希望自己能够解释清楚吧...

一 RealCall

Call接口

RealCall实现了Call接口,首先先看下Call接口,主要是对各种方法进行了定义。代码很简单:

public interface Call {
    //返回初始化这个Call的请求
    Request request();
    //同步执行
    Response execute() throws IOException;
    //异步执行
    void enqueue(Callback responseCallback);
    //取消执行
    void cancel();
    //判断是否执行
    boolean isExecuted();
    //判断是否取消
    boolean isCanceled();
    //Call工厂
    interface Factory {
        Call newCall(Request request);
    }
}

RealCall类中实现了Call接口,并在其中包含了这样几个实例变量:

//OkHttpClient实例
private final OkHttpClient client;

//拦截器,从故障中恢复并根据需要进行重定向
private final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;

// Guarded by this.
private boolean executed;

//原始请求
Request originalRequest;

其中RetryAndFollowUpInterceptor是一个拦截器,主要是在出现故障的时候使用,具体怎么用...等我看到那一块源码后再说...

这些可能接下来的代码中会包含到所以先写在这。接下来看一下RealCall中是怎么实现Call接口中的方法。

execute()

首先我们还是直接贴一下源码:

@Override public Response execute() throws IOException {
    synchronized (this) {
      //如果已经在执行,则抛出异常
 if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    try {
 //将我们的这个实例添加到dispatcher的runningSyncCalls队列中
  client.dispatcher().executed(this);
 //获取网络请求的结果
  Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } finally {
  //从dispatcher的runningSyncCall中删除本实例
      client.dispatcher().finished(this);
    }
  }

代码不难理解,其中之所以要把实例添加到dispatcher的队列中,是因为这样方便OkHttp对现在所有的网络请求进行同一管理,finished()方式就是在队列中找到该实例并删除掉。这个之前也说了,具体的话后面源码会分析到。

enqueue()

同样也是直接上源码:

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
//生成Runnable实例,添加到dispatcher中异步执行
 client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

这个代码也不难,因为是异步执行,所以我们要生成相应的Runnable实例,放在dispatcher的线程池中执行。这里的AsyncCall是在RealCall中定义的一个内部类,是一个继承了NameRunnable类的实例,而Runnable类是一个实现了Runnable接口的抽象类,里面主要是添加了一个name的变量,用于对当前线程命名...??没搞懂为什么要给线程命名,这个先不管了。总之AsyncCall是一个实现了Runnable接口的类就是了。

AsyncCall

关于AsyncCall,就看下源码:

final class AsyncCall extends NamedRunnable {
    
//自己定义的Callback
private final Callback responseCallback;

    private AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl().toString());
      this.responseCallback = responseCallback;
    }
 
 //返回请求的目标host,在dispatcher中用到
 //dispatcher中要求对同一个主机的请求数不能超过某个值(默认5)
String host() {
      return originalRequest.url().host();
    }

    Request request() {
      return originalRequest;
    }
 //该RealCall实例
 RealCall get() {
      return RealCall.this;
    }
 //重写Runnable的execute()方法
    @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
 //请求失败,调用Callback中的onFailure()方法
 responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
 //请求成功,调用Callback中的onResponse()方法
  responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        if (signalledCallback) {
 //用于防止多次调用Callback中的方法
 Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
 //在dispatcher中删除掉该AsyncCall实例
  client.dispatcher().finished(this);
      }
    }
  }

这里主要说一下这个retryAndFollowUpInterceptor.isCanceled(),这个主要是返回retryAndFollowUpInterceptor的cancel变量的状态,这个状态默认是false的,只有我们手动cancel了retryAndFollowUpInterceptor的时候才会变为true,所以这里大概可以认为是我们的请求出现故障,且取消了重定向的执行的时候,这时候我们的网络请求是失败的。

cancel()

@Override public void cancel() {
    retryAndFollowUpInterceptor.cancel();
  }

getResponseWithInterceptorChain()

RealCall中还有另外一个重要的方法就是getResponseWithInterceptorChain()方法,这个的话...得等我以后来补充说明了...

private Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List 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);
  }

总体而言,RealCall是我们真正发起网络请求的核心,利用其可以实现同步请求,异步请求,取消请求,以及查看请求状态。

二 Dispatcher

Dispatcher的中文意思是调度器,顾名思义这是用于对各种Call的调度,实际也是如此,在Dispathcer里面就是管理记录着所有的RealCall。在Dispatcher中,利用Synchronized来对几乎所有的方法进行修饰,从而保证线程安全性。因为这里面大多数的方法主要是进行一些队列数据的存储和变量值的修改,并没有什么耗时的操作,所以用Synchronized来修饰,简单实用。先来看下里面包含的实例变量。

  //线程池中同时存在的最大请求数量
  private int maxRequests = 64;
  //对同一个host的最大请求数量
  private int maxRequestsPerHost = 5;
  //空闲线程,当同步队列+异步队列==0的时候调用ideaCallback.run()方法
  private Runnable idleCallback;

  //线程池,懒加载
  private ExecutorService executorService;

  //尚未执行的异步请求队列,
  private final Deque readyAsyncCalls = new ArrayDeque<>();

  //执行中的异步请求队列,包括已经调用cancel()但是还未finished的
  private final Deque runningAsyncCalls = new ArrayDeque<>();

  //执行中的同步请求队列,包括已经调用cancel()但是还未finished的
  private final Deque runningSyncCalls = new ArrayDeque<>();

  //懒加载,通过定义为synchronized方法保证线程安全
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

在这里面,idleCallback的作用是在Dispatcher中所有线程都执行结束之后执行,这个应该是用于我们进行自定义,暂时没看到那里有调用到其setter方法。

对于maxRequests和maxRequestsPerHost的setter和getter就不讲了,不过在这两者里面的setter方法中,当重新设置值之后,都会调用一个promoteCalls()方法,将等待中的请求添加到runningAysncCalls队列和线程池中。看一下它的源码:

  private void promoteCalls() {
    //队列中线程数量超过最大值,不用增加
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    //没有在等待中的请求
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
 //对等待请求队列进行遍历
    for (Iterator i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
  //如果与call的目标主机host相同的请求数小于maxRequestsPerHost的数量,就可以添加到队列中,并将其添加到请求中执行
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);
        executorService().execute(call);
      }
  //达到最大值,停止添加
      if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
    }
  }

在promoteCall()中,runningCallsForHost()是用来计算队列中同一host的请求个数的,这个方法在之后添加异步请求到runningAysncCalls和线程池的方法中还会继续看到。其中call.host()我们在AsyncCall中已经提到过。源码如下:

  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

接下来看一下几个将Call添加到队列中的方法,这些在RealCall中其实已经看到过一次了。

executed(RealCall call)

将同步请求添加到runningSyncCalls中

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

enqueue(AsyncCall call)

添加异步请求,判断要添加到哪个队列中

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

finished

当同步/异步网络请求结束之后,会将该call从对应的队列中删除。

  //异步请求结束
 void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
 //同步请求结束
  void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

在这里,两种请求都调用了同一个方法,看一下该方法的源码:

 private  void finished(Deque calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
 //移除失败,抛出异常
  if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      //当finish的是异步请求时promoteCall为true,调用promoteCalls(),将其他异步请求放入队列和线程池中
  if (promoteCalls) promoteCalls();
 //计算当前正在运行的Call数量
  runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }
 //当同步队列+异步队列==0且idleCallback不为空时调用idleCallback
    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

在源码中,除了在相应队列中移除掉该call实例之外,对于异步请求的情况还会往队列和线程池中添加新的call请求,并且,对网络中已经没有call请求在执行的情况进行了考虑。

cancelAll

对所有的call请求都调用cancel方法,不多解释了。

  public synchronized void cancelAll() {
    //等待中的异步请求
for (AsyncCall call : readyAsyncCalls) {
      call.get().cancel();
    }
 //执行中的异步请求
    for (AsyncCall call : runningAsyncCalls) {
      call.get().cancel();
    }
 //执行中的同步请求
    for (RealCall call : runningSyncCalls) {
      call.cancel();
    }
  }

主要的方法就是这些了。总的来说,Dispatcher实现的是对Call的记录和管理。

这部分大概就讲这么多了,这也是okhttp框架中比较容易懂的部分了...