探究 | App Startup真的能減少啟動(dòng)耗時(shí)嗎

前言

之前我們說(shuō)了啟動(dòng)優(yōu)化的一些常用方法,但是有的小伙伴就很不屑了:

“這些方法很久之前就知道了,不知道說(shuō)點(diǎn)新東西琳要?比如App Startup闲礼?能對(duì)啟動(dòng)優(yōu)化有幫助嗎牍汹?”

ok,既然你誠(chéng)心誠(chéng)意的發(fā)問了柬泽,那我就大發(fā)慈悲的告訴你:俺也不知道??慎菲。

走吧,一起瞅瞅這個(gè)App Startup吧锨并,是不是真的能給我們的啟動(dòng)帶來(lái)優(yōu)化呢露该?

(想看結(jié)果的可以直接跳到最后的實(shí)踐總結(jié)階段)

Contentprovider中初始化

想必大家都了解,很多三方庫(kù)都需要在Application中進(jìn)行初始化第煮,并順便獲取到Application的上下文解幼。

但是也有的庫(kù)不需要我們自己去初始化,它偷偷摸摸就給初始化了包警,用到的方法就是使用ContentProvider進(jìn)行初始化撵摆,定義一個(gè)ContentProvider,然后在onCreate拿到上下文揽趾,就可以進(jìn)行三方庫(kù)自己的初始化工作了台汇。而在APP的啟動(dòng)流程中,有一步就是要執(zhí)行到程序中所有注冊(cè)過(guò)的ContentProvider的onCreate方法,所以這個(gè)庫(kù)的初始化就默默完成了苟呐。

這種做法確實(shí)給集成庫(kù)的開發(fā)者們帶來(lái)了很大的便利痒芝,現(xiàn)在很多庫(kù)都用到了這種方法,比如Facebook牵素,F(xiàn)irebase严衬,這里拿Facebook舉例看看他的ContentProvider:

    <provider
        android:name="com.facebook.internal.FacebookInitProvider"
        android:authorities="${applicationId}.FacebookInitProvider"
        android:exported="false" />
public final class FacebookInitProvider extends ContentProvider {
    private static final String TAG = FacebookInitProvider.class.getSimpleName();

    @Override
    @SuppressWarnings("deprecation")
    public boolean onCreate() {
        try {
            FacebookSdk.sdkInitialize(getContext());
        } catch (Exception ex) {
            Log.i(TAG, "Failed to auto initialize the Facebook SDK", ex);
        }
        return false;
    }

    //...
}

可以看到,在Fackbook的sdk中笆呆,定義了一個(gè)FacebookInitProvider请琳,并且在onCreate中進(jìn)行了初始化。所以我們才無(wú)需單獨(dú)對(duì)Facebook的sdk進(jìn)行初始化赠幕。

雖然更方便了俄精,但是這種做法有給啟動(dòng)優(yōu)化帶來(lái)什么好處嗎?我們一起再回顧下之前的啟動(dòng)流程研究下榕堰,截取一部分:

  • ...
  • attachBaseContext
  • Application attach
  • installContentProviders
  • Application onCreate
  • Looper.loop
  • Activity onCreate竖慧,onResume

這其中installContentProviders方法就是用來(lái)啟動(dòng)并執(zhí)行各個(gè)ContentProvideronCreate方法的,它會(huì)在ApplicationonCreate方法之前執(zhí)行逆屡。

所以這些庫(kù)只是把Application的三方庫(kù)初始化工作提前放到ContentProvider中了圾旨,并不會(huì)減少啟動(dòng)耗時(shí),反而會(huì)增加啟動(dòng)耗時(shí)魏蔗。

怎么說(shuō)呢砍的?因?yàn)椴煌膸?kù)就定義了不同的ContentProvider類,多了這么多ContentProvider莺治,ContentProvider作為四大組件之一廓鞠,啟動(dòng)也是耗時(shí)的,自然也就增加App啟動(dòng)消耗的時(shí)間了谣旁。

這時(shí)候就需要App Startup來(lái)對(duì)此情況進(jìn)行優(yōu)化了~

官網(wǎng)簡(jiǎn)介

The App Startup library provides a straightforward, performant way to initialize components at application startup. Both library developers and app developers can use App Startup to streamline startup sequences and explicitly set the order of initialization.Instead of defining separate content providers for each component you need to initialize, App Startup allows you to define component initializers that share a single content provider. This can significantly improve app startup time.

主要說(shuō)了兩點(diǎn)特性:

  • 可以共享單個(gè)Contentprovider诫惭。
  • 可以明確地設(shè)置初始化順序。

可以共享單個(gè)Contentprovider

這一點(diǎn)功能就能解決剛才的問題了蔓挖,不同的庫(kù)不再需要去啟動(dòng)多個(gè)Contentprovider了夕土,而是共享同一個(gè)Contentprovider

這樣就至少不會(huì)增加啟動(dòng)耗時(shí)了瘟判。

怎么操作呢怨绣?假如我們是FacebookSDK設(shè)計(jì)者,我們就來(lái)改一下剛才的FacebookSDK拷获,集成App Startup

//導(dǎo)入庫(kù)
implementation "androidx.startup:startup-runtime:1.0.0"


// Initializes facebooksdk.
class FacebookSDKInitializer : Initializer<Unit> {
    private  val TAG = "FacebookSDKInitializer"

    override fun create(context: Context): Unit {
        try {
            FacebookSdk.sdkInitialize(context)
        } catch (ex: Exception) {
            Log.i(TAG, "Failed to auto initialize the Facebook SDK", ex)
        }
    }

    
    override fun dependencies(): List<Class<out Initializer<*>>> {
        return emptyList()
    }
}


//AndroidManifest.xml中定義
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">

    <meta-data  android:name="com.example.FacebookSDKInitializer"
          android:value="androidx.startup" />
</provider>

實(shí)現(xiàn)了Initializer接口篮撑,然后在onCreate方法中進(jìn)行初始化即可,只要所有的庫(kù)都按照這個(gè)標(biāo)準(zhǔn)來(lái)初始化匆瓜,而不是自己?jiǎn)为?dú)自定義ContentProvider赢笨,那么確實(shí)可以減少啟動(dòng)耗時(shí)未蝌。

其中,tools:node="merge"標(biāo)簽就是用來(lái)合并所有申明了InitializationProviderContentProvider茧妒。

等等萧吠,Initializer接口還有一個(gè)方法dependencies,這又是干啥的呢桐筏?

可以明確地設(shè)置初始化順序

這也就是App Startup的第二個(gè)特性了纸型,可以設(shè)置初始化順序。

可以想象梅忌,按照上述做法狰腌,所有庫(kù)都這樣設(shè)定了,那么都會(huì)在同一個(gè)ContentProvider也就是androidx.startup.InitializationProvider中初始化牧氮,但是如果我需要設(shè)定不同庫(kù)的初始化順序怎么辦呢琼腔?

比如上述的facebook初始化,我需要設(shè)定在另一個(gè)庫(kù)WorkManager之后運(yùn)行踱葛,那么我們就可以重寫dependencies方法:

class FacebookSDKInitializer : Initializer<Unit> {
    private  val TAG = "FacebookSDKInitializer"

    override fun create(context: Context): Unit {
        try {
            FacebookSdk.sdkInitialize(context)
        } catch (ex: Exception) {
            Log.i(TAG, "Failed to auto initialize the Facebook SDK", ex)
        }
    }

    
    override fun dependencies(): List<Class<out Initializer<*>>> {
        return listOf(WorkManagerInitializer::class.java)
    }
}

不錯(cuò)吧展姐,這樣設(shè)定之后,三方庫(kù)的初始化順序就變成了:

WorkManager初始化 -> FacebookSDK初始化剖毯。

實(shí)踐出真理

說(shuō)了這么多,從理論上來(lái)說(shuō)教馆,確實(shí)App Startup減少了耗時(shí)逊谋,畢竟將多個(gè)ContentProvider融合成了一個(gè),那么我們秉著“實(shí)踐才是檢驗(yàn)真理的唯一標(biāo)準(zhǔn)”土铺,就來(lái)實(shí)踐看看耗時(shí)減少了多少胶滋。

該怎么統(tǒng)計(jì)這個(gè)啟動(dòng)時(shí)間呢?一般有以下幾個(gè)方案:

  • 如果是Application和Activity的時(shí)間可以通過(guò)TraceView悲敷、systrace等 的方式進(jìn)行時(shí)間統(tǒng)計(jì)究恤,但是ContentProvider的初始化在Application之前,不適用我們這次實(shí)踐后德。

  • Android官方提供了一個(gè)可以統(tǒng)計(jì)線上應(yīng)用啟動(dòng)時(shí)間的工具——Android Vitals部宿,它可以在GooglePlay管理中心顯示應(yīng)用啟動(dòng)過(guò)長(zhǎng)情況的啟動(dòng)時(shí)間,很顯然這個(gè)也不適用于我們瓢湃,這個(gè)必須上線到Googleplay理张。

  • 視頻錄制。如果是線下的app绵患,我們可以采用視頻錄制的方法準(zhǔn)確測(cè)量啟動(dòng)時(shí)間雾叭,也就是通過(guò)判定視頻的每一幀截圖來(lái)知曉什么時(shí)候app啟動(dòng)了,然后統(tǒng)計(jì)這個(gè)啟動(dòng)時(shí)間落蝙。具體做法就是使用adb shell screenrecord命令進(jìn)行屏幕錄制然后分析視頻织狐,有興趣的小伙伴可以網(wǎng)上找找資料暂幼,這里就不細(xì)說(shuō)了。

  • 最后移迫,就是用系統(tǒng)自帶的統(tǒng)計(jì)時(shí)間TotalTime旺嬉。

這個(gè)時(shí)間是Android源碼中幫我們計(jì)算的,可統(tǒng)計(jì)到Activity的啟動(dòng)時(shí)間起意,如果我們?cè)贖ome頁(yè)執(zhí)行命令鹰服,也就能得到一個(gè)冷啟動(dòng)的時(shí)間。雖然這個(gè)時(shí)間不是很準(zhǔn)確揽咕,但是我只需要比較App StartUp使用的的前后時(shí)間大小悲酷,所以也夠用了,開干亲善。

1)測(cè)試2個(gè)ContentProvider

第一次设易,我們測(cè)試2個(gè)ContentProvider的情況。

        <provider
            android:name=".appstartup.LibraryAContentProvider"
            android:authorities="${applicationId}.LibraryAContentProvider"
            android:exported="false" />

        <provider
            android:name=".appstartup.LibraryBContentProvider"
            android:authorities="${applicationId}.LibraryBContentProvider"
            android:exported="false" />

安裝到手機(jī)后蛹头,打開應(yīng)用顿肺,Terminal中輸入命令:

adb shell am start -W -n packagename/packageName.MainActivity

由于每次啟動(dòng)時(shí)間不一,所以我們運(yùn)行五次渣蜗,取平均值:

TotalTime: 927
TotalTime: 938
TotalTime: 948
TotalTime: 934
TotalTime: 937

平均值:936.8

然后注釋剛才的ContentProvider注冊(cè)代碼屠尊,添加App startup代碼,并注冊(cè):

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">

            <meta-data  android:name="com.example.studynote.appstartup.LibraryAInitializer"
                android:value="androidx.startup" />

            <meta-data  android:name="com.example.studynote.appstartup.LibraryBInitializer"
                android:value="androidx.startup" />
        </provider>

運(yùn)行App耕拷,并執(zhí)行命令讼昆,得出啟動(dòng)時(shí)間:

TotalTime: 931
TotalTime: 947
TotalTime: 937
TotalTime: 940
TotalTime: 932

平均值:937.4

咦?骚烧?我手機(jī)壞了嗎浸赫?怎么跟預(yù)想的不一樣啊,結(jié)果耗時(shí)還增加了赃绊?

按道理來(lái)說(shuō)原來(lái)有兩個(gè)ContentProvider既峡,用了App startup,集成為一個(gè)碧查,耗時(shí)不應(yīng)該減少么运敢。

其實(shí)這就涉及到ContentProvider的實(shí)際耗時(shí)了,我在網(wǎng)上找到一張圖忠售,關(guān)于ContentProvider耗時(shí)者冤,是Google官方做的統(tǒng)計(jì),圖片來(lái)源于郭神的博客:

image

可以看到這里統(tǒng)計(jì)的1個(gè)ContentProvider耗時(shí)2ms左右档痪,10ContentProvider耗時(shí)6ms左右涉枫。

所以我們只減少了一個(gè)ContentProvider的耗時(shí),幾乎可以忽略不計(jì)腐螟。再加上我們用到的App Startup庫(kù)中InitializationProvider的一些任務(wù)也會(huì)產(chǎn)生耗時(shí)愿汰,比如:

  • 會(huì)去遍歷所有metadata標(biāo)簽的組件
  • 會(huì)通過(guò)反射獲取每個(gè)組件的Initializer接口困后,并獲取相應(yīng)的依賴項(xiàng),并進(jìn)行排序衬廷。

這些操作也是耗時(shí)的摇予,也就是集成App Startup庫(kù)之后增加的耗時(shí)時(shí)間。所以就有可能會(huì)發(fā)生上面的情況了吗跋,集成App Startup庫(kù)之后啟動(dòng)耗時(shí)反而增多侧戴。

那難道這個(gè)庫(kù)就沒用了嗎?肯定不是的跌宛,當(dāng)ContentProvider的數(shù)量變多酗宋,它的作用就體現(xiàn)出來(lái)了,再試下10個(gè)ContentProvider的情況疆拘。

2)10個(gè)ContentProvider

首先寫好10個(gè)ContentProvider蜕猫,并在AndroidManifest.xml中注冊(cè):

        <provider
            android:name=".appstartup.LibraryAContentProvider"
            android:authorities="${applicationId}.LibraryAContentProvider"
            android:exported="false" />

<!--      省略剩下9個(gè)provider注冊(cè)代碼        -->

運(yùn)行五次,取平均值:

TotalTime: 1758
TotalTime: 1759
TotalTime: 1733
TotalTime: 1737
TotalTime: 1747

平均值:1746.8

然后注釋剛才的ContentProvider注冊(cè)代碼哎迄,添加App startup代碼回右,并注冊(cè):

        <provider
            android:name="androidx.startup.InitializationProvider"
            android:authorities="${applicationId}.androidx-startup"
            android:exported="false"
            tools:node="merge">

            <meta-data  android:name="com.example.studynote.appstartup.LibraryAInitializer"
                android:value="androidx.startup" />

            <!--省略剩下9個(gè)meta-data注冊(cè)代碼-->
        </provider>

運(yùn)行App,并執(zhí)行命令漱挚,得出啟動(dòng)時(shí)間:

TotalTime: 1741
TotalTime: 1755
TotalTime: 1722
TotalTime: 1739
TotalTime: 1730

平均值:1737.4

可以看到翔烁,這里App Startup的作用就體現(xiàn)了出來(lái),在使用App Startup之前的啟動(dòng)耗時(shí)是1746.8ms旨涝,使用之后啟動(dòng)耗時(shí)是1737.4ms蹬屹,減少了9.4ms

所以得出結(jié)論颊糜,當(dāng)集成的庫(kù)使用的ContentProvider達(dá)到一定個(gè)數(shù)之后,確實(shí)能減少耗時(shí)秃踩,但是減少的不多衬鱼,比如這里我們是10個(gè)ContentProvider集成App Startup后能減少的耗時(shí)在10ms左右,再結(jié)合上圖官方的統(tǒng)計(jì)時(shí)間來(lái)看憔杨,一般一個(gè)項(xiàng)目集成了十幾個(gè)使用ContentProvider的庫(kù)鸟赫,耗時(shí)減少應(yīng)該能在20ms之內(nèi)。

所以我們的App Startup解決的就是這個(gè)耗時(shí)時(shí)間消别,雖然不多抛蚤,但是也確實(shí)有減少耗時(shí)的功能。

思考

雖然這個(gè)庫(kù)能解決一定的三方庫(kù)初始化耗時(shí)問題寻狂,但是我覺得還是有很大的局限性岁经,比如這些問題:

  • 本身依賴的庫(kù)就不多。如果我們的項(xiàng)目本身依賴就不多蛇券,那么有沒有必要去集成這個(gè)呢缀壤?極端情況下樊拓,只依賴了一個(gè)庫(kù),那么還要專門提供一個(gè)InitializationProvider塘慕,是不是又變相的增加了耗時(shí)呢筋夏?
  • 延時(shí)初始化。上次我們說(shuō)過(guò)图呢,有些庫(kù)并不需要一開始就初始化条篷,那么我們最好將其延遲初始化,進(jìn)行懶加載蛤织。
  • 異步初始化赴叹。同樣,有些庫(kù)不需要在主線程進(jìn)行初始化瞳筏,那么我們可以對(duì)其進(jìn)行異步初始化稚瘾,從而減少啟動(dòng)耗時(shí)。
  • 多個(gè)異步任務(wù)依賴關(guān)系姚炕。如果有些任務(wù)需要異步執(zhí)行的同時(shí)還有互相的依賴關(guān)系摊欠,該怎么辦呢。

如果我們?cè)谑褂?code>App Startup的時(shí)候柱宦,有以上需求些椒,那么有沒有解決辦法呢?

  • 沒有掸刊,也可以說(shuō)有免糕,就是關(guān)閉App Startup的初始化動(dòng)作,然后自己進(jìn)行初始化任務(wù)管理忧侧。

這可不是開玩笑石窑,App Startup的目的只是解決一個(gè)問題,就是多個(gè)ContentProvider創(chuàng)建的問題蚓炬,通過(guò)一個(gè)統(tǒng)一的ContentProvider來(lái)形成規(guī)范松逊,減少耗時(shí)。所以它的用法應(yīng)該是針對(duì)各個(gè)三方庫(kù)的設(shè)計(jì)者肯夏,當(dāng)你設(shè)計(jì)一個(gè)庫(kù)的時(shí)候经宏,如果想靜默初始化,就可以接入App Startup驯击。當(dāng)盡量多的庫(kù)遵循這個(gè)要求烁兰,都接入App Startup的時(shí)候,開發(fā)者的啟動(dòng)耗時(shí)自然就降低了徊都。

但是如果我們有其他的需求沪斟,比如上述說(shuō)到的延遲初始化,異步初始化等問題暇矫,我們就要關(guān)閉部分庫(kù)或者所有庫(kù)的App Startup的功能币喧,然后自己?jiǎn)为?dú)對(duì)任務(wù)進(jìn)行初始化工作轨域,比如通過(guò)啟動(dòng)器來(lái)處理各個(gè)初始化任務(wù)的關(guān)系。

如果一個(gè)庫(kù)已經(jīng)集成了App Startup功能杀餐,我們?cè)撛趺搓P(guān)閉呢干发?這就用到tools:node="remove"標(biāo)簽了。

<!-- 禁用所有InitializationProvider組件初始化 -->
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    tools:node="remove" />


<!-- 禁用單個(gè)InitializationProvider組件初始化 -->
<provider
    android:name="androidx.startup.InitializationProvider"
    android:authorities="${applicationId}.androidx-startup"
    android:exported="false"
    tools:node="merge">

    <meta-data  android:name="com.example.FacebookSDKInitializer"
            android:value="androidx.startup"
            tools:node="remove"/>
</provider>

這樣FacebookSDK就不會(huì)自動(dòng)進(jìn)行初始化了史翘,需要我們手動(dòng)調(diào)用初始化方法枉长。

總結(jié)

1)App Startup的設(shè)計(jì)是為了解決一個(gè)問題:

  • 即不同的庫(kù)使用不同的ContentProvider進(jìn)行初始化,導(dǎo)致ContentProvider太多琼讽,管理雜亂必峰,影響耗時(shí)的問題。

2)App Startup具體能減少多少耗時(shí)時(shí)間:

  • 上面也實(shí)踐過(guò)了钻蹬,如果二三十個(gè)三方庫(kù)都集成了App Startup吼蚁,減少的耗時(shí)大概在20ms以內(nèi)。

3)App Startup的使用場(chǎng)景應(yīng)該是:

  • 針對(duì)三方庫(kù)的設(shè)計(jì)者或者組件化的場(chǎng)景问欠。當(dāng)你設(shè)計(jì)一個(gè)庫(kù)或者一個(gè)組件的時(shí)候肝匆,就可以接入App Startup。當(dāng)盡量多的庫(kù)遵循這個(gè)標(biāo)準(zhǔn)顺献,都接入App Startup的時(shí)候旗国,就能形成一種規(guī)范,App的啟動(dòng)耗時(shí)自然就降低了注整。

4)如果想解決多個(gè)庫(kù)初始化任務(wù)太多導(dǎo)致的啟動(dòng)耗時(shí)問題:

  • 請(qǐng)左轉(zhuǎn)前往各種啟動(dòng)器能曾,比如alibaba/alpha

參考

Google文檔

App Startup-郭霖

Android啟動(dòng)時(shí)間—siyu8023

App Startup源碼—葉志陳

拜拜

感謝大家的閱讀,有一起學(xué)習(xí)的小伙伴可以關(guān)注下我的公眾號(hào)——碼上積木????
每日三問知識(shí)點(diǎn)/面試題肿轨,積少成多寿冕。
這里有一群很好的Android小伙伴,歡迎大家加入~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椒袍,一起剝皮案震驚了整個(gè)濱河市驼唱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌槐沼,老刑警劉巖曙蒸,帶你破解...
    沈念sama閱讀 222,681評(píng)論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捌治,死亡現(xiàn)場(chǎng)離奇詭異岗钩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)肖油,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,205評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門兼吓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人森枪,你說(shuō)我怎么就攤上這事视搏∩竽酰” “怎么了?”我有些...
    開封第一講書人閱讀 169,421評(píng)論 0 362
  • 文/不壞的土叔 我叫張陵浑娜,是天一觀的道長(zhǎng)佑力。 經(jīng)常有香客問我,道長(zhǎng)筋遭,這世上最難降的妖魔是什么打颤? 我笑而不...
    開封第一講書人閱讀 60,114評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮漓滔,結(jié)果婚禮上编饺,老公的妹妹穿的比我還像新娘。我一直安慰自己响驴,他們只是感情好透且,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,116評(píng)論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著豁鲤,像睡著了一般秽誊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上畅形,一...
    開封第一講書人閱讀 52,713評(píng)論 1 312
  • 那天养距,我揣著相機(jī)與錄音,去河邊找鬼日熬。 笑死棍厌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的竖席。 我是一名探鬼主播耘纱,決...
    沈念sama閱讀 41,170評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼莫矗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼擎宝!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拦惋,我...
    開封第一講書人閱讀 40,116評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤憎亚,失蹤者是張志新(化名)和其女友劉穎员寇,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體第美,經(jīng)...
    沈念sama閱讀 46,651評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蝶锋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,714評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了什往。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扳缕。...
    茶點(diǎn)故事閱讀 40,865評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出躯舔,到底是詐尸還是另有隱情驴剔,我是刑警寧澤,帶...
    沈念sama閱讀 36,527評(píng)論 5 351
  • 正文 年R本政府宣布粥庄,位于F島的核電站丧失,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏惜互。R本人自食惡果不足惜利花,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,211評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望载佳。 院中可真熱鬧炒事,春花似錦、人聲如沸蔫慧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,699評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)姑躲。三九已至睡扬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間黍析,已是汗流浹背卖怜。 一陣腳步聲響...
    開封第一講書人閱讀 33,814評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阐枣,地道東北人马靠。 一個(gè)月前我還...
    沈念sama閱讀 49,299評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蔼两,于是被迫代替她去往敵國(guó)和親甩鳄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,870評(píng)論 2 361

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