最近學習了一下okhttp的源碼,發(fā)現(xiàn)okhttp是真滴復雜。因為okhttp是一個網(wǎng)絡請求庫,它涉及了網(wǎng)絡請求的方方面面,比如:http協(xié)議,socket通信,計算機網(wǎng)絡,線程的調(diào)度等等。所以我們不扣具體的實現(xiàn)細節(jié),只學習其中的原理和大概實現(xiàn),okhttp的內(nèi)容很多,所以分篇學習記錄。
okhttp的基本使用
OkHttpClient.Builder builder=new OkHttpClient.Builder();
OkHttpClient okHttpClient = builder.build(); //構造 OkHttpClient
//構造get請求
Request request = new Request.Builder()
.get() //Method GET
.url("https://www.baidu.com")
.build(); //構造請求信息
//構造post請求
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),
"json");
Request request = new Request.Builder()
.post(body) //Method POST
.url("https://www.baidu.com")
.build(); //構造請求信息
//發(fā)起同步請求
try {
Response response=okHttpClient.newCall(request).execute(); //發(fā)起同步請求
} catch (IOException e) {
e.printStackTrace();
}
//發(fā)起異步請求
okHttpClient.newCall(request)
.enqueue(new Callback() { //發(fā)起異步請求
@Override
public void onResponse(final Call call, final Response response) throws IOException {
//成功拿到響應
int code = response.code();
ResponseBody body = response.body();
String string = body.string();
Log.d("hx","code:"+code+"body:"+string);
}
@Override
public void onFailure(final Call call, final IOException e) {
e.printStackTrace();
}
});
通過基本的get,post,同步或異步請求示例,可以看出okhttp的請求流程:
- 1.構造OkhttpClient
- 2.構造請求信息Request(get或post)
- 3.執(zhí)行請求(同步或異步)
請求流程非常簡單,只有3步,這是okhttp幫我們封裝好的。但具體的請求原理,內(nèi)部怎么實現(xiàn)的呢?下面進入源碼看看
Okhttpclient
通過流程我們知道,要請求首先要構造一個okhttpClient對象。看一下他的構造方法:
public OkHttpClient() {
this(new Builder());
}
OkHttpClient(Builder builder) {
this.dispatcher = builder.dispatcher;
this.proxy = builder.proxy;
this.protocols = builder.protocols;
this.connectionSpecs = builder.connectionSpecs;
this.interceptors = Util.immutableList(builder.interceptors);
this.networkInterceptors = Util.immutableList(builder.networkInterceptors);
.....省略
}
okhttpClient對象的創(chuàng)建需要傳一個Builder對象,它的無參構造函數(shù)其實還是調(diào)用帶有Builder參數(shù)的構造函數(shù)。Builder是OkhttpClient的靜態(tài)內(nèi)部類,通過OkhttpClient的構造函數(shù)可以看到,okhttpClient對象在創(chuàng)建的時候需要初始化很多的網(wǎng)絡請求配置,所以就采用了建造者模式,方便調(diào)用者使用鏈式調(diào)用。
看一下靜態(tài)內(nèi)部類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;
......
public Builder() {
dispatcher = new Dispatcher();
protocols = DEFAULT_PROTOCOLS;
connectionSpecs = DEFAULT_CONNECTION_SPECS;
eventListenerFactory = EventListener.factory(EventListener.NONE);
proxySelector = ProxySelector.getDefault();
cookieJar = CookieJar.NO_COOKIES;
socketFactory = SocketFactory.getDefault();
hostnameVerifier = OkHostnameVerifier.INSTANCE;
....
}
可以看到,使用 OkhttpClient okhttpClient=new OkhttpCient()
這種方式創(chuàng)建的okhttpClient對象,會使用okhttp的默認配置。包括在前面的基本使用中使用的方式:
OkHttpClient.Builder builder=new OkHttpClient.Builder();
OkHttpClient okHttpClient = builder.build(); //構造 OkHttpClient
看一下Builder類的build();
public OkHttpClient build() {
return new OkHttpClient(this);
}
使用默認配置的話兩者都一樣可以創(chuàng)建okHttpClient對象。但是如果想自定義okhttp請求過程中的配置的話,就要通過builder.build()的方式創(chuàng)建。okhttp有很多的優(yōu)點,其中之一就是請求配置可以高度定制:
圖中列出的方法都是可以配置的屬性,其實還有很多沒列出來,有興趣的自己去文檔或源碼吧
Request
Request代表著一個請求信息
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;
}
通過它的成員可以看到,request包含的請求信息有:請求的url,請求方法method,請求頭header,請求體requestBody等。request的構造函數(shù)里面需要傳一個內(nèi)部類對象builder,request可以通過其內(nèi)部類對象builder修改其攜帶的請求信息
執(zhí)行請求
看一下執(zhí)行請求 okHttpClient.newCall(request)
的源碼:
@Override
public Call newCall(Request request) {
return RealCall.newRealCall(this, request, false /* for web socket */);
}
newCall(Request) 方法調(diào)用了 RealCall.newRealCall() 方法:
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;
}
okHttpClient.newCall(request)最終返回了一個RealCall對象。RealCall實現(xiàn)了Call接口,Call接口代表著一個準備被執(zhí)行的請求任務:
public interface Call extends Cloneable {
//返回這個call初始化時傳入的原始request
Request request();
//同步執(zhí)行請求任務(立即執(zhí)行),阻塞拿到響應
Response execute() throws IOException;
//異步執(zhí)行請求任務(通過線程池提交執(zhí)行)
void enqueue(Callback responseCallback);
//是否已執(zhí)行完畢
boolean isExecuted();
//是否取消
boolean isCanceled();
okhttp3.Call clone();
interface Factory {
okhttp3.Call newCall(Request request);
}
}
可以看到前面okhttp用法示例中發(fā)起同步請求的方法:
//發(fā)起同步請求
Response response=okHttpClient.newCall(request).execute(); //發(fā)起同步請求
以及異步請求的方法:
okHttpClient.newCall(request) .enqueue(new Callback() {.... } //發(fā)起異步請求
都是在Call中定義的. 拿到了Call的實例,RealCall 對象后,就可以發(fā)起請求了。首先看一下同步請求:
同步請求
//RealCall.execute()
@Override
public Response execute() throws IOException {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
try {
//實際調(diào)用的是dispatch.execute()
client.dispatcher().executed(this);
//執(zhí)行請求,拿到響應結果
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);
}
}
同步請求做了三步操作:
- 1.調(diào)用dispatcher的execute()
- 2.執(zhí)行請求任務
- 3.結束請求
再看一下異步請求:
異步請求
// //RealCall.enqueue()
@Override
public void enqueue(Callback responseCallback) {
synchronized (this) {
if (executed) throw new IllegalStateException("Already Executed");
executed = true;
}
captureCallStackTrace();
eventListener.callStart(this);
//實際是調(diào)用dispatcher的enqueue()方法
client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
很簡單,只是調(diào)用dispatcher的enqueue().所以不管是同步請求還是異步請求,都會去調(diào)用Dispatcher的相關方法。Dispatcher是負責請求任務的調(diào)度,是比較重要的一個類,大概的看一下:
public final class Dispatcher {
//最大并發(fā)數(shù),最多發(fā)起64個請求
private int maxRequests = 64;
//同一個host最多發(fā)起5個請求
private int maxRequestsPerHost = 5;
//空閑的任務
private @Nullable Runnable idleCallback;
//執(zhí)行異步請求任務的線程池
private @Nullable ExecutorService executorService;
//等待的異步請求隊列
final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的異步請求隊列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();
//正在執(zhí)行的同步請求隊列
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<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
}
return executorService;
}
....
}
由上面代碼可以得出Dispatcher內(nèi)部實現(xiàn)了懶加載的無邊界限制的線程池。參數(shù)解析
- 0:核心線程數(shù)量,保持在線程池中的線程數(shù)量(即使已經(jīng)空閑),為0代表線程空閑后不會保留,等待一段時間后停止。
- Integer.MAX_VALUE:表示線程池可以容納最大線程數(shù)量
- TimeUnit.SECOND:當線程池中的線程數(shù)量大于核心線程時,空閑的線程就會等待60s才會被終止,如果小于,則會立刻停止。
- new SynchronousQueue<Runnable>():線程等待隊列。同步隊列,按序排隊,先來先服務
- Util.threadFactory("OkHttp Dispatcher", false):線程工廠,直接創(chuàng)建一個名為OkHttp Dispatcher的非守護線程。
回到之前RealCall中的同步請求調(diào)用:client.dispatcher().executed(this)
:
//Dispatcher.executed
synchronized void executed(RealCall call) {
//只是把請求添加到正在執(zhí)行的請求隊列中
runningSyncCalls.add(call);
}
RealCall中的異步請求調(diào)用:client.dispatcher().enqueue(new AsyncCall(responseCallback))
,首先是創(chuàng)建了一個AsyncCall對象,然后傳給了enqueue()。先看一下AsyncCall:
final class AsyncCall extends NamedRunnable {
//響應回調(diào)
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 {
//異步執(zhí)行執(zhí)行請求拿到數(shù)據(jù)(這是在線程池中創(chuàng)建的線程里面執(zhí)行的,所以是異步的)
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);
}
}
}
那么NamedRunnable 又是什么呢:
public abstract class NamedRunnable implements Runnable {
protected final String name;
public NamedRunnable(String format, Object... args) {
this.name = Util.format(format, args);
}
@Override
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(name);
try {
execute();
} finally {
Thread.currentThread().setName(oldName);
}
}
protected abstract void execute();
}
可以看到NamedRunnable 只是一個實現(xiàn)了runnable接口的抽象類而已,run方法執(zhí)行的時候會執(zhí)行execute()方法,AsyncCall繼承了NamedRunnable,實現(xiàn)了excute()方法,在excute()中會去請求網(wǎng)絡拿到響應,然后結束請求。
繼續(xù)回到前面的RealCall中的異步請求調(diào)用:client.dispatcher().enqueue(new AsyncCall(responseCallback))
:
//Dispatcher.enqueue()
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}
可以看到dispatcher().enqueue()
里面的操作:
1.先判斷當前正在并發(fā)執(zhí)行的請求數(shù)是否小于最大數(shù)64,并且同一個host的請求數(shù)是否小于最大值5
2.如果2個條件都滿足直接把當前請求添加到正在執(zhí)行的請求隊列,然后通過線程池提交執(zhí)行請求任務
3.如果條件不滿足,則添加到等待的請求隊列中
在第二步,線程池執(zhí)行請求了,就會去執(zhí)行AsyncCall的execute()里面的代碼,去執(zhí)行請求,拿到響應結果,然后結束。
對比同步請求和異步請求的調(diào)用結果,可以發(fā)現(xiàn)他們的請求流程基本是一樣的:
- runningAsyncCalls.add(call) 都是先把當前請求Call添加到正在運行的請求隊列中
- Response response = getResponseWithInterceptorChain() 執(zhí)行請求,拿到響應結果
- client.dispatcher().finished(this) 結束請求
不同的是:
- 同步請求是直接添加到請求隊列,去請求。而異步請求不會立即請求,會先判斷當前正在運行的請求是否超過最大并發(fā)數(shù)以及同一host的請求數(shù)是否超過最大值5個,如果都不超過最大值,直接添加到請求隊列,去請求。超過了,則添加到等待隊列等待執(zhí)行。
- 同步請求并沒有創(chuàng)建工作線程去執(zhí)行,而異步請求是在線程池創(chuàng)建的異步線程中執(zhí)行的,包括Callback 回調(diào)也是。所以用異步請求拿到響應后,不能直接做UI的更新操作,因為不是在主線程。
結束請求
前面總結了同步請求或異步請求的基本請求流程,在請求的三步都是結束請求,那么結束請求具體做了什么操作呢?看一下RealCall里面無論是同步請求還是異步請求,結束請求調(diào)用client.dispatcher().finished(this)
的源碼:
//dispatcher.finished(RealCall call)
void finished(RealCall call) {
finished(runningSyncCalls, call, false);
}
同步或異步結束請求時都是調(diào)用這個代碼,只是第三個參數(shù)不同,同步請求時false,異步請求時true.再看一下finished()
:
//dispatcher. finished(Deque<T> calls, T call, boolean promoteCalls)
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!");
//如果需要將等待請求調(diào)整為正在執(zhí)行的請求,就將等待的請求添加到正在執(zhí)行的請求隊列,并執(zhí)行
if (promoteCalls) promoteCalls();
//統(tǒng)計正在執(zhí)行請求的數(shù)量
runningCallsCount = runningCallsCount();
idleCallback = this.idleCallback;
}
if (runningCallsCount == 0 && idleCallback != null) {
idleCallback.run();
}
}
可以看到如果是同步請求,第三個參數(shù)promoteCalls為false,不會去調(diào)用promoteCalls()方法,如果是異步請求,promoteCalls為true,會去調(diào)用promoteCalls()將等待的請求調(diào)整為執(zhí)行請求:
//dispatcher.promoteCalls
private void promoteCalls() {
if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.
for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
AsyncCall call = i.next();
//同一host上的請求數(shù)小于5個,就將當前的請求從等待調(diào)整為執(zhí)行
if (runningCallsForHost(call) < maxRequestsPerHost) {
i.remove();
runningAsyncCalls.add(call);
executorService().execute(call);
}
if (runningAsyncCalls.size() >= maxRequests) return; // Reached max capacity.
}
}
結束請求的基本操作就是從請求隊列移除當前請求,然后根據(jù)是否是異步請求來調(diào)整請求的狀態(tài)。如果是異步請求,就會去查看等待隊列中的請求是否能夠調(diào)整為執(zhí)行請求,如果可以調(diào)整,就添加到執(zhí)行請求隊列并提交給線程池執(zhí)行。
至此,一次網(wǎng)絡請求的流程就結束了,再最后總結一下okhttp的基本請求過程:
1.通過OkhttpClient的內(nèi)部類builder創(chuàng)建一個OkhttpClient對象
2.創(chuàng)建一個Request對象,通過Request對象封來裝請求信息
3.通過OkhttpClient拿到Call接口的實現(xiàn)類對象RealCall,再根據(jù)Requset的請求信息去調(diào)用RealCall的同步或異步請求方法來完成網(wǎng)絡請求