OkHttp 源碼分析

流程分析

我們從一個簡單的 HTTP 請求開始:

client = new OkHttpClient();
Request request = new Request.Builder().url("your url").build();
//同步發(fā)起請求
Response syncResponse = client.newCall(request).execute();
//異步發(fā)起請求
client.newCall(request).enqueue(new Callback() {
    @Override
    public void onFailure(@NotNull Call call, @NotNull IOException e) { }
    @Override
    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { }
});

上面的代碼將會發(fā)起兩個簡單的 HTTP 請求,請求流程如下圖所示。

上面的流程圖只畫到了責(zé)任鏈的部分排宰,前面的介紹完后會單獨(dú)介紹責(zé)任鏈及每個 interceptor 的原理那婉。

OkHttpClient

我們使用 new OkHttpClient() 創(chuàng)建一個默認(rèn)的 OkHttpClient板甘,同樣也可以使用 OkHttpClient.Builder 來通過自定義的參數(shù)來構(gòu)造一個 client。
后面我們使用網(wǎng)絡(luò)請求時都將通過這個 client 來進(jìn)行详炬,可以將它理解為整個 OkHttp 的核心類虾啦,其對整體 OkHttp 進(jìn)行了封裝,對外提供了請求的發(fā)起以及一些參數(shù)配置的接口,我們可以通過 OkHttpClient.Builder 來設(shè)置傲醉,對內(nèi)負(fù)責(zé)協(xié)調(diào)各個類的運(yùn)作蝇闭,本身其實并沒有包含過多的代碼。

Request

Request 很好理解硬毕,負(fù)責(zé)組裝請求呻引。

Call(RealCall)

然后調(diào)用 client.newCall(request) 方法,該方法是指創(chuàng)建一個新的將要被執(zhí)行的請求吐咳,通過 newCall 方法獲取一個 Call 對象(實現(xiàn)為 RealCall)逻悠,這時我們使用 Call 的 execute/enqueue 將發(fā)起一個同步/異步請求。

所以每一個 Request 最終將會被封裝成一個 RealCall 對象韭脊,RealCall 與 Request 是一一對應(yīng)的關(guān)系童谒,Call 用來描述一個可被執(zhí)行、中斷的請求沪羔,我們每發(fā)起一個請求時就會創(chuàng)建一個 RealCall 對象饥伊,最終調(diào)用 RealCall#getResponseWithInterceptorChain() 發(fā)起請求,該方法將返回一個響應(yīng)結(jié)果 Response蔫饰。

Dispatcher

Dispatcher 用于管理其對應(yīng) OkHttpClient 的所有請求琅豆,通過上面的流程圖可以看到,使用異步請求時會將請求委托給 Dispatcer 對象來處理篓吁,Dispatcher 對象隨 OkHttpClient 創(chuàng)建而創(chuàng)建茫因。

實際上,Dispatcher 不僅用于管理異步請求杖剪,也負(fù)責(zé)管理同步請求冻押,當(dāng)我們發(fā)起一個請求時,無論是異步還是同步都會被 Dispatcher 記錄下來盛嘿。我們可以通過 OkHtpClient#dispatcher() 獲取 Dispatcher 對象對請求進(jìn)行統(tǒng)一的控制洛巢,例如結(jié)束所有請求、獲取線程池等等孩擂。
Dispatcher 中包含三個隊列:

  • readyAsyncCalls:一個新的異步請求首先會被加入該隊列中
  • runningAsyncCalls:當(dāng)前正在運(yùn)行中的異步請求
  • runningSyncCalls:當(dāng)前正在運(yùn)行的同步請求

Dispatcher 中包含一個默認(rèn)的線程池用于執(zhí)行所有的異步請求狼渊,也可以通過構(gòu)造器指定一個線程池箱熬,所有的異步請求都將會通過這個線程池來執(zhí)行类垦。異步請求與同步請求一樣,最終也會調(diào)用 RealCall#getResponseWithInterceptorChain() 發(fā)起請求城须,只不過一個是直接調(diào)用蚤认,一個是在線程池中調(diào)用。

通過上面的介紹已經(jīng)發(fā)現(xiàn)了關(guān)鍵之處就在于那個名字超長的方法糕伐,只要調(diào)用了它就能返回一個 Response砰琢,這個方法就開始涉及到廣為人知的 OkHttp 的責(zé)任鏈模式了。

OkHttp 責(zé)任鏈

講真的,網(wǎng)上現(xiàn)在隨便搜搜 OkHttp 源碼的都是在將責(zé)任鏈陪汽,搞的我都不想講了训唱,但是作為一個 OkHttp 源碼分析的文章不講又感覺過不去,那還是說一下吧(這里點(diǎn)名批評一些為知筆記的 MarkDown 編輯器挚冤,寫了一下午的東西說沒就沒况增,絲毫沒有給我反應(yīng)的余地)。

這一切都還要從那個名字超長的方法開始說起训挡,我們知道澳骤,無論如何都會調(diào)用 RealCall#getResponseWithInterceptorChain() 發(fā)起請求并獲取最終的 Response。

這個方法會根據(jù)用戶設(shè)置的 Interceptor 以及默認(rèn)的幾個 Interceptor 組裝 Interceptor 列表澜薄,然后創(chuàng)建責(zé)任鏈为肮。責(zé)任鏈創(chuàng)建好后會調(diào)用其 process 方法獲取 Response 并返回,其中涉及兩個概念:Interceptor肤京、Chain颊艳。

Interceptor

Interceptor 接口作為一個攔截器的抽象概念,被設(shè)計為責(zé)任鏈上的單位節(jié)點(diǎn)蟆沫,用于觀察籽暇、攔截、處理請求等饭庞,例如添加 Header戒悠、重定向、數(shù)據(jù)處理等等舟山。
Interceptor 之間互相獨(dú)立绸狐,每個 Interceptor 只負(fù)責(zé)自己關(guān)注的任務(wù),不與其他 Interceptor 接觸累盗。
Interceptor 接口中只包含一個方法(OkHttp 現(xiàn)在已經(jīng)用 Kotlin 重寫了):

interface Interceptor {
  @Throws(IOException::class)
  fun intercept(chain: Chain): Response
}

intercept 方法接收一個 Chain 作為參數(shù)寒矿,并返回一個 Response,該方法的一般處理邏輯如下:

上面的流程中的步驟并不是必選的若债,也不是一定要按照這個步驟來符相,你完全可以按照自己的想法進(jìn)行你喜歡的各種騷操作。
在 RealCall 中會按照順序添加如下幾個默認(rèn)的 Interceptor 到責(zé)任鏈中用來完成基本功能:

  • 用戶設(shè)置的 Interceptor
  • RetryAndFollowUpInterceptor:失敗重試及重定向
  • BridgeInterceptor:處理網(wǎng)絡(luò) Header蠢琳、Cookie啊终、gzip 等
  • CacheInterceptor:管理緩存
  • ConnectInterceptor:連接服務(wù)器
  • 如果是 WebSocket 請求則添加對應(yīng)的 Interceptors
  • CallServerInterceptor:數(shù)據(jù)發(fā)送/接收

后面再詳細(xì)介紹這幾個 Interceptor 的具體含義及原理。
責(zé)任鏈將會按照添加的順序依次執(zhí)行這些 Interceptor傲须,所以蓝牲,順序是很重要的,通過這些 Interceptor 的處理泰讽,最終會返回一個完美的 Response 給到 RealCall 里面的那個名字超長的方法例衍,然后在返回到下游用戶昔期。至此,一個完整的請求就落下帷幕了佛玄。

Chain

Chain 被用來描述責(zé)任鏈硼一,通過其中的 process 方法開始依次執(zhí)行鏈上的每個節(jié)點(diǎn),并返回處理后的 Response梦抢。
Chain 的唯一實現(xiàn)為 RealInterceptorChain(下文簡稱 RIC)欠动,RIC 可以稱之為攔截器責(zé)任鏈,其中的節(jié)點(diǎn)由 RealCall 中添加進(jìn)來的 Interceptor 們組成惑申。由于 Interceptor 的互相獨(dú)立性具伍,RIC 中還會包含一些公共參數(shù)及共享的對象。

Interceptor 與 Chain 彼此互相依賴圈驼,互相調(diào)用人芽,共同發(fā)展,形成了一個完美的調(diào)用鏈绩脆,下面來看下他們的調(diào)用關(guān)系圖:

通過上圖可以明確的看到篡石,當(dāng)我們在某個 Interceptor 中調(diào)用 Chain#process 方法獲取 Response 時恕洲,將會依調(diào)用當(dāng)前位置之后的 Interceptor 來處理這個請求屈梁,處理完成后把 Response 返回到當(dāng)前 Interceptor盐碱,然后處理完再向上級返回,直到遍歷結(jié)束玉锌。

網(wǎng)絡(luò)連接與數(shù)據(jù)收發(fā)

上面已經(jīng)介紹了 OkHttp 的基本概念名挥、基礎(chǔ)配置、線程控制主守、責(zé)任鏈禀倔,下面再說說一個網(wǎng)絡(luò)框架的靈魂:網(wǎng)絡(luò)請求的建立與數(shù)據(jù)收發(fā)。
RealCall 中添加的幾個不同的 Interceptor 就互相協(xié)作完成了這些功能参淫,只要明白了這幾個基礎(chǔ)的 interceptor 就明白了 OkHttp 的靈魂救湖。
其實我不太建議閱讀源碼時太過關(guān)心實現(xiàn)細(xì)節(jié),只要明白設(shè)計思路涎才,大體上的實現(xiàn)就差不多了鞋既,不然容易被負(fù)責(zé)的細(xì)節(jié)繞暈。
那么在介紹這幾個 interceptor 之前先介紹一些 OkHttp 中的基本概念耍铜。

連接如何建立

我們之前看的 Volley 啊等等很多網(wǎng)絡(luò)請求框架很多底層都是通過 HTTPURLConnection 來與服務(wù)端建立連接的邑闺,而 OkHttp 就比較優(yōu)秀了。因為 HTTP 協(xié)議是建立在 TCP/IP 協(xié)議基礎(chǔ)之上的业扒,底層還是走的 Socket检吆,所以 OkHttp 直接使用 Socket 來完成 HTTP 請求舒萎。

Route

route 為用于連接到服務(wù)器的具體路由程储。其中包含了 IP 地址蹭沛、端口、代理等參數(shù)章鲤。
由于存在代理或者 DNS 可能返回多個 IP 地址的情況摊灭,所以同一個接口地址可能會對應(yīng)多個 route。
在創(chuàng)建 Connection 時將會使用 Route 而不是直接用 IP 地址败徊。

RouteSelector

Route 選擇器帚呼,其中存儲了所有可用的 route,在準(zhǔn)備連接時時會通過 RouteSelector#next 方法獲取下一個 Route皱蹦。
值得注意的是煤杀,RouteSelector 中包含了一個 routeDatabase 對象,其中存放著連接失敗的 Route沪哺,RouteSelector 會將其中存儲的上次連接失敗的 route 放在最后沈自,以此提高連接速度。

RealConnection

RealConnection 實現(xiàn)了 Connection 接口辜妓,其中使用 Socket 建立 HTTP/HTTPS 連接,并且獲取 I/O 流枯途,同一個 Connection 可能會承載多個 HTTP 的請求與響應(yīng)
其實可以大概的理解為是對 Socket 籍滴、I/O 流以及一些協(xié)議的封裝酪夷,這個里面涉及到的計算機(jī)網(wǎng)絡(luò)相關(guān)的知識較多,例如 TLS 握手孽惰,HTTPS 驗證等等晚岭。

RealConnectionPool

這是用來存儲 RealConnection 的池子,內(nèi)部使用一個雙端隊列來進(jìn)行存儲勋功。
在 OkHttp 中腥例,一個連接(RealConnection)用完后不會立馬被關(guān)閉并釋放掉,而且是會存儲到連接池(RealConnectionPool)中酝润。
除了緩存連接外燎竖,緩存池還負(fù)責(zé)定期清理過期的連接,在 RealConnection 中會維護(hù)一個用來描述該連接空閑時間的字段要销,每添加一個新的連接到連接池中時都會進(jìn)行一次檢測构回,遍歷所有的連接,找出當(dāng)前未被使用且空閑時間最長的那個連接疏咐,如果該連接空閑時長超出閾值纤掸,或者連接池已滿,將會關(guān)閉該連接浑塞。
另外 RealConnection 中還維護(hù)一個 Transmitter 的弱引用列表借跪,用來存儲當(dāng)前正在使用該連接的 Transmitter。當(dāng)列表為空時表示該連接已經(jīng)未在使用酌壕。

ExchangeCodec

ExchangeCodec 負(fù)責(zé)對 Request 編碼及解碼 Response掏愁,也就是寫入請求及讀取響應(yīng)歇由,我們的請求及響應(yīng)數(shù)據(jù)都通過它來讀寫。
所以 Connection 負(fù)責(zé)建立連接果港,ExchangeCodec 負(fù)責(zé)收發(fā)數(shù)據(jù)沦泌。
ExchangeCodec 接口的實現(xiàn)類有兩個:Http1ExchangeCodec 及 Http2ExchangeCodec,分別對應(yīng)兩種協(xié)議版本辛掠。

Exchange

Exchange 功能類似 ExchangeCodec谢谦,但它是對應(yīng)的是單個請求,其在 ExchangeCodec 基礎(chǔ)上擔(dān)負(fù)了一些連接管理及事件分發(fā)的作用萝衩。
具體而言回挽,Exchange 與 Request 一一對應(yīng),新建一個請求時就會創(chuàng)建一個 Exchange猩谊,該 Exchange 負(fù)責(zé)將這個請求發(fā)送出去并讀取到響應(yīng)數(shù)據(jù)厅各,而發(fā)送與接收數(shù)據(jù)使用的是 ExchangeCodec。

Transmitter

Transmitter 是 OkHttp 網(wǎng)絡(luò)層的橋梁预柒,我們上面說的這些概念最終都是通過 Transmitter 來融合在一起队塘,并對外提供功能實現(xiàn)。

好了宜鸯,現(xiàn)在基本概念介紹完畢憔古,開始看看 interceptor 吧。

RetryAndFollowUpInterceptor

這個 interceptor 顧名思義淋袖,負(fù)責(zé)失敗重試以及重定向鸿市。
可能出觸發(fā)重試或重定向的條件如下:

  • 401:未授權(quán)
  • 407:代理未授權(quán)
  • 503:服務(wù)未授權(quán)
  • 3xx:請求重定向
  • 408:請求超時
  • 以及一些 I/O 異常等等連接失敗的情況

下面看一下其中的邏輯:

我們上面說過,因為代理及 DNS 的原因即碗,對于同一個 url 可能會有多個 IP 地址焰情,連接時通過 RouteSelector 選擇合適的 Route 進(jìn)行連接,所以這里的失敗重試并不是指對同一 IP 地址的多次重試剥懒,是逐個嘗試路由表中的地址内舟。
上面連接失敗之后,進(jìn)行重試時雖然并沒有其它操作初橘,但實際上開始連接時會自動調(diào)用下一個 Route 進(jìn)行連接验游。

上圖流程中有兩處重點(diǎn)這里再介紹一下,分別是 followUpRequest 及 recover 方法保檐。
recover 方法用于判斷連接是否可以恢復(fù)重試耕蝉,代碼如下:

  private fun recover(
    e: IOException,
    transmitter: Transmitter,
    requestSendStarted: Boolean,
    userRequest: Request
  ): Boolean {
    // 用戶設(shè)置的是否重連參數(shù)
    if (!client.retryOnConnectionFailure) return false
    // 單次請求或 FileNotFoundException 異常不可恢復(fù) 
    if (requestSendStarted && requestIsOneShot(e, userRequest)) return false
    // 不可恢復(fù)的異常,例如協(xié)議錯誤夜只、驗證錯誤等
    if (!isRecoverable(e, requestSendStarted)) return false
    // 沒有更多的路由了
    if (!transmitter.canRetry()) return false
    return true
  }

現(xiàn)在再來看看 followUpRequest 方法垒在,這個代碼較多就不貼了。
當(dāng)走到這個方法時意味著已經(jīng)連接到服務(wù)器并接收到響應(yīng)了扔亥,此時需要通過響應(yīng)碼來判斷是否需要重定向场躯。

如果響應(yīng)碼為 401 或者 407 則表示請求未認(rèn)證谈为,此時重新對請求進(jìn)行認(rèn)證,然后返回認(rèn)證后的 Request推盛。

響應(yīng)碼為 3xx 表示重定向,此時重定向地址在響應(yīng) Header 的 Location 字段中谦铃,然后通過這個新的地址以及之前的 Request 構(gòu)建一個新的 Request 并返回耘成。

響應(yīng)碼 503 表示服務(wù)器錯誤,但這個是暫時的驹闰,可能馬上就會恢復(fù)瘪菌,所以會直接返回之前的請求。

BridgeInterceptor

BridgeInterceptor 是用戶與網(wǎng)絡(luò)之間的橋梁嘹朗,負(fù)責(zé)將用戶請求轉(zhuǎn)換為網(wǎng)絡(luò)請求师妙,也就是根據(jù) Request 信息組建網(wǎng)絡(luò) Header 以及設(shè)置響應(yīng)數(shù)據(jù)。
實際上屹培,BridgeInterceptor 除了設(shè)置例如 Content-Length默穴、Connect、Host 之類的基本請求頭之外褪秀,還負(fù)責(zé)設(shè)置 Cookie 以及 gzip.
BridgeInterceptor 在開始進(jìn)行網(wǎng)絡(luò)請求之前會先通過 url 判斷是否有 cookie蓄诽,有的話就會把這個 cookie 帶上,請求結(jié)束后同樣也會判斷響應(yīng)頭是否包含 Set-Cookie 字段媒吗,包含則會將其存下來下次使用仑氛。但是存儲 Cookie 的操作會委托給 CookieJar 來實現(xiàn),OkHttp 默認(rèn)提供了一個空的 CookieJar 對象闸英,也就是說默認(rèn)不會做出任何操作锯岖,但可以在創(chuàng)建 OkHttp 時指定一個自己的 CookieJar 來使用。

如果 Request 請求頭中沒有包含 Accept-Encoding 以及 Range 字段則會給其添加一個 "Accept-Encoding: gzip" 請求頭甫何,接收到響應(yīng)數(shù)據(jù)后如果響應(yīng)表示使用了 gzip 則會把響應(yīng)數(shù)據(jù)交給 okio 的 GzipSource 解碼出吹。

CacheInterceptor

CacheInterceptor 負(fù)責(zé)緩存響應(yīng)數(shù)據(jù)。
該方法首先會通過 Cache 對象嘗試獲取緩存的數(shù)據(jù)辙喂,然后再通過 CacheStrategy 獲取緩存策略趋箩,通過該策略的計算結(jié)果,我們可以獲取到兩個可空對象:networkRequest 以及 cacheResponse加派。
其中 networkRequest 為原始 Request 但可能為空叫确,具體是不是空的通過 CacheStrategy 控制。
cacheResponse 是通過 Cache 獲取到的 Response芍锦,同上竹勉,同樣也可能為空。
然后就可以通過判斷兩個對象的可空性來處理緩存娄琉,邏輯如下:

  • 如果兩者都為空次乓,則表示既禁止了使用網(wǎng)絡(luò)請求吓歇,也不可以使用緩存或者未命中緩存,直接返回 504 錯誤票腰。
  • 如果只有 networkRequest 為空城看,表示禁止了網(wǎng)絡(luò)請求,此時直接返回從緩存中命中的 Response杏慰。
  • 如果兩者都不為空测柠,則開始發(fā)起請求并獲取響應(yīng)數(shù)據(jù)。
  • 如果此時 cacheResponse 不為空缘滥,且響應(yīng)碼為 304轰胁,直接返回 cacheResponse,并使用響應(yīng)數(shù)據(jù)更新緩存朝扼。
  • 如果 cacheResponse 為空則會將響應(yīng)數(shù)據(jù)存儲到 Cache 中赃阀。
  • 返回響應(yīng)數(shù)據(jù)。

需要注意擎颖,上面說的 Cache 對象默認(rèn)為空榛斯,如果為空則與其相關(guān)的操作都不會被執(zhí)行,且 cacheResponse 一定為空搂捧。
我們可以在 OkHttpClient 中設(shè)置 Cache肖抱。

ConnectInterceptor

ConnectInterceptor 用來打開一個到服務(wù)端的連接。
其中代碼很簡單异旧,會通過 Transmitter#newExchange 方法創(chuàng)建一個 Exchange 對象意述,并調(diào)用 Chain#process 方法。

newExchange 方法中會先通過 ExchangeFinder 嘗試去 RealConnectionPool 中尋找已存在的連接吮蛹,未找到則會重新創(chuàng)建一個 RealConnection 并開始連接荤崇,然后將其存入 RealConnectionPool,此時已經(jīng)準(zhǔn)備好了 RealConnection 對象潮针,然后通過請求協(xié)議創(chuàng)建不同的 ExchangeCodec 并返回术荤。具體細(xì)節(jié)上面已經(jīng)說過了,這里不做詳細(xì)介紹每篷。
通過上面面步驟創(chuàng)建好 ExchangeCodec 之后瓣戚,再根據(jù)它以及其他參數(shù)創(chuàng)建 Exchange 對象并返回。

ConnectInterceptor 將 newExchange 方法返回的 Exchange 對象作為參數(shù)焦读,調(diào)用 Chain#process 方法子库。

CallServerInterceptor

CallServerInterceptor 負(fù)責(zé)讀寫數(shù)據(jù)。
這是最后一個 interceptor 了矗晃,到了這里該準(zhǔn)備的都準(zhǔn)備好了仑嗅,通過它,將會把 Request 中的數(shù)據(jù)發(fā)送到服務(wù)端,并獲取到數(shù)據(jù)寫入 Response仓技。

上圖為該 interceptor 整體流程圖鸵贬。其中操作主要都放在了 Exchange 等對象中,這里不做過多介紹脖捻。

那么 OkHttp 的源碼到這里就分析的差不多啦阔逼,其實還有很多東西都沒有講到,OkHttp 是個龐大的框架地沮,其中涉及到的東西實在太多了嗜浮,而且包括了很多計算機(jī)網(wǎng)絡(luò)的基礎(chǔ)知識,奈何本人才疏學(xué)淺诉濒,就只講這么多吧周伦。

如果覺得還不錯的話夕春,歡迎關(guān)注我的個人公眾號:zhangke_blog

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末未荒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子及志,更是在濱河造成了極大的恐慌片排,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件速侈,死亡現(xiàn)場離奇詭異率寡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)倚搬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門冶共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人每界,你說我怎么就攤上這事捅僵。” “怎么了眨层?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵庙楚,是天一觀的道長。 經(jīng)常有香客問我趴樱,道長馒闷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任叁征,我火速辦了婚禮纳账,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捺疼。我一直安慰自己塞祈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著议薪,像睡著了一般尤蛮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上斯议,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天产捞,我揣著相機(jī)與錄音,去河邊找鬼哼御。 笑死坯临,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的恋昼。 我是一名探鬼主播看靠,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼液肌!你這毒婦竟也來了挟炬?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤嗦哆,失蹤者是張志新(化名)和其女友劉穎谤祖,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體老速,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粥喜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了橘券。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片额湘。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖旁舰,靈堂內(nèi)的尸體忽然破棺而出锋华,到底是詐尸還是另有隱情,我是刑警寧澤鬓梅,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布供置,位于F島的核電站,受9級特大地震影響绽快,放射性物質(zhì)發(fā)生泄漏芥丧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一坊罢、第九天 我趴在偏房一處隱蔽的房頂上張望续担。 院中可真熱鬧,春花似錦活孩、人聲如沸物遇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽询兴。三九已至乃沙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間诗舰,已是汗流浹背警儒。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留眶根,地道東北人蜀铲。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像属百,于是被迫代替她去往敵國和親记劝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

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

  • 那么我今天給大家簡單地講一下Okhttp這款網(wǎng)絡(luò)框架及其原理族扰。它是如何請求數(shù)據(jù)厌丑,如何響應(yīng)數(shù)據(jù)的 有什么優(yōu)點(diǎn)?它的應(yīng)...
    卓而不群_0137閱讀 313評論 0 1
  • 版本號:3.13.1 一.基本使用 Call可以理解為Request和Response之間的橋梁别伏,Http請求過程...
    慕涵盛華閱讀 1,016評論 0 8
  • 1蹄衷、說明 經(jīng)翻譯之后的結(jié)果如下HTTP是現(xiàn)代應(yīng)用網(wǎng)絡(luò)的方式忧额。這是我們交換數(shù)據(jù)和媒體的方式厘肮。高效地執(zhí)行HTTP可以使...
    Kevin_Lv閱讀 373評論 0 1
  • OkHttp作為時下最受歡迎的網(wǎng)絡(luò)請求框架之一,它有著自己的優(yōu)點(diǎn): 使用了眾多的設(shè)計模式(如:Builder模式睦番、...
    永恒之眼V閱讀 282評論 1 1
  • 功能介紹為了讓大家更清楚的知道今天要學(xué)習(xí)的效,當(dāng)文章瀏覽到某一個位置的時候點(diǎn)擊左上角返回托嚣,然后再次點(diǎn)擊剛剛瀏覽過的...
    陽光之城alt閱讀 1,661評論 0 2