日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android—OkHttp同步异步请求过程源码分析与拦截器

發(fā)布時間:2023/12/18 Android 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android—OkHttp同步异步请求过程源码分析与拦截器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

OkHttp同步請求步驟:

  • 創(chuàng)建OkHttpClient,客戶對象
  • 創(chuàng)建Request,請求主體,在請求主體設(shè)置請求的url,超時時間等
  • 用newCall(request)將Reuqest對象封裝成Call對象,然后用Call對象的execute()發(fā)起同步請求。
  • execute()返回的是Response對象。可以用execute().body().toString()得到請求所返回的主體內(nèi)容。
  • val client = OkHttpClient() val request = Request.Builder().url("https://www.baidu.com").build() val response = client.newCall(request).execute().body().toString()

    注意:發(fā)送請求后,就會進(jìn)入阻塞狀態(tài),直到收到響應(yīng)。

    OkHttp異步請求步驟:

  • 創(chuàng)建OkHttpClient,客戶對象
  • 創(chuàng)建Request,請求主體,在請求主體設(shè)置請求的url,超時時間等
  • 用newCall(request)將Reuqest對象封裝成Call對象,然后用Call對象的enqueue()發(fā)起異步請求。
  • enqueue(object: Callback{重寫onFailure、onResponse方法}) 在onResponse方法中獲取申請數(shù)據(jù)內(nèi)容。
  • val client = OkHttpClient() val request = Request.Builder().url("https://www.baidu.com").build() val response = client.newCall(request).enqueue(object: Callback {override fun onFailure(call: Call, e: IOException) {TODO("Not yet implemented")}override fun onResponse(call: Call, response: Response) {response.body().toString()} })

    源碼分析:

    注意:下面的源代碼段可能來自不同一個類文件,只是將他們放一起,容易觀察,主要放一些關(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個攔截器

    全部攔截器的基本流程:

  • 在發(fā)起請求前對request進(jìn)行處理。
  • 調(diào)用chain.proceed()方法,獲取下一個攔截器的response。
  • 對reponse進(jìn)行處理,返回給上一個攔截器。
  • RetryAndFollowUpInterceptor 重定向攔截器

    負(fù)責(zé)失敗重連的攔截器。

  • 創(chuàng)建StreamAllocation對象,但是沒有使用,它的使用是在ConnectInterceptor。它負(fù)責(zé)為一次“請求”尋找“連接”并建立“流”。Connection是建立在Socket之上的物理通信信道,而Stream則是代表邏輯的流,如果有多個stream(即多個 Request) 都是連接在一個 host 和 port上,那么它們就可以共同使用同一個 socket ,這樣做的好處就是可以減少TCP的一個三次握手的時間。
  • 調(diào)用RealInterceptorChain.proceed(...)進(jìn)行網(wǎng)絡(luò)請求
  • 根據(jù)異常結(jié)果或響應(yīng)結(jié)果判斷是否進(jìn)行重新請求(20次)
  • 調(diào)用下一個攔截器,對response進(jìn)行處理,返回上一個攔截器
  • BridgeInterceptor? 橋攔截器

    該攔截器是鏈接客戶端代碼和網(wǎng)絡(luò)代碼的橋梁,它首先將客戶端構(gòu)建的Request對象信息構(gòu)建成真正的網(wǎng)絡(luò)請求;然后發(fā)起網(wǎng)絡(luò)請求,最后就是講服務(wù)器返回的消息封裝成一個Response對象。

  • 將用戶構(gòu)建的Request請求轉(zhuǎn)化為能夠進(jìn)行網(wǎng)絡(luò)訪問的請求
  • 執(zhí)行符合條件的請求
  • 將Response轉(zhuǎn)化為用戶可用的Response,OkHttp支持Gzip壓縮,GzipSource類對數(shù)據(jù)進(jìn)行解壓。
  • CacheInterceptor 緩存攔截器

    該攔截器用于處理緩存的功能,主要取得緩存 response 返回并刷新緩存。

  • 底層使用的是 DiskLruCache 緩存機(jī)制。
  • CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
  • get() 方法獲取一個 CacheStrategy 對象。CacheStrategy,它是一個策略器,負(fù)責(zé)判斷是使用緩存還是請求網(wǎng)絡(luò)獲取新的數(shù)據(jù)。
  • responseCache.put(userResponse);
  • put(userResponse)方法將 userResponse 緩存到本地。?
  • 為什么需要緩存 Response?

    • 客戶端緩存就是為了下次請求時節(jié)省請求時間,可以更快的展示數(shù)據(jù)。
    • OKHTTP 支持緩存的功能

    ConnectInterceptor? 連接攔截器

    該攔截器的功能就是負(fù)責(zé)與服務(wù)器建立 Socket 連接,并且創(chuàng)建了一個 HttpCodec它包括通向服務(wù)器的輸入流和輸出流。

  • 獲取到第一個攔截器生成的StreamAllocation對象,
  • 通過StreamAllocation對象,streamAllocation.newStream()創(chuàng)建HttpCodec對象,
  • streamAllocation.connection()獲取一個RealConnection對象
  • 將HttpCodec、RealConnection對象傳遞給攔截器
  • 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ù)的讀寫操作的。

  • 首先是獲取了httpCodec對象,該對象的主要功能就是對不同http協(xié)議(http1.1和http/2)的請求和響應(yīng)做處理,該對象的初始化是在ConnectIntercepor的intercept里面
  • OkHttp通過OKIO的Sink對象(該對象可以看做Socket的OutputStream對象)的writeRequest來向服務(wù)器發(fā)送請求的。
  • 將OKIO的Source對象作為輸入流InputStream對象讀取數(shù)據(jù)封裝為Response對象。
  • 100-continue用于客戶端在發(fā)送POST數(shù)據(jù)給服務(wù)器前,征詢服務(wù)器情況,看服務(wù)器是否處理POST的數(shù)據(jù),如果不處理,客戶端則不上傳POST數(shù)據(jù),如果處理,則POST上傳數(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來管理。

  • StreamAllocation里面包含了RealConnection對象,該對象歸根是由ConnectionPool的get() 方法遍歷 connections 中的所有 RealConnection 尋找同時滿足條件的RealConnection,重復(fù)利用RealConnection。
  • ConnectionPool類里put方法,采用GC回收算法,異步觸發(fā)清理任務(wù),然后將健康的connection添加到connections隊(duì)列中。調(diào)用cleanup方法執(zhí)行清理,并等待一段時間,持續(xù)清理,其中cleanup方法返回的值來來決定而等待的時間長度。
  • 總結(jié)

    以上是生活随笔為你收集整理的Android—OkHttp同步异步请求过程源码分析与拦截器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。