Fragment源碼閱讀筆記

0 認知

Fragment官方的翻譯名為:片段,表示 Activity中的行為或用戶界面部分。

相比Activity
相比Activity,Fragment的創建、銷毀只需要依附到宿主Activity中,不需要與ActivityManagerService跨進程交互,所有的生命周期在宿主Activity中完成,可以在多個FragmentActivity中被多次重用,所以它更加靈活。

相比View
相比View,它擁有更多的生命周期(onAttach、onCreate、onCreateView、onStart、onResume、onPause、onStop、onDestroyView、onDestroy),可以管理menu,持有Activity引用(View持有的context有可能為ContextThemeWrapper對象),更利于模塊化。

1 構造

Fragment有兩種方式創建并依附到宿主Activity。

fromLayout方式
在xml中配置fragment標簽,例如

<FrameLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:tag="tag"
        android:name="com.asha.fragmentdemo.MyDialogFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</FrameLayout>

記得fragment必須帶上1、android:name;2、android:tag或android:id二選一,否則會在創建過程中檢查參數時報錯。
當這個layout被inflate后,LayoutInfalter會回調Activity中的FragmentManager去處理這個tag,根據android:name實例化此tag對應的Fragment對象,通知它生成并返回view。隨后由FragmentManager管理此Fragment的生命周期。

FragmentManager方式
在代碼中實例化Fragment,被創建一個Bundle作為參數存儲載體賦值給Fragment,隨后通過FragmentManager開啟個transaction、add Fragment、commit,隨后某個時間點,FragmentMangager會處理此commit提交的aciton,完成Fragment的依附,示例代碼如下:

blankFragment = new BlankFragment();
Bundle bundle = new Bundle();
bundle.putInt("data",-1);
blankFragment.setArguments(bundle);
getSupportFragmentManager().beginTransaction().add(R.id.container, blankFragment, "BlankFragmentTag").commit();

通過setArguments(bundle)有利于保存與恢復,后面會有介紹。

2 狀態

fragment的狀態變化由FragmentManager管理,fragment狀態主要可以分為以下6種:

//android.support.v4.app.Fragment
static final int INITIALIZING = 0;     // Not yet created.
static final int CREATED = 1;          // Created.
static final int ACTIVITY_CREATED = 2; // The activity has finished its creation.
static final int STOPPED = 3;          // Fully created, not started.
static final int STARTED = 4;          // Created and started, not resumed.
static final int RESUMED = 5;          // Created started and resumed.

2.1 fragment狀態變化

fragment狀態變化主要來自以下兩個方面:

宿主FragmentActivty的生命周期變化
FragmentActivity生命周期的變化會調用FragmentController的對應回調,如當FragmentActivity調用onDestory后,成員變量FragmentController對象被調用了dispatchDestroy,代碼如下

//android.support.v4.app.FragmentActivty
final FragmentController mFragments = FragmentController.createController(new HostCallbacks());

@Override
protected void onDestroy() {
    super.onDestroy();

    doReallyStop(false);

    mFragments.dispatchDestroy();
    mFragments.doLoaderDestroy();
}

對應FragmentController內傳遞了給了構造時聚合進來的mHost對象中的FragmentManager對象,代碼如下

// android.support.v4.app.FragmentController
public void dispatchDestroy() {
    mHost.mFragmentManager.dispatchDestroy();
}

隨后調用到FragmentManager中的moveToState方法,處理狀態改變,代碼如下

// android.support.v4.app.FragmentManager
public void dispatchDestroyView() {
    moveToState(Fragment.CREATED, false);
}

FragmentTransaction
與上一條直接在主線程中立即調用不同,FragmentTransaction添加一系列add、remove、replace操作op并鏈表形式存儲,執行commit后,會在FragmentManager.enqueueAction,通過handler.post方法在主線程中下一個未知時間點執行此action,此action代碼如下:

// android.support.v4.app.BackStackRecord extends FragmentTransaction

public void run() {
    ...
    Op op = mHead;
    while (op != null) {
        ...
        switch (op.cmd) {
            case OP_ADD:
                ...
                mManager.addFragment(f, false);
                break;
            case OP_REPLACE:
                ...
                mManager.removeFragment(old, transition, transitionStyle);
                ...
                mManager.addFragment(f, false);
                ...
                break;
            case OP_REMOVE:
                ...
                mManager.removeFragment(f, transition, transitionStyle);
                break;
            case OP_HIDE:
                ...
                break;
            case OP_SHOW:
                ...
                break;
            case OP_DETACH: 
                ...
                break;
            case OP_ATTACH:
                ...
                break;
            default:
                throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
        }
        op = op.next;
    }
    mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
    ...
}

在上述removeFragment、addFragment等操作都會進入對應的moveToState函數,最后mManager調用moveToState函數來同步目前管理的fragment狀態遷移到mManager.mCurState狀態。

2.2 moveToState

FragmentManager中moveToState函數有多個參數形式,moveToState方法所有形參定義如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f)
void moveToState(int newState, boolean always)
void moveToState(int newState, int transit, int transitStyle, boolean always)
void moveToState(Fragment f, int newState, int transit, int transitionStyle,boolean keepActive) 

舉個例子,當FragmentActivity已經onResume后,即FragmentActivity已經顯示在屏幕中,此時FragmentActivity中onResume已經調用了mFragmentManager的dispatchResume函數,即

// android.support.v4.app.FragmentManager

public void dispatchResume() {
    mStateSaved = false;
    moveToState(Fragment.RESUMED, false);
}

通過層層調用,進入到了上述第三個moveToState,在此代碼中,FragmentManager實例的成員變量mCurState直接被賦值為Fragment.RESUMED狀態,隨后遍歷實例內管理的mActive數組中的fragment對象,讓他們進入到Fragment.RESUMED狀態,代碼如下:

// android.support.v4.app.FragmentManager

int mCurState = Fragment.INITIALIZING;
void moveToState(int newState, int transit, int transitStyle, boolean always) {
    ...
    mCurState = newState;
    if (mActive != null) {
        ...
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null) {
                moveToState(f, newState, transit, transitStyle, false);
                ...
            }
        }
        ...
    }
}

先忽略現有fragment的狀態遷移,如果此時有新的fragment通過FragmentTransaction加入到mManager(為FragmentManager實例),上面分析過android.support.v4.app.BackStackRecord會在某個時間點執行action,此時新的frament被加入到mManager.mActive數組中,同時會調用mManager.moveToState同步到現有狀態,即Fragment.RESUME/5,代碼如下:

// android.support.v4.app.BackStackRecord extends FragmentTransaction

public void run() {
      ...
      // mManager為FragmentManager實例
      mManager.moveToState(mManager.mCurState, transition, transitionStyle, true);
      ...
}

所以執行到了FragmentManager的第四個moveToState,此時新的Fragment剛被創建成功,成員變量mState默認INITIALIZING/0,

// android.support.v4.app.Fragment
int mState = INITIALIZING;

FragmentManager的第四個moveToState函數帶有Fragment參數,進入后先會使用f.mState(即fragment當前狀態)與目標狀態newState進行比較,f.mState即0 < newState即5,則fragment需要從狀態0到狀態5,分別需要經歷INITIALIZING/0、CREATED/1、ACTIVITY_CREATED/2、STOPPED/3、STARTED/4,最后賦值為5,注意switch中沒有break,需要一直按順執行,不同的狀態分支需要執行不同的函數通知fragment進入此狀態,同時回調fragment中對應的生命周期函數。從代碼框架如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
        boolean keepActive) {
    ...
    if (f.mState < newState) {
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
                f.onAttach(mHost.getContext());
                ...
                if (!f.mRetaining) {
                    f.performCreate(f.mSavedFragmentState);
                }
                f.mRetaining = false;
                if (f.mFromLayout) {
            ...
                    f.onViewCreated(f.mView, f.mSavedFragmentState);
                }
            case Fragment.CREATED:
                if (newState > Fragment.CREATED) {
                    if (!f.mFromLayout) {
                        ...
                        f.onViewCreated(f.mView, f.mSavedFragmentState);
                    }
                    f.performActivityCreated(f.mSavedFragmentState);
                    ...
                }
            case Fragment.ACTIVITY_CREATED:
            case Fragment.STOPPED:
                if (newState > Fragment.STOPPED) {
                    f.performStart();
                }
            case Fragment.STARTED:
                if (newState > Fragment.STARTED) {
                    ...
                    f.performResume();
                    ...
                }
        }
    } else if (f.mState > newState) {
        switch (f.mState) {
            case Fragment.RESUMED:
                if (newState < Fragment.RESUMED) {
                    f.performPause();
                    ...
                }
            case Fragment.STARTED:
                if (newState < Fragment.STARTED) {
                    f.performStop();
                }
            case Fragment.STOPPED:
                if (newState < Fragment.STOPPED) {
                    f.performReallyStop();
                }
            case Fragment.ACTIVITY_CREATED:
                if (newState < Fragment.ACTIVITY_CREATED) {
                    ...
                    f.performDestroyView();
                    ...
                }
            case Fragment.CREATED:
                if (newState < Fragment.CREATED) {
                    ...
                    if (!f.mRetaining) {
                        f.performDestroy();
                    }
                    f.onDetach();
                }
        }
    }
    f.mState = newState;
}   

同理從RESUMED狀態被destroy,需要從5遷移到0,執行上述函數f.mState > newState部分的邏輯,一步一步回到INITIALIZING狀態。

當然如果宿主Activity與fragement同時被銷毀,fragement會接收到FragmentActivity對應的生命周期dispatch,從5到4,從4到3,從3到2,從2到1,從1到0即可完成狀態遷移。

3 生命周期

fragment_lifecycle.png

先來一張官方文檔的圖,第二章已經介紹了狀態遷移,對應的狀態改變會調用對應的生命周期回調,調用時機已經非常清晰,說一下幾個注意點:

onAttach后即持有activity引用
不要被onActivityCreated迷惑,fragment.onAttach時就可以使用activity了,因為在f.onAttach前就進行了一系列基礎變量的賦值,代碼如下:

// android.support.v4.app.FragmentManager

void moveToState(Fragment f, int newState, int transit, int transitionStyle,
            boolean keepActive) {
    ...
    if (f.mState < newState) {
        ...
        switch (f.mState) {
            case Fragment.INITIALIZING:
                ...
                f.mHost = mHost;
                f.mParentFragment = mParent;
                f.mFragmentManager = mParent != null
                        ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
                f.mCalled = false;
                f.onAttach(mHost.getContext());
                ...
        }
        ...
    }
    ...
}

onActivityCreated是一個什么狀態?
FragmentActivity在調用onCreate開始的時候調用FragmentManager實例的dispatchCreate函數,在FragmentActivity在調用onCreate結束的時候會調用dispatchActivityCreated,即通知fragment的activity已經調用onCreate完畢。

為什么fromLayout的Fragment在INITIALIZING階段就需要onViewCreated?
FragmentActivity在onCreate的時候調用了setContentView,此時需要通過R.layout.xxx方式或者直接LayoutInflater的方式創建view,LayoutInflater創建view時在createViewFromTag函數內會根據xml里定義的tag和attr去實例化對應的View,當LayoutInflater讀取到fragment這個tag后,先讓LayoutInflater內的context去處理onCreateView,看是否能返回對應的view,層層調用進入FragmentManager.onCreateView去實例化fragment、賦值、狀態遷移,關鍵是在這個時候就需要返回fragment內對應的view,此時FragmentActivity在onCreate階段,FragmentManager應該在Fragment.CREATED階段,所以狀態同步時newStateFragment.CREATED,在Fragment.CREATED分支無法調用onViewCreated,而fromLayout的Fragment在INITIALIZING階段就需要創建view并返回了,所以在INITIALIZING就得調用了onViewCreated了。

如果fragment.setRetainInstance(true),在一定情況下生命周期函數調用就發生改變了
這個一定情況是指configChange的情況,下一章具體講。

4 狀態保存

4.1 需要保存哪些東西?

Fragment
如果需要重新構造一個除了內存地址不一樣,屬性與原來實例一模一樣的Fragment,需要序列化以下四個對象或屬性:

- FragmentState
FragmentState包含了重新構造這個Fragment所需的最基本的屬性,包括完整類名、在mActive內的index,是否從layout生成的,id,tag,容器id,是否retainInstance,是否已經detached,構造時傳入的參數,定義如下:

// android.support.v4.app.FragmentState

public FragmentState(Fragment frag) {
    mClassName = frag.getClass().getName();
    mIndex = frag.mIndex;
    mFromLayout = frag.mFromLayout;
    mFragmentId = frag.mFragmentId;
    mContainerId = frag.mContainerId;
    mTag = frag.mTag;
    mRetainInstance = frag.mRetainInstance;
    mDetached = frag.mDetached;
    mArguments = frag.mArguments;
}

- ViewState
Fragment內部管理的view的狀態,我們知道view自身有一套狀態保存的機制,通過根節點的view一層一層dispatch出去(dispatchSaveInstanceState、dispatchRestoreInstanceState)觸發保存和恢復先前的狀態。這種恢復屬于view被新建實例后恢復原來的狀態,比如EditText選中了一段文字,旋轉屏幕重新創建view實例,會重新focus,重新選中剛剛所選的那段文字。

而由Fragment管理的view脫離了原來的dispatch流程,是由Fragment自主管理觸發saveViewState和restoreViewState,脫離dispatch的方法在sdk11之前wrap一個NoSaveStateFrameLayout,11及之后直接設置屬性即可,代碼如下:

// android.support.v4.app.FragmentManager
// moveToState

f.mView = f.performCreateView(f.getLayoutInflater(
        f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
    f.mInnerView = f.mView;
    if (Build.VERSION.SDK_INT >= 11) {
        ViewCompat.setSaveFromParentEnabled(f.mView, false);
    } else {
        f.mView = NoSaveStateFrameLayout.wrap(f.mView);
    }
    ...
}

- mUserVisibleHint
用這個標記可以控制Fragment是否延遲執行mLoaderManager內的任務,即如果mUserVisibleHint為false,這個Fragment在需要遷移到大于STOPPED的狀態時先忽略,當所有其他mUserVisibleHint為true的Fragment內的runningLoader執行完成,再遷移到FragmentManager現有狀態。

那為什么是停止在STOPPED狀態?
因為這個狀態下一個狀態就會觸發Fragment.onStart,onStart會調用這個Fragment內的mLoaderManager啟動內部的loader去做加載操作,如果延遲加載這部分,可以讓其他更重要的loader做完操作后再進行,提升體驗。

- 可自定義的onSaveInstanceState
這個與Activity一致,不再贅述。

FragmentManager
FragmentManager與Fragment一樣,都有一個序列化、反序列化基礎屬性的State類:FragmentManagerState。FragmentManagerState保存三個可序列化對象數組:

// android.support.v4.app.FragmentManagerState
final class FragmentManagerState implements Parcelable {
    FragmentState[] mActive;
    int[] mAdded;
    BackStackState[] mBackStack;
}

對應保存FragmentManager內的三個數組屬性,定義如下:

// android.support.v4.app.FragmentManager

ArrayList<BackStackRecord> mBackStack;
ArrayList<Fragment> mActive;
ArrayList<Fragment> mAdded;

mAdded內保存的是所有add到FragmentManager內的Fragemnt,mActive中包含了所有mAdded對象外,還保存了與backStack相關的所有Fragment。所以說mAdded是mActive的子集,對應序列化對象時,mAdded只需要記住這個Fragment對象在mActive中的索引值,就可以找回原來Fragment對應的新Fragment。mBackStack保存了所有addToBackStack的FragementTransaction,可以記錄某次commit操作所有Fragment的變化,便于按下back鍵后回滾到上一步。

4.2 Fragment保存機制

fragment有兩種保存機制,一種是fragment.onSaveInstanceState方式,另一種是fragment.setRetainInstance(true)方式,我們來看看以下幾個經常出現的場景:

宿主FragmentActivity從后臺直接恢復
由于FragmentActivity只是在onStop狀態,FragmentActivity內的FragmentManager實例狀態為STOPPED狀態,FragmentManager實例和其內部管理的Fragment實例都還健在,只是需要從STOPPED狀態遷移到RESUMED即可。

FragmentActivity的recreate
recreate有兩種情況會觸發,一種是直接調用Activity.recreate(),另一種是RELAUNCH_ACTIVITY。兩種方式走到AMS層后都是走相同的流程。

RELAUNCH_ACTIVITY會在旋轉屏幕等onConfigurationChanged的情況未被Activity處理后發生。例如發生了ConfigurationChanged,而Manifest.xml中此Activity的android:configChanges沒有配置此Configuration,即Activity不處理此Configuration,AMS就會RELAUNCH此Activity。

發生recreate后,AMS會銷毀現有的Activity實例,重新啟動一個新的Activity實例。

如果Fragment設置了fragment.setRetainInstance(true)
AMS在銷毀舊Activity實例時會調用ActivityThread.performDestoryActivity -> Activity.retainNonConfigurationInstances -> FragmentActivity.onRetainNonConfigurationInstances -> FragmentController.retainNonConfig -> mFragmentManager.retainNonConfig,在FragmentManager中返回了mActive數組拷貝,代碼如下:

// android.support.v4.app.FragmentManager

ArrayList<Fragment> retainNonConfig() {
    ArrayList<Fragment> fragments = null;
    if (mActive != null) {
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.get(i);
            if (f != null && f.mRetainInstance) {
                if (fragments == null) {
                    fragments = new ArrayList<Fragment>();
                }
                fragments.add(f);
                f.mRetaining = true;
                ...
            }
        }
    }
    return fragments;
}

我們來看看Activity中保存一部分實例,代碼如下:

// android.app.Activity

NonConfigurationInstances retainNonConfigurationInstances() {
    Object activity = onRetainNonConfigurationInstance();
    HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
    List<Fragment> fragments = mFragments.retainNonConfig();
    ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
    ...

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

nci所保存的對象可以是任意對象,不需要做序列化和反序列化操作,恢復時是原有對象實例。nci對象返回給ActivityThread,保存在此進程的ActivityThread對象的mActivities鍵值對的此Activity Binder對應的ActivityClientRecord中。值得注意的是,nci.activity是在Activity中調用onRetainNonConfigurationInstance返回的對象,不是此Activiyt實例。

所以原Activity被recreate后,會生成新的Activity,新的FragmentManager,但是是舊的Fragment對象,所以它的生命周期會有所不同,它不會有onCreate和onDestroy,不會被FragmentManager從mActive中刪除,因為在Activity onDestroy時需要被retainNonConfig保存下來。

當新Activity onCreate后會調用FragmentManager的restoreAllState,在此把之前保存在nci對象里的mActive重新取出來,一個一個賦值給新建的mActive對象,完成nonConfig對象的恢復。

如果Fragment未設置fragment.setRetainInstance(true),默認為false
則走原有流程。

宿主FragmentActivity從后臺恢復時由于內存不足已經被kill
眾所周知Activity的onSaveInstanceState在onPause之后在onStop之前,所以當Activity在被放到后臺即onStop前會調用onSaveInstanceState,在此函數中調用了FragmentManager.saveAllState,主要存儲FragmentManager的三個數mActive、mAdded、mBackStack的內容到FragmentManagerState中,mActive中保存了上述的FragmentState。當Activity被重新創建調用onCreate時會得到剛剛保存的savedInstanceState,再通過這個savedInstanceState獲得剛保存的FragmentManagerState,去創建一個新的FragmentManager對象,去重新生成Fragment。此時恢復的FragmentActivity、FragmentManager、Fragment都是新的實例。

此時不管fragment是否setRetainInstance(true),Fragment實例都會重新被創建,原因一:retainNonConfig是在Activity在onDestroy被保存的;原因二:只有被relaunch的activity在destroy時才會在ActivityThread代碼中被調用retainNonConfig去通知Activity返回需要保存實例,其他的destroy不會。

4.3 補充

3.0后處理旋轉屏幕ConfigurationChanged
3.0后需要配置screenSize來處理旋轉屏幕ConfigurationChanged

<activity android:name=".MainActivity" android:configChanges="orientation|screenSize">

如果配置了這個屬性,旋轉屏幕,Activity只會回調onConfigurationChanged,不會調用其他任何生命周期函數,當然也不會被重新生成實例,FragmentManager實例、Fragment實例都是不會發生變化的。

5 小細節

remove fragment
如果Fragment通過layout.xml方式加入到Activity中,被FragmentManager進行remove或者replace操作后,Fragment實例在FragmentManager中被刪去,而Fragment內對應的view沒有被賦值mContainerView,所以內部的view沒有被移除,導致界面一直存在,此時這個Fragment已經被detach,如果再對它調用getActivity將返回null。

add fragment
如果Activity沒有處理screenSize的onConfigurationChanged,那么此Activity將被recreate。在重新調用Activity onCreate時,FragmentManager的mActive和mAdd內的Fragment被重新創建,如果此時在Activity的onCreate重新add一個Fragment,那么就會出現兩個Fragment的情況。
如何解決?在添加前先檢查下FragmentManager內是否存在此Fragment,不存在再添加即可,代碼如下:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(v);
    BlankFragment blankFragment = (BlankFragment) getSupportFragmentManager().findFragmentByTag("BlankFragmentTag");
    if ( blankFragment == null ){
        blankFragment = new BlankFragment();
        blankFragment.setUserVisibleHint(false);
        Bundle bundle = new Bundle();
        bundle.putInt("data",-1);
        blankFragment.setArguments(bundle);
        getSupportFragmentManager().beginTransaction().add(R.id.container, blankFragment, "BlankFragmentTag").commit();
    }
}

hide fragment
因為hide狀態沒有被保存下來,在recreate或者內存不足重啟Activity的情況下,原來被hide的Fragment將被重新show,所以得注意這個問題。

replace fragment
目前的版本存在一個bug,一個ViewGroup加了一個以上Fragment后,FragmentManager去replace此ViewGroup內的Fragement無法正確replace,原因是在ArrayList for循環里做了ArrayList remove操作,目前還沒修復。

addBackStack情況下,remove Fragment后Fragment還繼續存在mActive中
由于remove后有可能popBackStack(),所以mAdd內被刪除后,mActive內還保存著此Fragment引用,此時findFragmentById或者findFragmentByTag都可以找到這個Fragment。同理,setRetainInstance(true)的Fragment被remove后也存在在mActive中。

DialogFragment中的onCreateView
同一個activity中的fragment和DialogFragment用onCreateView中所帶的參數inflater去inflate view,view.getContext()返回值是不同的。。前者是此activiy;后者返回的是ContenxThemeWrapper,內部wrap了activity。原因是dialog要創建新的context存放對應的theme去inflate view。

6 reference

本文所涉及源碼版本為

compile 'com.android.support:support-v4:23.1.0'
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容