Android okhttp詳解

一:okhttp是什么

? ? ? ?大家都知道HTTP是現(xiàn)代應(yīng)用網(wǎng)絡(luò)請(qǐng)求的方式咆槽,是一個(gè)簡(jiǎn)單的請(qǐng)求-響應(yīng)協(xié)議来吩。在數(shù)據(jù)和媒體中進(jìn)行交換俐银。有效地請(qǐng)求使您的東西加載更快,并節(jié)省帶寬理逊。OKHttp是安卓開發(fā)中十分常用的網(wǎng)絡(luò)請(qǐng)求框架橡伞。

二:為什么使用

? ? ? 1:緩存響應(yīng)數(shù)據(jù)來(lái)減少重復(fù)的網(wǎng)絡(luò)請(qǐng)求

? ? ? ?2:可以從很多常用的連接問(wèn)題中自動(dòng)恢復(fù);(Okhttp處理了很多的網(wǎng)絡(luò)疑難雜癥:會(huì)從很多常用的連接問(wèn)題中自動(dòng)恢復(fù)晋被。如果您的服? ? ? ? 務(wù)器配置了多個(gè)IP地址兑徘,當(dāng)?shù)谝粋€(gè)IP連接失敗時(shí),OkHttp會(huì)嘗試連接下一個(gè)IP羡洛。)

? ? ? ?3:支持gzip壓縮響應(yīng)體

? ? ? ?4:使用簡(jiǎn)單挂脑,可擴(kuò)展性非常的強(qiáng);(責(zé)任鏈模式使得很容易添加一個(gè)自定義攔截器對(duì)請(qǐng)求和返回結(jié)果進(jìn)行處理)

三:用來(lái)干什么

? ? ? 1:支持一般的get請(qǐng)求欲侮,post請(qǐng)求

? ? ? ?2:基于Http的文件上傳最域,文件下載,上傳下載的進(jìn)度回調(diào)

? ? ? ?3:圖片加載

? ? ? ?4:支持請(qǐng)求回調(diào)锈麸,對(duì)象返回

? ? ? ?5:? ?表單請(qǐng)求

四:請(qǐng)求流程

? ? ? ?1:生成一個(gè)OkHttpClient(用以總的控制)

? ? ? ?2:用各種鍵值對(duì)包裝我們的Request

? ? ? ?3:將請(qǐng)求加入隊(duì)列生成一個(gè)Call管理請(qǐng)求

? ? ? ?4:若是同步請(qǐng)求直接執(zhí)行excute等待處理返回Response,若為異步則實(shí)現(xiàn)Callback回調(diào)牺蹄,在onResponse里獲取Response參數(shù)

請(qǐng)求流程

五:同步與異步的實(shí)現(xiàn)

在發(fā)起請(qǐng)求時(shí)忘伞,整個(gè)框架主要通過(guò)Call來(lái)封裝每一次的請(qǐng)求。同時(shí)Call持有OkHttpClient和一份HttpEngine沙兰。而每一次的同步或者異步請(qǐng)求都會(huì)有Dispatcher的參與氓奈。

Dispatcher

Dispatcher內(nèi)部實(shí)現(xiàn)了懶加載無(wú)邊界限制的線程池方式,同時(shí)該線程池采用了 SynchronousQueue這種阻塞隊(duì)列鼎天。SynchronousQueue每個(gè)插入操作必須等待另一個(gè)線程的移除操作舀奶,同樣任何一個(gè)移除操作都等待另一個(gè)線程的插入操作。因此此隊(duì)列內(nèi)部其實(shí)沒有任何一個(gè)元素斋射,或者說(shuō)容量是0育勺,嚴(yán)格說(shuō)并不是一種容器但荤。由于隊(duì)列沒有容量,因此不能調(diào)用peek操作涧至,因?yàn)橹挥幸瞥貢r(shí)才有元素腹躁。顯然這是一種快速傳遞元素的方式,也就是說(shuō)在這種情況下元素總是以最快的方式從插入者(生產(chǎn)者)傳遞給移除者(消費(fèi)者)南蓬,這在多任務(wù)隊(duì)列中是最快處理任務(wù)的方式纺非。對(duì)于高頻繁請(qǐng)求的場(chǎng)景,無(wú)疑是最適合的赘方。

同步

Dispatcher會(huì)在同步執(zhí)行任務(wù)隊(duì)列中記錄當(dāng)前被執(zhí)行過(guò)得任務(wù)Call烧颖,同時(shí)在當(dāng)前線程中去執(zhí)行Call的getResponseWithInterceptorChain()方法,直接獲取當(dāng)前的返回?cái)?shù)據(jù)Response窄陡;

同步請(qǐng)求

異步

異步執(zhí)行是通過(guò)Call.enqueue(Callback responseCallback)來(lái)執(zhí)行炕淮,在Dispatcher中添加一個(gè)封裝了Callback的Call的匿名內(nèi)部類Runnable來(lái)執(zhí)行當(dāng)前 的Call。這里一定要注意的地方這個(gè)AsyncCall是Call的匿名內(nèi)部類泳梆。AsyncCall的execute方法仍然會(huì)回調(diào)到Call的 getResponseWithInterceptorChain方法來(lái)完成請(qǐng)求鳖悠,同時(shí)將返回?cái)?shù)據(jù)或者狀態(tài)通過(guò)Callback來(lái)完成。

異步請(qǐng)求

六:攔截器

攔截器

攔截器可以注冊(cè)為應(yīng)用攔截器和網(wǎng)絡(luò)攔截器优妙。假如有一個(gè)壓縮攔截器和一個(gè)檢驗(yàn)和攔截器:你需要決定是先數(shù)據(jù)進(jìn)行壓縮然后檢驗(yàn)和乘综,還是先檢驗(yàn)和然后進(jìn)行壓縮。OkHttp使用列表來(lái)跟蹤攔截器套硼,并且攔截器按順序被調(diào)用卡辰。

應(yīng)用攔截器

應(yīng)用攔截器

1:不需要關(guān)心像重定向和重試這樣的中間響應(yīng)。

2:總是調(diào)用一次邪意,即使HTTP響應(yīng)從緩存中獲取服務(wù)九妈。

3:監(jiān)視應(yīng)用原始意圖。不關(guān)心OkHttp注入的像If-None-Match頭雾鬼。

4:允許短路并不調(diào)用Chain.proceed()萌朱。

5:允許重試并執(zhí)行多個(gè)Chain.proceed()調(diào)用。

網(wǎng)絡(luò)攔截器

網(wǎng)絡(luò)攔截器

1:可以操作像重定向和重試這樣的中間響應(yīng)策菜。

2:對(duì)于短路網(wǎng)絡(luò)的緩存響應(yīng)不會(huì)調(diào)用晶疼。

3:監(jiān)視即將要通過(guò)網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)。

4:訪問(wèn)運(yùn)輸請(qǐng)求的Connection又憨。

七:線程池技術(shù)

相比我們對(duì)于異步任務(wù)的需求應(yīng)該遇到了不少翠霍,首先想到的便是Thread,handler等異步機(jī)制蠢莺,Java已經(jīng)做了很好的封裝寒匙,但是當(dāng)我們需要使用許多異步任務(wù),而這些任務(wù)只做了一點(diǎn)點(diǎn)事情就完成了它的使命躏将,當(dāng)我們不斷的創(chuàng)建锄弱,銷毀線程的時(shí)候考蕾,對(duì)系統(tǒng)的開銷是相當(dāng)大的,因?yàn)檫@些過(guò)程也是耗費(fèi)時(shí)間的棵癣,這個(gè)時(shí)候我們就需要用到線程池了辕翰,我們只要往池子里放任務(wù),他就會(huì)自動(dòng)幫你管理線程的創(chuàng)建與銷毀狈谊,利用緩存復(fù)用減少線程銷毀創(chuàng)建喜命,優(yōu)化系統(tǒng)性能。以下是線程池的優(yōu)點(diǎn):

1:通過(guò)對(duì)線程進(jìn)行緩存河劝,減少了創(chuàng)建銷毀的時(shí)間損失

2:通過(guò)控制線程數(shù)量閥值壁榕,減少了當(dāng)線程過(guò)少時(shí)帶來(lái)的CPU閑置(比如說(shuō)長(zhǎng)時(shí)間卡在I\O上了)與線程過(guò)多時(shí)對(duì)JVM的內(nèi)存與線程切換壓力而Dispatcher內(nèi)部的核心即線程池

Dispatch線程池

int corePoolSize: 最小并發(fā)線程數(shù),這里并發(fā)同時(shí)包括空閑與活動(dòng)的線程赎瞎,如果是0的話牌里,空閑一段時(shí)間后所有線程將全部被銷毀。

int maximumPoolSize: 最大線程數(shù)务甥,當(dāng)任務(wù)進(jìn)來(lái)時(shí)可以擴(kuò)充的線程最大值牡辽,當(dāng)大于了這個(gè)值就會(huì)根據(jù)丟棄處理機(jī)制來(lái)處理

long keepAliveTime: 當(dāng)線程數(shù)大于corePoolSize時(shí),多余的空閑線程的最大存活時(shí)間敞临,類似于HTTP中的Keep-alive

TimeUnit unit: 時(shí)間單位态辛,一般用秒

BlockingQueue workQueue: 工作隊(duì)列

ThreadFactory threadFactory: 單個(gè)線程的工廠挺尿,可以打Log奏黑,設(shè)置Daemon(即當(dāng)JVM退出時(shí)编矾,線程自動(dòng)結(jié)束)等

OkHttp內(nèi)Dispatcher原理其實(shí)當(dāng)我們調(diào)用client.newCall(request).enqueue(new callback(){...})的時(shí)候?qū)嵸|(zhì)是下圖


反向代理模式

為了解決單生產(chǎn)者多消費(fèi)者問(wèn)題,OkHttp采用了反向代理模式限寞,來(lái)解決非阻塞缎岗,高并發(fā)問(wèn)題

工作模式

Dispatch模式

OkHttp內(nèi)Dispatcher原理其實(shí)當(dāng)我們調(diào)用client.newCall(request).enqueue(new callback(){...})的時(shí)候?qū)嵸|(zhì)是判斷線程池內(nèi)有無(wú)空閑拦盹,否則進(jìn)入等待隊(duì)列,AsyncCall實(shí)質(zhì)是Runnable溪椎,當(dāng)任務(wù)執(zhí)行完畢后普舆,總會(huì)調(diào)用finally{client.dispatcher().finished(this);}清除此任務(wù)。我們回頭看看OkhttpClient初始化內(nèi)其它一些參數(shù)Expires校读,Cache-Control沼侣,Last-Modified,If-Modified-Since歉秫,ETag蛾洛,If-None-Match...這些都牽扯到緩存問(wèn)題。

Response response = getResponseWithInterceptorChain();

其實(shí)這個(gè)方法是返回我們的response對(duì)象雁芙。到這里整個(gè)流程已經(jīng)完畢轧膘。

使用OkHttpClient 的時(shí)候需要注意以下幾點(diǎn):

1:最好只使用一個(gè)共享的OkHttpClient 實(shí)例,將所有的網(wǎng)絡(luò)請(qǐng)求都通過(guò)這個(gè)實(shí)例處理兔甘。因?yàn)槊總€(gè)OkHttpClient 實(shí)例都有自己的連接池和線程池谎碍,重用這個(gè)實(shí)例能降低延時(shí),減少內(nèi)存消耗裂明,而重復(fù)創(chuàng)建新實(shí)例則會(huì)浪費(fèi)資源椿浓。

2:OkHttpClient的線程池和連接池在空閑的時(shí)候會(huì)自動(dòng)釋放,所以一般情況下不需要手動(dòng)關(guān)閉闽晦,但是如果出現(xiàn)極端內(nèi)存不足的情況扳碍,可以使用以下代碼釋放內(nèi)存:

???? ?client.dispatcher().executorService().shutdown(); ??//清除并關(guān)閉線程池

??????client.connectionPool().evictAll(); ????????????????//清除并關(guān)閉連接池

??????client.cache().close(); ??????

3:如果對(duì)一些請(qǐng)求需要特殊定制,可以使用

OkHttpClient eagerClient = client.newBuilder()

????.readTimeout(500, TimeUnit.MILLISECONDS)

????.build();

這樣創(chuàng)建的實(shí)例與原實(shí)例共享線程池仙蛉、連接池和其他設(shè)置項(xiàng)笋敞,只需進(jìn)行少量配置就可以實(shí)現(xiàn)特殊需求。

Request

Request是網(wǎng)絡(luò)請(qǐng)求的對(duì)象荠瘪,其本身的構(gòu)造函數(shù)是private的夯巷,只能通過(guò)Request.Builder來(lái)構(gòu)建。下面代碼展示了常用的設(shè)置項(xiàng)哀墓。

Request request = new?Request.Builder()

????????.url("https://api.github.com/repos/square/okhttp/issues")? ? ? ? ? ? ? ? ? ? ? ? ?//設(shè)置訪問(wèn)url

????????.get()? //類似的有post趁餐、delete、patch篮绰、head后雷、put等方法,對(duì)應(yīng)不同的網(wǎng)絡(luò)請(qǐng)求方法

????????.header("User-Agent", "OkHttp Headers.java") ??????????????????????????????????????//設(shè)置header

????????.addHeader("Accept", "application/json; q=0.5")? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//添加header

????????.removeHeader("User-Agent")? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //移除header

????????.headers(new?Headers.Builder().add("User-Agent", "OkHttp Headers.java").build()) ??

? ? ? ? ?//移除原有所有header,并設(shè)置新header

????????.addHeader("Accept", "application/vnd.github.v3+json")

????????.build();? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//構(gòu)建request

RequestBody

在Request中使用post臀突、patch等方法時(shí)勉抓,需要傳入一個(gè)RequestBody參數(shù),除了上一節(jié)講到的構(gòu)造方法外候学,RequestBody還有兩個(gè)子類:FormBody和MultipartBody藕筋。

FromBody用于提交表單鍵值對(duì),其作用類似于HTML中的<form>標(biāo)記梳码。

RequestBody formBody = new?FormBody.Builder() ????????????????????????????//提交表單鍵值對(duì)

????????.add("platform", "android")? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //添加鍵值對(duì)

????????.add("name", "XXX")

????????.add("subject", "Hello")

????????.addEncoded(URLEncoder.encode("詳細(xì)","GBK"), URLEncoder.encode("無(wú)","GBK"))

? ? ? ? //添加已編碼的鍵值對(duì)

????????.add("描述","你好")? ? ? ? ? //其實(shí)會(huì)自動(dòng)編碼隐圾,但是無(wú)法控制編碼格式

????????.build();

使用MultipartBody.Builder可以構(gòu)建與HTML文件上傳格式兼容的復(fù)雜請(qǐng)求體。多塊請(qǐng)求體中每塊請(qǐng)求都是一個(gè)獨(dú)立的請(qǐng)求體边翁,都可以定義自己的請(qǐng)求頭翎承。這些請(qǐng)求頭應(yīng)該用于描述對(duì)應(yīng)的請(qǐng)求體,例如Content-Disposition符匾,Content-Length叨咖,和Content-Type會(huì)自動(dòng)被添加到請(qǐng)求頭中。

Call

Call對(duì)象表示一個(gè)已經(jīng)準(zhǔn)備好可以執(zhí)行的請(qǐng)求啊胶,用這個(gè)對(duì)象可以查詢請(qǐng)求的執(zhí)行狀態(tài)甸各,或者取消當(dāng)前請(qǐng)求。它具有以下方法:

Call call=client.newCall(request); ???????????????//獲取Call對(duì)象

Response response=call.execute(); ????????????????//同步執(zhí)行網(wǎng)絡(luò)請(qǐng)求焰坪,不要在主線程執(zhí)行

call.enqueue(new?Callback()); ????????????????????//異步執(zhí)行網(wǎng)絡(luò)請(qǐng)求

call.cancel(); ???????????????????????????????????//取消請(qǐng)求

call.isCanceled(); ???????????????????????????????//查詢是否取消

call.isExecuted(); ???????????????????????????????//查詢是否被執(zhí)行過(guò)

要注意的是趣倾,每個(gè)Call對(duì)象只能執(zhí)行一次請(qǐng)求。如果想重復(fù)執(zhí)行相同的請(qǐng)求某饰,可以:

Call reCall=client.newCall(call.request()); ??????//獲取另一個(gè)相同配置的Call對(duì)象

Response

Response是網(wǎng)絡(luò)請(qǐng)求的結(jié)果下面是一些常用方法:

Response response=call.execute(); ???????????????//獲取Response對(duì)象

response.code(); ????????????????????????????????//請(qǐng)求的狀態(tài)碼

response.isSuccessful(); ????????????????????????//如果狀態(tài)碼為[200..300)儒恋,則表明請(qǐng)求成功

Headers headers=response.headers(); ?????????????//獲取響應(yīng)頭

List<String> names=response.headers("name"); ????//獲取響應(yīng)頭中的某個(gè)字段

ResponseBody body=response.body(); ??????????????//獲取響應(yīng)體

其中ResponseBody代表響應(yīng)體,用于操作網(wǎng)絡(luò)請(qǐng)求返回的內(nèi)容黔漂。常用方法如下:

body.contentLength(); ??????????????????????????//body的長(zhǎng)度

String content=body.string(); ??????????????????//以字符串形式解碼bodybyte[] byteContent=body.bytes(); ???????????????//以字節(jié)數(shù)組形式解碼body

InputStreamReader reader=body.charStream(); ????//將body以字符流的形式解碼

InputStream inputStream=body.byteStream(); ?????//將body以字節(jié)流的形式解碼

ResponseBody

1:ResponseBody必須關(guān)閉诫尽,不然可能造成資源泄漏,你可以通過(guò)以下方法關(guān)閉ResponseBody,對(duì)同一個(gè)ResponseBody只要關(guān)閉一次就可以了炬守。

Response.close();

Response.body().close();

Response.body().source().close();

Response.body().charStream().close();

Response.body().byteString().close();

Response.body().bytes();

Response.body().string();

2:ResponseBody只能被消費(fèi)一次牧嫉,也就是string(),bytes(),byteStream()或 charStream()方法只能調(diào)用其中一個(gè)。

3:如果ResponseBody中的數(shù)據(jù)很大减途,則不應(yīng)該使用bytes() 或 string()方法酣藻,它們會(huì)將結(jié)果一次性讀入內(nèi)存,而應(yīng)該使用byteStream()或 charStream()鳍置,以流的方式讀取數(shù)據(jù)辽剧。

八:OkHttp中的設(shè)計(jì)模式

1:責(zé)任鏈模式:攔截器鏈

2:?jiǎn)卫J剑壕€程池

3:觀察者模式:各種回調(diào)監(jiān)聽

4:策略模式:緩存策略

5:Builder模式:OkHttpClient的構(gòu)建過(guò)程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市税产,隨后出現(xiàn)的幾起案子怕轿,更是在濱河造成了極大的恐慌坊夫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撤卢,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡梧兼,警方通過(guò)查閱死者的電腦和手機(jī)放吩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)羽杰,“玉大人渡紫,你說(shuō)我怎么就攤上這事】既” “怎么了惕澎?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)颜骤。 經(jīng)常有香客問(wèn)我唧喉,道長(zhǎng),這世上最難降的妖魔是什么忍抽? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任八孝,我火速辦了婚禮,結(jié)果婚禮上鸠项,老公的妹妹穿的比我還像新娘干跛。我一直安慰自己,他們只是感情好祟绊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布楼入。 她就那樣靜靜地躺著,像睡著了一般牧抽。 火紅的嫁衣襯著肌膚如雪嘉熊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天阎姥,我揣著相機(jī)與錄音记舆,去河邊找鬼。 笑死呼巴,一個(gè)胖子當(dāng)著我的面吹牛泽腮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播衣赶,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼诊赊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了府瞄?” 一聲冷哼從身側(cè)響起碧磅,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碘箍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鲸郊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體丰榴,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年秆撮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了四濒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡职辨,死狀恐怖盗蟆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舒裤,我是刑警寧澤喳资,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站腾供,受9級(jí)特大地震影響仆邓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伴鳖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一宏赘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧黎侈,春花似錦察署、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至休吠,卻和暖如春扳埂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瘤礁。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工阳懂, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柜思。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓岩调,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赡盘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子号枕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

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