一觸即發(fā) App啟動優(yōu)化最佳實踐

一觸即發(fā) App啟動優(yōu)化最佳實踐

文中的很多圖都是Google性能優(yōu)化指南第六季中的一些截圖

Google給出的優(yōu)化指南來鎮(zhèn)樓
https://developer.android.com/topic/performance/launch-time.html

閃屏定義

Android官方的性能優(yōu)化典范赋续,從第六季開始捧书,發(fā)起了一系列針對App啟動的優(yōu)化實踐,地址如下:
https://www.youtube.com/watch?v=Vw1G1s73DsY&index=74&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE

可想而知琼梆,App的啟動性能是非常重要的肢簿。同時湾碎,Google針對App閃屏球昨,也給出了非常詳細的設計定義烤蜕,如下所示辕近。

https://material.google.com/patterns/launch-screens.html

1.png

其實最早的時候韵吨,閃屏是用來在App未完全啟動的時候,讓用戶不至于困惑App是否啟動而加入的一個設計移宅。而現(xiàn)在的很多App归粉,基本上都把閃屏當做一個廣告、宣傳的頁面了漏峰,貌似已經(jīng)失去了原本的意義糠悼,但閃屏,不管怎么說浅乔,在一個App啟動的時候倔喂,都是非常重要的席噩,設計的事情悼枢,交給UE吧,開發(fā)要做的双揪,就是讓App的啟動體驗渴邦,做到最好。

App啟動流程

App啟動的整個過程,可以分解成下面幾個過程:

  1. 用戶在Launcher上點擊App Icon
  2. 系統(tǒng)為App創(chuàng)建進程踢步,顯示啟動窗口
  3. App在進程中創(chuàng)建自己的組件

這個過程可以用下面這幅圖來描述:

2.png

而我們能夠優(yōu)化的兼丰,也就是下面Application的創(chuàng)建部分面徽,系統(tǒng)的進程分配以及一些窗口切換的動畫效果等,都是跟ROM相關的眶蕉,我們無法處理饭入。所以,我們需要把重點放到Application的創(chuàng)建過程。

上面是官方的說明,下面我們用更加通俗的語言來解釋一遍蹄葱。

當用戶點擊桌面icon的時候,系統(tǒng)準備好了,給App分配進程空間,就好像去酒店開房,但是你又不能直接進入房間泉蝌,你得坐電梯去房間诅愚,那么你坐電梯的這個時間泳赋,實際上就是系統(tǒng)的準備時間,那么系統(tǒng)的這個準備時間一般來說不會太長,但假如的開的是一個總統(tǒng)套房呢泵三,系統(tǒng)就得花不少時間來打理,所以系統(tǒng)給所有用戶都準備了一個過渡界面,這個界面,就是啟動時的黑屏\白屏,也就是你坐電梯里面看的小廣告,看完小廣告,你就到房間了,然后你想干嘛都可以了桐经,這個想干嘛的速度,就完全取決于你開門的速度了誓沸,你門開得快,自然那啥快,所以這里是開發(fā)者可以優(yōu)化的地方,有些開發(fā)者掏個鑰匙要好幾秒罪佳,有的只要幾百毫秒蕾管,完全影響了后面那啥的效率掏熬。

那么一般來說捆蜀,故事到這里就結束了疮丛,但是誊薄,系統(tǒng)锰茉,也就是這個酒店著瓶,并不是一個野雞酒店威酒,他也想盡量做得讓顧客滿意苏遥,這樣才會有回頭客啊田炭,所以,酒店做了一個優(yōu)化欺缘,可以讓每個顧客自己定義在坐電梯的時候想看什么栋豫!也就是說,系統(tǒng)在加載App的時候谚殊,首先是加載了資源文件丧鸯,這里就包括了要啟動的Activity的Theme,而這個Theme呢嫩絮,是可以自定義的丛肢,也就是顧客在坐電梯時想看的東西围肥,而不是千篇一律的白屏或者黑屏,他可以定制很多東西蜂怎,例如ActionBar穆刻、背景、StatBar等等杠步。

啟動時間的測量

關于Activity啟動時間的定義

對于Activity來說氢伟,啟動時,首先執(zhí)行的是onCreate()幽歼、onStart()朵锣、onResume()這些生命周期函數(shù),但即使這些生命周期方法回調(diào)結束了甸私,應用也不算已經(jīng)完全啟動诚些,還需要等View樹全部構建完畢,一般認為皇型,setContentView中的View全部顯示結束了诬烹,算作是應用完全啟動了。

Display Time

從API19之后犀被,Android在系統(tǒng)Log中增加了Display的Log信息椅您,通過過濾ActivityManager以及Display這兩個關鍵字,可以找到系統(tǒng)中的這個Log:

$ adb logcat | grep “ActivityManager”
ActivityManager: Displayed com.example.launcher/.LauncherActivity: +999ms

抓到的Log如圖所示:

3.png

那么這個時間寡键,實際上是Activity啟動掀泳,到Layout全部顯示的過程,但是要注意西轩,這里并不包括數(shù)據(jù)的加載员舵,因為很多App在加載時會使用懶加載模式,即數(shù)據(jù)拉取后藕畔,再刷新默認的UI马僻。

reportFullyDrawn

前面說了,系統(tǒng)日志中的Display Time只是布局的顯示時間注服,并不包括一些數(shù)據(jù)的懶加載等消耗的時間韭邓,所以,系統(tǒng)給我們定義了一個類似的『自定義上報時間』——reportFullyDrawn溶弟。

同樣是借用Google的一張圖來說明:

4.png

reportFullyDrawn是由我們自己調(diào)用的女淑,一般在數(shù)據(jù)全部加載完畢后,手動調(diào)用辜御,這樣就會在Log中增加一條日志:

$ adb logcat | grep “ActivityManager”
ActivityManager: Displayed com.example.launcher/. LauncherActivity: +999ms
ActivityManager: Fully drawn com.example.launcher/. LauncherActivity: +1s999ms

一般來說鸭你,使用的場景如下:

public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Void> {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void onLoadFinished(Loader<Void> loader, Void data) {
        // 加載數(shù)據(jù)
        // ……
        // 上報reportFullyDrawn
        reportFullyDrawn();
    }

    @Override
    public Loader<Void> onCreateLoader(int id, Bundle args) {
        return null;
    }

    @Override
    public void onLoaderReset(Loader<Void> loader) {

    }
}

但是要注意,這個方式需要API19+,所以袱巨,這里需要對SDK版本進行判斷阁谆。

計算啟動時間——ADB

通過ADB命令可以統(tǒng)計應用的啟動時間,指令如下所示:

?  ~  adb shell am start -W com.xys.preferencetest/.MainActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.xys.preferencetest/.MainActivity }
Status: ok
Activity: com.xys.preferencetest/.MainActivity
ThisTime: 1047
TotalTime: 1047
WaitTime: 1059
Complete

該指令一共給出了三個時間:

  • ThisTime:最后一個啟動的Activity的啟動耗時
  • TotalTime:自己的所有Activity的啟動耗時
  • WaitTime: ActivityManagerService啟動App的Activity時的總時間(包括當前Activity的onPause()和自己Activity的啟動)

這三個時間不是很好理解愉老,我們可以把整個過程分解

1.上一個Activity的onPause()——2.系統(tǒng)調(diào)用AMS耗時——3.第一個Activity(也許是閃屏頁)啟動耗時——4.第一個Activity的onPause()耗時——5.第二個Activity啟動耗時

那么场绿,ThisTime表示5(最后一個Activity的啟動耗時)。TotalTime表示3.4.5總共的耗時(如果啟動時只有一個Activity俺夕,那么ThisTime與TotalTime應該是一樣的)裳凸。WaitTime則表示所有的操作耗時,即1.2.3.4.5所有的耗時劝贸。

每次給出的時間可能并不一樣,而且應用從首次安裝啟動到后面每次正常啟動逗宁,時間都會不同映九,區(qū)別于系統(tǒng)是否要分配進程空間。

計算啟動時間——Screen Record

通過錄屏進行啟動的分析瞎颗,是一個很好的辦法件甥,在API21+,Android給我們提供了一個更加方便哼拔、準確的方式:

?  ~ adb shell screenrecord --bugreport /sdcard/test.mp4

Android在screenrecord中新增了一個參數(shù)——bugreport引有,那么加了這個參數(shù)之后,錄制出來的視頻倦逐,在左上角就會增加一行數(shù)字的顯示譬正,如圖所示。

在視頻開始前檬姥,會顯示設備信息和一些參數(shù):

6.png

視頻開始后曾我,左上角會有一行數(shù)字:

5.png

例如圖中的:15:31:22.261 f=171(0)

其中,前面的4個數(shù)字健民,就是時間戳抒巢,即15點31分22秒261,f=后面的數(shù)字是當前的幀數(shù)秉犹,注意蛉谜,不是幀率,而是代表當前是第幾幀崇堵,括號中的數(shù)字型诚,代表的是『Dropped frames
count』,即掉幀數(shù)筑辨。

有了這個東西俺驶,再結合視頻就可以非常清楚的看見這些信息了。

啟動時間的調(diào)試

模擬啟動延時

在測試的時候,我們可以通過下面的方式來進行啟動的延遲模擬:

SystemClock.sleep(2000)

或者直接通過:

try {
    Thread.sleep(2000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

或者通過:

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // Delay
    }

}, 2000);

這些方案都可以進行啟動延遲的模擬暮现。

強制冷啟動

在『開發(fā)者選項』中的Background Process Limit中設置為No Background Processes

7.png

優(yōu)化點

Static Block

很多代碼中的Static Block还绘,都是做一些初始化工作,特別是ContentProvider中在Static Block中初始化一些UriMatcher栖袋,這些東西可以做成懶加載模式拍顷。

Application

Application是程序的主入口,特別是很多第三方SDK都會需要在Application的onCreate里面做很多初始化操作塘幅,不得不說昔案,各種第三方SDK,都特別喜歡這個『兵家必爭之地』电媳,再加上自己的一些庫的初始化踏揣,會讓整個Application不堪重負。

優(yōu)化的方法匾乓,無非是通過以下幾個方面:

  • 延遲初始化
  • 后臺任務
  • 界面預加載

阻塞

阻塞有很多種情況捞稿,例如磁盤IO阻塞(讀寫文件、SharedPerfences)拼缝、網(wǎng)絡阻塞(現(xiàn)在應該不會了)以及高CPU占用的代碼(加解密娱局、渲染、解析等等)咧七。

View層級

見《Android群英傳》

耗時方法

通過使用TraceView && Systrace && Method Tracing工具來進行排查衰齐,見《Android群英傳:神兵利器》

App啟動優(yōu)化的一般過程

  1. 通過TraceView、Systrace來分析耗時的方法與組件继阻。
  2. 梳理啟動加載的每一個庫耻涛、組件。
  3. 將梳理出來的庫穴翩,按功能和需求進行劃分犬第,設計該庫的啟動時機。
  4. 與交互溝通芒帕,設計啟動畫面歉嗓,按前文方法進行優(yōu)化。

解決方案

Theme

當系統(tǒng)加載一個Activity的時候背蟆,onCreate()是一個耗時過程鉴分,那么在這個過程中,系統(tǒng)為了讓用戶能有一個比較好的體驗带膀,實際上會先繪制一些初始界面志珍,類似于PlaceHolder。

系統(tǒng)首先會讀取當前Activity的Theme垛叨,然后根據(jù)Theme中的配置來繪制伦糯,當Activity加載完畢后,才會替換為真正的界面。所以敛纲,Google官方提供的解決方案喂击,就是通過android:windowBackground屬性,來進行加載前的配置淤翔,同時翰绊,這里不僅可以配置顏色,還能配置圖片旁壮,例如监嗜,我們可以使用一個layer-list來作為android:windowBackground要顯示的圖:

start_window.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
            android:opacity="opaque">
    <item android:drawable="@android:color/darker_gray"/>
    <item>
        <bitmap
            android:gravity="center"
            android:src="@mipmap/ic_launcher"/>
    </item>
</layer-list>

可以看見,這里通過layer-list來實現(xiàn)圖片的疊加抡谐,讓開發(fā)者可以自由組合裁奇。

配置中的android:opacity="opaque"參數(shù)是為了防止在啟動的時候出現(xiàn)背景的閃爍。

接下來可以設置一個新的Style麦撵,這個Style就是Activity預加載的Style框喳。

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="StartStyle" parent="AppTheme">
        <item name="android:windowBackground">@drawable/start_window</item>
    </style>
</resources>

OK,下面在Mainifest中給Activity指定需要預加載的Style:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.xys.startperformancedemo">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:theme="@style/StartStyle">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>

這里需要注意下厦坛,一定是Activity的Theme,而不是Application的Theme乍惊。

最后杜秸,我們在Activity加載真正的界面之前,將Theme設置回正常的Theme就好了:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme(R.style.AppTheme);
        super.onCreate(savedInstanceState);
        SystemClock.sleep(2000);
        setContentView(R.layout.activity_main);
    }
}

在這個Activity中润绎,我使用SystemClock.sleep(2000)撬碟,模擬了一個Activity加載的耗時過程,在super.onCreate(savedInstanceState)調(diào)用前莉撇,將主題重新設置為原來的主題呢蛤。

通過這種方式設置的效果如下:

9.gif

啟動的時候,會先展示一個畫面棍郎,這個畫面就是系統(tǒng)解析到的Style其障,等Activity加載完全完畢后,才會加載Activity的界面涂佃,而在Activity的界面中励翼,我們將主題重新設置為正常的主題,從而達到一個友好的啟動體驗辜荠,這種方式其實并沒有真正的加速啟動過程汽抚,而是通過交互體驗來優(yōu)化了展示的效果。

異步初始化

這個很簡單伯病,就是讓App在onCreate里面盡可能的少做事情造烁,而利用手機的多核特性,盡可能的利用多線程,例如一些第三方框架的初始化惭蟋,如果能放線程苗桂,就盡量的放入線程中,最簡單的敞葛,你可以直接new Thread()誉察,當然,你也可以通過公共的線程池來進行異步的初始化工作惹谐,這個是最能夠壓縮啟動時間的方式

延遲初始化

延遲初始化并不是減少了啟動時間持偏,而是讓耗時操作讓位、讓資源給UI繪制氨肌,將耗時的操作延遲到UI加載完畢后鸿秆,所以,這里建議通過mDecoView.post方法怎囚,來進行延遲加載卿叽,代碼如下:

getWindow().getDecorView().post(new Runnable() {

  @Override public void run() {
    ……
  }
});

我們的ContentView就是通過mDecoView.addView加入到根布局的,所以恳守,通過這種方式考婴,可以讓延遲加載的內(nèi)容,在ContentView初始化完畢后催烘,再進行執(zhí)行沥阱,保證了UI繪制的流暢性。

IntentService

IntentService是繼承于Service并處理異步請求的一個類伊群,在IntentService的內(nèi)部考杉,有一個工作線程來處理耗時操作,啟動IntentService的方式和啟動傳統(tǒng)Service一樣舰始,同時崇棠,當任務執(zhí)行完后,IntentService會自動停止丸卷,而不需要去手動控制枕稀。

public class InitIntentService extends IntentService {

    private static final String ACTION = "com.xys.startperformancedemo.action";

    public InitIntentService() {
        super("InitIntentService");
    }

    public static void start(Context context) {
        Intent intent = new Intent(context, InitIntentService.class);
        intent.setAction(ACTION);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        SystemClock.sleep(2000);
        Log.d(TAG, "onHandleIntent: ");
    }
}

我們將耗時任務丟到IntentService中去處理,系統(tǒng)會自動開啟線程去處理及老,同時抽莱,在任務結束后,還能自己結束Service骄恶,多么的人性化食铐!OK,只需要在Application或者Activity的onCreate中去啟動這個IntentService即可:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    InitIntentService.start(this);
}

最后不要忘記在Mainifest注冊Service僧鲁。

使用ActivityLifecycleCallbacks

Framework提供的這個方法可以監(jiān)控到所有Activity的生命周期虐呻,在這里象泵,我們就可以通過onActivityCreated這樣一個回調(diào),來將一些UI相關的初始化操作放到這里斟叼,同時偶惠,通過unregisterActivityLifecycleCallbacks來避免重復的初始化。同時朗涩,這里onActivityCreated回調(diào)的參數(shù)Bundle忽孽,可以用來區(qū)別是否是被系統(tǒng)所回收的Activity。

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化基本內(nèi)容
        // ……
        registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                unregisterActivityLifecycleCallbacks(this);
                // 初始化UI相關的內(nèi)容
                // ……
            }

            @Override
            public void onActivityStarted(Activity activity) {
            }

            @Override
            public void onActivityResumed(Activity activity) {
            }

            @Override
            public void onActivityPaused(Activity activity) {
            }

            @Override
            public void onActivityStopped(Activity activity) {
            }

            @Override
            public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
            }

            @Override
            public void onActivityDestroyed(Activity activity) {
            }
        });
    }
}

資源優(yōu)化

有幾個方面谢床,一個自然是優(yōu)化布局兄一、布局層級,一個是優(yōu)化資源识腿,盡可能的精簡資源出革、避免垃圾資源,這些可以通過混淆和tinyPNG這些工具來實現(xiàn)渡讼。

甩鍋方案

下面是兩種不同的方案骂束,都是在Style中進行配置:

<item name="android:windowDisablePreview">true</item>

<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>

我們先來看看這樣做的效果:

8.gif

設置效果類似,即通過取消成箫、透明化系統(tǒng)的統(tǒng)一的加載頁面來達到啟動的『加速』展箱,實際上,是一個『甩鍋』的過程蹬昌。強烈建議開發(fā)者不要通過這種方式去做『所謂的啟動加速』,這種方式雖然看上去自己的App啟動非澄雠海快,瞬間就完成了凳厢,但實際上,是將真正的啟動界面給隱藏了竞慢。

系統(tǒng)說:這鍋先紫,我們不背!

無解

對應5.0以下的65535問題筹煮,目前只能通過Multidex來進行處理遮精,而在5.0以下的機器上,系統(tǒng)在加載前的合并Dex的過程败潦,有可能非常長本冲,這也是暫時無解的問題,只能希望后面Multidex進行優(yōu)化劫扒。

OK檬洞,App的啟動優(yōu)化基本如上,其重點過程沟饥,依然是分析耗時的操作添怔,以及如何設計合理的啟動順序湾戳,希望各位能夠通過文中介紹的方式來進行App的啟動優(yōu)化。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末广料,一起剝皮案震驚了整個濱河市砾脑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌艾杏,老刑警劉巖韧衣,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異购桑,居然都是意外死亡畅铭,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門其兴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來顶瞒,“玉大人,你說我怎么就攤上這事元旬×裥欤” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵匀归,是天一觀的道長坑资。 經(jīng)常有香客問我,道長穆端,這世上最難降的妖魔是什么袱贮? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮体啰,結果婚禮上攒巍,老公的妹妹穿的比我還像新娘。我一直安慰自己荒勇,他們只是感情好柒莉,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沽翔,像睡著了一般兢孝。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仅偎,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天跨蟹,我揣著相機與錄音,去河邊找鬼橘沥。 笑死窗轩,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的座咆。 我是一名探鬼主播品姓,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼寝并,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了腹备?” 一聲冷哼從身側響起衬潦,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎植酥,沒想到半個月后镀岛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡友驮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年漂羊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卸留。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡走越,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耻瑟,到底是詐尸還是另有隱情旨指,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布喳整,位于F島的核電站谆构,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏框都。R本人自食惡果不足惜搬素,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望魏保。 院中可真熱鬧熬尺,春花似錦、人聲如沸谓罗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妥衣。三九已至,卻和暖如春戒傻,著一層夾襖步出監(jiān)牢的瞬間税手,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工需纳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留芦倒,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓不翩,卻偏偏與公主長得像兵扬,于是被迫代替她去往敵國和親麻裳。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353

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