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