学习笔记--Fragment之FragmentTransaction事务执行流程

FragmentTransaction

Fragment的使用方式

class TestActivity :AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test)
        
        val firstFragment = FirstFragment()
        val secondFragment = SecondFragment()
        val thirdFragment = ThirdFragment()

        // 大部分的Fragment的使用方法都如下所示
        // 所以就从Fragment的基础使用开始入手分析
        supportFragmentManager
            .beginTransaction()
            .add(R.id.container, firstFragment)
            .replace(R.id.container,secondFragment)
            .remove(secondFragment)
            .remove(firstFragment)
            .add(R.id.container,thirdFragment)
            .addToBackStack(null)
            .commit()
    }
}

FragmentManager的来源

跟踪AppCompatActivity的getSupportFragmentmanager()方法可以发现:

//------------------------------FragmentActivity.java------------------------------

public class FragmentActivity extends ComponentActivity implements
    ActivityCompat.OnRequestPermissionsResultCallback,
    ActivityCompat.RequestPermissionsRequestCodeValidator {
    
    ...
    
    // 赋值mFragments创建一个FragmentController实例,并传递HostCallbacks实例
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    
    ...
    
    /**
     * Return the FragmentManager for interacting with fragments associated
     * with this activity.
     */
    @NonNull
    public FragmentManager getSupportFragmentManager() {
        // 直接委托给FragmentController实例对象mFragments的getSupportFragmentManager方法
        return mFragments.getSupportFragmentManager();
    }
    
    ...
    
    /**
     * Destroy all fragments.
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
         // 通过FragmentController调度Fragment的destroy生命周期
        mFragments.dispatchDestroy();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    }
    
    ...
    
    /**
     * Dispatch onPause() to fragments.
     */
    @Override
    protected void onPause() {
        super.onPause();
        mResumed = false;
        // 通过FragmentController调度Fragment的pause生命周期
        mFragments.dispatchPause();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
    }
    
    ...
    
    /**
     * Dispatch onResume() to fragments.
     */
    @Override
    protected void onPostResume() {
        super.onPostResume();
        onResumeFragments();
    }

    /**
     * This is the fragment-orientated version of {@link #onResume()} that you
     * can override to perform operations in the Activity at the same point
     * where its fragments are resumed.  Be sure to always call through to
     * the super-class.
     */
    protected void onResumeFragments() {
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
        // 通过FragmentController调度Fragment的resume生命周期
        mFragments.dispatchResume();
    }
    
    ...
    
    /**
     * Dispatch onStart() to all fragments.
     */
    @Override
    protected void onStart() {
        super.onStart();

        mStopped = false;

        if (!mCreated) {
            mCreated = true;
            mFragments.dispatchActivityCreated();
        }

        mFragments.noteStateNotSaved();
        mFragments.execPendingActions();

        // NOTE: HC onStart goes here.

        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
        // 通过FragmentController调度Fragment的resume生命周期
        mFragments.dispatchStart();
    }
    
    
    /**
     * Dispatch onStop() to all fragments.
     */
    @Override
    protected void onStop() {
        super.onStop();

        mStopped = true;
        markFragmentsCreated();

        // 通过FragmentController调度Fragment的resume生命周期
        mFragments.dispatchStop();
        mFragmentLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
    }
    
    ...
    
    // HostCallbacks的继承自FragmentHostCallback,后面会介绍该类的用途
    class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
        ViewModelStoreOwner,
        OnBackPressedDispatcherOwner {
        
        ...        
    }
}
  • 由于AppCompatActivity继承自FragmentActivity,并且没有覆写getSupportFragmentManager,所以追踪到
    FragmentActivity#getSupportFragmentManager中。
  • 可以看到FragmentActivity将getSupportFragmentManager直接委托给了FragmentController对象。而且不仅仅是getSupportFragmentManager的委托,在FragmentActivity的各个生命周期的方法中也是将Fragment的其它生命周期交由FragmentController进行调度

接着看看FragmentController这个类

FragmentController类
//------------------------------FragmentController.java------------------------------

public class FragmentController {
    private final FragmentHostCallback<?> mHost;

    /**
     * Returns a {@link FragmentController}.
     */
    @NonNull
    public static FragmentController createController(@NonNull FragmentHostCallback<?> callbacks) {
        return new FragmentController(checkNotNull(callbacks, "callbacks == null"));
    }

    private FragmentController(FragmentHostCallback<?> callbacks) {
        mHost = callbacks;
    }

    /**
     * Returns a {@link FragmentManager} for this controller.
     */
    // 将持有的FragmentHostCallback实例mHost的mFragmentManager返回
    @NonNull
    public FragmentManager getSupportFragmentManager() {
        return mHost.mFragmentManager;
    }
    
    ...
    
    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the create state.
     * <p>Call when Fragments should be created.
     *
     * @see Fragment#onCreate(Bundle)
     */
    public void dispatchCreate() {
        mHost.mFragmentManager.dispatchCreate();
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the activity created state.
     * <p>Call when Fragments should be informed their host has been created.
     *
     * @see Fragment#onActivityCreated(Bundle)
     */
    public void dispatchActivityCreated() {
        mHost.mFragmentManager.dispatchActivityCreated();
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the start state.
     * <p>Call when Fragments should be started.
     *
     * @see Fragment#onStart()
     */
    public void dispatchStart() {
        mHost.mFragmentManager.dispatchStart();
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the resume state.
     * <p>Call when Fragments should be resumed.
     *
     * @see Fragment#onResume()
     */
    public void dispatchResume() {
        mHost.mFragmentManager.dispatchResume();
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the pause state.
     * <p>Call when Fragments should be paused.
     *
     * @see Fragment#onPause()
     */
    public void dispatchPause() {
        mHost.mFragmentManager.dispatchPause();
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the stop state.
     * <p>Call when Fragments should be stopped.
     *
     * @see Fragment#onStop()
     */
    public void dispatchStop() {
        mHost.mFragmentManager.dispatchStop();
    }

    /**
     * @deprecated This functionality has been rolled into {@link #dispatchStop()}.
     */
    @Deprecated
    public void dispatchReallyStop() {
    }

    /**
     * Moves all Fragments managed by the controller's FragmentManager
     * into the destroy view state.
     * <p>Call when the Fragment's views should be destroyed.
     *
     * @see Fragment#onDestroyView()
     */
    public void dispatchDestroyView() {
        mHost.mFragmentManager.dispatchDestroyView();
    }

    /**
     * Moves Fragments managed by the controller's FragmentManager
     * into the destroy state.
     * <p>
     * If the {@link androidx.fragment.app.FragmentHostCallback} is an instance of {@link ViewModelStoreOwner},
     * then retained Fragments and any other non configuration state such as any
     * {@link androidx.lifecycle.ViewModel} attached to Fragments will only be destroyed if
     * {@link androidx.lifecycle.ViewModelStore#clear()} is called prior to this method.
     * <p>
     * Otherwise, the FragmentManager will look to see if the
     * {@link FragmentHostCallback host's} Context is an {@link Activity}
     * and if {@link Activity#isChangingConfigurations()} returns true. In only that case
     * will non configuration state be retained.
     * <p>Call when Fragments should be destroyed.
     *
     * @see Fragment#onDestroy()
     */
    public void dispatchDestroy() {
        mHost.mFragmentManager.dispatchDestroy();
    }
    
    ...
}
  • 首先可以看到FragmentController的构造函数传递了FragmentHostCallback实例并赋值给mHost,而这个实例则是在FragmentActivity调用FragmentController#createController传递过来的。
  • 最终getSupportFragmentManager返回的就是mHost的mFragmentManager。
  • 生命周期的调度方法也是由mHost的mFragmentManager对象完成的。

那么mHost到底是一个怎样的存在呢?

HostCallbacks类

HostCallbacks类是FragmentActivity的内部类,继承自FragmentHostCallback(在上文FragmentActivity的源码截取中有体现)。那来看看FragmentHostCallback

//------------------------------FragmentHostCallback.java------------------------------

/**
 * Integration points with the Fragment host.
 * <p>
 * Fragments may be hosted by any object; such as an {@link Activity}. In order to
 * host fragments, implement {@link FragmentHostCallback}, overriding the methods
 * applicable to the host.
 */
// 官方注释:Fragment可以被如何对象持有,例如(Activity),为了成为Fragment的宿主,必须继承FragmentHostCallback。
public abstract class FragmentHostCallback<E> extends FragmentContainer {
    ...
}

根据类头注释可知,FragmentHostCallback是官方为Fragments的宿主写的抽象类,而作为Fragment的宿主则必须继承该抽象类,并为其覆写其中方法。
而继承自FragmentHostCallback的FragmentActivity$HostCallbacks,就是FragmentActivity可以成为Fragment宿主的原因所在。

思考一个问题

直接在FragmentActivity中使用FragmentManager也可以达到Fragment宿主的目的,为什么还要这样设计?

在我们的认知中,FragmentActivity是专门用来管理Fragment的类,事实上FragmentActivity和Fragment没有进行任何关联甚至没有任何Fragment的导包,仅仅通过创建FragmentController实例并实现了FragmentHostCallback接口,就可以实现对Fragment的管理工作。这样的设计可以屏蔽宿主对Fragment的直接引用,从而拓展了Fragment的应用场景,而不仅仅局限于Activity中。如果未来需要出现一个新的宿主,就会显得十分方便。

FragmentTransaction类

通过supportManager.beginTransaction()就可以获得FragmentTransaction对象。FragmentTransaction是可以操作事务的对象,所谓的事务,就是可以一次性添加多个操作。比如Fragment使用方法中的add,replace,remove,remove,add可以组成是一个事务。也就是说一个事务可以存在多个操作。
事务具有原子性,也就是说事务中的操作要么全部成功,要么全部不成功,不会出现部分成功的情况。

追踪beginTransaction:

//------------------------------FragmentManagerImpl.java------------------------------

@NonNull
@Override
public FragmentTransaction beginTransaction() {
    // 返回一个新创建的BackStackRecord对象
    return new BackStackRecord(this);
}

可以发现返回的是一个BackStackRecord对象。BackStackRecord对象继续追踪

//------------------------------BackStackRecord.java------------------------------

/**
 * Entry of an operation on the fragment back stack.
 */
// BackStackRecord继承自FragmentTransaction,是Fragment回退栈的一个元素
final class BackStackRecord extends FragmentTransaction implements
    FragmentManager.BackStackEntry, FragmentManagerImpl.OpGenerator {
    
    static final String TAG = FragmentManagerImpl.TAG;

    final FragmentManagerImpl mManager;
    ...
    
    public BackStackRecord(FragmentManagerImpl manager) {
        mManager = manager;
    }
    
    ...

BackStackRecord可以理解为Fragment回退栈的一个元素。由于继承自FragmentTransaction,BackStackRecord也可以理解成一个事务,这样设计的目的是在操作回退栈时,可以逆向执行事务,从而实现Fragment的出栈操作。

FragmentTransaction的操作

接下来我们分析下事务中如何进行Fragment的操作的,以supportFragmentManager.beginTransaction().add(R.id.container, firstFragment)的add操作为例

//------------------------------FragmentTransaction.java------------------------------

/**
 * Calls {@link #add(int, Fragment, String)} with a null tag.
 */
@NonNull
public FragmentTransaction add(@IdRes int containerViewId, @NonNull Fragment fragment) {
    // 直接调用doAddOp方法
    // 参数OP_ADD是一个int常量,代表的是本次操作是一个添加Fragment的操作
    doAddOp(containerViewId, fragment, null, OP_ADD);
    return this;
}

继续跟进doAddOp方法

//------------------------------FragmentTransaction.java------------------------------
void doAddOp(int containerViewId, Fragment fragment, @Nullable String tag, int opcmd) {
    final Class<?> fragmentClass = fragment.getClass();
    final int modifiers = fragmentClass.getModifiers();
    if (fragmentClass.isAnonymousClass() || !Modifier.isPublic(modifiers)
            || (fragmentClass.isMemberClass() && !Modifier.isStatic(modifiers))) {
        throw new IllegalStateException("Fragment " + fragmentClass.getCanonicalName()
                + " must be a public static class to be  properly recreated from"
                + " instance state.");
    }

    if (tag != null) {
        if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
            throw new IllegalStateException("Can't change tag of fragment "
                    + fragment + ": was " + fragment.mTag
                    + " now " + tag);
        }
        fragment.mTag = tag;
    }

    if (containerViewId != 0) {
        if (containerViewId == View.NO_ID) {
            throw new IllegalArgumentException("Can't add fragment "
                    + fragment + " with tag " + tag + " to container view with no id");
        }
        if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
            throw new IllegalStateException("Can't change container ID of fragment "
                    + fragment + ": was " + fragment.mFragmentId
                    + " now " + containerViewId);
        }
        fragment.mContainerId = fragment.mFragmentId = containerViewId;
    }

    // 将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法
    addOp(new Op(opcmd, fragment));
}

doAddOp方法进行了参数的校验,然后将传递进来的opcmd以及fragment对象封装成一个Op(Operation)对象,进而调用addOp方法

//------------------------------FragmentTransaction.java------------------------------

ArrayList<Op> mOps = new ArrayList<>();

...

void addOp(Op op) {
    mOps.add(op);
    op.mEnterAnim = mEnterAnim;
    op.mExitAnim = mExitAnim;
    op.mPopEnterAnim = mPopEnterAnim;
    op.mPopExitAnim = mPopExitAnim;
}

可以看到addOp方法,仅仅是将op对象存储到集合mOps中,然后就结束了。为什么存储起来,因为一次事务有可能有多个操作,所以要存储起来,等到事务执行时再统一执行。

接下来追踪replace的操作:

//------------------------------FragmentTransaction.java------------------------------

@NonNull
public FragmentTransaction replace(@IdRes int containerViewId, @NonNull Fragment fragment,
        @Nullable String tag)  {
    if (containerViewId == 0) {
        throw new IllegalArgumentException("Must use non-zero containerViewId");
    }
    // 调用doAddOp方法
    // 参数OP_REPLACE是一个int常量,代表的是本次操作是一个替换Fragment的操作
    doAddOp(containerViewId, fragment, tag, OP_REPLACE);
    return this;
}

可以看到replace操作,也是经过doAddOp校验参数之后,会将opcmd和fragment组装成一个Op对象,同样加入mOps集合中。

那也来看下remove操作

//------------------------------FragmentTransaction.java------------------------------

@NonNull
public FragmentTransaction remove(@NonNull Fragment fragment) {
    addOp(new Op(OP_REMOVE, fragment));

    return this;
}

remove的操作,不需要经过校验参数,直接组装成Op对象,加入mOps集合中。

FragmentTransaction的提交

我们就以commit()为例,进行追踪

//------------------------------FragmentTransaction.java------------------------------

public abstract int commit();

commit在FragmentTransaction是个抽象方法,而它的实现在BackStackRecord中

//------------------------------BackStackRecord.java------------------------------

@Override
public int commit() {
    // 直接调用commitInternal方法,传递参数为false
    return commitInternal(false);
}


// 关注allowStateLoss参数,allowStateLoss的作用是当一条事务执行前会进行宿主状态的检查。
// 如果宿主已经处于stop状态,再提交一条事务则会发生异常
// 举一个例子:如果宿主因为内存不足被回收,而宿主已经执行了onSaveInstanceState,而此时又调用commit方法提交了事务,此时若allowStateLoss为false,就有可能触发异常了
int commitInternal(boolean allowStateLoss) {
    // mCommitted是一个标志位,表示一个事务最多只能被提交一次,多次提交会抛出异常
    if (mCommitted) throw new IllegalStateException("commit already called");
    if (FragmentManagerImpl.DEBUG) {
        Log.v(TAG, "Commit: " + this);
        LogWriter logw = new LogWriter(TAG);
        PrintWriter pw = new PrintWriter(logw);
        dump("  ", pw);
        pw.close();
    }
    // 对标志位进行赋值
    mCommitted = true;
    if (mAddToBackStack) {
        mIndex = mManager.allocBackStackIndex(this);
    } else {
        mIndex = -1;
    }
    
    // 调用FragmentManager的enqueueAction方法,把本次操作和allowStateLoss参数传递过去
    mManager.enqueueAction(this, allowStateLoss);
    return mIndex;
}
//------------------------------BackStackRecord.java------------------------------


/**
 * Adds an action to the queue of pending actions.
 *
 * @param action the action to add
 * @param allowStateLoss whether to allow loss of state information
 * @throws IllegalStateException if the activity has been destroyed
 */
public void enqueueAction(OpGenerator action, boolean allowStateLoss) {
    // 对allowStateLoss进行校验,如果allowStateLoss为false,则进入分支
    // allowStateLoss设计的目的是提醒开发者,宿主已经进行了onSaveInstance方法,当宿主因为内存不足等问题被回收,重新恢复时,新提交的事务是不能被恢复。以免在状态恢复时,让用户看到更奇怪的现象
    if (!allowStateLoss) {
        // 检查宿主的状态是否丢失,下面会追踪分析具体实现
        checkStateLoss();
    }
    synchronized (this) {
        if (mDestroyed || mHost == null) {
            if (allowStateLoss) {
                // This FragmentManager isn't attached, so drop the entire transaction.
                return;
            }
            throw new IllegalStateException("Activity has been destroyed");
        }
        if (mPendingActions == null) {
            mPendingActions = new ArrayList<>();
        }
        // 将action,也就是本次提交的事务添加到了一条等待队列中
        mPendingActions.add(action);
        // 然后执行scheduleCommit
        scheduleCommit();
    }
}
private void checkStateLoss() {
    // 如果宿主已经执行了onSaveInstanceState或者已经处于stop状态,则抛出异常
    if (isStateSaved()) {
        throw new IllegalStateException(
                "Can not perform this action after onSaveInstanceState");
    }
}

@Override
public boolean isStateSaved() {
    // See saveAllState() for the explanation of this.  We do this for
    // all platform versions, to keep our behavior more consistent between
    // them.
    // 返回的是宿主是否已经执行了onSaveInstanceState或者已经处于了stop的状态
    return mStateSaved || mStopped;
}

那么如何安全的提交事务呢?

FragmentTransaction的4种提交方式
  • commit():如果在宿主执行了onSaveInstanceState之后再执行该操作,会抛出异常。属于异步操作(不是子线程操作,而是会将操作发送的主线程的轮询队列中,当主线程轮询到了才进行事务的操作。)
  • commitAllowStateLoss():如果在宿主执行了onSaveInstanceState之后再执行该操作,不会去检查宿主状态,不会抛出异常。但该操作不会被Activity记录,恢复时也就没办法恢复这些提交操作,所以该操作适用不重要的事务。同属于异步事务。
  • commitNow():会立刻执行当前提交的Transaction事务。属于同步事务。
  • commitNowAllowStateLoss():具备以上两者的特性,既是同步执行,也不会检查宿主的状态,有可能该操作不会被正确恢复。

一般情况选择commitNowAllowStateLoss(),这样不会抛出异常中断用户的操作。

Fragment页面重叠

先来讲下页面的状态保存和恢复

  • onSaveInstanceState负责页面的状态保存。
  • onRestoreInstanceState以及onCreate方法会进行状态的恢复。

onSaveInstanceStateonRestoreInstanceState并不一定会成对出现。
页面状态保存(即onSaveInstanceState)的触发时机:

  • Home键回到桌面
  • 从最近列表跳转别的应用
  • 横竖屏切换
  • 内存不足时销毁重建

Fragment页面重叠的主要原因是**FragmentActivity本身已经具有Fragment状态保存和状态恢复的功能。当Activity被回收并重建时,Fragment也会有相应的恢复操作,如果除了FragmentActivity自身自带的恢复操作之外,开发者在Activity的生命周期中也进行了Fragment的创建操作,则会造成重叠的效果 ** 。

解决方法:

  • 在添加Fragment之前先判断FragmentManager中是否已经存在需要添加的Fragment(通过Tag)

Fragment的懒加载

在新的androidx的包下,官方推荐使用ViewPager2来替代ViewPager来实现懒加载。

ViewPager2内部是根据RecyclerView来实现的,而且可以实现按需预加载。官方内置了ViewPager2组件的绑定适配器FragmentStateAdapter

FragmentStateAdapter的作用是当页面切换时,可见的Fragment进入resume状态,不可见的都是进入onStart这个生命周期的状态,所以相当于实现了Fragment的懒加载。

如果想继续使用ViewPager的话,官方也提供了新的适配器FragmentStatePagerAdapter,在创建FragmentStatePagerAdapter可以传递两种Behavior

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({BEHAVIOR_SET_USER_VISIBLE_HINT, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT})
    private @interface Behavior { }

    /**
     * Indicates that {@link Fragment#setUserVisibleHint(boolean)} will be called when the current
     * fragment changes.
     *
     * @deprecated This behavior relies on the deprecated
     * {@link Fragment#setUserVisibleHint(boolean)} API. Use
     * {@link #BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT} to switch to its replacement,
     * {@link FragmentTransaction#setMaxLifecycle}.
     * @see #FragmentStatePagerAdapter(FragmentManager, int)
     */
    @Deprecated
    // 当ViewPager执行页面切换时,会执行setUserVisibleHint方法,这是向前兼容的
    public static final int BEHAVIOR_SET_USER_VISIBLE_HINT = 0;

    /**
     * Indicates that only the current fragment will be in the {@link Lifecycle.State#RESUMED}
     * state. All other Fragments are capped at {@link Lifecycle.State#STARTED}.
     *
     * @see #FragmentStatePagerAdapter(FragmentManager, int)
     */
    // 当ViewPager执行页面切换时,只有可见的被选中的那个Fragment才会进入onResume状态,其它不可见的都会进入onStart
    public static final int BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT = 1;

实际上ViewPager2的FragmentStateAdapter和ViewPager的FragmentStatePagerAdapter是两个相似的功能,在页面切换的时候不会再去执行Fragment#setUserVisibleHint(boolean)方法了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350