RePlugin 記錄(三)Activity調(diào)用流程

通過Replugin 記錄(二)插件啟動過程,我們知道插件中的Activity被gradle修改為繼承PluginActivity,其中startActivty被重寫,通過Factory2.startActivity顯式調(diào)用Replugin啟動插件activity的方法

    public void startActivity(Intent intent) {
        //反射調(diào)用Factory2.startActivity
        if (RePluginInternal.startActivity(this, intent)) {
            // 這個地方不需要回調(diào)startActivityAfter染苛,因為Factory2最終還是會回調(diào)回來沙热,最終還是要走super.startActivity()
            return;
        }

        super.startActivity(intent);
    }

除此之外還可以通過RePlugin.startActivity()在宿主啟動插件activity袭景。這兩個方法最終都會調(diào)用Factory類的startActivityWithNoInjectCN酝豪。

   /**
     * 內(nèi)部接口敛惊,僅為Factory2.startActivity(context, intent) 和 RePlugin.startActivity方法而使用
     *
     * @param context  應(yīng)用上下文或者Activity上下文
     * @param intent   Intent對象
     * @param plugin   插件名
     * @param activity 待啟動的activity類名
     * @param process  是否在指定進(jìn)程中啟動
     * @return 插件機(jī)制層膊毁,是否成功胀莹,例如沒有插件存在、沒有合適的Activity坑
     * Added by Jiongxuan Zhang
     */
    public static final boolean startActivityWithNoInjectCN(Context context, Intent intent, String plugin, String activity, int process) {
        boolean result = sPluginManager.startActivity(context, intent, plugin, activity, process);

        RePlugin.getConfig().getEventCallbacks().onStartActivityCompleted(plugin, activity, result);
        return result;
    }

PluginCommImpl.startActivity

/**
     * 啟動一個插件中的activity媚媒,如果插件不存在會觸發(fā)下載界面
     * @param context 應(yīng)用上下文或者Activity上下文
     * @param intent
     * @param plugin 插件名
     * @param activity 待啟動的activity類名
     * @param process 是否在指定進(jìn)程中啟動
     * @return 插件機(jī)制層是否成功,例如沒有插件存在涩僻、沒有合適的Activity坑
     */
    public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process) {
        if (LOG) {
            LogDebug.d(PLUGIN_TAG, "start activity: intent=" + intent + " plugin=" + plugin + " activity=" + activity + " process=" + process);
        }

        return mPluginMgr.mInternal.startActivity(context, intent, plugin, activity, process, true);
    }

在開始看startActivity之前缭召,我們要理一下一般插件化啟動插件Activity做的工作。
1逆日、把要啟動的目標(biāo)Activity替換成注冊在mainfest上的占坑Activity(其中涉及進(jìn)程選擇啟動嵌巷,坑位選擇分配)
而替換的原因是系統(tǒng)在startActivity的過程中會根據(jù)intent去PackageManagerService查詢我們要啟動的activity是否已經(jīng)在mainfest中注冊了。(PluginLibraryInternalProxy.startActivity)
2室抽、在startActivity流程從ActivityManagerService回到activity所在進(jìn)程時的調(diào)用流程時搪哪,把坑位Activity替換成真正要啟動的Activity。(RePluginClassLoader.loadClass 完成)
3坪圾、插件化意味著類是動態(tài)加載的晓折,系統(tǒng)默認(rèn)創(chuàng)建的ClassLoader只能加載Apk中的類即宿主中的類惑朦,所以需要對ClassLoader進(jìn)行操作使得它能加載插件中的類。(記錄一中的PatchClassLoaderUtils.patch完成)

更多插件化相關(guān)知識可以看術(shù)哥的這篇文章漓概,講的很好 http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/ Android 插件化原理解析——Activity生命周期管理

PluginLibraryInternalProxy.startActivity

 /**
     * @hide 內(nèi)部方法漾月,插件框架使用
     * 啟動一個插件中的activity,如果插件不存在會觸發(fā)下載界面
     * @param context 應(yīng)用上下文或者Activity上下文
     * @param intent
     * @param plugin 插件名
     * @param activity 待啟動的activity類名
     * @param process 是否在指定進(jìn)程中啟動
     * @param download 下載
     * @return 插件機(jī)制層是否成功胃珍,例如沒有插件存在梁肿、沒有合適的Activity坑
     */
    public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download) {
     
        // 是否啟動下載
        // 若插件不可用(不存在或版本不匹配),則直接彈出“下載插件”對話框
        // 因為已經(jīng)打開UpdateActivity觅彰,故在這里返回True吩蔑,告訴外界已經(jīng)打開,無需處理
        if (download) {
            if (PluginTable.getPluginInfo(plugin) == null) {
                // 如果用戶在下載即將完成時突然點(diǎn)按“取消”填抬,則有可能出現(xiàn)插件已下載成功烛芬,但沒有及時加載進(jìn)來的情況
                // 因此我們會判斷這種情況,如果是痴奏,則重新加載一次即可蛀骇,反之則提示用戶下載
                // 原因:“取消”會觸發(fā)Task.release方法,最終調(diào)用mDownloadTask.destroy读拆,導(dǎo)致“下載服務(wù)”的Receiver被注銷擅憔,即使文件下載了也沒有回調(diào)回來
                // NOTE isNeedToDownload方法會調(diào)用pluginDownloaded再次嘗試加載
                // 以下兩種情況需要下載插件:
                // 1、V5文件不存在(常見)檐晕;
                // 2暑诸、V5文件非法(加載失敗)
                if (isNeedToDownload(context, plugin)) {
                    return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
                }
            }
        }

        /* 檢查是否是動態(tài)注冊的類 */
        // 如果要啟動的 Activity 是動態(tài)注冊的類辟灰,則不使用坑位機(jī)制个榕,而是直接動態(tài)類。
        // 所以如果動態(tài)類是四大組件芥喇,被替換的類需要在宿主中注冊
        // 原因:宿主的某些動態(tài)注冊的類不能運(yùn)行在坑位中(如'桌面'插件的入口Activity)
        if (Factory2.isDynamicClass(plugin, activity)) {
            intent.putExtra(IPluginManager.KEY_COMPATIBLE, true);
            intent.setComponent(new ComponentName(IPC.getPackageName(), activity));
            context.startActivity(intent);
            return true;
        }

        // 如果插件狀態(tài)出現(xiàn)問題西采,則每次彈此插件的Activity都應(yīng)提示無法使用,或提示升級(如有新版)
        // Added by Jiongxuan Zhang
        if (PluginStatusController.getStatus(plugin) < PluginStatusController.STATUS_OK) {
            return RePlugin.getConfig().getCallbacks().onPluginNotExistsForActivity(context, plugin, intent, process);
        }

        // 若為首次加載插件继控,且是“大插件”械馆,則應(yīng)異步加載,同時彈窗提示“加載中”
        // Added by Jiongxuan Zhang
        if (!RePlugin.isPluginDexExtracted(plugin)) {
            PluginDesc pd = PluginDesc.get(plugin);
            if (pd != null && pd.isLarge()) {
                return RePlugin.getConfig().getCallbacks().onLoadLargePluginForActivity(context, plugin, intent, process);
            }
        }

        // 緩存打開前的Intent對象武通,里面將包括Action等內(nèi)容
        Intent from = new Intent(intent);

        // 幫助填寫打開前的Intent的ComponentName信息(如有霹崎。沒有的情況如直接通過Action打開等)
        if (!TextUtils.isEmpty(plugin) && !TextUtils.isEmpty(activity)) {
            from.setComponent(new ComponentName(plugin, activity));
        }
        //根據(jù)給要跳轉(zhuǎn)的actvity所在插件找到一個合適的坑位
        ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);
    
        // 將Intent指向到“坑位”。這樣:
        // from:插件原Intent
        // to:坑位Intent
        intent.setComponent(cn);
        //啟動坑位Activity冶忱,在RePluginClassLoader.loadClass加載Activity的時候會把它換回真正要加載的Activity
        context.startActivity(intent);

        // 通知外界尾菇,已準(zhǔn)備好要打開Activity了
        // 其中:from為要打開的插件的Intent,to為坑位Intent
        RePlugin.getConfig().getEventCallbacks().onPrepareStartPitActivity(context, from, intent);

        return true;
    }

PluginCommImpl.loadPluginActivity
插件的坑位分配

 public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {

        ActivityInfo ai = null;
        String container = null;
        PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);

        // 獲取 ActivityInfo
       // 找到對應(yīng)插件的activityInfo。前面在加載插件的時候會把mainfest的四大組件信息緩存下來
         ai = getActivityInfo(plugin, activity, intent);
         // 存儲此 Activity 在插件 Manifest 中聲明主題到 Intent派诬,作為坑位選擇的一個條件
         intent.putExtra(INTENT_KEY_THEME_ID, ai.theme);
         // 根據(jù) activity 的 processName劳淆,選擇進(jìn)程 ID 標(biāo)識
        // 在Loader.loadDex加載插件的時候調(diào)用adjustPluginProcess(mPackageInfo.applicationInfo);
       // 調(diào)整activity的目標(biāo)進(jìn)程。(根據(jù)組件的目標(biāo)進(jìn)程填寫千埃,判斷是在UI進(jìn)程憔儿,還是在常駐進(jìn)程還是自定義進(jìn)程
       // 其中自定義進(jìn)程需要把進(jìn)程映射成p0, p1, p2這幾個指定的進(jìn)程)
       // 注:這里即使是調(diào)整成常駐進(jìn)程,在startPluginProcess 中無效放可,最后會由系統(tǒng)分配進(jìn)程
         if (ai.processName != null) {
             process = PluginClientHelper.getProcessInt(ai.processName);
         }
         // 容器選擇(啟動目標(biāo)進(jìn)程)谒臼,返回目標(biāo)進(jìn)程的一個binder客戶端后續(xù)請求調(diào)用
         IPluginClient client = MP.startPluginProcess(plugin, process, info);
         // 目標(biāo)進(jìn)程遠(yuǎn)程分配坑位
         container = client.allocActivityContainer(plugin, process, ai.name, intent);   
       //指向在宿主mainfest中注冊好的組件坑位了
        return new ComponentName(IPC.getPackageName(), container);
    }

PmHostSvc.startPluginProcess

 @Override
    public IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {
        synchronized (this) {
            return mPluginMgr.startPluginProcessLocked(plugin, process, info);
        }
    }

PmBase.startPluginProcessLocked

final IPluginClient startPluginProcessLocked(String plugin, int process, PluginBinderInfo info) {
        // 如果是activity,binder這類請求耀里,沒有指定進(jìn)程則強(qiáng)制使用UI進(jìn)程
        if (Constant.ENABLE_PLUGIN_ACTIVITY_AND_BINDER_RUN_IN_MAIN_UI_PROCESS) {
            if (info.request == PluginBinderInfo.ACTIVITY_REQUEST) {
                if (process == IPluginManager.PROCESS_AUTO) {
                    process = IPluginManager.PROCESS_UI;
                }
            }
            if (info.request == PluginBinderInfo.BINDER_REQUEST) {
                if (process == IPluginManager.PROCESS_AUTO) {
                    process = IPluginManager.PROCESS_UI;
                }
            }
        }

        //根據(jù)插件名稱蜈缤,指定進(jìn)程從已創(chuàng)建的進(jìn)程中獲取,如果已存在且還存活則直接返回冯挎。
       //對比PluginProcessMain.allocProcess來看
        IPluginClient client = PluginProcessMain.probePluginClient(plugin, process, info);
        if (client != null) {
            return client;
        }

        int index = IPluginManager.PROCESS_AUTO;
        // 分配進(jìn)程底哥。三種情況,ui進(jìn)程房官,自定義進(jìn)程(需要在mainfest寫meta-data
        // 將自定義的進(jìn)程映射成p0,p1,p2)趾徽,由replugin來分配stub進(jìn)程
        //(不屬于ui也不是自定義進(jìn)程,不過看樣子已經(jīng)不用了,所以不考慮這種情況)
        index = PluginProcessMain.allocProcess(plugin, process);
        // 分配的坑位不屬于UI翰守、自定義進(jìn)程和自動分配的進(jìn)程孵奶,就返回。
        if (!(index == IPluginManager.PROCESS_UI
                || PluginProcessHost.isCustomPluginProcess(index)
                || PluginManager.isPluginProcess(index))) {
            return null;
        }

        //根據(jù)上面分配的進(jìn)程index,組裝成一個uri來啟動預(yù)先在mainfest中埋好的provider蜡峰,因為provider
       // 在mainfest指定了目標(biāo)進(jìn)程了袁,從而間接達(dá)到開啟進(jìn)程的效果畦韭。
        boolean rc = PluginProviderStub.proxyStartPluginProcess(mContext, index);
        if (!rc) {
            return null;
        }

        // 再次獲取
        client = PluginProcessMain.probePluginClient(plugin, process, info);
        if (client == null) {
            return null;
        }
        return client;
    }

自定義進(jìn)程:


image.png

PluginProcessPer.allocActivityContainer

 @Override
    public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {
        // 一旦有分配谋币,則進(jìn)入監(jiān)控狀態(tài)(一是避免不退出的情況,二也是最重要的是避免現(xiàn)在就退出的情況)
        RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);
        String loadPlugin = null;
        //辙培,省略了process的調(diào)整油航,因為后面bindActivity其實沒有用到
       //  省略了plugin的判斷從PluginCommImpl.loadPluginActivity過來的話因為插件的判斷已經(jīng)在前面判斷過了 

        String container = bindActivity(loadPlugin, process, target, intent);
        return container;
    }

PluginProcessPer.bindActivity

final String bindActivity(String plugin, int process, String activity, Intent intent) {

        /* 獲取插件對象 */
        Plugin p = mPluginMgr.loadAppPlugin(plugin);
        /* 獲取 ActivityInfo */
        ActivityInfo ai = p.mLoader.mComponents.getActivity(activity);

        /* 獲取 Container */
        String container;

        // 根據(jù)是自定義進(jìn)程還是UI進(jìn)程崭庸,取不同的坑位信息。邏輯是一樣的谊囚,所以只看一個mACM.alloc就夠了
        if (ai.processName.contains(PluginProcessHost.PROCESS_PLUGIN_SUFFIX2)) {
            String processTail = PluginProcessHost.processTail(ai.processName);
            container = mACM.alloc2(ai, plugin, activity, process, intent, processTail);
        } else {
            container = mACM.alloc(ai, plugin, activity, process, intent);
        }
        //檢查 activity 是否存在怕享。這個classLoader是PluginDexClassLoader,如果在插件找不到
       // 會嘗試在宿主中找該activity
        c = p.mLoader.mClassLoader.loadClass(activity);

        return container;
    }

PluginContainers.alloc

final String alloc(ActivityInfo ai, String plugin, String activity, int process, Intent intent) {
        ActivityState state;
       String defaultPluginTaskAffinity = ai.applicationInfo.packageName;
       //省略了其他啟動模式秒啦,這里是/* SingleTask, SingleTop, Standard */
        synchronized (mLock) {
              state = allocLocked(ai, mLaunchModeStates.getStates(ai.launchMode, ai.theme), plugin, activity, intent);
         }
        return state.container;
    }

PluginContainers.allocLocked

private final ActivityState allocLocked(ActivityInfo ai, HashMap<String, ActivityState> map,
                                            String plugin, String activity, Intent intent) {
        // 這里就貼下代碼熬粗,原本的注釋也夠清楚的了,state.occupy(plugin, activity)將坑位和插件目標(biāo)activity關(guān)聯(lián)起來
        // 坑和狀態(tài)的 map 為空
        if (map == null) {
            return null;
        }

        // 首先找上一個活的搀玖,或者已經(jīng)注冊的余境,避免多個坑到同一個activity的映射
        for (ActivityState state : map.values()) {
            if (state.isTarget(plugin, activity)) {
                return state;
            }
        }

        // 新分配:找空白的,第一個
        for (ActivityState state : map.values()) {
            if (state.state == STATE_NONE) {
                state.occupy(plugin, activity);
                return state;
            }
        }

        ActivityState found;

        // 重用:則找最老的那個
        found = null;
        for (ActivityState state : map.values()) {
            if (!state.hasRef()) {
                if (found == null) {
                    found = state;
                } else if (state.timestamp < found.timestamp) {
                    found = state;
                }
            }
        }
        if (found != null) {
            found.occupy(plugin, activity);
            return found;
        }

        // 強(qiáng)擠:最后一招,擠掉:最老的那個
        found = null;
        for (ActivityState state : map.values()) {
            if (found == null) {
                found = state;
            } else if (state.timestamp < found.timestamp) {
                found = state;
            }
        }
        if (found != null) {
            found.finishRefs();
            found.occupy(plugin, activity);
            return found;
        }
        // never reach here
        return null;
    }

上面進(jìn)入startActivity后芳来,最后系統(tǒng)調(diào)回ActivityThread.performLaunchActivity來創(chuàng)建Activity實例

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        if (r.packageInfo == null) {
            //獲取LoadedApk含末,此時LoadedApk中的classLoader已經(jīng)是RePluginClassLoader了
           // 看記錄一的流程PatchClassLoaderUtils.patch方法
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        //創(chuàng)建context的時候把classLoader設(shè)置為r.packageInfo.getClassLoader();
        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //最終調(diào)用cl.loadClass(className).newInstance()
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        r.intent.setExtrasClassLoader(cl);
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
     
}

RePluginClassLoader.loadClass

@Override
    protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
        Class<?> c = null;
        //調(diào)用PmBase.loadClass 對應(yīng)插件classLoader加載類或者是加載動態(tài)注冊類
        c = PMF.loadClass(className, resolve);
        if (c != null) {
            return c;
        }
        //
        try {
            //如果找不到就用默認(rèn)系統(tǒng)生成的PathClassLoader即加載宿主的類
            c = mOrig.loadClass(className);
            return c;
        } catch (Throwable e) {
            //
        }
        //
        return super.loadClass(className, resolve);
    }

PmBase.loadClass

 final Class<?> loadClass(String className, boolean resolve) {
     
        //這里略去了contentprovider和service的情況
        // 判斷是否坑位Activity
        if (mContainerActivities.contains(className)) {
            Class<?> c = mClient.resolveActivityClass(className);
            if (c != null) {
                return c;
            }
            //找不到要加載的Activity返回DummyActivity,直接finish
            return DummyActivity.class;
        }
        // 插件定制表
        DynamicClass dc = mDynamicClasses.get(className);
        if (dc != null) {
           // 省略了加載失敗的處理即舌,其實也挺清晰的就是插件對應(yīng)的classLoader去加載類
            Plugin p = loadAppPlugin(dc.plugin);
           if (p != null) {
                try {
                    Class<?> cls = p.getClassLoader().loadClass(dc.className);
                    return cls;
                } catch (Throwable e) {
                }
            } 
            //返回一個關(guān)閉自己的組件
            // return dummy class
            if ("activity".equals(dc.classType)) {
                return DummyActivity.class;
            } else if ("service".equals(dc.classType)) {
                return DummyService.class;
            } else if ("provider".equals(dc.classType)) {
                return DummyProvider.class;
            }
            //如果不是組件佣盒,返回一個加載失敗用的類
            return dc.defClass;
        }

        //加載默認(rèn)插件的類
        return loadDefaultClass(className);
    }

PluginProcessPer.resolveActivityClass

 final Class<?> resolveActivityClass(String container) {
        String plugin = null;
        String activity = null;

        // 根據(jù)container找之前bindActivity登記的ActivityState從而找到對應(yīng)插件和activity,如果找不到顽聂,則用forward activity
        PluginContainers.ActivityState state = mACM.lookupByContainer(container);
        if (state == null) {
            //ForwardActivity作用是當(dāng)坑位出現(xiàn)丟失或錯亂肥惭,則通過讀取Intent.Category來做個中轉(zhuǎn),再嘗試啟動一次
            return ForwardActivity.class;
        }
        plugin = state.plugin;
        activity = state.activity;

        Plugin p = mPluginMgr.loadAppPlugin(plugin);
        ClassLoader cl = p.getClassLoader();
        
        Class<?> c = null;
        try {
            //對應(yīng)插件來加載activity類
            c = cl.loadClass(activity);
        } catch (Throwable e) {
            
        }
        return c;
    }

參考:
Android 插件化原理解析——Activity生命周期管理:
http://weishu.me/2016/03/21/understand-plugin-framework-activity-management/
RePlugin中如何打開插件中的自定義進(jìn)程Activity:https://mp.weixin.qq.com/s/IpNcyTjML16og4LrxjxFmQ

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末紊搪,一起剝皮案震驚了整個濱河市蜜葱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耀石,老刑警劉巖牵囤,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滞伟,居然都是意外死亡揭鳞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門梆奈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來野崇,“玉大人,你說我怎么就攤上這事鉴裹∥杪妫” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵径荔,是天一觀的道長督禽。 經(jīng)常有香客問我,道長总处,這世上最難降的妖魔是什么狈惫? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鹦马,結(jié)果婚禮上胧谈,老公的妹妹穿的比我還像新娘。我一直安慰自己荸频,他們只是感情好菱肖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旭从,像睡著了一般稳强。 火紅的嫁衣襯著肌膚如雪场仲。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天退疫,我揣著相機(jī)與錄音渠缕,去河邊找鬼。 笑死褒繁,一個胖子當(dāng)著我的面吹牛亦鳞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棒坏,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼燕差,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坝冕?” 一聲冷哼從身側(cè)響起谁不,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎徽诲,沒想到半個月后刹帕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谎替,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年偷溺,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钱贯。...
    茶點(diǎn)故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡挫掏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秩命,到底是詐尸還是另有隱情尉共,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布弃锐,位于F島的核電站袄友,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏霹菊。R本人自食惡果不足惜剧蚣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望旋廷。 院中可真熱鬧鸠按,春花似錦、人聲如沸饶碘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扎运。三九已至瑟曲,卻和暖如春募书,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背测蹲。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鬼吵,地道東北人扣甲。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像齿椅,于是被迫代替她去往敵國和親琉挖。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評論 2 355

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