基于Retrofit+RxJava的Android分層網(wǎng)絡(luò)請求框架

目前已經(jīng)有不少Android客戶端在使用Retrofit+RxJava實現(xiàn)網(wǎng)絡(luò)請求了陪腌,相比于xUtils烟瞧,Volley等網(wǎng)絡(luò)訪問框架参滴,其具有網(wǎng)絡(luò)訪問效率高(基于OkHttp)砾赔、內(nèi)存占用少弥咪、代碼量小以及數(shù)據(jù)傳輸安全性高等特點聚至。

Retrofit源碼更是經(jīng)典的設(shè)計模式教程扳躬,筆者已在之前的文章中分享過自己的一些體會甚亭,有興趣的話可點擊以下鏈接了解:《Retrofit源碼設(shè)計模式解析(上)》亏狰、《Retrofit源碼設(shè)計模式解析(下)

但在具體業(yè)務(wù)場景下,比如涉及到多種網(wǎng)絡(luò)請求(GET/PUT/POST/DELETE等)促脉,多種請求方式(異步/同步)時瘸味,按照Retrofit官方文檔實現(xiàn)網(wǎng)絡(luò)請求仍然會顯得比較繁瑣够挂,本文主要介紹筆者基于Retrofit+RxJava封裝的Android分層網(wǎng)絡(luò)請求框架孽糖,適用于下圖所示的業(yè)務(wù)場景:Android移動端通過移動網(wǎng)關(guān)調(diào)用接口平臺發(fā)布的業(yè)務(wù)服務(wù)

Android網(wǎng)絡(luò)訪問架構(gòu)

上述業(yè)務(wù)架構(gòu)可能是目前移動應(yīng)用中使用的比較廣的尘奏,其具有以下優(yōu)點:

  • 由于移動網(wǎng)關(guān)系統(tǒng)和統(tǒng)一服務(wù)發(fā)布平臺的存在罪既,移動端不需要直接調(diào)用業(yè)務(wù)系統(tǒng)的服務(wù)琢感,避免了移動端同時對接多個業(yè)務(wù)系統(tǒng),降低移動端系統(tǒng)的復(fù)雜性烘挫;
  • 移動網(wǎng)關(guān)會對移動端的請求進(jìn)行鑒權(quán)柬甥,屏蔽外部惡意訪問苛蒲,有效提高內(nèi)部業(yè)務(wù)系統(tǒng)的安全性卤橄;
  • 統(tǒng)一服務(wù)發(fā)布平臺集成所有的業(yè)務(wù)接口窟扑,對外提供格式統(tǒng)一的接口服務(wù),這對于內(nèi)部系統(tǒng)的可維護性和可擴展性是至關(guān)重要的漏健。
  • 業(yè)務(wù)系統(tǒng)只需要按照格式將其服務(wù)在接口平臺上發(fā)布即可,無需關(guān)心具體的調(diào)用者殖属。

因此洗显,本文分享的分層網(wǎng)絡(luò)請求框架的前提是:Android移動端直接對接移動網(wǎng)關(guān)谭溉。主要有以下內(nèi)容:

  1. 網(wǎng)關(guān)請求封裝。移動網(wǎng)關(guān)的請求格式(參數(shù)损搬、字段巧勤、通信方式等)應(yīng)該是固定的弄匕,并且對業(yè)務(wù)是透明的,不觸碰具體業(yè)務(wù)數(shù)據(jù)剩瓶。負(fù)責(zé)直接對接客戶端的請求,包括請求的鑒權(quán)豌鹤,客戶端與后臺的數(shù)據(jù)格式的轉(zhuǎn)換等枝缔。
  2. 基礎(chǔ)業(yè)務(wù)請求愿卸。基礎(chǔ)業(yè)務(wù)請求涉及到正式/測試環(huán)境的切換儒溉,網(wǎng)關(guān)返回業(yè)務(wù)數(shù)據(jù)的統(tǒng)一解析睁搭,以及添加業(yè)務(wù)相關(guān)的網(wǎng)關(guān)默認(rèn)字段等;
  3. 業(yè)務(wù)Module統(tǒng)一網(wǎng)絡(luò)請求管理。業(yè)務(wù)Module負(fù)責(zé)統(tǒng)一管理一個業(yè)務(wù)模塊中所有的網(wǎng)絡(luò)請求寓调,接收鑒別請求對應(yīng)的字段锄码,包含服務(wù)名、服務(wù)分組名痛悯、請求方法以及請求參數(shù)等重窟;
  4. Model層網(wǎng)絡(luò)請求。Model層的網(wǎng)絡(luò)請求是按服務(wù)劃分的扭仁,一個應(yīng)用Module通常會對應(yīng)多個服務(wù)厅翔,并且接收Activity的參數(shù)刀闷,組裝請求bean仰迁;
  5. Activity層的網(wǎng)絡(luò)訪問徐许。Activity直接調(diào)用Model層的方法怯邪,傳入界面相關(guān)的參數(shù),回調(diào)響應(yīng)結(jié)果澄步。
  6. 文件上下傳及其它網(wǎng)絡(luò)訪問村缸。通過Retrofit+RxJava還可以實現(xiàn)文件上下傳以及軟件更新等其它網(wǎng)絡(luò)訪問武氓,本文也會一并簡要介紹。

一东羹、網(wǎng)關(guān)請求封裝

通過Retrofit注解定義移動網(wǎng)關(guān)接口属提,比如請求方式美尸,參數(shù)格式,字段等恕酸。以POST請求為例蕊温,參數(shù)格式為表單數(shù)據(jù)惶岭,字段包含服務(wù)名、服務(wù)分組名症革、方法名鸯旁、參數(shù)、請求頭Map以及其他參數(shù)等艇挨。

@FormUrlEncoded
@POST("./")
Observable<WGResponseBean> postRequest (
        @FieldMap("param") String param,
        @HeaderMap Map<String, String> headMap);

Retrofit的FieldMap不支持字段值為null缩滨,如參數(shù)中有null值,需要使用Field苞冯。

如上所述侧巨,@POST表示該請求是一個POST方法,常用的POST提交數(shù)據(jù)的方式有:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/json
  • text/xml

application/x-www-form-urlencoded對應(yīng)表單數(shù)據(jù)司忱,在Retrofit中,通過@FormUrlEncoded標(biāo)注的參數(shù)將以表單形式進(jìn)行提交鳍烁。multipart/form-data一般用于文件上傳的時候老翘,這個在后面會提到锻离。application/json通過JSON方式與服務(wù)端進(jìn)行數(shù)據(jù)交換墓怀,text/xml使用XML數(shù)據(jù)格式傀履。

定義了網(wǎng)關(guān)請求之后,需要創(chuàng)建對應(yīng)的Service碴犬,而Service的使用方式并不確定服协,這里通過一個抽象類對其進(jìn)行封裝啦粹。

public abstract class WgReqService<T> {

    // 網(wǎng)關(guān)網(wǎng)絡(luò)請求
    protected WGApi wgApi;

    // 省略代碼
    public WgReqService(String baseUrl) {
        wgApi = new NetWork.Builder(baseUrl).build().getApi(WGApi.class);
    }

    public abstract T wgReq(WGRequestBean wgRequest, Map<String, String> headMap);
}

以同步/異步網(wǎng)絡(luò)請求為例窘游,分別繼承自WgReqService忍饰,實現(xiàn)對應(yīng)的wgReq方法即可艾蓝。

public class WgReqAsync<T> extends WgReqService<Observable<T>> {    // 省略代碼 
    @Override
    public Observable<T> wgReq(WGRequestBean wgRequest, Map<String, String> headMap) {
        // 省略代碼
    }
}
public class WgReqSync extends WgReqService<WGResponseBean> {
    // 省略代碼
    @Override
    public WGResponseBean wgReq(WGRequestBean wgRequest, Map<String, String> headMap) {
        // 省略代碼
    }
}

由于采用了RxJava赢织,因此在異步實現(xiàn)中逛拱,泛型參數(shù)為Observable<T>朽合,而同步請求時直接返回網(wǎng)關(guān)的出參Bean。另外宪彩,需要說明的是WgReqAsync包含域Func1<WGResponseBean, T>尿孔,F(xiàn)unc1為RxJava支持的接口活合,這里表示將網(wǎng)關(guān)返回的業(yè)務(wù)數(shù)據(jù)進(jìn)行統(tǒng)一解析的方法物赶。

二、基礎(chǔ)業(yè)務(wù)請求

通過上述的分析可知告嘲,業(yè)務(wù)請求可以有同步/異步等多種實現(xiàn)方式奖地,同時涉及到正式/測試環(huán)境的切換参歹,網(wǎng)關(guān)返回業(yè)務(wù)數(shù)據(jù)的統(tǒng)一解析,以及添加業(yè)務(wù)相關(guān)的網(wǎng)關(guān)默認(rèn)字段等缸血,這里以異步請求為例:

public class BaseWgRequest implements Func1<WGResponseBean, BusinessBean> {

    // 網(wǎng)關(guān)請求Helper類
    private WgReqAsync<BusinessBean> wgReqAsync;

    // 服務(wù)名
    protected String service;
    // 服務(wù)組名
    protected String alias;
    // 解析類
    protected Class<? extends BusinessBean> rClazz;

    // 省略代碼
}

BaseWgRequest持有WgReqAsync<BusinessBean>引用蜜氨,并通過其完成網(wǎng)關(guān)訪問,service捎泻、alias等域指定相應(yīng)的服務(wù)飒炎,Class<? extends BusinessBean>表示對業(yè)務(wù)返回值進(jìn)行解析的類。

return JSON.parseObject(wgResponse.getData(), rClazz != null ? rClazz : BusinessBean.class);

異步請求中笆豁,通過上述域及業(yè)務(wù)相關(guān)的網(wǎng)關(guān)默認(rèn)字段封裝請求體郎汪,同時獲取請求head。

// 請求
return wgReqAsync.wgReq(ParamUtil.getWGRequestBean(service, alias, method, param),
            BaseConstants.getHeaderMap());

三闯狱、業(yè)務(wù)Module

首先申明煞赢,對整個項目進(jìn)行多工程劃分(業(yè)務(wù)工程和庫工程獨立,便于庫工程獨立維護)哄孤,同時業(yè)務(wù)工程中分為多個功能Module(便于功能模塊插件化照筑、熱加載),這種方式在比較大型的項目中應(yīng)用效果可能比較好蛾默,在小型項目中并不推薦。這里的業(yè)務(wù)Module是以功能模塊進(jìn)行劃分的牧挣,對一個功能模塊中的所有網(wǎng)絡(luò)請求進(jìn)行統(tǒng)一管理浸踩,能有效的單元測試据块,提高整體開發(fā)效率像屋。

如上所述己莺,業(yè)務(wù)Module的主要職責(zé)是接收鑒別請求對應(yīng)的字段阵子,包含服務(wù)名、服務(wù)分組名领突、請求方法以及請求參數(shù)等君旦,并繼承自上述 BaseWgRequest實現(xiàn)。

public class WelNetwork extends BaseWgRequest {}

業(yè)務(wù)Module包含了一個功能模塊中的所有網(wǎng)絡(luò)請求方法捞魁,以登錄為例:

public Observable<BusinessBean> userLoginWork(SysUsersReqDto sysUsersReqDto) {
    return wgRequest(service, alias, BusinessConstants.userLoginWork, ParamUtil.getJsonParam(sysUsersReqDto));
}

這里重點說明下登錄方法的入?yún)⑾珺aseWgRequest關(guān)注的是與網(wǎng)關(guān)接口相關(guān)的參數(shù)凑懂,由于業(yè)務(wù)Module繼承自BaseWgRequest接谨,這一層的方法不再關(guān)注網(wǎng)關(guān)相關(guān)內(nèi)容,重點是業(yè)務(wù)相關(guān)的請求入?yún)⑸ㄒ埂Q句話說堕阔,業(yè)務(wù)Module的入?yún)⒅苯訉?yīng)業(yè)務(wù)接口的入?yún)⒊剑哂性L問形式的無關(guān)性⊥诉耄考慮清楚每一個層次的關(guān)注重點,是搭建軟件架構(gòu)的基礎(chǔ)垢油。

四、Model層網(wǎng)絡(luò)請求

在本系統(tǒng)中硝枉,按照服務(wù)名對Model進(jìn)行了劃分欣福,需要申明的是雏逾,由于每個公司的具體情況不一樣,這種劃分方式不一定適用于你的系統(tǒng)。不過這種分層方式仍有借鑒之處妹孙。

由于Model中方法的訪問可能不止一處,因此對外(Activity)提供單實例對象嚣崭。這里提供一種最簡單餓漢模式的單實例:

private static LoginModel loginModel = new LoginModel();

public static LoginModel getInstance() {
    return loginModel;
}

同時懦傍,在其構(gòu)造器中初始化業(yè)務(wù)Module訪問類说榆。

private LoginModel() {
        super();
        welNetwork = new WelNetwork.Builder().service(BusinessConstants.SysLogin).alias(BaseConstants.getALIAS())
                .rClazz(SysUsersResDto.class).build();
}

上面提到,業(yè)務(wù)Module關(guān)注的是業(yè)務(wù)接口的入?yún)ⅲ敲催@個入?yún)⒕褪怯蠱odel提供的神汹。一個功能模塊可能對應(yīng)多個服務(wù)沧卢,那么這些服務(wù)需要持有業(yè)務(wù)Module的引用,并通過業(yè)務(wù)Module的方法實現(xiàn)自身的方法呈队。還是以登錄為例:

public Observable<BusinessBean> userLoginWork(String username, String password) {
        return welNetwork.userLoginWork(new SysUsersReqDto.Builder(username).userPwd(password)
                .devType("1").devIp(DeviceUtils.getClientIpAddress()).build());
}

Model負(fù)責(zé)連接Activity和業(yè)務(wù)Module,對上直接對接Activity,Activity關(guān)注的是用戶輸入的用戶名和密碼朽砰,并不知道業(yè)務(wù)接口需要的數(shù)據(jù)格式睦裳,而業(yè)務(wù)Module關(guān)注的是業(yè)務(wù)接口的入?yún)⒏袷健R虼朔嗡兀琈odel層對這兩種數(shù)據(jù)進(jìn)行適配课舍,常見的就是對請求bean的組裝,比如上述登錄方法接收用戶名和密碼站辉,組裝成業(yè)務(wù)Module所需的SysUsersReqDto摧阅。

五祝钢、Activity層的網(wǎng)絡(luò)訪問

通過上述分層封裝盹沈,在Activity中的網(wǎng)絡(luò)訪問就非常簡單了岗憋。直接上示例代碼:

LoginModel.getInstance().userLoginWork(usernameStr, passwordStr)
    .subscribe(new RxObserver<BusinessBean>(this) {
    @Override
    public void onSuccess(BusinessBean businessBean) {
          handleLoginResult(businessBean);
    }
});

需要說明的是RxObserver,RxObserver<T>繼承自Subscriber<T>晋修,Subscriber是RxJava的回調(diào)類,RxObserver包含抽象方法onSuccess,并在onNext實現(xiàn)中進(jìn)行調(diào)用忠怖。

public abstract class RxObserver<T> extends Subscriber<T> {
    // 省略代碼
    @Override
    public void onNext(T t) {
        onSuccess(t);
    }

    public abstract void onSuccess(T t);
}

從Activity的角度來講问麸,其負(fù)責(zé)用戶交互,因此只關(guān)注用戶輸入和接口返回具體數(shù)據(jù)来颤,并對數(shù)據(jù)進(jìn)行處理项阴。而至于網(wǎng)關(guān)的實現(xiàn),業(yè)務(wù)接口的入?yún)⒏袷窖炊担W(wǎng)絡(luò)請求的方式等底層實現(xiàn)漏策,則對Activity完全閉合。
上述簡要介紹了題目所講到的基于Retrofit+RxJava的Android分層網(wǎng)絡(luò)請求框架,由于涉及具體業(yè)務(wù),只能開放部分代碼樣例谦絮。至于對架構(gòu)的觀點叫胖,可參考《什么是架構(gòu)怎棱?》砸捏。

  1. 根據(jù)要解決的問題,對目標(biāo)系統(tǒng)的邊界進(jìn)行界定咐扭。
  1. 并對目標(biāo)系統(tǒng)按某個原則的進(jìn)行切分。切分的原則,要便于不同的角色昙篙,對切分出來的部分焚辅,并行或串行開展工作,一般并行才能減少時間。
  2. 并對這些切分出來的部分颁股,設(shè)立溝通機制葡缰。
  3. 根據(jù)3,使得這些部分之間能夠進(jìn)行有機的聯(lián)系间影,合并組裝成為一個整體付燥,完成目標(biāo)系統(tǒng)的所有工作漩怎。

界定-切分-溝通-系統(tǒng),是架構(gòu)設(shè)計的基本步驟。

本系統(tǒng)界定為基于Retrofit+RxJava實現(xiàn)Android分層網(wǎng)絡(luò)請求蚕断,然后將整個系統(tǒng)進(jìn)行切分五個層次葛假,每個層次的關(guān)注點相異,但又相互聯(lián)系,這五個層次通過抽象(抽象類或接口)、繼承苦银、復(fù)合等方法進(jìn)行溝通,形成一個統(tǒng)一系統(tǒng)团秽,完成Android中的網(wǎng)絡(luò)請求图毕。

六冬阳、文件上下傳及其它網(wǎng)絡(luò)訪問

除上述網(wǎng)關(guān)請求外,Android中還經(jīng)常涉及文件上下傳蹲堂、軟件更新等與網(wǎng)絡(luò)相關(guān)的操作能犯,這里也對其進(jìn)行簡要的介紹枕磁。如上所述,文件上傳需要采用multipart/form-data數(shù)據(jù)提交方式,因此在Retrofit中定義方法時,需要采用@Multipart注解。

@Multipart
@POST("./")
Observable<UploadFileResponseBean> uploadFile(@Part MultipartBody.Part file,
                                                   @PartMap Map<String, RequestBody> params,
                                                   @HeaderMap Map<String, String> headMap);

同時,其入?yún)㈩愋蜑锧Part,或@PartMap。需要注意的是,傳入該方法的參數(shù)為MultipartBody.Part赴魁。

// 根據(jù)文件路徑生成文件
File file = new File(requestBean.getFilePath());
// 根據(jù)文件創(chuàng)建請求體
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
// 創(chuàng)建實際請求用的MultipartBody
MultipartBody.Part body = MultipartBody.Part.createFormData("file", file.getName(), requestFile);

其他封裝形式與上述網(wǎng)關(guān)請求類似潘拱,這里不再贅述禽最。

對于文件的下載虑乖,筆者嘗試了《Retrofit 2 — How to Download Files from Server
》的方法,但由于其涉及下載進(jìn)度的監(jiān)聽以及下載完成的操作等继找,對后續(xù)系統(tǒng)的封裝并不好凯亮,這里就不詳細(xì)介紹了。

針對文件下載這種場景,如果自定義實現(xiàn),需要處理OOM、多線程等問題疏之。DownloadManager是Android2.3以后引入的系統(tǒng)自帶類庫几缭,通過getSystemService(Context.DOWNLOAD_SERVICE)就能獲取并使用某抓,系統(tǒng)服務(wù)已經(jīng)完成網(wǎng)絡(luò)訪問控制崎坊、文件讀寫控制男翰、通知欄進(jìn)度顯示鸦列、大文件續(xù)傳等一系列文件下載可能遇到的問題应民。因此归园,推薦系統(tǒng)自帶實現(xiàn)晤揣,這個列出簡要參考代碼,詳細(xì)情況請參考《DownloadManager官方文檔》

DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(apkUrl));
// 設(shè)置目標(biāo)文件路徑
request.setDestinationInExternalPublicDir(dir, fileName);
// 僅在WIFI網(wǎng)絡(luò)下載
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);
// 設(shè)置標(biāo)題及描述
request.setTitle(getString(R.string.app_name));
// 發(fā)送請求
downloadManager.enqueue(request);

最后池户,舉個GET請求的栗子斟湃,查詢軟件是否有更新一般會采用GET請求,比如請求參數(shù)包括系統(tǒng)、包名、版本號等入?yún)⒌恼埱蟾袷綖椋?/p>

@GET("./")
Observable<ApkUpdateResponseBean> apkUpdate(
        @Query("os") String os,
        @Query("packageName") String packageName,
        @Query("version") String version);

@Query表示請求字段烤宙。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末罢猪,一起剝皮案震驚了整個濱河市房揭,隨后出現(xiàn)的幾起案子蓬痒,更是在濱河造成了極大的恐慌亲轨,老刑警劉巖讯嫂,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件库正,死亡現(xiàn)場離奇詭異属瓣,居然都是意外死亡粗截,警方通過查閱死者的電腦和手機婿屹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任碰煌,我火速辦了婚禮洪乍,結(jié)果婚禮上巷波,老公的妹妹穿的比我還像新娘垮耳。我一直安慰自己查蓉,他們只是感情好鹃共,可當(dāng)我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布税迷。 她就那樣靜靜地躺著喝检,像睡著了一般纺涤。 火紅的嫁衣襯著肌膚如雪拧咳。 梳的紋絲不亂的頭發(fā)上阅签,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天瓢宦,我揣著相機與錄音玫镐,去河邊找鬼葱椭。 笑死驳概,一個胖子當(dāng)著我的面吹牛稚照,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤坛掠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后欠拾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體格带,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡镶摘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年俊卤,在試婚紗的時候發(fā)現(xiàn)自己被綠了约啊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爱态,我是刑警寧澤洞渔,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布慎皱,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏纸泄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耀销,春花似錦硝清、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽串前。三九已至,卻和暖如春宣增,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背解阅。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工肋拔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留呀酸,地道東北人窿吩。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像曲管,于是被迫代替她去往敵國和親螟蝙。 傳聞我的和親對象是個殘疾皇子锁保,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,969評論 2 355

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