你想要的系列:網(wǎng)絡(luò)請(qǐng)求框架OkHttp3全解系列 - (一)OkHttp的基本使用

歡迎關(guān)注公眾號(hào):胡飛洋

因?yàn)橐恢睕](méi)有去詳細(xì)了解okhttp原理,在網(wǎng)上找了很多文章,發(fā)現(xiàn)沒(méi)有類似 郭霖的Glide系列那種 細(xì)致的詳解系列槐雾,很不爽,決定自己整一下幅狮,應(yīng)該會(huì)耗費(fèi)不少時(shí)間募强,不過(guò)也是對(duì)自己的挑戰(zhàn),還有點(diǎn)興奮呢崇摄。也希望和大家一起討論钻注。
這是第一篇,按照慣例配猫,就介紹基本的使用方法幅恋,比較簡(jiǎn)單。
好了泵肄,閑話少敘捆交,開(kāi)始!

所需 預(yù)備知識(shí):
HTTP協(xié)議詳解
HTTP請(qǐng)求報(bào)文和響應(yīng)報(bào)文

OkHttp3是由square公司開(kāi)發(fā)腐巢,Android中公認(rèn)最好用的網(wǎng)絡(luò)請(qǐng)求框架品追,在接口封裝上做的簡(jiǎn)單易用,GitHub地址冯丙。

它有以下默認(rèn)特性:

  • 支持HTTP/2肉瓦,允許所有同一個(gè)主機(jī)地址的請(qǐng)求共享同一個(gè)socket連接
  • 使用連接池減少請(qǐng)求延時(shí)
  • 透明的GZIP壓縮減少響應(yīng)數(shù)據(jù)的大小
  • 緩存響應(yīng)內(nèi)容,避免一些完全重復(fù)的請(qǐng)求

當(dāng)網(wǎng)絡(luò)出現(xiàn)問(wèn)題的時(shí)候OkHttp 會(huì)自動(dòng)恢復(fù)一般的連接問(wèn)題胃惜,如果你的服務(wù)有多個(gè)IP地址泞莉,當(dāng)?shù)谝粋€(gè)IP請(qǐng)求失敗時(shí),OkHttp會(huì)交替嘗試你配置的其他IP船殉。

一鲫趁、引入

gradle引入依賴即可。

    implementation 'com.squareup.okhttp3:okhttp:3.14.7'
    implementation 'com.squareup.okio:okio:1.17.5'

3.14.x版本及以前的版本利虫,采用Java語(yǔ)言編寫(xiě)挨厚,4.0.0以后采用kotlin語(yǔ)言;本系列文章中源碼引自3.14.x版本糠惫,以Java語(yǔ)言講解疫剃。

其中Okio庫(kù) 是對(duì)Java.io和java.nio的補(bǔ)充,以便能夠更加方便硼讽,快速的訪問(wèn)巢价、存儲(chǔ)和處理你的數(shù)據(jù)。OkHttp的底層使用該庫(kù)作為支持。

另外蹄溉,別忘了申請(qǐng)網(wǎng)絡(luò)請(qǐng)求權(quán)限咨油,如果還使用網(wǎng)絡(luò)請(qǐng)求的緩存功能,那么還要申請(qǐng)讀寫(xiě)外存的權(quán)限:

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

二柒爵、使用方式

基本使用步驟如下

  • 構(gòu)建客戶端對(duì)象OkHttpClient
  • 構(gòu)建請(qǐng)求Request
  • 生成Call對(duì)象
  • Call發(fā)起請(qǐng)求(同步/異步)

下面跟著具體使用實(shí)例役电,詳細(xì)介紹。

2.1 get請(qǐng)求

以百度主頁(yè)為例棉胀,進(jìn)行Get請(qǐng)求:

        OkHttpClient httpClient = new OkHttpClient();

        String url = "https://www.baidu.com/";
        Request getRequest = new Request.Builder()
                .url(url)
                .get()
                .build();

        Call call = httpClient.newCall(getRequest);

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //同步請(qǐng)求法瑟,要放到子線程執(zhí)行
                    Response response = call.execute();
                    Log.i(TAG, "okHttpGet run: response:"+ response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

首先,創(chuàng)建了OkHttpClient實(shí)例唁奢,接著用Request.Builder構(gòu)建了Request實(shí)例并傳入了百度主頁(yè)的url霎挟,然后httpClient.newCall方法傳入Request實(shí)例生成call,最后在子線程調(diào)用call.execute()執(zhí)行請(qǐng)求獲得結(jié)果response麻掸。

所以酥夭,使用OkHttp進(jìn)行g(shù)et請(qǐng)求,是比較簡(jiǎn)單的脊奋,只要在構(gòu)建Request實(shí)例時(shí)更換url就可以了熬北。

有個(gè)問(wèn)題,你可能注意到了诚隙,這里是放在子線程執(zhí)行請(qǐng)求的讶隐,這是因?yàn)閏all.execute()是同步方法。想要在主線程直接使用而不用手動(dòng)創(chuàng)建子線程可以嘛久又?當(dāng)然可以巫延,使用call.enqueue(callback)即可:

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

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

call.enqueue會(huì)異步執(zhí)行,需要注意的是地消,兩個(gè)回調(diào)方法onFailure炉峰、onResponse是執(zhí)行在子線程的,所以如果想要執(zhí)行UI操作犯建,需要使用Handler切換到UI線程讲冠。

另外,注意每一個(gè)Call只能執(zhí)行一次(原因會(huì)在下篇流程分析中說(shuō)明)适瓦。

執(zhí)行后結(jié)果打印如下:

2020-05-04 21:52:56.446 32681-3631/com.hfy.androidlearning I/OkHttpTestActivity: okHttpGet run: response:<!DOCTYPE html>
    <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新聞</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地圖</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>視頻</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>貼吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登錄</a> </noscript> <script>document.write('<a + encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登錄</a>');
                    </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多產(chǎn)品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>關(guān)于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必讀</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意見(jiàn)反饋</a>&nbsp;京ICP證030173號(hào)&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

可見(jiàn)百度首頁(yè)的get請(qǐng)求成功響應(yīng)了谱仪。

2.2 post請(qǐng)求

2.2.1 post請(qǐng)求提交String玻熙、文件

post請(qǐng)求與get請(qǐng)求的區(qū)別是 在構(gòu)造Request對(duì)象時(shí),需要多構(gòu)造一個(gè)RequestBody對(duì)象疯攒,用它來(lái)攜帶我們要提交的數(shù)據(jù)嗦随。示例如下:

        OkHttpClient httpClient = new OkHttpClient();

        MediaType contentType = MediaType.parse("text/x-markdown; charset=utf-8");
        String content = "hello!";
        RequestBody body = RequestBody.create(contentType, content);

        Request getRequest = new Request.Builder()
                .url("https://api.github.com/markdown/raw")
                .post(body)
                .build();

        Call call = httpClient.newCall(getRequest);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "okHttpPost enqueue: \n onResponse:"+ response.toString() +"\n body:" +response.body().string());
            }
        });

對(duì)比get請(qǐng)求,把構(gòu)建Request時(shí)的get()改成post(body),并傳入RequestBody實(shí)例枚尼。RequestBody實(shí)例是通過(guò)create方法創(chuàng)建贴浙,需要指定請(qǐng)求體內(nèi)容類型、請(qǐng)求體內(nèi)容署恍。這里是傳入了一個(gè)指定為markdown格式的文本崎溃。

結(jié)果打印如下:

2020-05-05 13:18:26.445 13301-13542/com.hfy.androidlearning I/OkHttpTestActivity: okHttpPost enqueue: 
     onResponse:Response{protocol=http/1.1, code=200, message=OK, url=https://api.github.com/markdown/raw}
     body:<p>hello!</p>

請(qǐng)求成功并把請(qǐng)求體內(nèi)容又返回來(lái)了。

傳入RequestBody的 MediaType 還可以是其他類型盯质,如客戶端要給后臺(tái)發(fā)送json字符串袁串、發(fā)送一張圖片,那么可以定義為:

// RequestBody:jsonBody呼巷,json字符串
String json = "jsonString";
RequestBody jsonBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json);

//RequestBody:fileBody, 上傳文件
File file = new File(Environment.getExternalStorageDirectory(), "1.png");
RequestBody fileBody = RequestBody.create(MediaType.parse("image/png"), file);

MediaType更多類型信息可以查看 RFC 2045囱修。

2.2.2 post請(qǐng)求提交表單

構(gòu)建RequestBody除了上面的方式,還有它的子類FormBody王悍,F(xiàn)ormBody用于提交表單鍵值對(duì)破镰,這種能滿足平常開(kāi)發(fā)大部分的需求。

//RequestBody:FormBody压储,表單鍵值對(duì)
RequestBody formBody = new FormBody.Builder()
        .add("username", "hfy")
        .add("password", "qaz")
        .build();

FormBody是通過(guò)FormBody.Builder用構(gòu)建者模式創(chuàng)建鲜漩,add鍵值對(duì)即可。它的contentType在內(nèi)部已經(jīng)指定了渠脉。

  private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");

2.2.2 post請(qǐng)求提交復(fù)雜請(qǐng)求體

RequestBody另一個(gè)子類MultipartBody宇整,用于post請(qǐng)求提交復(fù)雜類型的請(qǐng)求體。復(fù)雜請(qǐng)求體可以同時(shí)包含多種類型的的請(qǐng)求體數(shù)據(jù)芋膘。

上面介紹的 post請(qǐng)求 string鳞青、文件、表單为朋,只有單一類型臂拓。考慮一種場(chǎng)景--注冊(cè)場(chǎng)景习寸,用戶填寫(xiě)完姓名胶惰、電話,同時(shí)要上傳頭像圖片霞溪,這時(shí)注冊(cè)接口的請(qǐng)求體就需要 接受 表單鍵值對(duì) 以及文件了孵滞,那么前面講的的post就無(wú)法滿足了。那么就要用到MultipartBody了鸯匹。 完整代碼如下:

        OkHttpClient httpClient = new OkHttpClient();

//        MediaType contentType = MediaType.parse("text/x-markdown; charset=utf-8");
//        String content = "hello!";
//        RequestBody body = RequestBody.create(contentType, content);

        //RequestBody:fileBody,上傳文件
        File file = drawableToFile(this, R.mipmap.bigpic, new File("00.jpg"));
        RequestBody fileBody = RequestBody.create(MediaType.parse("image/jpg"), file);


        //RequestBody:multipartBody, 多類型 (用戶名坊饶、密碼、頭像)
        MultipartBody multipartBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("username", "hufeiyang")
                .addFormDataPart("phone", "123456")
                .addFormDataPart("touxiang", "00.png", fileBody)
                .build();


        Request getRequest = new Request.Builder()
                .url("http://yun918.cn/study/public/file_upload.php")
                .post(multipartBody)
                .build();

        Call call = httpClient.newCall(getRequest);

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

                Log.i(TAG, "okHttpPost enqueue: \n onFailure:"+ call.request().toString() +"\n body:" +call.request().body().contentType()
                +"\n IOException:"+e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.i(TAG, "okHttpPost enqueue: \n onResponse:"+ response.toString() +"\n body:" +response.body().string());
            }
        });

可見(jiàn)殴蓬,在構(gòu)建RequestBody時(shí)是使用MultipartBody.Builder構(gòu)建了MultipartBody實(shí)例匿级,通過(guò)addFormDataPart方法傳入了姓名、電話的鍵值對(duì),也通過(guò)addFormDataPart("touxiang", "00.png", fileBody)傳入了頭像圖片痘绎,其中"touxiang"是key值津函, "00.png"是文件名,fileBody是要以上傳的圖片創(chuàng)建的RequestBody孤页。
因?yàn)樗袛?shù)據(jù)都是以鍵值對(duì)的表單形式提交尔苦,所以要設(shè)置setType(MultipartBody.FORM)。

請(qǐng)求抓包結(jié)果:


在這里插入圖片描述

可見(jiàn)請(qǐng)求體重確實(shí)包含了姓名散庶、電話蕉堰、頭像,并且注意到Content-Type值是 multipart/form-data悲龟。響應(yīng)是200屋讶,說(shuō)明請(qǐng)求成功了。

其他請(qǐng)求方式像put须教、header皿渗、delete,主要在構(gòu)建Request時(shí)把get()或post()換成put()轻腺、header()乐疆、delete()就可以了,但一般在Android端很少用到贬养。

2.4 請(qǐng)求配置項(xiàng)

先看幾個(gè)問(wèn)題:

  1. 如何全局設(shè)置超時(shí)時(shí)長(zhǎng)?
  2. 緩存位置挤土、最大緩存大小 呢?
  3. 考慮有這樣一個(gè)需求误算,我要監(jiān)控App通過(guò) OkHttp 發(fā)出的 所有 原始請(qǐng)求仰美,以及整個(gè)請(qǐng)求所耗費(fèi)的時(shí)間,如何做儿礼?

這些問(wèn)題咖杂,在OkHttp這里很簡(jiǎn)單。把OkHttpClient實(shí)例的創(chuàng)建蚊夫,換成以下方式即可:

        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .writeTimeout(10, TimeUnit.SECONDS)
                .cache(new Cache(getExternalCacheDir(),500 * 1024 * 1024))
                .addInterceptor(new Interceptor() {
                    @Override
                    public Response intercept(Chain chain) throws IOException {
                        Request request = chain.request();
                        String url = request.url().toString();
                        Log.i(TAG, "intercept: proceed start: url"+ url+ ", at "+System.currentTimeMillis());
                        Response response = chain.proceed(request);
                        ResponseBody body = response.body();
                        Log.i(TAG, "intercept: proceed end: url"+ url+ ", at "+System.currentTimeMillis());
                        return response;
                    }
                })
                .build();

這里通過(guò)OkHttpClient.Builder通過(guò)構(gòu)建者模式設(shè)置了連接诉字、讀取、寫(xiě)入的超時(shí)時(shí)長(zhǎng)知纷,用cache()方法傳入了由緩存目錄壤圃、緩存大小構(gòu)成的Cache實(shí)例,這樣就解決了前兩個(gè)問(wèn)題琅轧。

還注意到埃唯,使用addInterceptor()方法添加了Interceptor實(shí)例,且重寫(xiě)了intercept方法鹰晨。Interceptor意為攔截器,intercept()方法會(huì)在開(kāi)始執(zhí)行請(qǐng)求時(shí)調(diào)用。其中chain.proceed(request)內(nèi)部是真正請(qǐng)求的過(guò)程模蜡,是阻塞操作漠趁,執(zhí)行完后會(huì)就會(huì)得到請(qǐng)求結(jié)果ResponseBody,所以chain.proceed(request)的前后取當(dāng)前時(shí)間忍疾,那么就知道整個(gè)請(qǐng)求所耗費(fèi)的時(shí)間闯传。上面chain.proceed(request)的前后分別打印的日志和時(shí)間,這樣第三個(gè)問(wèn)題也解決了卤妒。

具體Interceptor是如何工作甥绿,會(huì)在下一篇流程分析中介紹。

另外则披,通常OkHttpClient實(shí)例是全局唯一的共缕,這樣這些基本配置就是統(tǒng)一,且內(nèi)部維護(hù)的連接池也可以有效復(fù)用(會(huì)在下一篇流程分析中介紹)士复。

全局配置的有了图谷,單個(gè)請(qǐng)求的也可以有一些單獨(dú)的配置。

        Request getRequest = new Request.Builder()
                .url("http://yun918.cn/study/public/file_upload.php")
                .post(multipartBody)
                .addHeader("key","value")
                .cacheControl(CacheControl.FORCE_NETWORK)
                .build();

這個(gè)Request實(shí)例阱洪,

  • 使用addHeader()方法添加了請(qǐng)求頭便贵。
  • 使用cacheControl(CacheControl.FORCE_NETWORK)設(shè)置此次請(qǐng)求是能使用網(wǎng)絡(luò),不用緩存冗荸。(還可以設(shè)置只用緩存FORCE_CACHE承璃。)

好了,okhttp的使用就講這里了蚌本。下篇是工作流程分析盔粹,敬請(qǐng)期待~

感謝
Okhttp3基本使用
OkHttp使用詳解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市魂毁,隨后出現(xiàn)的幾起案子玻佩,更是在濱河造成了極大的恐慌,老刑警劉巖席楚,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咬崔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡烦秩,警方通過(guò)查閱死者的電腦和手機(jī)垮斯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)只祠,“玉大人兜蠕,你說(shuō)我怎么就攤上這事∨浊蓿” “怎么了熊杨?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵曙旭,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我晶府,道長(zhǎng)桂躏,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任川陆,我火速辦了婚禮剂习,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘较沪。我一直安慰自己鳞绕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布尸曼。 她就那樣靜靜地躺著们何,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骡苞。 梳的紋絲不亂的頭發(fā)上垂蜗,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音解幽,去河邊找鬼贴见。 笑死,一個(gè)胖子當(dāng)著我的面吹牛躲株,可吹牛的內(nèi)容都是我干的片部。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼霜定,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼档悠!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起望浩,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤辖所,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后磨德,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體缘回,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年典挑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酥宴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡您觉,死狀恐怖拙寡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情琳水,我是刑警寧澤肆糕,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布般堆,位于F島的核電站,受9級(jí)特大地震影響擎宝,放射性物質(zhì)發(fā)生泄漏郁妈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一绍申、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顾彰,春花似錦极阅、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至厕隧,卻和暖如春奔脐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吁讨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工髓迎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人建丧。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓排龄,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親翎朱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子橄维,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345