序言
上一篇文章介紹了Retrofit的原理逗抑,今天,我們就來(lái)探究一下OkHttp的原理。Retrofit是對(duì)OkHttp做封裝邮府,真正發(fā)送網(wǎng)絡(luò)請(qǐng)求的就是OkHttp框架浙于。
使用Retrofit框架過(guò)程中,對(duì)OkHttp最直觀的認(rèn)識(shí)就是OkHttpClient挟纱,這篇文章會(huì)按如下目錄展開(kāi)介紹:
- OkHttp的用法
- OkHttp在Retrofit中的使用
- OkHttp的原理
- 對(duì)比OkHttp與其他網(wǎng)絡(luò)庫(kù)
- 總結(jié)
OkHttp的用法
本文基于OkHttp3.10.0
版本進(jìn)行介紹羞酗。
- 首先添加OkHttp依賴:
implementation("com.squareup.okhttp3:okhttp:3.10.0")
如果項(xiàng)目中添加了Retrofit2依賴,就不用再添加OkHttp3的依賴紊服,因?yàn)镽etrofit2庫(kù)使用到了OkHttp3檀轨,并且已經(jīng)添加了OkHttp3的依賴,所以不用我們重復(fù)添加欺嗤。
- 使用方法:
//創(chuàng)建OkHttpClient對(duì)象
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
//創(chuàng)建Request請(qǐng)求對(duì)象
Request request = new Request.Builder()
.url(url)
.build();
//創(chuàng)建Call對(duì)象参萄,并執(zhí)行同步獲取網(wǎng)絡(luò)數(shù)據(jù)
Response response = client.newCall(request).execute();
return response.body().string();
}
總結(jié)一下,OkHttp的使用步驟如下:
- 創(chuàng)建OkHttpClient對(duì)象
- 創(chuàng)建Request對(duì)象
- 創(chuàng)建Call對(duì)象
- 通過(guò)Call對(duì)象發(fā)起請(qǐng)求煎饼,同步請(qǐng)求調(diào)用call.execute方法讹挎,異步請(qǐng)求調(diào)用call.enqueue方法
OkHttp在Retrofit中的使用
結(jié)合之前的一篇文章Retrofit源碼解析,我們來(lái)看一下在Retrofit中怎樣使用OkHttp吆玖。
-
創(chuàng)建OkHttpClient對(duì)象
在初始化Retrofit對(duì)象的時(shí)候筒溃,我們會(huì)傳入一個(gè)OkHttpClient對(duì)象:
DeviceRetrofit() {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(Config.HTTP_TIMEOUT, TimeUnit.MILLISECONDS)
.retryOnConnectionFailure(true)
.addInterceptor(new DeviceInterceptor())
.addInterceptor(OkHttpUtils.getHttpInterceptor(TAG))
.build();
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.baseUrl(Config.DEVICE_HOST)
.addConverterFactory(JacksonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
mService = retrofit.create(DeviceService.class);
}
這里完成了第一步,創(chuàng)建OkHttpClient對(duì)象沾乘。
-
創(chuàng)建Request對(duì)象
當(dāng)我們通過(guò)Retrofit發(fā)起一個(gè)請(qǐng)求的時(shí)候怜奖,在ServiceMethod中會(huì)創(chuàng)建一個(gè)okhttp3.Request對(duì)象:
/**
* 創(chuàng)建Call對(duì)象
*/
private okhttp3.Call createRawCall() throws IOException {
Request request = serviceMethod.toRequest(args);
okhttp3.Call call = serviceMethod.callFactory.newCall(request);
if (call == null) {
throw new NullPointerException("Call.Factory returned null.");
}
return call;
}
/**
* 創(chuàng)建Request對(duì)象
*/
Request toRequest(Object... args) throws IOException {
RequestBuilder requestBuilder = new RequestBuilder(httpMethod, baseUrl, relativeUrl, headers,
contentType, hasBody, isFormEncoded, isMultipart);
@SuppressWarnings("unchecked") // It is an error to invoke a method with the wrong arg types.
ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;
int argumentCount = args != null ? args.length : 0;
if (argumentCount != handlers.length) {
throw new IllegalArgumentException("Argument count (" + argumentCount
+ ") doesn't match expected count (" + handlers.length + ")");
}
for (int p = 0; p < argumentCount; p++) {
handlers[p].apply(requestBuilder, args[p]);
}
return requestBuilder.build();
}
這里完成了第二步,創(chuàng)建了一個(gè)Request對(duì)象翅阵。
-
創(chuàng)建Call對(duì)象
第二步的代碼中歪玲,有一個(gè)createRawCall方法,它會(huì)從callFactory工廠中創(chuàng)建一個(gè)Call對(duì)象掷匠,之前說(shuō)過(guò)滥崩,callFactory就是OkHttpClient,所以看OkHttpClient的newCall方法:
@Override public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
這里完成了第三步讹语,創(chuàng)建了一個(gè)RealCall對(duì)象钙皮。
-
調(diào)用Call的execute方法:
在RxJavaCallAdapterFactory.CallOnSubscribe.call方法中,會(huì)調(diào)用call.execute()發(fā)送同步請(qǐng)求:
static final class CallOnSubscribe<T> implements Observable.OnSubscribe<Response<T>> {
private final Call<T> originalCall;
CallOnSubscribe(Call<T> originalCall) {
this.originalCall = originalCall;
}
@Override public void call(final Subscriber<? super Response<T>> subscriber) {
// Since Call is a one-shot type, clone it for each new subscriber.
final Call<T> call = originalCall.clone();
// Attempt to cancel the call if it is still in-flight on unsubscription.
subscriber.add(Subscriptions.create(new Action0() {
@Override public void call() {
call.cancel();
}
}));
try {
Response<T> response = call.execute();
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(response);
}
} catch (Throwable t) {
Exceptions.throwIfFatal(t);
if (!subscriber.isUnsubscribed()) {
subscriber.onError(t);
}
return;
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
}
這里就完成了第四步募强,調(diào)用call.execute方法株灸。
可見(jiàn),Retrofit中確實(shí)是按照OkHttp3的四個(gè)步驟進(jìn)行使用擎值。
OkHttp的原理
上面介紹了OkHttp的使用方法慌烧,以及Retrofit中怎樣使用OkHttp。那么下面我們就來(lái)分析一下OkHttp的原理鸠儿,OkHttp是怎樣發(fā)送Http請(qǐng)求的屹蚊?我們還是按照這四個(gè)步驟進(jìn)行分析厕氨。
-
創(chuàng)建OkHttpClient對(duì)象
我們看一下OkHttpClient的實(shí)現(xiàn):
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
Protocol.HTTP_2, Protocol.HTTP_1_1);
static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
final Dispatcher dispatcher; //請(qǐng)求分發(fā)器
final @Nullable Proxy proxy;
final List<Protocol> protocols; //支持的協(xié)議,默認(rèn)http1.1和http2
final List<ConnectionSpec> connectionSpecs;
final List<Interceptor> interceptors; //用戶設(shè)置的攔截器
final List<Interceptor> networkInterceptors;
final EventListener.Factory eventListenerFactory; //請(qǐng)求事件監(jiān)聽(tīng)器工廠
final ProxySelector proxySelector;
final CookieJar cookieJar; //可設(shè)置往請(qǐng)求頭中加入cookie信息
final @Nullable Cache cache; //可設(shè)置是否緩存請(qǐng)求Response策略
final @Nullable InternalCache internalCache; //OkHttp3.0之后使用cache汹粤,不再使用此成員變量
final SocketFactory socketFactory;
final @Nullable SSLSocketFactory sslSocketFactory;
final @Nullable CertificateChainCleaner certificateChainCleaner;
final HostnameVerifier hostnameVerifier;
final CertificatePinner certificatePinner;
final Authenticator proxyAuthenticator;
final Authenticator authenticator;
final ConnectionPool connectionPool;
final Dns dns;
final boolean followSslRedirects;
final boolean followRedirects;
final boolean retryOnConnectionFailure;
final int connectTimeout; //默認(rèn)連接超時(shí)時(shí)間10s
final int readTimeout; //默認(rèn)讀取超時(shí)時(shí)間10s
final int writeTimeout; //默認(rèn)寫(xiě)入超時(shí)時(shí)間10s
final int pingInterval;
//省略其他代碼
}
部分關(guān)鍵的成員標(biāo)了注釋命斧,各位同學(xué)可以看一下。著重關(guān)注一下Dispatcher類嘱兼,它是網(wǎng)絡(luò)請(qǐng)求分發(fā)器国葬,同步請(qǐng)求和異步請(qǐng)求會(huì)做不同的分發(fā)處理,后面會(huì)詳細(xì)介紹該類芹壕。
-
創(chuàng)建Request對(duì)象
看一下Request的實(shí)現(xiàn):
/**
* An HTTP request. Instances of this class are immutable if their {@link #body} is null or itself
* immutable.
*/
public final class Request {
final HttpUrl url;
final String method;
final Headers headers;
final @Nullable RequestBody body;
final Object tag;
private volatile CacheControl cacheControl; // Lazily initialized.
Request(Builder builder) {
this.url = builder.url;
this.method = builder.method;
this.headers = builder.headers.build();
this.body = builder.body;
this.tag = builder.tag != null ? builder.tag : this;
}
//省略其他代碼
}
從注釋中可以知道汇四,這個(gè)類代表Http請(qǐng)求,其中包含了Http請(qǐng)求所需的信息踢涌。
-
url
:請(qǐng)求地址 -
methdo
:請(qǐng)求類型通孽,如GET、POST睁壁、DELETE背苦、PATCH、PUT等 -
headers
:請(qǐng)求頭信息 -
body
:請(qǐng)求體 -
tag
:標(biāo)簽
我們?cè)诳匆幌翲eader類的結(jié)構(gòu):
public final class Headers {
private final String[] namesAndValues;
Headers(Builder builder) {
this.namesAndValues = builder.namesAndValues.toArray(new String[builder.namesAndValues.size()]);
}
//省略其他代碼
}
可以看到Headers類中通過(guò)一個(gè)字符串?dāng)?shù)組保存頭信息潘明,保存方式如下{key1, value1, key2, value2, ...}
再看一下RequestBody類的結(jié)構(gòu):
public abstract class RequestBody {
/** Returns the Content-Type header for this body. */
public abstract @Nullable MediaType contentType();
/**
* Returns the number of bytes that will be written to {@code sink} in a call to {@link #writeTo},
* or -1 if that count is unknown.
*/
public long contentLength() throws IOException {
return -1;
}
/** Writes the content of this request to {@code sink}. */
public abstract void writeTo(BufferedSink sink) throws IOException;
//省略其他代碼
}
RequestBody是一個(gè)抽象類行剂,其中有兩個(gè)抽象方法:
-
writeTo方法
:用于把請(qǐng)求體的內(nèi)容寫(xiě)入到sink(發(fā)送請(qǐng)求到服務(wù)器的對(duì)象)中 -
contentType方法
:標(biāo)志請(qǐng)求體內(nèi)容的類型。
Request相關(guān)內(nèi)容介紹完畢钉疫,接下來(lái)看一下創(chuàng)建Call部分硼讽。
-
創(chuàng)建Call對(duì)象
Call對(duì)象,我們可以理解為一次網(wǎng)絡(luò)請(qǐng)求的封裝牲阁,一個(gè)Call對(duì)象只能被執(zhí)行一次。那么壤躲,是如果保證只能被執(zhí)行一次的特性呢城菊?
上面提到過(guò),Retrofit會(huì)為一次請(qǐng)求創(chuàng)建一個(gè)RealCall對(duì)象:
final class RealCall implements Call {
final OkHttpClient client;
final RetryAndFollowUpInterceptor retryAndFollowUpInterceptor;
/**
* There is a cycle between the {@link Call} and {@link EventListener} that makes this awkward.
* This will be set after we create the call instance then create the event listener instance.
*/
private EventListener eventListener;
/** The application's original request unadulterated by redirects or auth headers. */
final Request originalRequest;
final boolean forWebSocket;
// Guarded by this.
private boolean executed;
private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
this.client = client;
this.originalRequest = originalRequest;
this.forWebSocket = forWebSocket;
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
}
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
@Override 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));
}
//省略其他代碼
}
我們可以看到碉克,RealCall中有個(gè)布爾型的變量executed
凌唬,標(biāo)志該Call是否已經(jīng)執(zhí)行了。
在execute和enqueue兩個(gè)方法中都會(huì)校驗(yàn)executed漏麦,這就保證了一個(gè)Call只能執(zhí)行一次客税。
-
調(diào)用call.execute或call.enqueue
再看execute和enqueue兩個(gè)方法,都會(huì)調(diào)用Dispatcher對(duì)應(yīng)的方法撕贞。那就看看Dispatcher的實(shí)現(xiàn):
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() {
}
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
//省略其他代碼
}
Dispatcher中有幾個(gè)成員變量更耻,依次介紹一下:
-
maxRequests
:表示異步請(qǐng)求支持的最大并發(fā)請(qǐng)求數(shù) -
maxRequestsPerHost
:表示每個(gè)Host支持的最大并發(fā)請(qǐng)求數(shù),Host可以理解為baseUrl -
idleCallback
:分發(fā)器閑置回調(diào)捏膨,即分發(fā)器中沒(méi)有正在執(zhí)行的請(qǐng)求和準(zhǔn)備執(zhí)行的請(qǐng)求秧均,就會(huì)回調(diào)idleCallback.run方法 -
executorService
:線程池食侮,用于管理異步請(qǐng)求的線程 -
readyAsyncCalls
:處于準(zhǔn)備狀態(tài)的異步請(qǐng)求隊(duì)列 -
runningAsyncCalls
:正在執(zhí)行的異步請(qǐng)求隊(duì)列 -
runningSyncCalls
:正在執(zhí)行的同步請(qǐng)求隊(duì)列
著重關(guān)注兩個(gè)異步請(qǐng)求隊(duì)列,什么時(shí)候會(huì)進(jìn)入異步準(zhǔn)備隊(duì)列目胡,什么時(shí)候又會(huì)進(jìn)入異步執(zhí)行隊(duì)列呢锯七?
runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost
滿足上面條件,即異步線程數(shù)量已經(jīng)超過(guò)最大請(qǐng)求數(shù)誉己,或者單個(gè)Host的異步請(qǐng)求數(shù)超過(guò)最大請(qǐng)求數(shù)眉尸,就會(huì)進(jìn)入異步準(zhǔn)備隊(duì)列,否則直接進(jìn)入異步執(zhí)行隊(duì)列巨双。
我們?cè)倏碊ispatcher的executed和enqueue方法效五,發(fā)現(xiàn)executed方法很簡(jiǎn)單:
/** Used by {@code Call#execute} to signal it is in-flight. */
synchronized void executed(RealCall call) {
runningSyncCalls.add(call);
}
這里只是把同步請(qǐng)求加入同步執(zhí)行隊(duì)列中,并沒(méi)有具體的請(qǐng)求執(zhí)行操作炉峰。那么這個(gè)執(zhí)行操作在哪里呢畏妖?
我們看回RealCall的execute方法:
@Override public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
client.dispatcher().executed(this);
Response result = getResponseWithInterceptorChain();
if (result == null) throw new IOException("Canceled");
return result;
} catch (IOException e) {
eventListener.callFailed(this, e);
throw e;
} finally {
client.dispatcher().finished(this);
}
}
在調(diào)用了Dispatcher.executed方法后,會(huì)調(diào)用getResponseWithInterceptorChain()
方法疼阔,那么可以肯定戒劫,這個(gè)方法就是真正執(zhí)行請(qǐng)求的地方。
再看一下異步請(qǐng)求婆廊,RealCall的enqueue方法中迅细,調(diào)用Dispatcher.enqueue方法時(shí),會(huì)創(chuàng)建一個(gè)AsyncCall對(duì)象作為參數(shù)傳入淘邻。那我們看一下AsyncCall的實(shí)現(xiàn):
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;
}
@Override protected void execute() {
boolean signalledCallback = false;
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) {
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);
}
}
}
在AsyncCall的execute方法中茵典,也會(huì)調(diào)用getResponseWithInterceptorChain()
方法。
也就是說(shuō)宾舅,不管是同步還是異步請(qǐng)求统阿,都是通過(guò)getResponseWithInterceptorChain()方法真正執(zhí)行請(qǐng)求的。
接下來(lái)筹我,當(dāng)然是看getResponseWithInterceptorChain方法的實(shí)現(xiàn):
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);
}
這個(gè)方法中扶平,先得到一個(gè)interceptors列表,其中包含的元素有:
用戶設(shè)置的interceptor列表
-
RetryAndFollowUpInterceptor
:錯(cuò)誤重試和重定向攔截器 -
BridgeInterceptor
:把用戶的request轉(zhuǎn)換為Http的request蔬蕊,把Http的response轉(zhuǎn)換為用戶需要response的轉(zhuǎn)換橋攔截器 -
CacheInterceptor
:處理Response緩存的攔截器 -
ConnectInterceptor
:建立服務(wù)器連接的攔截器 用戶設(shè)置的networkInterceptors列表
-
CallServerInterceptor
:真正向服務(wù)器發(fā)送請(qǐng)求并且得到響應(yīng)的攔截器
然后創(chuàng)建一個(gè)RealInterceptorChain對(duì)象结澄,繼而調(diào)用其proceed方法。
RealInterceptorChain實(shí)現(xiàn)了Interceptor.Chain接口岸夯,先看一下接口定義:
/**
* Observes, modifies, and potentially short-circuits requests going out and the corresponding
* responses coming back in. Typically interceptors add, remove, or transform headers on the request
* or response.
*/
public interface Interceptor {
Response intercept(Chain chain) throws IOException;
interface Chain {
Request request();
Response proceed(Request request) throws IOException;
/**
* Returns the connection the request will be executed on. This is only available in the chains
* of network interceptors; for application interceptors this is always null.
*/
@Nullable Connection connection();
Call call();
int connectTimeoutMillis();
Chain withConnectTimeout(int timeout, TimeUnit unit);
int readTimeoutMillis();
Chain withReadTimeout(int timeout, TimeUnit unit);
int writeTimeoutMillis();
Chain withWriteTimeout(int timeout, TimeUnit unit);
}
}
再看RealInterceptorChain的定義:
public final class RealInterceptorChain implements Interceptor.Chain {
private final List<Interceptor> interceptors;
private final StreamAllocation streamAllocation;
private final HttpCodec httpCodec;
private final RealConnection connection;
private final int index;
private final Request request;
private final Call call;
private final EventListener eventListener;
private final int connectTimeout;
private final int readTimeout;
private final int writeTimeout;
private int calls;
public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
this.interceptors = interceptors;
this.connection = connection;
this.streamAllocation = streamAllocation;
this.httpCodec = httpCodec;
this.index = index;
this.request = request;
this.call = call;
this.eventListener = eventListener;
this.connectTimeout = connectTimeout;
this.readTimeout = readTimeout;
this.writeTimeout = writeTimeout;
}
//省略部分代碼
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
RealConnection connection) throws IOException {
if (index >= interceptors.size()) throw new AssertionError();
calls++;
// If we already have a stream, confirm that the incoming request will use it.
if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must retain the same host and port");
}
// If we already have a stream, confirm that this is the only call to chain.proceed().
if (this.httpCodec != null && calls > 1) {
throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
+ " must call proceed() exactly once");
}
// Call the next interceptor in the chain.
RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
writeTimeout);
Interceptor interceptor = interceptors.get(index);
Response response = interceptor.intercept(next);
// Confirm that the next interceptor made its required call to chain.proceed().
if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
throw new IllegalStateException("network interceptor " + interceptor
+ " must call proceed() exactly once");
}
// Confirm that the intercepted response isn't null.
if (response == null) {
throw new NullPointerException("interceptor " + interceptor + " returned null");
}
if (response.body() == null) {
throw new IllegalStateException(
"interceptor " + interceptor + " returned a response with no body");
}
return response;
}
}
我們著重看proceed方法麻献,proceed方法我用一張流程圖來(lái)總結(jié):
通過(guò)這張圖,可以很清晰的看到執(zhí)行的流程:
通過(guò)RealInterceptorChain一層一層調(diào)用各interceptor的intercept方法猜扮,并且在此過(guò)程中可以處理request勉吻,比如往其中添加參數(shù),或者做一些其他操作破镰。一直到CallServerInterceptor餐曼,它是最后一個(gè)攔截器压储,真正用來(lái)執(zhí)行網(wǎng)絡(luò)請(qǐng)求,從服務(wù)器獲取Response源譬。然后把Response一層一層網(wǎng)上傳遞集惋,在此過(guò)程中,可以對(duì)Response做一定的處理踩娘。
這個(gè)過(guò)程用到了責(zé)任鏈模式刮刑,以鏈?zhǔn)秸{(diào)用,每一個(gè)節(jié)點(diǎn)可以執(zhí)行不同的邏輯养渴,各節(jié)點(diǎn)之間解耦雷绢,擴(kuò)展性強(qiáng),可以隨意添加節(jié)點(diǎn)理卑。
想知道怎樣執(zhí)行網(wǎng)絡(luò)請(qǐng)求的同學(xué)翘紊,可以自行研究各個(gè)Interceptor,尤其是CallServerInterceptor藐唠,它是真正執(zhí)行網(wǎng)絡(luò)請(qǐng)求的地方帆疟。主要是調(diào)用了Http1Codec.flushRequest方法,繼而調(diào)用BufferedSink.flush方法宇立,之后的就留給各位同學(xué)自己去看了踪宠。
最后得到的Response對(duì)象交給Retrofit進(jìn)行轉(zhuǎn)換適配,到這里OkHttp的原理分析完畢妈嘹。
對(duì)比OkHttp與其他網(wǎng)絡(luò)庫(kù)
這里主要對(duì)比幾個(gè)常用的網(wǎng)絡(luò)擴(kuò)柳琢,包括android-async-http、volley润脸、OkHttp和Retrofit柬脸。
- android-async-http(loopj)
- 基于HttpClient
- 自動(dòng)請(qǐng)求重試
- 持久化cookie,保存在sp中
- 作者已經(jīng)停止對(duì)項(xiàng)目維護(hù)津函,因此不推薦在項(xiàng)目中使用
- volley(Google)
- 基于HttpURLConnection
- 封裝圖片框架肖粮,支持圖片加載
- 與Activity生命周期的聯(lián)動(dòng),Activit結(jié)束時(shí)取消所有的網(wǎng)絡(luò)請(qǐng)求
- 適合數(shù)據(jù)量小尔苦,頻率高的請(qǐng)求,不適合上傳或下載大文件行施,原因:線程池默認(rèn)大小是4允坚,Request.getBody()返回的是字節(jié)數(shù)組,也就是對(duì)于Post或Put請(qǐng)求蛾号,會(huì)把傳輸?shù)臄?shù)據(jù)一股腦讀到內(nèi)存中稠项,很容易出現(xiàn)oom
- OkHttp(Square)
- 不基于HttpClient和HttpURLConnection,本身可以理解為一個(gè)封裝之后的HttpURLConnection
- 集各種優(yōu)點(diǎn)與一身
- Android4.4源碼可以看到HttpURLConnection已經(jīng)替換為OkHttp實(shí)現(xiàn)鲜结,可見(jiàn)其強(qiáng)大
- Retrofit(Square)
- 基于OkHttp
- RESTful Api設(shè)計(jì)風(fēng)格
- 通過(guò)注解配置請(qǐng)求
- 支持同步展运、異步
- 易與其他框架配合使用活逆,如Rxjava
- 使用方法較多,原理比較復(fù)雜拗胜,存在一定的門檻
- 高度解耦蔗候,擴(kuò)展性極好,堪稱代碼設(shè)計(jì)的典范
總結(jié)
相信看到這里埂软,各位同學(xué)對(duì)OkHttp有了一個(gè)深刻的認(rèn)識(shí)锈遥,再結(jié)合Retrofit那篇文章看,就更能前后貫通理解整個(gè)Retrofit+OkHttp的流程勘畔。