Okhttp网络请求工具介绍
1.Okhttp請求流程:
Okhttp內部的大致請求流程圖如下所示:
?使用方式:
public class OkHttpUtils {private static String TAG = "OkHttpUtils";private static String url = "http://www.baidu.com";//OkHttp 異步get請求public static void OkHttpEnqueue() {OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder().url(url).get() //默認就是get請求 可以不寫.build();final Call call = okHttpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {}@Overridepublic void onResponse(Call call, Response response) throws IOException {}});}//OkHttp 同步get請求public static void okHttpSync() {OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder().url(url).build();Call call = okHttpClient.newCall(request);new Thread(new Runnable() {@Overridepublic void run() {try {Response response = call.execute();Log.d(TAG, "run: " + response.body().string());} catch (Exception e) {e.printStackTrace();}}});}//OkHttp 異步post方式提交Stringpublic static void OkHttpPostEnqueue() {MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");String requestBody = "I am Jeled";OkHttpClient okHttpClient = new OkHttpClient();Request request = new Request.Builder().url(url).post(RequestBody.create(mediaType, requestBody)).build();Call call = okHttpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.d(TAG, response.protocol() + " " + response.code() + " " + response.message());Headers header = response.headers();for (int i = 0; i < header.size(); i++) {Log.d(TAG, header.name(i) + ":" + header.value(i));}Log.d(TAG, "onResponse:" + response.body().string());}});}//OkHttp 異步post方式提交流public static void OkHttpPost() {RequestBody requestBody = new RequestBody() {@Nullable@Overridepublic MediaType contentType() {return MediaType.parse("text/x-markdown; charset=utf-8");}@Overridepublic void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("I am Jeled");}};Request request = new Request.Builder().url(url).post(requestBody).build();OkHttpClient okHttpClient = new OkHttpClient();Call call = okHttpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.d(TAG, response.protocol() + " " + response.code() + " " + response.message());Headers header = response.headers();for (int i = 0; i < header.size(); i++) {Log.d(TAG, header.name(i) + ":" + header.value(i));}Log.d(TAG, "onResponse:" + response.body().string());}});}//OkHttp 異步post方式提交文件public static void OkHttpPostFile() {MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");File file = new File("test.md");Request request = new Request.Builder().url(url).post(RequestBody.create(mediaType,file)).build();OkHttpClient okHttpClient = new OkHttpClient();Call call = okHttpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.d(TAG, response.protocol() + " " + response.code() + " " + response.message());Headers header = response.headers();for (int i = 0; i < header.size(); i++) {Log.d(TAG, header.name(i) + ":" + header.value(i));}Log.d(TAG, "onResponse:" + response.body().string());}});}//OkHttp 異步post方式提交表單public static void OkHttpPostForm() {RequestBody requestBody = new FormBody.Builder().add("search","Jurassic Park").build();Request request = new Request.Builder().url(url).post(requestBody).build();OkHttpClient okHttpClient = new OkHttpClient();Call call = okHttpClient.newCall(request);call.enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {Log.d(TAG, "onFailure:" + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {Log.d(TAG, response.protocol() + " " + response.code() + " " + response.message());Headers header = response.headers();for (int i = 0; i < header.size(); i++) {Log.d(TAG, header.name(i) + ":" + header.value(i));}Log.d(TAG, "onResponse:" + response.body().string());}});}}備注:以上就是Okhttp的get或是post的同步或異步的請求的簡單使用
源碼梳理:
1、攔截器:
-
用戶自定義攔截器:(繼承Interceptor接口,實現intercept方法)
-
RetryAndFollowUpInterceptor:失敗重試以及重定向攔截器
-
BridgeInterceterptor:橋接攔截器 (請求時,對必要的Header進行一些添加,接收響應時,移除必要的Header)
-
CacheInterceptor:緩存攔截
1.根據request得到cache中緩存的response
2.確認 request判斷緩存的策略,是否要使用了網絡,緩存或兩者都使用
3.調用下一個攔截器,決定從網絡上來得到response
4.如果本地已經存在cacheResponse,那么讓它和網絡得到的networkResponse做比較,決定是否來更新緩存的cacheResponse
5.緩存未緩存過的response
緩存攔截器會根據請求的信息和緩存的響應的信息來判斷是否存在緩存可用,如果有可以使用的緩存,那么就返回該緩存給用戶,否則就繼續使用責任鏈模式來從服務器中獲取響應。當獲取到響應的時候,又會把響應緩存到磁盤上面
-
ConnectionInterceptor:連接攔截器
1.判斷當前的連接是否可以使用:流是否已經被關閉,并且已經被限制創建新的流;
2.如果當前的連接無法使用,就從連接池中獲取一個連接;
3.連接池中也沒有發現可用的連接,創建一個新的連接,并進行握手,然后將其放到連接池中
-
NetworkInterceptors:網絡攔截器(配置OkHttpClient時設置的 NetworkInterceptors)
-
CallServerInterceptor:請求攔截器(負責向服務器發送請求數據、從服務器讀取響應數據)
核心
// Call the next interceptor in the chain. //調用鏈的下一個攔截器RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec, connection, index + 1, request); //(1)Interceptor interceptor = interceptors.get(index); //(2)Response response = interceptor.intercept(next); //(3) 復制代碼1.實例化下一個攔截器對應的RealIterceptorChain對象,這個對象會在傳遞給當前的攔截器
得到當前的攔截器:interceptors是存放攔截器的ArryList
調用當前攔截器的intercept()方法,并將下一個攔截器的RealIterceptorChain對象傳遞下去
除了client中用戶設置的interceptor,第一個調用的就是retryAndFollowUpInterceptor
小結
1.攔截器用了責任鏈設計模式,它將請求一層一層向下傳,直到有一層能夠得到Response就停止向下傳遞
2.然后將response向上面的攔截器傳遞,然后各個攔截器會對respone進行一些處理,最后會傳到RealCall類中通過execute來得到response
簡而言之:每一個攔截器都對應一個 RealInterceptorChain ,然后每一個interceptor 再產生下一個RealInterceptorChain,直到 List 迭代完成,如下圖所示
關于調度Dispatch
在開始下面的內容前,我們先簡單的對Dispatcher有個認識 👇👇👇
public final class Dispatcher {private int maxRequests = 64;private int maxRequestsPerHost = 5;private @Nullable Runnable idleCallback;/** Executes calls. Created lazily. */private @Nullable ExecutorService executorService;/** Ready async calls in the order they'll be run. *///正在準備中的異步請求隊列 private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();/** Running asynchronous calls. Includes canceled calls that haven't finished yet. *///運行中的異步請求 private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>(); /** Running synchronous calls. Includes canceled calls that haven't finished yet. *///同步請求 private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>(); public Dispatcher(ExecutorService executorService) {this.executorService = executorService;}public Dispatcher() {}public synchronized ExecutorService executorService() {if (executorService == null) {executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));}return executorService;}...... } 復制代碼1.有一個最大請求次數:64
2.有一個最大請求主機數:5
3.有一個懶加載的線程池,當執行 executorService() 方式時才創建
4.有三個隊列(準備中的異步請求 | 運行中的異步請求 | 同步請求)
1.創建OkhttpClient
OkHttpClient client = new OkHttpClient() 復制代碼這部分中Java和kotlin中沒有什么區別,都用了 建造者模式 ,Builder里面的可配置參數也是一樣的
public OkHttpClient() {this(new Builder()); }OkHttpClient(Builder builder) {.... }public static final class Builder {Dispatcher dispatcher;// 分發器@Nullable Proxy proxy;List<Protocol> protocols;List<ConnectionSpec> connectionSpecs;// 傳輸層版本和連接協議final List<Interceptor> interceptors = new ArrayList<>();// 攔截器final List<Interceptor> networkInterceptors = new ArrayList<>();EventListener.Factory eventListenerFactory;ProxySelector proxySelector;CookieJar cookieJar;@Nullable Cache cache;@Nullable InternalCache internalCache;// 內部緩存SocketFactory socketFactory;@Nullable SSLSocketFactory sslSocketFactory;// 安全套接層socket 工廠,用于HTTPS@Nullable CertificateChainCleaner certificateChainCleaner;// 驗證確認響應證書 適用 HTTPS 請求連接的主機名。HostnameVerifier hostnameVerifier;// 驗證確認響應證書 適用 HTTPS 請求連接的主機名。 CertificatePinner certificatePinner;// 證書鎖定,使用CertificatePinner來約束哪些認證機構被信任。Authenticator proxyAuthenticator;// 代理身份驗證Authenticator authenticator;// 身份驗證ConnectionPool connectionPool;// 連接池Dns dns;boolean followSslRedirects; // 安全套接層重定向boolean followRedirects;// 本地重定向boolean retryOnConnectionFailure;// 重試連接失敗int callTimeout;int connectTimeout;int readTimeout;int writeTimeout;int pingInterval;// 這里是默認配置的構建參數public Builder() {dispatcher = new Dispatcher();protocols = DEFAULT_PROTOCOLS;connectionSpecs = DEFAULT_CONNECTION_SPECS;...}// 這里傳入自己配置的構建參數Builder(OkHttpClient okHttpClient) {this.dispatcher = okHttpClient.dispatcher;this.proxy = okHttpClient.proxy;this.protocols = okHttpClient.protocols;this.connectionSpecs = okHttpClient.connectionSpecs;this.interceptors.addAll(okHttpClient.interceptors);this.networkInterceptors.addAll(okHttpClient.networkInterceptors);...}復制代碼 open class OkHttpClient internal constructor(builder: Builder ) : Cloneable, Call.Factory, WebSocket.Factory {//...constructor() : this(Builder())//...internal constructor(okHttpClient: OkHttpClient) : this() {this.dispatcher = okHttpClient.dispatcherthis.connectionPool = okHttpClient.connectionPoolthis.interceptors += okHttpClient.interceptorsthis.networkInterceptors += okHttpClient.networkInterceptorsthis.eventListenerFactory = okHttpClient.eventListenerFactorythis.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailurethis.authenticator = okHttpClient.authenticatorthis.followRedirects = okHttpClient.followRedirectsthis.followSslRedirects = okHttpClient.followSslRedirectsthis.cookieJar = okHttpClient.cookieJarthis.cache = okHttpClient.cachethis.dns = okHttpClient.dnsthis.proxy = okHttpClient.proxythis.proxySelector = okHttpClient.proxySelectorthis.proxyAuthenticator = okHttpClient.proxyAuthenticatorthis.socketFactory = okHttpClient.socketFactorythis.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNullthis.x509TrustManagerOrNull = okHttpClient.x509TrustManagerthis.connectionSpecs = okHttpClient.connectionSpecsthis.protocols = okHttpClient.protocolsthis.hostnameVerifier = okHttpClient.hostnameVerifierthis.certificatePinner = okHttpClient.certificatePinnerthis.certificateChainCleaner = okHttpClient.certificateChainCleanerthis.callTimeout = okHttpClient.callTimeoutMillisthis.connectTimeout = okHttpClient.connectTimeoutMillisthis.readTimeout = okHttpClient.readTimeoutMillisthis.writeTimeout = okHttpClient.writeTimeoutMillisthis.pingInterval = okHttpClient.pingIntervalMillisthis.minWebSocketMessageToCompress = okHttpClient.minWebSocketMessageToCompressthis.routeDatabase = okHttpClient.routeDatabase} }復制代碼2.執行請求
同步請求
OkHttpClient client = new OkHttpClient(); //同步請求 Response response = client.newCall(request).execute(); 復制代碼整體流程
通過創建完OkHttpClient對象,調用內部的 newCall() 方法,將最終的請求交給RealCall的 execute() 方法,在該方法內部處理
1.確保Call方法只執行一次(有版本區別,請看下文)
2.通知dispatcher進入執行狀態
3.通過一系列的攔截器的請求處理和響應處理得到最終的結果
4告訴dispatcher已經執行完畢
java版本
/** * Prepares the {@code request} to be executed at some point in the future. */ @Override public Call newCall(Request request) {return RealCall.newRealCall(this, request, false /* for web socket */); }// RealCall為真正的請求執行者 static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {// Safely publish the Call instance to the EventListener.RealCall call = new RealCall(client, originalRequest, forWebSocket);call.eventListener = client.eventListenerFactory().create(call);return call; }復制代碼 private boolean executed; @Override public Response execute() throws IOException {synchronized (this) {// 每個Call只能執行一次if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();timeout.enter();eventListener.callStart(this);try {// 通知dispatcher已經進入執行狀態client.dispatcher().executed(this);// 通過一系列的攔截器請求處理和響應處理得到最終的返回結果Response result = getResponseWithInterceptorChain();if (result == null) throw new IOException("Canceled");return result;} catch (IOException e) {e = timeoutExit(e);eventListener.callFailed(this, e);throw e;} finally {// 通知 dispatcher 自己已經執行完畢client.dispatcher().finished(this);} } 復制代碼 Response getResponseWithInterceptorChain() throws IOException {// Build a full stack of interceptors.List<Interceptor> interceptors = new ArrayList<>();// 在配置 OkHttpClient 時設置的 interceptors;interceptors.addAll(client.interceptors());// 負責失敗重試以及重定向interceptors.add(retryAndFollowUpInterceptor);// 請求時,對必要的Header進行一些添加,接收響應時,移除必要的Headerinterceptors.add(new BridgeInterceptor(client.cookieJar()));// 負責讀取緩存直接返回、更新緩存interceptors.add(new CacheInterceptor(client.internalCache()));// 負責和服務器建立連接interceptors.add(new ConnectInterceptor(client));if (!forWebSocket) {// 配置 OkHttpClient 時設置的 networkInterceptorsinterceptors.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); }// StreamAllocation 對象,它相當于一個管理類,維護了服務器連接、并發流 // 和請求之間的關系,該類還會初始化一個 Socket 連接對象,獲取輸入/輸出流對象。 public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection) throws IOException {...// Call the next interceptor in the chain.// 實例化下一個攔截器對應的RealIterceptorChain對象RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,writeTimeout);// 得到當前的攔截器Interceptor interceptor = interceptors.get(index);// 調用當前攔截器的intercept()方法,并將下一個攔截器的RealIterceptorChain對象傳遞下去,最后得到響應Response response = interceptor.intercept(next);...return response; 復制代碼Kotlin版本
//newcall真正的執行在RealCall類 override fun newCall(request: Request): Call = RealCall(this, request, forWebSocket = false)復制代碼RealCall.kt
private val executed = AtomicBoolean()override fun execute(): Response {check(executed.compareAndSet(false, true)) { "Already Executed" }timeout.enter()callStart()try {client.dispatcher.executed(this)return getResponseWithInterceptorChain()} finally {client.dispatcher.finished(this)}}復制代碼 @Throws(IOException::class)internal fun getResponseWithInterceptorChain(): Response {// Build a full stack of interceptors.val interceptors = mutableListOf<Interceptor>()interceptors += client.interceptorsinterceptors += RetryAndFollowUpInterceptor(client)interceptors += BridgeInterceptor(client.cookieJar)interceptors += CacheInterceptor(client.cache)interceptors += ConnectInterceptorif (!forWebSocket) {interceptors += client.networkInterceptors}interceptors += CallServerInterceptor(forWebSocket)val chain = RealInterceptorChain(call = this,interceptors = interceptors,index = 0,exchange = null,request = originalRequest,connectTimeoutMillis = client.connectTimeoutMillis,readTimeoutMillis = client.readTimeoutMillis,writeTimeoutMillis = client.writeTimeoutMillis)var calledNoMoreExchanges = falsetry {val response = chain.proceed(originalRequest)if (isCanceled()) {response.closeQuietly()throw IOException("Canceled")}return response} catch (e: IOException) {calledNoMoreExchanges = truethrow noMoreExchanges(e) as Throwable} finally {if (!calledNoMoreExchanges) {noMoreExchanges(null)}}} 復制代碼在Java版本中通過使用 synchronized 關鍵字來保證保證線程安全,并且確保executed只會被執行一次
kotlin版本中直接移除了 synchronized 關鍵字,并且將executed字段設置為具有原子性特征的boolean值,且通過CAS操作去確保是否已經執行了
異步請求
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url(url).build(); client.newCall(request).enqueue(new Callback() {@Override public void onFailure(Call call, IOException e) {}@Override public void onResponse(Call call, Response response) throws IOException {} 復制代碼整體流程
- 通過Request的構建者模式創建一個Request對象,然后調用OkHttpClient內部的 newCall() 方法,將最終的請求交給RealCall的 enqueue() 方法,在方法內部進行邏輯處理
? 直接進入RealCall代碼
java版本
public void enqueue(Callback responseCallback) {synchronized (this) {if (executed) throw new IllegalStateException("Already Executed");executed = true;}captureCallStackTrace();eventListener.callStart(this);client.dispatcher().enqueue(new AsyncCall(responseCallback));} 復制代碼從上面看到,通過加鎖后確保只會執行一次后,將當前的請求交給dispatcher攔截器中的enqueue()方法執行,那么我們來看看這個代碼
Dispatcher.java
void enqueue(AsyncCall call) {synchronized (this) {readyAsyncCalls.add(call);if (!call.get().forWebSocket) {AsyncCall existingCall = findExistingCallWithHost(call.host());if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);}}promoteAndExecute();} 復制代碼在攔截器中的方法中,發現它首先將這個請求添加到了readyAsyncCalls這個隊列中,你問我怎么就知道它是隊列了?你還記得上面我說的嗎?👆👆👆
添加到隊列后接著執行**promoteAndExecute()**方法
private boolean promoteAndExecute() {assert (!Thread.holdsLock(this));List<AsyncCall> executableCalls = new ArrayList<>();boolean isRunning;synchronized (this) {for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {AsyncCall asyncCall = i.next();if (runningAsyncCalls.size() >= maxRequests) break; //判斷是否大于最大的請求數if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // 判斷主機數是否已經達到最大.// 如果其中的runningAsynCalls不滿,且call占用的host小于最大數量,則將call加入到runningAsyncCalls中執行,//利用線程池執行call否者將call加入到readyAsyncCalls中。i.remove();asyncCall.callsPerHost().incrementAndGet();executableCalls.add(asyncCall);runningAsyncCalls.add(asyncCall);}isRunning = runningCallsCount() > 0;}for (int i = 0, size = executableCalls.size(); i < size; i++) {AsyncCall asyncCall = executableCalls.get(i);asyncCall.executeOn(executorService());}return isRunning;} 復制代碼call加入到線程池中執行了。現在再看AsynCall的代碼,它是RealCall中的內部類
final class AsyncCall extends NamedRunnable {private final Callback responseCallback;AsyncCall(Callback responseCallback) {super("OkHttp %s", redactedUrl());this.responseCallback = responseCallback;}String host() {return originalRequest.url().host();}Request request() {return originalRequest;}RealCall get() {return RealCall.this;}/*** Attempt to enqueue this async call on {@code executorService}. This will attempt to clean up* if the executor has been shut down by reporting the call as failed.*/void executeOn(ExecutorService executorService) {assert (!Thread.holdsLock(client.dispatcher()));boolean success = false;try {executorService.execute(this);success = true;} catch (RejectedExecutionException e) {InterruptedIOException ioException = new InterruptedIOException("executor rejected");ioException.initCause(e);eventListener.callFailed(RealCall.this, ioException);responseCallback.onFailure(RealCall.this, ioException);} finally {if (!success) {client.dispatcher().finished(this); // This call is no longer running!}}}@Override protected void execute() {boolean signalledCallback = false;timeout.enter();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) {e = timeoutExit(e);if (signalledCallback) {// Do not signal the callback twice!Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);} else {eventListener.callFailed(RealCall.this, e);responseCallback.onFailure(RealCall.this, e);}} finally {client.dispatcher().finished(this);}} } 復制代碼kotlin版本
override fun enqueue(responseCallback: Callback) {check(executed.compareAndSet(false, true)) { "Already Executed" }callStart()client.dispatcher.enqueue(AsyncCall(responseCallback))} 復制代碼 internal fun enqueue(call: AsyncCall) {synchronized(this) {readyAsyncCalls.add(call)// Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to// the same host.if (!call.call.forWebSocket) {val existingCall = findExistingCallWithHost(call.host)if (existingCall != null) call.reuseCallsPerHostFrom(existingCall)}}promoteAndExecute()} 復制代碼 private fun promoteAndExecute(): Boolean {this.assertThreadDoesntHoldLock()val executableCalls = mutableListOf<AsyncCall>()val isRunning: Booleansynchronized(this) {val i = readyAsyncCalls.iterator()while (i.hasNext()) {val asyncCall = i.next()if (runningAsyncCalls.size >= this.maxRequests) break // Max capacity.if (asyncCall.callsPerHost.get() >= this.maxRequestsPerHost) continue // Host max capacity.i.remove()asyncCall.callsPerHost.incrementAndGet()executableCalls.add(asyncCall)runningAsyncCalls.add(asyncCall)}isRunning = runningCallsCount() > 0}for (i in 0 until executableCalls.size) {val asyncCall = executableCalls[i]asyncCall.executeOn(executorService)}return isRunning} 復制代碼 inner class AsyncCall(private val responseCallback: Callback) : Runnable {@Volatile var callsPerHost = AtomicInteger(0)private setfun reuseCallsPerHostFrom(other: AsyncCall) {this.callsPerHost = other.callsPerHost}val host: Stringget() = originalRequest.url.hostval request: Requestget() = originalRequestval call: RealCallget() = this@RealCall/*** Attempt to enqueue this async call on [executorService]. This will attempt to clean up* if the executor has been shut down by reporting the call as failed.*/fun executeOn(executorService: ExecutorService) {client.dispatcher.assertThreadDoesntHoldLock()var success = falsetry {executorService.execute(this)success = true} catch (e: RejectedExecutionException) {val ioException = InterruptedIOException("executor rejected")ioException.initCause(e)noMoreExchanges(ioException)responseCallback.onFailure(this@RealCall, ioException)} finally {if (!success) {client.dispatcher.finished(this) // This call is no longer running!}}}override fun run() {threadName("OkHttp ${redactedUrl()}") {var signalledCallback = falsetimeout.enter()try {val response = getResponseWithInterceptorChain()signalledCallback = trueresponseCallback.onResponse(this@RealCall, response)} catch (e: IOException) {if (signalledCallback) {// Do not signal the callback twice!Platform.get().log("Callback failure for ${toLoggableString()}", Platform.INFO, e)} else {responseCallback.onFailure(this@RealCall, e)}} catch (t: Throwable) {cancel()if (!signalledCallback) {val canceledException = IOException("canceled due to $t")canceledException.addSuppressed(t)responseCallback.onFailure(this@RealCall, canceledException)}throw t} finally {client.dispatcher.finished(this)}}}} 復制代碼AysncCall中的execute()中的方法,同樣是通過Response response = getResponseWithInterceptorChain();來獲得response,這樣異步任務也同樣通過了
總結
首先,請求的時候初始化一個Call的實例,然后執行它的**execute()方法或enqueue()方法,內部最后都會執行到getResponseWithInterceptorChain()**方法,
這個方法通過攔截器組成的責任鏈模式,依次經過用戶自定義普通攔截器、重試攔截器(RetryAndFollowUpInterceptor)、橋接攔截器(BridgeInterceptor)、緩存攔截器(BridgeInterceptor)、連接攔截器(CallServerInterceptor)和用戶自定義網絡攔截器以及訪問服務器攔截器等攔截處理過程,最終將獲取到的響應結果交給用戶
最后上圖
連接攔截器
在Okhttp整個過程中,比較重要的兩個攔截器,緩存攔截器和連接攔截器,關于緩存攔截器在文一開始的時候就簡單的說了下👆👆👆
現在說下另一個比較重要的攔截器
Java版本
@Override public Response intercept(Chain chain) throws IOException {RealInterceptorChain realChain = (RealInterceptorChain) chain;Request request = realChain.request();StreamAllocation streamAllocation = realChain.streamAllocation();// We need the network to satisfy this request. Possibly for validating a conditional GET.boolean doExtensiveHealthChecks = !request.method().equals("GET");// HttpCodec是對 HTTP 協議操作的抽象,有兩個實現:Http1Codec和Http2Codec,顧名思義,它們分別對應 HTTP/1.1 和 HTTP/2 版本的實現。在這個方法的內部實現連接池的復用處理HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);RealConnection connection = streamAllocation.connection();return realChain.proceed(request, streamAllocation, httpCodec, connection); }復制代碼通過調用了 streamAllocation 的 newStream() 方法的時候,經過一系列判斷到達 StreamAllocation類 中的 findConnection() 方法
private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,int pingIntervalMillis, boolean connectionRetryEnabled) throws IOException {...// Attempt to use an already-allocated connection. We need to be careful here because our// already-allocated connection may have been restricted from creating new streams.// 嘗試使用已分配的連接,已經分配的連接可能已經被限制創建新的流releasedConnection = this.connection;// 釋放當前連接的資源,如果該連接已經被限制創建新的流,就返回一個Socket以關閉連接toClose = releaseIfNoNewStreams();if (this.connection != null) {// We had an already-allocated connection and it's good.result = this.connection;releasedConnection = null;}if (!reportedAcquired) {// If the connection was never reported acquired, don't report it as released!// 如果該連接從未被標記為獲得,不要標記為發布狀態,reportedAcquired 通過 acquire() 方法修改releasedConnection = null;}if (result == null) {// Attempt to get a connection from the pool.// 嘗試供連接池中獲取一個連接Internal.instance.get(connectionPool, address, this, null);if (connection != null) {foundPooledConnection = true;result = connection;} else {selectedRoute = route;}}}// 關閉連接closeQuietly(toClose);if (releasedConnection != null) {eventListener.connectionReleased(call, releasedConnection);}if (foundPooledConnection) {eventListener.connectionAcquired(call, result);}if (result != null) {// If we found an already-allocated or pooled connection, we're done.// 如果已經從連接池中獲取到了一個連接,就將其返回return result;}// If we need a route selection, make one. This is a blocking operation.boolean newRouteSelection = false;if (selectedRoute == null && (routeSelection == null || !routeSelection.hasNext())) {newRouteSelection = true;routeSelection = routeSelector.next();}synchronized (connectionPool) {if (canceled) throw new IOException("Canceled");if (newRouteSelection) {// Now that we have a set of IP addresses, make another attempt at getting a connection from// the pool. This could match due to connection coalescing.// 根據一系列的 IP地址從連接池中獲取一個鏈接List<Route> routes = routeSelection.getAll();for (int i = 0, size = routes.size(); i < size;i++) {Route route = routes.get(i);// 從連接池中獲取一個連接Internal.instance.get(connectionPool, address, this, route);if (connection != null) {foundPooledConnection = true;result = connection;this.route = route;break;}}}if (!foundPooledConnection) {if (selectedRoute == null) {selectedRoute = routeSelection.next();}// Create a connection and assign it to this allocation immediately. This makes it possible// for an asynchronous cancel() to interrupt the handshake we're about to do.// 在連接池中如果沒有該連接,則創建一個新的連接,并將其分配,這樣我們就可以在握手之前進行終端route = selectedRoute;refusedStreamCount = 0;result = new RealConnection(connectionPool, selectedRoute);acquire(result, false);}}// If we found a pooled connection on the 2nd time around, we're done.if (foundPooledConnection) {// 如果我們在第二次的時候發現了一個池連接,那么我們就將其返回eventListener.connectionAcquired(call, result);return result;}// Do TCP + TLS handshakes. This is a blocking operation.// 進行 TCP 和 TLS 握手result.connect(connectTimeout, readTimeout, writeTimeout, pingIntervalMillis,connectionRetryEnabled, call, eventListener);routeDatabase().connected(result.route());Socket socket = null;synchronized (connectionPool) {reportedAcquired = true;// Pool the connection.// 將該連接放進連接池中Internal.instance.put(connectionPool, result);// If another multiplexed connection to the same address was created concurrently, then// release this connection and acquire that one.// 如果同時創建了另一個到同一地址的多路復用連接,釋放這個連接并獲取那個連接if (result.isMultiplexed()) {socket = Internal.instance.deduplicate(connectionPool, address, this);result = connection;}}closeQuietly(socket);eventListener.connectionAcquired(call, result);return result; } 復制代碼通過上面的源碼也可以證明我們開頭的結論是正確的,添加連接池里面去了,簡單來說,連接復用省去了TCP和TLS握手的過程,因為建立連接本身也是需要消耗時間的,連接復用后就可以提升網絡訪問效率
最后說下ConnectionPool的作用
public final class ConnectionPool { private final Deque<RealConnection> connections = new ArrayDeque<>();//......void put(RealConnection connection) {assert (Thread.holdsLock(this));if (!cleanupRunning) {cleanupRunning = true;executor.execute(cleanupRunnable);}connections.add(connection);}private final Runnable cleanupRunnable = () -> {while (true) {long waitNanos = cleanup(System.nanoTime());if (waitNanos == -1) return;if (waitNanos > 0) {long waitMillis = waitNanos / 1000000L;waitNanos -= (waitMillis * 1000000L);synchronized (ConnectionPool.this) {try {ConnectionPool.this.wait(waitMillis, (int) waitNanos);} catch (InterruptedException ignored) {}}}}}; } 復制代碼創建一個新的連接的時候,一方面需要它放進緩存里面另一邊,另一方面對緩存進行清理。在 ConnectionPool 中,當我們向連接池中緩存一個連接的時候,只要調用雙端隊列的 add() 方法,將其加入到雙端隊列即可,而清理連接緩存的操作則交給線程池來定時執行
kotlin版本
object ConnectInterceptor : Interceptor {@Throws(IOException::class)override fun intercept(chain: Interceptor.Chain): Response {val realChain = chain as RealInterceptorChainval exchange = realChain.call.initExchange(chain)val connectedChain = realChain.copy(exchange = exchange)return connectedChain.proceed(realChain.request)} }Okhttp面試推薦:
面試突擊:OkHttp 原理 8 連問?
總結
以上是生活随笔為你收集整理的Okhttp网络请求工具介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 音频转换器评估:将音乐文件转换成MP3格
- 下一篇: 首次力压 macOS!Linux 杀疯了