深入理解Fragment

這里主要介紹一些對Fragment的深入理解趋艘。挑了一些個(gè)人認(rèn)為比較有價(jià)值的枢泰,大部分技術(shù)博客通常都會(huì)忽略的點(diǎn),列了出來铡溪,如果你對Fragment有什么其他疑惑漂辐,也可以在評論區(qū)留言。

Fragment究竟是什么呢棕硫?

Fragment簡單說可以認(rèn)為是一個(gè)帶有生命周期的View髓涯。閱讀過Fragment整個(gè)實(shí)現(xiàn)過程的,其實(shí)可以知道Fragment能顯示其實(shí)最終還是把要顯示的View add到ViewGroup中哈扮,它的生命周期回調(diào)纬纪,其實(shí)也全部來自Activity。然后又封裝了一些本來Activity才有的特性滑肉,比如過渡動(dòng)畫包各,返回棧等等。你甚至可以說Activity能做的大部分事情靶庙,F(xiàn)ragment都能做问畅,甚至市面有很多App是只有一個(gè)Activity或者少數(shù)幾個(gè)Activity,然后絕大部分界面全部用Fragment實(shí)現(xiàn)的六荒。比如:知乎护姆。
如何知道呢?你可以使用下面的命令來查看當(dāng)前手機(jī)顯示的Activity名稱掏击。

Linux/Unix/Mac

adb shell dumpsys activity | grep "mFocusedActivity"

Windows

adb shell dumpsys activity | findstr "mFocusedActivity"
  • 為什么有App要這么做呢卵皂?
    用過知乎的應(yīng)該會(huì)知道知乎的頁面跳轉(zhuǎn)邏輯很復(fù)雜。點(diǎn)開一個(gè)回答砚亭,然后點(diǎn)到回答的列表灯变,會(huì)發(fā)現(xiàn)回答列表也有底欄,同時(shí)底欄的操作邏輯跟主界面時(shí)候的操作邏輯完全一致钠惩。每個(gè)底欄都有自己獨(dú)立的返回棧柒凉。所以借助Fragment可以實(shí)現(xiàn)一些非常復(fù)雜的界面的跳轉(zhuǎn)邏輯。試想如果用Activity要實(shí)現(xiàn)知乎這種復(fù)雜的跳轉(zhuǎn)邏輯改有多復(fù)雜了篓跛。
    其次膝捞,F(xiàn)ragment的消耗要比Activity小,F(xiàn)ragment最終被處理是FragmentTransaction.commit方法∈咭В看過整個(gè)Fragment實(shí)現(xiàn)過程的話鲤遥,會(huì)知道commit操作后最終會(huì)調(diào)用:
    private void scheduleCommit() {
        synchronized (this) {
            boolean postponeReady =
                    mPostponedTransactions != null && !mPostponedTransactions.isEmpty();
            boolean pendingReady = mPendingActions != null && mPendingActions.size() == 1;
            if (postponeReady || pendingReady) {
                mHost.getHandler().removeCallbacks(mExecCommit);
                mHost.getHandler().post(mExecCommit);
            }
        }
    }

前面對請求做了一系列處理后,最終通過scheduleCommit講請求以Handler消息形式重新提交林艘。直到Fragment中的View被Add進(jìn)父ViewGroup都不會(huì)涉及到跨進(jìn)程通信盖奈。但是Activity啟動(dòng)過程就不一樣了,startActivity最終會(huì)通過

ActivityManagerNative.getDefault().startActivity

向ActivityManagerService所在進(jìn)程發(fā)送一個(gè)跨進(jìn)程通信消息狐援,然后ActivityManagerService響應(yīng)消息回傳钢坦,App的ActivityThread接收到消息后打開Activity界面。整個(gè)過程是需要進(jìn)行跨進(jìn)程通信的啥酱,消耗當(dāng)然要比Fragment高了爹凹。

  • 那是不是就沒有壞處了?
    也不是镶殷,F(xiàn)ragment將失去Activity本身的很多特性禾酱,比如啟動(dòng)模式,不能直接通過ACTION啟動(dòng)绘趋。將失去Manifest中你能看到的Activity的特性颤陶。當(dāng)然如果你不關(guān)心這些東西,那完全用Fragment替換也無妨陷遮。

究竟應(yīng)該用 android.support.v4.app.Fragment還是android.app.Fragment滓走?

android.app.Fragment是Google在Android3.0(API11)的時(shí)候推出的。現(xiàn)在大部分AndroidApp應(yīng)該都已經(jīng)最低兼容到4.0(API14)拷呆。首先從Api上來說不存在問題闲坎,雖然android.support.v4.app.Fragment兼容到v4,但對于大多數(shù)App來說兼容到14就夠了茬斧。那從使用角度來看呢腰懂?

        android.support.v4.app.Fragment supportFragment = new android.support.v4.app.Fragment();
        android.support.v4.app.FragmentTransaction supportTransaction = getSupportFragmentManager().beginTransaction();
        supportTransaction.add(supportFragment, "tag");
        supportTransaction.commit();

        android.app.Fragment fragment = new android.app.Fragment();
        android.app.FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.add(fragment, "tag");
        transaction.commit();

使用上就是包路徑不一樣,然后一個(gè)是getSupportFragmentManager一個(gè)是getFragmentManager项秉。Api層面基本算是完全兼容了绣溜。還有一點(diǎn)小區(qū)別就是SupportFragmentManager會(huì)多出一些方法,比如getFragments(但這個(gè)方法現(xiàn)在已經(jīng)被標(biāo)記為@RestrictTo(LIBRARY_GROUP)不推薦使用了)拿到SupportFragmentManager持有的Fragment引用娄蔼。
另外就是android.app.Fragment是android.app.Activity原生支持怖喻。不需要額外引入lib庫。但android.support.v4.app.Fragment只能用在android.support.v4.app.FragmentActivity中岁诉,需要額外引入supportV4包锚沸。getSupportFragmentManager方法就來自FragmentActivity。但考慮到通常Activity大家都會(huì)選擇繼承AppCompatActivity或者FragmentActivity涕癣,所以通常也不會(huì)有太大問題哗蜈。
說了這么多,可以這么說從兩個(gè)Fragment Api基本一致,使用起來也基本沒太大區(qū)別距潘。
基本表現(xiàn)一致炼列,那怎么選?

還是推薦用android.support.v4.app.Fragment音比。

為什么俭尖?我們之前嘗試過使用原生Fragment,從API層面確實(shí)沒遇到太多麻煩洞翩。主要是在適配第三方庫的時(shí)候遇到了很多麻煩稽犁,因?yàn)榈谌綆旎救慷际沁x擇使用的v4Fragment。另外還有一個(gè)麻煩是v4是額外的包菱农,所以v4Fragment是可以升級的缭付,但Fragment就只能跟隨手機(jī)系統(tǒng)版本升級了。比如v4FragmentManager后面新添加的方法registerFragmentLifecycleCallbacks可以用來很方便的監(jiān)聽Fragment生命周期循未,但是用FragmentManager就沒有這個(gè)待遇了。使用v4Fragment就沒有那么多麻煩了秫舌。雖然v4要額外引入supportV4包的妖,但這個(gè)包對于大家做應(yīng)用開發(fā)的話,基本是必定要引入的足陨。所以這個(gè)引入成本是必須承受的嫂粟。

為什么DialogFragment能自動(dòng)恢復(fù)?

Google推薦我們使用DialogFragment墨缘。因?yàn)镈ialogFragment在Activity重建后依然可以自動(dòng)恢復(fù)星虹,但是Dialog就不可以。那問題來了镊讼,為什么DialogFragment就可以自動(dòng)恢復(fù)宽涌,但是Dialog就不可以?
首先Dialog不能恢復(fù)蝶棋,這個(gè)應(yīng)該很容易理解卸亮,因?yàn)锳ctivity都被重建了,當(dāng)然不會(huì)自動(dòng)恢復(fù)玩裙。除非你手動(dòng)保存Dialog狀態(tài)兼贸,然后在重建時(shí)候重新show出來。
那Fragment呢吃溅?在FragmentActivity源碼中是這么處理onSaveInstanceState的溶诞。

    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Parcelable p = mFragments.saveAllState();
        if (p != null) {
            outState.putParcelable(FRAGMENTS_TAG, p);
        }
    ......
    }

mFragments是個(gè)FragmentController。通過saveAllState拿到了所有Fragment的State决侈,在onSaveInstanceState的時(shí)候保存起來了螺垢。
然后我們在看下FragmentActivity的onCreate方法。

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        mFragments.attachHost(null /*parent*/);

        super.onCreate(savedInstanceState);

        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            mFragments.restoreLoaderNonConfig(nc.loaders);
        }
        if (savedInstanceState != null) {
            Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG);
            mFragments.restoreAllState(p, nc != null ? nc.fragments : null);

            // Check if there are any pending onActivityResult calls to descendent Fragments.
            if (savedInstanceState.containsKey(NEXT_CANDIDATE_REQUEST_INDEX_TAG)) {
                mNextCandidateRequestIndex =
                        savedInstanceState.getInt(NEXT_CANDIDATE_REQUEST_INDEX_TAG);
                int[] requestCodes = savedInstanceState.getIntArray(ALLOCATED_REQUEST_INDICIES_TAG);
                String[] fragmentWhos = savedInstanceState.getStringArray(REQUEST_FRAGMENT_WHO_TAG);
                if (requestCodes == null || fragmentWhos == null ||
                            requestCodes.length != fragmentWhos.length) {
                    Log.w(TAG, "Invalid requestCode mapping in savedInstanceState.");
                } else {
                    mPendingFragmentActivityResults = new SparseArrayCompat<>(requestCodes.length);
                    for (int i = 0; i < requestCodes.length; i++) {
                        mPendingFragmentActivityResults.put(requestCodes[i], fragmentWhos[i]);
                    }
                }
            }
        }
    ......
    }

在saveInstanceState不為null的時(shí)候,調(diào)用了restoreAllState來恢復(fù)Fragment甩苛。

    public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
        mHost.mFragmentManager.restoreAllState(state, nonConfig);
    }

繼續(xù)跟進(jìn)源碼可以看到具體的恢復(fù)過程蹂楣。

    void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
    ......
                Fragment f = fs.instantiate(mHost, mParent, childNonConfig);
    ......
    }

代碼比較多,這里只貼了實(shí)際恢復(fù)的部分讯蒲,其他代碼邏輯也不復(fù)雜痊土,可以自行查看。FragmentState最終調(diào)用了Fragment.instantiate

    public Fragment instantiate(FragmentHostCallback host, Fragment parent,
        ......
            mInstance = Fragment.instantiate(context, mClassName, mArguments);
        ......
        return mInstance;
    }

最終通過class.newInstance創(chuàng)建出了Fragment實(shí)例墨林。

    public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) {
        try {
            Class<?> clazz = sClassMap.get(fname);
            if (clazz == null) {
                // Class not found in the cache, see if it's real, and try to add it
                clazz = context.getClassLoader().loadClass(fname);
                sClassMap.put(fname, clazz);
            }
            Fragment f = (Fragment)clazz.newInstance();
            if (args != null) {
                args.setClassLoader(f.getClass().getClassLoader());
                f.mArguments = args;
            }
            return f;
        } catch (ClassNotFoundException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (java.lang.InstantiationException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        } catch (IllegalAccessException e) {
            throw new InstantiationException("Unable to instantiate fragment " + fname
                    + ": make sure class name exists, is public, and has an"
                    + " empty constructor that is public", e);
        }
    }

Activity的實(shí)例化也是通過反射創(chuàng)建的赁酝。這也是為什么系統(tǒng)能創(chuàng)建和恢復(fù)出Activity/Fragment實(shí)例的原因⌒竦龋恢復(fù)后引用雖然不是同一個(gè)酌呆,但是狀態(tài)是一致,所以也就會(huì)明白搔耕,為什么Activity/Fragment一定要有一個(gè)無參構(gòu)造方法隙袁,對于參數(shù)必須通過onSaveInstanceState來保存和恢復(fù)了。

如何優(yōu)雅的初始化Fragment弃榨?

大家可能會(huì)看到下面的代碼菩收。通常下面代碼會(huì)在Activity的onCrate方法中初始化。

private SampleFragment mFragment;
private void init(){
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        mFragment = new SampleFragment();
        transaction.add(R.id.fragment_content, mFragment);
        transaction.commit();
}

有什么問題嗎鲸睛?粗看似乎沒什么問題娜饵。但實(shí)際這是一種很不好的寫法。你可以寫個(gè)Demo然后利用Android Studio的dump java heap(或者在SampleFragment的構(gòu)造方法中打印日志也可以)看下內(nèi)存中有幾個(gè)Fragment實(shí)例官辈。然后把屏幕旋轉(zhuǎn)下箱舞,觸發(fā)Activity的SaveInstanceState然后再看下內(nèi)存中有幾個(gè)實(shí)例。你會(huì)發(fā)現(xiàn)內(nèi)存中出現(xiàn)了兩個(gè)SampleFragment實(shí)例拳亿。
把上面代碼改造成下面這樣晴股,再看下旋轉(zhuǎn)屏幕后,有幾個(gè)SampleFragment實(shí)例风瘦。

private SampleFragment mFragment;
private void initFrom(){
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_content);
        if (fragment == null) {
            fragment = new SampleFragment();
        }
        mFragment = (SampleFragment) fragment;
        transaction.add(R.id.fragment_content, mFragment);
        transaction.commit();
}

上面代碼無論怎么跑SampleFragment永遠(yuǎn)都只有一個(gè)實(shí)例队魏。為什么?
好了万搔。說了這么多胡桨,可能會(huì)很多人會(huì)說,用自己手動(dòng)new Fragment的寫法并沒有遇到過問題瞬雹,怎么回事昧谊?主要是因?yàn)楝F(xiàn)在很多App都沒有適配橫屏模式,很多都鎖死只能豎屏酗捌,然后現(xiàn)在的Android手機(jī)普遍內(nèi)存都比較大呢诬,也較少的出現(xiàn)系統(tǒng)內(nèi)存不足涌哲,觸發(fā)onSaveInstanceState的情況。所以問題沒有發(fā)生并不是說這樣是對的尚镰。
另外阀圾,你會(huì)發(fā)現(xiàn)無論你用add還是replace至少加一個(gè)tag或者Rid。為什么狗唉?其實(shí)就是為了讓你重新找回Fragment實(shí)例引用的初烘。

為什么使用FragmentTransaction的add(Fragment fragment, String tag);方法Fragment不會(huì)顯示?

前面說到Fragment在add或者replace的時(shí)候一定需要指定tag和RId中的至少一個(gè)分俯。那問題來了肾筐,假如我調(diào)用add不指定RId的方法會(huì)怎么樣?
實(shí)際測試下會(huì)發(fā)現(xiàn)界面沒有任何變化缸剪,如果你打印Fragment的生命周期的話會(huì)發(fā)現(xiàn)Fragment的生命周期是正常的吗铐,但實(shí)際沒有顯示。我們來看下Fragment實(shí)際是如何處理View的顯示的杏节。詳細(xì)的Fragment處理過程比較復(fù)雜唬渗,回頭有空了會(huì)寫一篇詳細(xì)的文章介紹Fragment是如何顯示在Activity中的,這里先略過拢锹。直接在android.support.v4.app.FragmentManager類中搜索:"case Fragment.CREATED:"找到下面代碼

                case Fragment.CREATED:
                    if (newState > Fragment.CREATED) {
                        if (DEBUG) Log.v(TAG, "moveto ACTIVITY_CREATED: " + f);
                        if (!f.mFromLayout) {
                            ViewGroup container = null;
                            if (f.mContainerId != 0) {
                                if (f.mContainerId == View.NO_ID) {
                                    throwException(new IllegalArgumentException(
                                            "Cannot create fragment "
                                                    + f
                                                    + " for a container view with no id"));
                                }
                   ......

注意到if (f.mContainerId != 0) 這一行谣妻,假如mContainerId為0則container就null。然后

                            if (f.mView != null) {
                                ......
                                if (container != null) {
                                    container.addView(f.mView);
                                }

最終調(diào)用了container的addView方法讓View實(shí)際添加了ViewGroup中卒稳。所以到這里就清楚了假如addView沒有指定id,那么container就是null他巨,container是null那么即使onCreateView返回了真實(shí)有效的View也一樣沒用充坑,因?yàn)闆]有地方可以給它add,當(dāng)然也就不會(huì)顯示染突。

為什么DialogFragment也不需要指定id捻爷,但是DialogFragment就可以正常顯示?

DialogFragment大家通撤萜螅可能會(huì)有類似下面的代碼也榄。

        new SampleDialogFragment().show(getSupportFragmentManager(), "dialog");

問題來了,我們確實(shí)沒有給DialogFragment指定id司志,那為什么DialogFragment還能正常顯示甜紫?跟進(jìn)show方法

    public void show(FragmentManager manager, String tag) {
        mDismissed = false;
        mShownByMe = true;
        FragmentTransaction ft = manager.beginTransaction();
        ft.add(this, tag);
        ft.commit();
    }

DialogFragment在show的時(shí)候調(diào)用的是無Rid的add方法。不開心了骂远,憑什么DialogFragment能顯示囚霸?
先不急,我們把前面的SampleFragment繼承從Fragment改成DialogFragment激才,然后也調(diào)用無RId的add方法看看會(huì)怎么樣拓型。
實(shí)際測試后會(huì)發(fā)現(xiàn)繼承修改成DialogFragment后额嘿,SampleFragment也能顯示了。
問題來了劣挫,為什么呢册养?明明沒有指定ID,View被搞哪里去了压固?
弄清楚這個(gè)問題前球拦,我們先想下

  • 怎么顯示一個(gè)View?
    把View添加進(jìn)ViewGroup
  • 那ViewGroup從哪里來邓夕?
    順著setContent方法一路找下去就會(huì)發(fā)現(xiàn)刘莹,Activity被顯示是因?yàn)閃indow,最終的根ViewGroup來自Window焚刚〉阃洌可以理解為有Window就可以顯示View。(一個(gè)Activity可以有多個(gè)Window矿咕,有興趣的可以搜下相關(guān)文檔抢肛,怎么創(chuàng)建View,怎么在Window中顯示View碳柱,這里不做詳細(xì)介紹)
    到這里一下子就柳暗花明了捡絮,一個(gè)View能否顯示要看是否被添加進(jìn)Window里了。實(shí)際負(fù)責(zé)顯示的是Window莲镣。DialogFragment雖然沒有被add進(jìn)父ViewGroup福稳,只要它被add進(jìn)Window其實(shí)一樣可以顯示。那DialogFragment是不是這樣做了呢瑞侮?DialogFragment源碼其實(shí)并不多的圆,自己可以詳細(xì)一點(diǎn)點(diǎn)看一遍,這里只說重點(diǎn)部分半火。
    public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
        if (!mShowsDialog) {
            return super.getLayoutInflater(savedInstanceState);
        }

        mDialog = onCreateDialog(savedInstanceState);

        if (mDialog != null) {
            setupDialog(mDialog, mStyle);

            return (LayoutInflater) mDialog.getContext().getSystemService(
                    Context.LAYOUT_INFLATER_SERVICE);
        }
        return (LayoutInflater) mHost.getContext().getSystemService(
                Context.LAYOUT_INFLATER_SERVICE);
    }

這里L(fēng)ayoutInflater被復(fù)寫成了Dialog的LayoutInflater越妈。

    @NonNull
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return new Dialog(getActivity(), getTheme());
    }

而且onCreateDialog方法默認(rèn)會(huì)創(chuàng)建一個(gè)Dialog,而且還加了NonNull的注解钮糖。

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (!mShowsDialog) {
            return;
        }

        View view = getView();
        if (view != null) {
            if (view.getParent() != null) {
                throw new IllegalStateException(
                        "DialogFragment can not be attached to a container view");
            }
            mDialog.setContentView(view);
        }
        final Activity activity = getActivity();
        if (activity != null) {
            mDialog.setOwnerActivity(activity);
        }
        mDialog.setCancelable(mCancelable);
        mDialog.setOnCancelListener(this);
        mDialog.setOnDismissListener(this);
        if (savedInstanceState != null) {
            Bundle dialogState = savedInstanceState.getBundle(SAVED_DIALOG_STATE_TAG);
            if (dialogState != null) {
                mDialog.onRestoreInstanceState(dialogState);
            }
        }
    }

這里一下就完全清楚了梅掠,View被添加到Dialog里。你只要繼承了DialogFragment店归,那么雖然你add沒有指定RId阎抒,但是View會(huì)被set到Dialog里,所以最終顯示是在Dialog中娱节。也就是說DialogFragment實(shí)際顯示還是Dialog挠蛉,但是利用了Fragment的生命周期管理來實(shí)現(xiàn)一些比如重建之類的工作。說到這里是不是對Fragment的add無Rid方法有了一個(gè)更深入的理解肄满?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谴古,一起剝皮案震驚了整個(gè)濱河市质涛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掰担,老刑警劉巖汇陆,帶你破解...
    沈念sama閱讀 216,919評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異带饱,居然都是意外死亡毡代,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評論 3 392
  • 文/潘曉璐 我一進(jìn)店門勺疼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來教寂,“玉大人,你說我怎么就攤上這事执庐±腋” “怎么了?”我有些...
    開封第一講書人閱讀 163,316評論 0 353
  • 文/不壞的土叔 我叫張陵轨淌,是天一觀的道長迂烁。 經(jīng)常有香客問我,道長递鹉,這世上最難降的妖魔是什么盟步? 我笑而不...
    開封第一講書人閱讀 58,294評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮躏结,結(jié)果婚禮上却盘,老公的妹妹穿的比我還像新娘。我一直安慰自己媳拴,他們只是感情好谷炸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著禀挫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拓颓。 梳的紋絲不亂的頭發(fā)上语婴,一...
    開封第一講書人閱讀 51,245評論 1 299
  • 那天,我揣著相機(jī)與錄音驶睦,去河邊找鬼砰左。 笑死,一個(gè)胖子當(dāng)著我的面吹牛场航,可吹牛的內(nèi)容都是我干的缠导。 我是一名探鬼主播,決...
    沈念sama閱讀 40,120評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼溉痢,長吁一口氣:“原來是場噩夢啊……” “哼僻造!你這毒婦竟也來了憋他?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,964評論 0 275
  • 序言:老撾萬榮一對情侶失蹤髓削,失蹤者是張志新(化名)和其女友劉穎竹挡,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體立膛,經(jīng)...
    沈念sama閱讀 45,376評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揪罕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了宝泵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片好啰。...
    茶點(diǎn)故事閱讀 39,764評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖儿奶,靈堂內(nèi)的尸體忽然破棺而出框往,到底是詐尸還是另有隱情,我是刑警寧澤廓握,帶...
    沈念sama閱讀 35,460評論 5 344
  • 正文 年R本政府宣布搅窿,位于F島的核電站,受9級特大地震影響隙券,放射性物質(zhì)發(fā)生泄漏男应。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評論 3 327
  • 文/蒙蒙 一娱仔、第九天 我趴在偏房一處隱蔽的房頂上張望沐飘。 院中可真熱鬧,春花似錦牲迫、人聲如沸耐朴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筛峭。三九已至,卻和暖如春陪每,著一層夾襖步出監(jiān)牢的瞬間影晓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評論 1 269
  • 我被黑心中介騙來泰國打工檩禾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挂签,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,819評論 2 370
  • 正文 我出身青樓盼产,卻偏偏與公主長得像饵婆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子戏售,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評論 2 354

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