OkHttp 源码简要分析二 (Dispatcher 请求机制详解)

mac2024-10-10  53

get 方式网络请求

private void okhttp() {         OkHttpClient mOkHttpClient = new OkHttpClient.Builder()                 .build();         Request request = new Request.Builder()                 .url("http://publicobject.com/helloworld.txt")                 .build();         Call call = mOkHttpClient.newCall(request);         call.enqueue(new Callback()         {             @Override             public void onFailure(Call call, IOException e) {             }             @Override             public void onResponse(Call call, Response response) throws IOException {                 String string = response.body().string();                 Log.e("onResponse", string);             }         });     }

这个是一个简单的网络请求,Call call = mOkHttpClient.newCall(request) 这行代码,返回对象是 RealCall ,它是 Call 的实现类

  @Override    public Call newCall(Request request) {     return new RealCall(this, request);   }

下面是 RealCall 的简化代码,看一下

final class RealCall implements Call {     private final OkHttpClient client;     private final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;     private boolean executed;     Request originalRequest;     protected RealCall(OkHttpClient client, Request originalRequest) {         this.client = client;         this.originalRequest = originalRequest;         this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client);     }     @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();             if (result == null) throw new IOException("Canceled");             return result;         } finally {             client.dispatcher().finished(this);         }     }     @Override public void enqueue(Callback responseCallback) {         synchronized (this) {             if (executed) throw new IllegalStateException("Already Executed");             executed = true;         }         client.dispatcher().enqueue(new AsyncCall(responseCallback));     }     ...     final class AsyncCall extends NamedRunnable {         private final Callback responseCallback;         private AsyncCall(Callback responseCallback) {             super("OkHttp %s", redactedUrl().toString());             this.responseCallback = responseCallback;         }         @Override protected void execute() {             ...         }     } }

继续看例子中的请求 call.enqueue() 这个方法,对应的是 RealCall 中的方法,注意,此时 client.dispatcher().enqueue(new AsyncCall(responseCallback)),这个方法注意两点,AsyncCall 和 Dispatcher, AsyncCall 是个包装类,NamedRunnable 实现了 Runnable 接口,抽象出 execute() 方法;AsyncCall 是个线程调度管理的类,这里是异步请求

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

首先会比较正在请求的网络个数有没有超过 maxRequests 默认 64 的值,runningCallsForHost(call) 这个方法是此次请求几口库的域名与正在请求的接口中使用相同域名接口的个数,如果它小于 maxRequestsPerHost 默认 5 的值,则可以请求,使用 executorService() 创建线程池来执行 Runnable,这里是创建了一个不限制线程个数的线程池;如果不满足if判断条件,则把此次请求添加到等待队列中。如果线程池执行了 Runnable ,重新回到 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) {                     Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);                 } else {                     responseCallback.onFailure(RealCall.this, e);                 }             } finally {                 client.dispatcher().finished(this);             }         }

Response response = getResponseWithInterceptorChain() 这个是获取服务端返回的报文,这个方法中步骤比较多,使用了责任链模式,这个下一篇再讲,这里知道是返回报文即可。这里先判断接口请求是否被取消,如果取消,回调 onFailure() 回调,否则是 onResponse() 回调;如果在 getResponseWithInterceptorChain() 过程中出现了异常,则会执行 catch 中的回调,也是 onFailure() 回调;由于这里使用了 try...catch 方法,所以最终会执行 finally 中的代码,这里掉用了 Dispatcher 的 finish() 方法

  void finished(AsyncCall call) {     finished(runningAsyncCalls, call, true);   }   private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {     int runningCallsCount;     Runnable idleCallback;     synchronized (this) {       if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");       if (promoteCalls) promoteCalls();       runningCallsCount = runningCallsCount();       idleCallback = this.idleCallback;     }     if (runningCallsCount == 0 && idleCallback != null) {       idleCallback.run();     }   }

这个方法中,现实使用 calls.remove(call) ,这个是把当前对象从集合中移除,如果失败,抛异常;成功的话,执行 promoteCalls() 方法;然后重新获取正在请求接口的个数,如果没有了正在请求的接口,并且 idleCallback 回调不为空,则执行 idleCallback 回调。现在看看 promoteCalls() 方法

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

首先判断是否达到了最大个数以及是否还存在等待请求的接口,如果满足条件,则遍历等待请求的集合,从前往后获取,只要当前请求url的域名没有超过5个的限制,则从等待集合中移除该请求,添加到请求集合中,并使用线程池开启线程来执行该请求,到此就一条新的请求开始了;由于是遍历,则会牵涉到循环,一旦正在请求的接口达到了64的上限,则跳出for循环。

同步请求有点特殊,需要我们自己开启线程,这个会把当前请求添加到正在请求的集合中,会影响异步请求 64 和 5 的限制,但对同步本身没有限制,因为它没有这两个值的限制。

  @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();       if (result == null) throw new IOException("Canceled");       return result;     } finally {       client.dispatcher().finished(this);     }   }

同步执行后,最终会执行 finally 方法,调用 Dispatcher 的 finished() 方法,检查是否还有请求需要执行。

最新回复(0)