Android—OkHttp同步异步请求过程源码分析与拦截器
OkHttp同步請求步驟:
注意:發送請求后,就會進入阻塞狀態,直到收到響應。
OkHttp異步請求步驟:
源碼分析:
注意:下面的源代碼段可能來自不同一個類文件,只是將他們放一起,容易觀察,主要放一些關鍵代碼,其他會有...代替。
1.關于創建OkHttpClient對象,下面源碼:
public OkHttpClient() {this(new Builder());}public Builder() {dispatcher = new Dispatcher(); protocols = DEFAULT_PROTOCOLS;connectionSpecs = DEFAULT_CONNECTION_SPECS; ......connectionPool = new ConnectionPool();.....}可以看到OkHttp采用了建造者模式,在Builder()里面封裝各種需要的屬性,關鍵的主要有dispatcher分發器,connectionSpecs決定是異步還是同步,connectionPool 連接池。連接池具體到連接攔截器才會使用到,每個連接都會放入連接池中,由它進行管理。?
總結:新建Client對象時,新建了一個分發器和一個連接池,還有一些屬性的初始化。
2.創建Request對象時,源碼:
public Builder() {this.method = "GET";this.headers = new Headers.Builder();}Request(Builder builder) {this.url = builder.url;this.method = builder.method;this.headers = builder.headers.build();this.body = builder.body;this.tags = Util.immutableMap(builder.tags);}可以看到Request里面也有一個Builder類,Builder構造函數默認請求方式為Get,還有對請求頭部的封裝。
總結:新建一個Request對象里面主要封裝了請求路徑,頭部信息等。
3.用newCall(request)將Reuqest對象封裝成Call對象時,源碼:
@Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */);}//可以看到newCall方法里面是調用了RealCall類的newRealCall方法,下面到RealCall類里看看。static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) { // 調用RealCall的構造函數RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call;}//下面是RealCall類構造函數private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {this.client = client;this.originalRequest = originalRequest;this.forWebSocket = forWebSocket;this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);......}總結:newCall方法實際生成RealCall對象,對象里面包含了Client客戶對象和Request的請求對象,還新建了一個RetryAndFollowUpInterceptor 重定向攔截器。
4.Call對象調用的execute()同步請求方法,源碼:
@Override public Response execute() throws IOException {..... //開啟事件監聽eventListener.callStart(this); try { //分發器用executed方法將Call對象添加進同步運行隊列client.dispatcher().executed(this); //結果是從攔截器鏈方法中獲取的Response result = getResponseWithInterceptorChain(); ......} finally { //finish方法里將Call對象從Calls隊列中移出client.dispatcher().finished(this); } }//下面進到client.dispatcher().executed(this)的excuted方法里面synchronized void executed(RealCall call) { //runningSyncCalls是正在運行的同步隊列runningSyncCalls.add(call); }總結:excute()同步申請方法,分發器將Call對象添加到同步運行隊列。請求數據從Response result = getResponseWithInterceptorChain();? 中獲取。
5.enqueue異步請求方法,源碼:
@Override public void enqueue(Callback responseCallback) { //判斷是否請求過這個Call對象synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;} //異常檢測captureCallStackTrace(); //事件監聽eventListener.callStart(this); //調用分發器的enqueue方法,分發器在client創建時新建的。client.dispatcher().enqueue(new AsyncCall(responseCallback));}可以看到enqueue方法里面又調用了分發器的enqueue方法,在enqueue方法里新建了一個AsyncCall對象,
AsyncCall對象傳入我們上一層傳入enqueue方法的CallBack對象。
接下來看看上面的AsyncCall類是什么東西。
final class AsyncCall extends NamedRunnable {private final Callback responseCallback;AsyncCall(Callback responseCallback) {super("OkHttp %s", redactedUrl());this.responseCallback = responseCallback;}........... }看到AsyncCall繼承自NamedRunnable,再來看看NamedRunnable是什么東西
public abstract class NamedRunnable implements Runnable {.....@Override public final void run() {String oldName = Thread.currentThread().getName();Thread.currentThread().setName(name);try {execute();} finally {Thread.currentThread().setName(oldName);}}..... }可以看到NamedRunnable實現了Runnable接口,里面最核心的就是在run方法里面運行了execute()方法,這個方法的具體實現其實跟同步請求execute方法一樣,在AsyncCall類里,和同步請求最后的execute()是同一個方法。
@Override protected void execute() {.......Response response = getResponseWithInterceptorChain(); .....}我把大部分代碼都省了,最重要的就上面那句,跟同步請求一樣,最后結果也是經過一系列攔截器的方法后的數據。
那么同步跟異步有什么區別呢?
異步傳入enqueue方法的CallBack的對象實現了Runnable接口,讓它在子線程中運行。
還有,接下來回到開頭看看client.dispatcher().enqueue(new AsyncCall(responseCallback));這句,分發器類里的變量和它的enqueue方法(剛剛看的是AsyncCall類)。
public final class Dispatcher {//默認的最大并發請求量 private int maxRequests = 64;//單個host支持的最大并發量private int maxRequestsPerHost = 5; .........//異步等待隊列private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//異步運行隊列private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//同步運行隊列private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();...........//計算隊列內請求數量的方法,如果異步請求滿足不超過64,5的條件則進行請求操作。//有的版本OkHttp是通過promoteAndExecute()進行條件判斷,原理差不多synchronized void enqueue(AsyncCall call) {if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {//把Call對象添加進runningAsyncCalls異步進行隊列runningAsyncCalls.add(call);//創建線程池并執行Call請求executorService().execute(call);} else {readyAsyncCalls.add(call);}}...... }分發器對Request類型進行判斷,把Call對象添加進readyAsyncCalls異步等待隊列或runningAsyncCalls,而在同步請求時分發器是把Call對象直接添加到runningSyncCalls同步運行隊列。異步請求最后開啟線程池獲取數據。
總結:enqueue方法傳入CallBack對象,CallBack對象被封裝為AsyncCall,AsyncCall內部實現了Runnable接口,分發器進行判斷,如果符合條件就把AsyncCall傳入了異步進行對列,開啟線程池在子線程獲取數據。否則添加進異步等待隊列。
readyAsyncCalls異步等待隊列的請求什么時候能運行呢?
我們已經知道異步跟同步請求通過分發器分發隊列,但是最后都要經過AsyncCall類的execute()方法來獲取數據,execute()方法最后finally里面運行client.dispatcher().finished(this);方法,我們進去finished方法看看。
//異步請求的finished方法 void finished(AsyncCall call) {finished(runningAsyncCalls, call, true);} //同步請求的finished方法void finished(RealCall call) {finished(runningSyncCalls, call, false);} //具體的finished方法private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {int runningCallsCount;Runnable idleCallback;synchronized (this) { //將實現的Call對象從隊列中移出if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); //注意promoteCalls是第三個參數,既如果是異步請求才會運行該方法,重新整理隊列。if (promoteCalls) promoteCalls();runningCallsCount = runningCallsCount();idleCallback = this.idleCallback;}if (runningCallsCount == 0 && idleCallback != null) {idleCallback.run();}}進入promoteCalls()方法
private void promoteCalls() {if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote. //循環到隊列最后一個元素,call為最后一個請求for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall call = i.next(); //如果符合條件就把call從等待隊列移除加入運行隊列。if (runningCallsForHost(call) < maxRequestsPerHost) {i.remove();runningAsyncCalls.add(call);executorService().execute(call);}if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.}}總結:在readyAsyncCalls隊列中的請求會在異步請求的finished方法里進行判斷,如果符合條件則進入runningAsyncCalls。
Dispatcher分發器:
從上面的Dispatcher類可以看出分發器有3個隊列,異步有兩個隊列是運用了消費者模式,類中還有excuted,enqueue,finished方法,Dispatcher主要作用就是根據Request類型將Call對象調入不同隊列,最后用finished將完成的請求移除隊列并把等待的請求調進運行隊列。
ExecutorService線程池:?
下面進到executorService().execute(call)看看,
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;}第一個參數代表核心線程數量,為0就代表線程空閑之后不會被保留,會被銷毀;如果大于0,即使本地任務執行完畢,核心線程也不會被銷毀。
第二個參數是int整數的最大值,他表示的是線程池中可以容納的最大線程數量。但是確實它得滿足64跟5的條件。
第三個keepAliveTime,當我們的線程池中線程數量大于核心線程數量時,空閑線程需要等待60秒的時間才會被終止。
OkHttp攔截器
我們已經知道了請求最后都是從Response result = getResponseWithInterceptorChain()這句中獲取的數據。
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 (!forWebSocket) {interceptors.addAll(client.networkInterceptors());}interceptors.add(new CallServerInterceptor(forWebSocket));Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,originalRequest, this, eventListener, client.connectTimeoutMillis(),client.readTimeoutMillis(), client.writeTimeoutMillis());return chain.proceed(originalRequest);}總歸創建了6個攔截器
全部攔截器的基本流程:
RetryAndFollowUpInterceptor 重定向攔截器
負責失敗重連的攔截器。
BridgeInterceptor? 橋攔截器
該攔截器是鏈接客戶端代碼和網絡代碼的橋梁,它首先將客戶端構建的Request對象信息構建成真正的網絡請求;然后發起網絡請求,最后就是講服務器返回的消息封裝成一個Response對象。
CacheInterceptor 緩存攔截器
該攔截器用于處理緩存的功能,主要取得緩存 response 返回并刷新緩存。
為什么需要緩存 Response?
- 客戶端緩存就是為了下次請求時節省請求時間,可以更快的展示數據。
- OKHTTP 支持緩存的功能
ConnectInterceptor? 連接攔截器
該攔截器的功能就是負責與服務器建立 Socket 連接,并且創建了一個 HttpCodec它包括通向服務器的輸入流和輸出流。
NetworkInterceptors 網絡攔截器
- 允許像重定向和重試一樣操作中間響應。
- 網絡發生短路時不調用緩存響應。
- 在數據被傳遞到網絡時觀察數據。
- 有權獲得裝載請求的連接。
CallServerInterceptor 調用服務攔截器
該攔截器的功能使用 HttpCodec與服務器進行數據的讀寫操作的。
OKHTTP的責任鏈模式優點:
- 可以降低邏輯的耦合,相互獨立的邏輯寫到自己的攔截器中,也無需關注其它攔截器所做的事情。
- 擴展性強,可以添加新的攔截器。
當然它也有缺點:
- 因為調用鏈路長,而且存在嵌套,遇到問題排查其它比較麻煩。
ConnectionPool
OkHttp中所有的連接(RealConnection)都是通過ConnectionPool來管理。
總結
以上是生活随笔為你收集整理的Android—OkHttp同步异步请求过程源码分析与拦截器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 病从口入 这样吃小心癌症找上门
- 下一篇: Java、Android—零碎难记笔试考