OKHttp原始碼學習同步請求和非同步請求(二)

wangyy發表於2018-04-26

 

 

 

OKHttp get

 1 private void doGet(String method, String s) throws IOException {
 2         String url = urlAddress + method + "?sex=" + s;
 3         Request request = new Request.Builder().url(url).get().build();
 4         Response respone = okHttpClient.newCall(request).execute();
 5         if (respone.isSuccessful()) {
 6             Log.d("test", respone.body().string());
 7         } else {
 8             Log.d("test", "get failed");
 9         }
10 
11     }

在get請求,用到了 Request Response okHttpClient,分別學習一下這三個類

Request:用於構建一個HTTP請求,使用了建造這模式.如果它們的{@link #body}為null或者它本身是不可變的,那麼這個類的例項是不可變的。

Response:用於構建一個HTTP響應。 這個類的例項不是不可變的:響應體是一次性的值,可能只消耗一次然後關閉。 所有其他屬性都是不可變的。 <p>這個類實現{@link Closeable}。 關閉它只是關閉其響應主體。

okHttpClient:{@linkplain Call calls}的工廠,可用於傳送HTTP請求並讀取其響應。

我們建立一個OKHttpClient時,完成了如下初始化的工作:

1  public OkHttpClient() {
2     this(new Builder());
3   }

 

 1 OkHttpClient(Builder builder) {
 2     this.dispatcher = builder.dispatcher;
 3     this.proxy = builder.proxy;
 4     this.protocols = builder.protocols;
 5     this.connectionSpecs = builder.connectionSpecs;
 6     this.interceptors = Util.immutableList(builder.interceptors);
 7     this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
 8     this.eventListenerFactory = builder.eventListenerFactory;
 9     this.proxySelector = builder.proxySelector;
10     this.cookieJar = builder.cookieJar;
11     this.cache = builder.cache;
12     this.internalCache = builder.internalCache;
13     this.socketFactory = builder.socketFactory;
14 
15     boolean isTLS = false;
16     for (ConnectionSpec spec : connectionSpecs) {
17       isTLS = isTLS || spec.isTls();
18     }
19 
20     if (builder.sslSocketFactory != null || !isTLS) {
21       this.sslSocketFactory = builder.sslSocketFactory;
22       this.certificateChainCleaner = builder.certificateChainCleaner;
23     } else {
24       X509TrustManager trustManager = systemDefaultTrustManager();
25       this.sslSocketFactory = systemDefaultSslSocketFactory(trustManager);
26       this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
27     }
28 
29     if (sslSocketFactory != null) {
30       Platform.get().configureSslSocketFactory(sslSocketFactory);
31     }
32 
33     this.hostnameVerifier = builder.hostnameVerifier;
34     this.certificatePinner = builder.certificatePinner.withCertificateChainCleaner(
35         certificateChainCleaner);
36     this.proxyAuthenticator = builder.proxyAuthenticator;
37     this.authenticator = builder.authenticator;
38     this.connectionPool = builder.connectionPool;
39     this.dns = builder.dns;
40     this.followSslRedirects = builder.followSslRedirects;
41     this.followRedirects = builder.followRedirects;
42     this.retryOnConnectionFailure = builder.retryOnConnectionFailure;
43     this.connectTimeout = builder.connectTimeout;
44     this.readTimeout = builder.readTimeout;
45     this.writeTimeout = builder.writeTimeout;
46     this.pingInterval = builder.pingInterval;
47 
48     if (interceptors.contains(null)) {
49       throw new IllegalStateException("Null interceptor: " + interceptors);
50     }
51     if (networkInterceptors.contains(null)) {
52       throw new IllegalStateException("Null network interceptor: " + networkInterceptors);
53     }
54   }

new Builder()的時候做了什麼呢?

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

所以到這裡明白了,new OKHttpClient()時初始化的引數其實是從Builder中獲取到的。當然這些都是預設值,我們也可以重新設定一些值比如

okHttpClient.Builder() .readTimeout(30, TimeUnit.SECONDS) .build();

 接下來再回到doGet方法的第四行看  OkHttpClient.newCall()返回一個RealCall物件並呼叫RealCall.enqueue()方法,最後會進入Dispacher.enqueue()方法中,這裡會將RealCall物件放入執行緒池中排程執行。

1  /**
2    * Prepares the {@code request} to be executed at some point in the future.
3    */
4   @Override public Call newCall(Request request) {
5     return RealCall.newRealCall(this, request, false /* for web socket */);
6   }

 

  //RealCall 類中
1
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { 2 // Safely publish the Call instance to the EventListener. 3 RealCall call = new RealCall(client, originalRequest, forWebSocket); 4 call.eventListener = client.eventListenerFactory().create(call); 5 return call; 6 }
//RealCall

1 @Override public Response execute() throws IOException {
 2     synchronized (this) {
 3       if (executed) throw new IllegalStateException("Already Executed");
 4       executed = true;
 5     }
 6     captureCallStackTrace();
 7     eventListener.callStart(this);
 8     try {
 9       client.dispatcher().executed(this);
10       Response result = getResponseWithInterceptorChain();
11       if (result == null) throw new IOException("Canceled");
12       return result;
13     } catch (IOException e) {
14       eventListener.callFailed(this, e);
15       throw e;
16     } finally {
17       client.dispatcher().finished(this);
18     }
19   }

第9行

client.dispatcher().executed(this);
Dispatcher是什麼呢? dispatcher是new Builder的時候new的一個Dispatcher 物件
接下來接著看
Dispatcher:何時執行非同步請求的策略。


//Dispatcher類中
1
/** Used by {@code Call#execute} to signal it is in-flight. */ 2 synchronized void executed(RealCall call) { 3 runningSyncCalls.add(call); 4 }

到這裡只是將人物新增到runningSyncCalls這個佇列當中,那什麼時候會執行這個任務呢?回到RealCall的execute方法,

dispatcher的executed方法執行結束之後會接著第10行  Response result = getResponseWithInterceptorChain();方法獲取響應,最後呼叫Dispatcher的finished方法
1  /** Used by {@code Call#execute} to signal completion. */
2   void finished(RealCall call) {
3     finished(runningSyncCalls, call, false);
4   }

 

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

在這個finish方法中,runningSyncCalls佇列做為引數calls被傳入。

首先是從佇列中移除請求,如果不能移除,則丟擲異常;

然後呼叫runningCallsCount統計目前還在執行的請求,最後,如果正在執行的請求數為0表示Dispatcher中沒有可執行的請求了,進入Idle狀態,此時如果idleCallback不為null,則呼叫其run方法。下面是runningCallsCount()方法的實現:

1 public synchronized int runningCallsCount() {
2     return runningAsyncCalls.size() + runningSyncCalls.size();
3   }

至此,同步請求的執行流程分析完成。接下來學習非同步請求的流程。

 

 1  private void doPost(String method, String s) {
 2         FormBody formBody = new FormBody.Builder().add("sex", s).build();
 3         RequestBody body =  RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{\"sex\",\""+s+"\"}");
 4         Request request = new Request.Builder().url(urlAddress + method).post(body).build();
 5         okHttpClient.newCall(request).enqueue(new Callback() {
 6             @Override
 7             public void onResponse(Call arg0, Response arg1) throws IOException {
 8                 Log.d("test", arg1.body().string());
 9             }
10             @Override
11             public void onFailure(Call arg0, IOException arg1) {
12                 Log.d("test", "post failed");
13             }
14         });
15     }

非同步請求會呼叫qnqueue方法:

1 @Override public void enqueue(Callback responseCallback) {
2     synchronized (this) {
3       if (executed) throw new IllegalStateException("Already Executed");
4       executed = true;
5     }
6     captureCallStackTrace();
7     eventListener.callStart(this);
8     client.dispatcher().enqueue(new AsyncCall(responseCallback));
9   }
在這裡有一個新的類AsyncCall,AsyncCall是RealCall的一個內部類並且繼承NamedRunnable,NamedRunnable實現了Runnable介面,並且在run方法中呼叫了需要子類複寫的execute

接下來看dispatcher的enqueue的方法:

1 synchronized void enqueue(AsyncCall call) {
2     if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
3       runningAsyncCalls.add(call);
4       executorService().execute(call);
5     } else {
6       readyAsyncCalls.add(call);
7     }
8   }
這裡先判斷正在執行的非同步請求的數量 如果小於maxRequests  並且與該請求相同的主機數量小於maxRequestsPerHost,也就是說符合放入runningAsyncCalls佇列的要求,那麼放入佇列,然後將AsyncCall交給執行緒池;如果不符合,那麼就放入到readyAsyncCalls佇列中。

 當執行緒池執行AsyncCall任務時,它的execute方法會被呼叫,下面看AsyncCall的execute方法
 1 @Override protected void execute() {
 2       boolean signalledCallback = false;
 3       try {
 4         Response response = getResponseWithInterceptorChain();
 5         if (retryAndFollowUpInterceptor.isCanceled()) {
 6           signalledCallback = true;
 7           responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
 8         } else {
 9           signalledCallback = true;
10           responseCallback.onResponse(RealCall.this, response);
11         }
12       } catch (IOException e) {
13         if (signalledCallback) {
14           // Do not signal the callback twice!
15           Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
16         } else {
17           eventListener.callFailed(RealCall.this, e);
18           responseCallback.onFailure(RealCall.this, e);
19         }
20       } finally {
21         client.dispatcher().finished(this);
22       }
23     }

在execute方法中,首先是呼叫getResponseWithInterceptorChain()方法獲取響應,然後獲取成功後,就呼叫回撥的onReponse方法,如果失敗,就呼叫回撥的onFailure方法。最後,呼叫Dispatcher的finished方法。 

然後看dispatcher的finished方法:

1 /** Used by {@code AsyncCall#run} to signal completion. */
2   void finished(AsyncCall call) {
3     finished(runningAsyncCalls, call, true);
4   }
 1 private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
 2     int runningCallsCount;
 3     Runnable idleCallback;
 4     synchronized (this) {
 5       if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
 6       if (promoteCalls) promoteCalls();
 7       runningCallsCount = runningCallsCount();
 8       idleCallback = this.idleCallback;
 9     }
10 
11     if (runningCallsCount == 0 && idleCallback != null) {
12       idleCallback.run();
13     }
14   }

與同步呼叫不同的是最後一個引數是true所以會執行promoteCalls方法。

 1 private void promoteCalls() {
 2     if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
 3     if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
 4 
//遍歷等待佇列 5 for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) { 6 AsyncCall call = i.next(); 7 //如果當前請求的主機處理的請求數量小於最大數量就將該請求從等待佇列移除並新增到runningAsyncCalls佇列中,然後交給執行緒池 8 if (runningCallsForHost(call) < maxRequestsPerHost) { 9 i.remove(); 10 runningAsyncCalls.add(call); 11 executorService().execute(call); 12 } 13 14 if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity. 15 } 16 }
 至此,非同步請求的過程學習完畢,不管是同步請求還是非同步請求,最終都會呼叫getResponseWithInterceptorChain()方法進行具體的網路請求,接下來學習一下具體的網路請求  getResponseWithInterceptorChain()
有用的集合類
Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

參考一篇比較好的講解Dispatcher的文章

https://www.cnblogs.com/laughingQing/p/7296486.html

 

相關文章