OkHttp 3.12.0源碼分析

主要參考文章:
1.Okhttp的基本使用
2.Okhttp主流程源碼分析

Okhttp 3.12.0 使用例子

String url = "http://wwww.baidu.com";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .get()//默認(rèn)就是GET請(qǐng)求襟齿,可以不寫
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    @Override
    public void onFailure(Call call, IOException e) {
        Log.d("ziq", "onFailure: ");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
        Log.d("ziq", "onResponse: " + response.body().string());
    }
});

1、初始化

建造者模式瓤介,public OkHttpClient() { this(new Builder());} 初始化了一些配置信息:支持協(xié)議灭衷、任務(wù)分發(fā)器(其內(nèi)部包含一個(gè)線程池辆亏,執(zhí)行異步請(qǐng)求)、連接池(其內(nèi)部包含一個(gè)線程池晋涣,維護(hù)connection)、連接/讀/寫超時(shí)時(shí)長(zhǎng)等信息沉桌。

public Builder() {
    dispatcher = new Dispatcher();//任務(wù)調(diào)度器
    protocols = DEFAULT_PROTOCOLS;//支持的協(xié)議 Protocol.HTTP_2, Protocol.HTTP_1_1
    connectionSpecs = DEFAULT_CONNECTION_SPECS;
    eventListenerFactory = EventListener.factory(EventListener.NONE);
    proxySelector = ProxySelector.getDefault();
    if (proxySelector == null) {
        proxySelector = new NullProxySelector();
    }
    cookieJar = CookieJar.NO_COOKIES;
    socketFactory = SocketFactory.getDefault();
    hostnameVerifier = OkHostnameVerifier.INSTANCE;//根據(jù)地址與證書 進(jìn)行校驗(yàn)X509Certificate
    certificatePinner = CertificatePinner.DEFAULT;
    proxyAuthenticator = Authenticator.NONE;//身份驗(yàn)證
    authenticator = Authenticator.NONE;//身份驗(yàn)證
    connectionPool = new ConnectionPool();//連接池
    dns = Dns.SYSTEM;
    followSslRedirects = true;
    followRedirects = true;
    retryOnConnectionFailure = true;
    callTimeout = 0;
    connectTimeout = 10_000;
    readTimeout = 10_000;
    writeTimeout = 10_000;
    pingInterval = 0;
}

下面列出 Dispatcher 的主要分析部分

//Dispatcher.java
public final class Dispatcher {
private int maxRequests = 64;//最大請(qǐng)求數(shù)量
private int maxRequestsPerHost = 5;//每臺(tái)主機(jī)最大的請(qǐng)求數(shù)量
private @Nullable Runnable idleCallback;
private @Nullable ExecutorService executorService;//線程池
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();//等待隊(duì)列
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();//異步運(yùn)行中隊(duì)列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();//同步運(yùn)行中隊(duì)列

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;
}

谢鹊。。留凭。
void enqueue(AsyncCall call) {
    synchronized (this) {
        readyAsyncCalls.add(call);//添加到異步運(yùn)行隊(duì)列中
    }
    promoteAndExecute();//將 readyAsyncCalls中的call 添加到 runningAsyncCalls并運(yùn)行
}

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();
        //正在運(yùn)行的任務(wù)數(shù)是否大于 64
            if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        //對(duì)應(yīng)主機(jī) 的 正在運(yùn)行的任務(wù)數(shù)是否大于 5
            if (runningCallsForHost(asyncCall) >= maxRequestsPerHost) continue; // Host max capacity.
            i.remove();
            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;
}

佃扼。。蔼夜。
}

2兼耀、生成call 并 請(qǐng)求

//OkHttpClient.java
@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

//RealCall.java
new RealCall(client, originalRequest, forWebSocket);

@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));
}

那么從dispatcher的代碼中可知道,call直接加入到 readyAsyncCalls隊(duì)列中求冷,然后 在promoteAndExecute方法中判斷是否 可以執(zhí)行 readyAsyncCalls中的任務(wù)瘤运,如果可以則 把任務(wù)加入到runningAsyncCalls ,并 到線程池中執(zhí)行匠题。

3拯坟、執(zhí)行call

asyncCall.executeOn(executorService());
AsyncCall extends NamedRunnable implements Runnable
,所以運(yùn)行run() ->execute() ;

final class AsyncCall extends NamedRunnable {
韭山。郁季。。
@Override protected void execute() {
    boolean signalledCallback = false;
    timeout.enter();
    try {
//1钱磅、進(jìn)過(guò)一系列攔截器后梦裂,返回結(jié)果
        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 {
//2、結(jié)束當(dāng)前call,運(yùn)行未執(zhí)行且符合條件的call
        client.dispatcher().finished(this);
    }
}

}

4续搀、Interceptor攔截器

到了這里塞琼,到達(dá)了okhttp最核心的部分, Interceptor禁舷,采用了責(zé)任鏈的設(shè)計(jì)模式

4.1client.dispatcher().finished(this)彪杉;
//Dispatcher.java
private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
    if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
        idleCallback = this.idleCallback;
    }
    boolean isRunning = promoteAndExecute();
    if (!isRunning && idleCallback != null) {
        idleCallback.run();
    }
}

可以看到毅往,移除call后,又回到 一開始 的promoteAndExecute() 方法派近,去把readyAsyncCalls中的call 拿出來(lái)運(yùn)行攀唯。

4.2 責(zé)任鏈

Response response = getResponseWithInterceptorChain();進(jìn)過(guò)一系列攔截器后,返回結(jié)果

//RealCall.java
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);
}

添加的各Interceptor渴丸,分別負(fù)責(zé)功能:
client.interceptors() 用戶自定義的Interceptor侯嘀,能攔截到所有的請(qǐng)求
RetryAndFollowUpInterceptor 負(fù)責(zé)失敗重連和重定向相關(guān)
BridgeInterceptor 負(fù)責(zé)配置請(qǐng)求的頭信息,比如Keep-Alive谱轨、gzip戒幔、Cookie等可以優(yōu)化請(qǐng)求
CacheInterceptor 負(fù)責(zé)緩存管理,使用DiskLruCache做本地緩存土童,CacheStrategy決定緩存策略
ConnectInterceptor 開始與目標(biāo)服務(wù)器建立連接诗茎,獲得RealConnection
client.networkInterceptors() 用戶自定義的Interceptor,僅在生產(chǎn)網(wǎng)絡(luò)請(qǐng)求時(shí)生效
CallServerInterceptor 實(shí)際網(wǎng)絡(luò)請(qǐng)求的地方献汗。

遞歸實(shí)現(xiàn):
1敢订、RealInterceptorChain.proceed(...) 為開頭敦腔,會(huì)生成 下一步的RealInterceptorChain next1疟赊,與當(dāng)前的RealInterceptorChain參數(shù)一樣,區(qū)別是index 加1了,所以會(huì)調(diào)用 下一級(jí) 攔截器的interceptor1.intercept(next)
2湘捎、interceptor1.intercept(next)調(diào)用 下一個(gè)鏈 next1.proceed(...)方法
3尿招、next1.proceed(...)調(diào)用下一個(gè)interceptor2.intercept(next) 形成 遞歸
4矾柜、最后的攔截器是 CallServerInterceptor,返回response泊业,然后一級(jí)一級(jí)返回response把沼。

//RealInterceptorChain.java
public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,RealConnection connection)throws IOException {
    if (index >= interceptors.size()) throw new AssertionError();
    calls++;
    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 (this.httpCodec != null && calls > 1) {
        throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
        + " must call proceed() exactly once");
    }
//1、生成 下一步的RealInterceptorChain吁伺,與當(dāng)前的RealInterceptorChain參數(shù)一樣饮睬,區(qū)別是index 加1 了
    RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
        connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
        writeTimeout);
    Interceptor interceptor = interceptors.get(index);
 //2、Interceptor interceptor內(nèi)會(huì)調(diào)用 next.proceed(...) 又回到這里的第一步篮奄,再生成一個(gè)chain 形成遞歸
    Response response = interceptor.intercept(next);
//結(jié)束遞歸

    if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
        throw new IllegalStateException("network interceptor " + interceptor
        + " must call proceed() exactly once");
    }
    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;
}

各個(gè)攔截器部分細(xì)節(jié)描述:

攔截器 描述
RetryAndFollowUpInterceptor 生成 StreamAllocation 實(shí)例捆愁,后面的攔截器使用
BridgeInterceptor 添加header參數(shù)
CacheInterceptor 默認(rèn)是沒(méi)有緩存的,全都是直接網(wǎng)絡(luò)請(qǐng)求窟却。
ConnectInterceptor 利用上面 的StreamAllocation 建立 HttpCodec昼丑、RealConnection實(shí)例從connectionPool 尋找是否有RealConnection,有就直接賦值給StreamAllocation 中的connection 找不到就result = new RealConnection(connectionPool, selectedRoute);新建夸赫,賦值給StreamAllocation并且緩存到connectionPool
CallServerInterceptor 使用前面其他攔截器生成的 StreamAllocation 菩帝, RealConnection, HttpCodec;
而HttpCodec 中使用okio 通過(guò)Socket傳輸數(shù)據(jù)呼奢,網(wǎng)絡(luò)請(qǐng)求宜雀。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市握础,隨后出現(xiàn)的幾起案子辐董,更是在濱河造成了極大的恐慌,老刑警劉巖禀综,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件简烘,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡定枷,警方通過(guò)查閱死者的電腦和手機(jī)孤澎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)依鸥,“玉大人亥至,你說(shuō)我怎么就攤上這事〖伲” “怎么了?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵絮供,是天一觀的道長(zhǎng)衣吠。 經(jīng)常有香客問(wèn)我,道長(zhǎng)壤靶,這世上最難降的妖魔是什么缚俏? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮贮乳,結(jié)果婚禮上忧换,老公的妹妹穿的比我還像新娘。我一直安慰自己向拆,他們只是感情好亚茬,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著浓恳,像睡著了一般刹缝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颈将,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天梢夯,我揣著相機(jī)與錄音,去河邊找鬼晴圾。 笑死颂砸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播人乓,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼梗醇,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了撒蟀?” 一聲冷哼從身側(cè)響起叙谨,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎保屯,沒(méi)想到半個(gè)月后手负,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡姑尺,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年竟终,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片切蟋。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡统捶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柄粹,到底是詐尸還是另有隱情喘鸟,我是刑警寧澤,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布驻右,位于F島的核電站什黑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏堪夭。R本人自食惡果不足惜愕把,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望森爽。 院中可真熱鬧恨豁,春花似錦、人聲如沸爬迟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)雕旨。三九已至扮匠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凡涩,已是汗流浹背棒搜。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留活箕,地道東北人力麸。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親克蚂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子闺鲸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容

  • OkHttp源碼的samples的簡(jiǎn)單使用的示例: public static void main(String....
    _warren閱讀 763評(píng)論 0 1
  • 前言 用OkHttp很久了,也看了很多人寫的源碼分析埃叭,在這里結(jié)合自己的感悟摸恍,記錄一下對(duì)OkHttp源碼理解的幾點(diǎn)心...
    Java小鋪閱讀 1,523評(píng)論 0 13
  • 關(guān)于okhttp是一款優(yōu)秀的網(wǎng)絡(luò)請(qǐng)求框架,關(guān)于它的源碼分析文章有很多赤屋,這里分享我在學(xué)習(xí)過(guò)程中讀到的感覺(jué)比較好的文章...
    蕉下孤客閱讀 3,605評(píng)論 2 38
  • 簡(jiǎn)介 OkHttp 是一款用于 Android 和 Java 的網(wǎng)絡(luò)請(qǐng)求庫(kù)立镶,也是目前 Android 中最火的一個(gè)...
    然則閱讀 1,220評(píng)論 1 39
  • 只要一看《正面管教》這本書媚媒,心情變得平和。和孩子們溝通起來(lái)涩僻,也心平氣和缭召,正常得多了。 否則逆日,看到不順眼的地方嵌巷,就忍...
    曦水閱讀 111評(píng)論 0 0