Android Jetpack框架之LiveData与ViewModel

Android Jetpack框架之LiveData与ViewModel分析与使用

1、前言

Google 为了帮助 Android 开发者更快更好地开发 App,推出了一系列组件,这些组件被打包成了一个整体,称作 Android Jetpack,它包含的组件如下图所示

框架图

jetpack概览

官方对应 Architecture 的说明:

Android architecture components are a collection of libraries that help you design robust, testable, and maintainable apps. Start with classes for managing your UI component lifecycle and handling data persistence.
Manage your app's lifecycle with ease. New lifecycle-aware components help you manage your activity and fragment lifecycles. Survive configuration changes, avoid memory leaks and easily load data into your UI.
Use LiveData to build data objects that notify views when the underlying database changes.
ViewModel Stores UI-related data that isn't destroyed on app rotations.
Room is an a SQLite object mapping library. Use it to Avoid boilerplate code and easily convert SQLite table data to Java objects. Room provides compile time checks of SQLite statements and can return RxJava, Flowable and LiveData observables.

翻译:

Android体系结构组件是一组库,可帮助您设计健壮,可测试和可维护的应用程序。 从用于管理UI组件生命周期和处理数据持久性的类开始。
轻松管理应用程序的生命周期。 新的生命周期感知组件可帮助您管理活动和碎片生命周期。 生存配置更改,避免内存泄漏并轻松将数据加载到UI中。
使用LiveData构建数据对象,以便在基础数据库更改时通知视图。
ViewModel存储在应用程序轮换中未销毁的UI相关数据。
Room是一个SQLite对象映射库。 使用它来避免样板代码并轻松地将SQLite表数据转换为Java对象。 Room提供SQLite语句的编译时检查,可以返回RxJava,Flowable和LiveData observable。

官方推荐的应用架构指南

常见架构原则
1.分离关注点
要遵循的最重要的原则是分离关注点一种常见的错误是在一个 ActivityFragment中编写所有代码。这些基于界面的类应仅包含处理界面和操作系统交互的逻辑。应尽可能使这些类保持精简,这样可以避免许多与生命周期相关的问题。

请注意,您并不拥有 ActivityFragment的实现,这些只是表示 Android 操作系统与应用之间关系的粘合类。操作系统可能会根据用户交互或因内存不足等系统条件随时销毁它们。为了提供令人满意的用户体验和更易于管理的应用维护体验,最好尽量减少对它们的依赖。
2.用过模型驱动界面
另一个重要原则是您应该通过模型驱动界面,最好是持久性模型。模型是负责处理应用数据的组件。它们独立于应用中的 View 对象和应用组件,因此不受应用的生命周期以及相关关注点的影响。
持久性是理想之选,原因如下

  • 如果 Android 操作系统销毁应用以释放资源,用户不会丢失数据。
  • 当网络连接不稳定或不可用时,应用会继续工作。

应用所基于的模型类应明确定义数据管理职责,这样将使应用更可测试且更一致。

看下图,该图显示了设计应用后所有模块应如何交互


架构指南图

请注意,每个组件仅依赖于其下一级的组件。例如,Activity 和 Fragment 仅依赖于视图模型。存储区是唯一一个依赖于其他多个类的类;在本例中,存储区依赖于持久性数据模型和远程后端数据源。

这种设计打造了一致且愉快的用户体验。无论用户是在上次关闭应用几分钟后还是几天后回到应用,他们都会立即看到应用在本地保留的用户信息。如果此数据已过时,则应用的存储区模块将开始在后台更新数据。

2、介绍LiveData与ViewModel

ViewModel类旨在以生命周期意识的方式存储和管理与UI相关的数据。 ViewModel类允许数据在配置更改(例如屏幕旋转)后继续存在。
示例代码

public class FlowerModel extends ViewModel {
    private MutableLiveData<Data> liveData;
    public MutableLiveData<Data> getLiveData() {
        if (liveData == null){
            liveData = new MutableLiveData<>();
        }
        return liveData;
    }
    public static class Data {
        public int send;
        public int less;
        public List<PaintView.DrawPath> list;
    }
}
flowerModel = ViewModelProviders.of(getActivity()).get(FlowerModel.class);

然后利用LiveData通知界面更新UI
LiveData 是一种可观察的数据存储器。应用中的其他组件可以使用此存储器来监控对象的更改,而无需在它们之间创建明确且严格的依赖路径。LiveData 组件还遵循应用组件(如 Activity、Fragment 和 Service)的生命周期状态,并包括清理逻辑以防止对象泄漏和过多的内存消耗。

  flowerModel.getLiveData().observe(getActivity(), new Observer<FlowerModel.Data>() {
            @Override
            public void onChanged(@Nullable FlowerModel.Data data) {
                //update UI
            }
        });

Demo演示

3、项目中应用

4、浅析原理与使用

ViewModel生命周期

  • ViewModel对象的范围是在获取ViewModel时传递给ViewModelProvider的Lifecycle生命周期-
  • ViewModel在内存中直到Activity销毁或Fragment被移除
  • 系统首次调用活动对象的onCreate()方法时,通常会请求ViewModel
  • 系统可能会在整个活动的整个生命周期中多次调用onCreate(),例如当设备屏幕旋转时
  • ViewModel从第一次请求ViewModel直到活动完成并销毁时存在
    ViewModel生命周期图

    看源码
    1,获取ViewModelProvider
    ViewModelProviders提供四个构造方法创建VIewProvider,两个带有factory两个没有,不过没有factory的其实使用的是默认的Factory,所以四个方法基本一致只是Fragment和Activity的区分
@NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(fragment), factory);
    }

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

获取ViewModelStore:由前面的源码可以知道创建ViewProvider时传入两个参数:ViewModelStore 和 Factory;显然从名字就可以看出他们的作用,Factory负责创建,ViewModelStore负责存储

@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

上面的执行是,先去ViewModelStore中获取,如果为空就调用Factory的create()创建ViewModel,并储存在VIewmoStore中

ViewModelProviders.of(SquareFragment.this).get(AudioLiveData.class)的执行流程大概图 忽略创建和存储细节

viewModel创建1

2,通过ViewModelStores.of(this)创建ViewModelStore 源码

    private ViewModelStores() {
    }
    @NonNull
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }
    @NonNull
    @MainThread
    public static ViewModelStore of(@NonNull Fragment fragment) {
        if (fragment instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) fragment).getViewModelStore();
        }
        return holderFragmentFor(fragment).getViewModelStore();
    }

先判断Activity是否为 ViewModelStoreOwner,如果是直接获取其中的ViewModelStore,否则调用holderFragmentFor(activity).getViewModelStore()获取

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }
...
    HolderFragment holderFragmentFor(FragmentActivity activity) {
            FragmentManager fm = activity.getSupportFragmentManager();
            //通过manager.findFragmentByTag(HOLDER_TAG)找到fragment
            HolderFragment holder = findHolderFragment(fm);
            if (holder != null) {
                return holder;
            }
            holder = mNotCommittedActivityHolders.get(activity);
            //在map中取
            if (holder != null) {
                return holder;
            }

            if (!mActivityCallbacksIsAdded) {
                mActivityCallbacksIsAdded = true;
                activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
            }
            //创建 HolderFragment
            holder = createHolderFragment(fm);
            //存到map中
            mNotCommittedActivityHolders.put(activity, holder);
            return holder;
        }

其中 findHolderFragment

private static HolderFragment findHolderFragment(FragmentManager manager) {
            if (manager.isDestroyed()) {
                throw new IllegalStateException("Can't access ViewModels from onDestroy");
            }

            Fragment fragmentByTag = manager.findFragmentByTag(HOLDER_TAG);
            if (fragmentByTag != null && !(fragmentByTag instanceof HolderFragment)) {
                throw new IllegalStateException("Unexpected "
                        + "fragment instance was returned by HOLDER_TAG");
            }
            return (HolderFragment) fragmentByTag;
        }

map中get

@SuppressWarnings("WeakerAccess")
    static class HolderFragmentManager {
        private Map<Activity, HolderFragment> mNotCommittedActivityHolders = new HashMap<>();
        private Map<Fragment, HolderFragment> mNotCommittedFragmentHolders = new HashMap<>();
...
    holder = mNotCommittedActivityHolders.get(activity);

createHolderFragment

private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
            HolderFragment holder = new HolderFragment();
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
            return holder;
        }

3, HolderFragment存储ViewModelStore
上面都是获取或者创建HolderFragment的过程,有没有想过我们存储ViewModel的地方,为什么一直在操作fragment ?我们回头看创建ViewModelStore的地方
有这么个判断 activity instanceof ViewModelStoreOwner

@NonNull
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }
...
public interface ViewModelStoreOwner {

    @NonNull
    ViewModelStore getViewModelStore();
}

而HolderFragment实现了ViewModelStoreOwner,同时保存了ViewModelStore

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
...
private ViewModelStore mViewModelStore = new ViewModelStore();
@NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        return mViewModelStore;
    }

ViwModelStore 利用HashMap获取,存储 ViewModel

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

获取ViewModelStore的整体执行流程


ViewModelStore创建2

总结一下ViewModel内部的存储逻辑

  • 根据传入的Activity/Fragment获取、创建、添加并以activity为键保存Fragment(因此在屏幕发生旋转时生命周期改变两次创建activity时获取的都是同一个fragment因为key相同)
  • 获取Fragment中保存的ViewModelStore对象(ViewModelStore中使用Map储存ViewModel)
  • 创建ViewModelProvider实例,ViewModelProvider中封装了获取的ViewModelStore和创建用的Factory
  • 从VIewModelStore的Map中或Factory的create()中获取ViewModel

LiveData 是一个观察者模型,但是它是一个与 Lifecycle 绑定了的 Subject,也就是说,只有当 UI 组件处于 ACTIVE 状态时,它的 Observer 才能收到消息,否则会自动切断订阅关系

  • LiveData 只要有数据更新,它的 observer 就会收到通知。如果我们要把 LiveData 用作事件总线(类似EventBus),还需要做一些定制,可以使用google官方demo里的 SingleLiveEvent 。
public class SingleLiveEvent<T> extends MutableLiveData<T> {
    private static final String TAG = "SingleLiveEvent";
    private final AtomicBoolean mPending = new AtomicBoolean(false);
    @MainThread
    public void observe(LifecycleOwner owner, final Observer<T> observer) {
        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }
        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }
    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }
    /**
     * Used for cases where T is Void, to make calls cleaner.
     */
    @MainThread
    public void call() {
        setValue(null);
    }
}
  • 我们没法直接修改 LiveData 的 value,因为它是不可变的(immutable),可变(mutable)版本是 MutableLiveData,通过调用 setValue(主线程)或 postValue(非主线程)可以修改它的 value
  • LiveData 有一个实现了中介者模式的子类 —— MediatorLiveData,它可以把多个 LiveData 整合成一个,只要任何一个 LiveData 有数据变化,它的观察者就会收到消息

汇总一下 LiveData 的使用场景:

* LiveData - immutable 版本*
* MutableLiveData - mutable 版本
* MediatorLiveData - 可汇总多个数据源
* SingleLiveEvent - 事件总线

LiveData转换

  • Transformations.map 对存储在LiveData对象中的值修改,并将结果发送到下游
 private class User{
        private User(String id){ this.userId = id; }
        private String userName;
        private String userId;
    }
    private MutableLiveData<User> userMutableLiveData;
    public void test(){
        userMutableLiveData = new MutableLiveData<>();
        LiveData<String> userName = Transformations.map(userMutableLiveData, new Function<User, String>() {
            @Override
            public String apply(User input) {

                return "姓名"+input.userName;
            }
        });
  • Transformations.switchMap 动态返回LiveData
private class User{
        private User(String id){ this.userId = id; }
        private String userName;
        private String userId;
    }
    private MutableLiveData<User> userMutableLiveData;
    public void test(){
        userMutableLiveData = new MutableLiveData<>();
        LiveData<String> userId = new MutableLiveData<>();
        LiveData<User> user2 = Transformations.switchMap(userId, new Function<String, LiveData<User>>() {
            @Override
            public LiveData<User> apply(String input) {
                return getUser(input);
            }
        });
    }
    private LiveData<User> getUser(String id){
        User user = new User(id);
         userMutableLiveData.setValue(user);
         return userMutableLiveData;
    }

MutableLiveData只是LiveData的一个扩展类,重写了LiveData中的protected方法postValue()和setValue(),调用了super.postValue()和super.setValue(),也就是说所有的方法都是在LiveData中实现

@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

先看 observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer)

flowerModel.getLiveData().observe(getActivity(), new Observer<FlowerModel.Data>() {
            @Override
            public void onChanged(@Nullable FlowerModel.Data data) {
                //update UI
                
            }
        });
...
 @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            // 如果已经销毁就返回
            return;
        }
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
        if (existing != null && !existing.isAttachedTo(owner)) {
            throw new IllegalArgumentException("Cannot add the same observer"
                    + " with different lifecycles");
        }
        if (existing != null) {
            return;
        }
        owner.getLifecycle().addObserver(wrapper);
    }
  • 在observe中首先判断了当前Lifecycler的状态,当Destroy时即观察者不处于活跃状态,不用接收数据
  • 创建LifecycleBoundObserver实例保存传入的LifecycleOwner和Observer,并保存在mObservers
  • 添加LifecycleOwner的观察者,响应生命周期的变化
    继续看 LifecycleBoundObserver
 private abstract class ObserverWrapper {
        final Observer<T> mObserver;
        boolean mActive;
        int mLastVersion = START_VERSION;
        ObserverWrapper(Observer<T> observer) {
          //保存观察者Observer
            mObserver = observer;
        }
        abstract boolean shouldBeActive();
        boolean isAttachedTo(LifecycleOwner owner) {
            return false;
        }
        void detachObserver() {
        }
        void activeStateChanged(boolean newActive) {
            if (newActive == mActive) {
                return;
            }
            // immediately set active state, 
            //so we'd never dispatch anything to inactive owner
            //立即设置活动状态,因此我们永远不会向非活动所有者发送任何内容
            mActive = newActive;
            boolean wasInactive = LiveData.this.mActiveCount == 0;
            LiveData.this.mActiveCount += mActive ? 1 : -1;
            if (wasInactive && mActive) {
                onActive();// 当Activity/Fragment为活跃状态时回调onActive()
            }
            if (LiveData.this.mActiveCount == 0 && !mActive) {
                onInactive(); // 当Activity/Fragment未活跃状态时回调onInactive()
            }
            if (mActive) {
                dispatchingValue(this);
            }
        }
    }
class LifecycleBoundObserver extends ObserverWrapper implements GenericLifecycleObserver {
        @NonNull final LifecycleOwner mOwner;
        LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(observer);// 调用父类ObserverWrapper的构造函数传递Owner
            mOwner = owner;
        }
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
        }
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
        // 实现GenericLifecycleObserver 当生命周期改变时回调onStateChanged
            if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
                removeObserver(mObserver);// DESTROYED时移除观察者
                return;
            }
            activeStateChanged(shouldBeActive());
        }
        @Override
        boolean isAttachedTo(LifecycleOwner owner) {
            return mOwner == owner;
        }
        @Override
        void detachObserver() {
            mOwner.getLifecycle().removeObserver(this);
        }
    }
   
  • ObserverWrapper 在Owner活跃状态改变时回调onActive和onInactive方法
  • LifecycleBoundObserver主要利用Lifecycler的生命周期观察者GenericLifecycleObserver,当设置了owner.getLifecycle().addObserver(wrapper)后,当生命周期改变时会回调onStateChange()方法,在生命周期为Destroy时移除Observer

setValue(T value) (主线程)

@MainThread
    protected void setValue(T value) {
        assertMainThread("setValue");//检测是否在主线程
        mVersion++;
        mData = value;
        dispatchingValue(null);
    }
...
 private void dispatchingValue(@Nullable ObserverWrapper initiator) {
        if (mDispatchingValue) {
            mDispatchInvalidated = true;
            return;
        }
        mDispatchingValue = true;
        do {
            mDispatchInvalidated = false;
            if (initiator != null) {
                considerNotify(initiator);
                initiator = null;
            } else {
          //遍历mObservers中所有的Observer,调用considerNotify()更新数据
                for (Iterator<Map.Entry<Observer<T>, ObserverWrapper>> iterator =
                        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                    considerNotify(iterator.next().getValue());
                    if (mDispatchInvalidated) {
                        break;
                    }
                }
            }
        } while (mDispatchInvalidated);
        mDispatchingValue = false;
    }
...
private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false);
            return;
        }
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //更新数据 通过接口回调
        observer.mObserver.onChanged((T) mData);
    }
...
public interface Observer<T> {
    void onChanged(@Nullable T t);
}
...
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class DefaultTaskExecutor extends TaskExecutor {
    private final Object mLock = new Object();
    private ExecutorService mDiskIO = Executors.newFixedThreadPool(2);
    @Nullable
    private volatile Handler mMainHandler;

    @Override
    public void executeOnDiskIO(Runnable runnable) {
        mDiskIO.execute(runnable);
    }
    @Override
    public void postToMainThread(Runnable runnable) {
     //子线程向主线程发消息
        if (mMainHandler == null) {
            synchronized (mLock) {
                if (mMainHandler == null) {
                    mMainHandler = new Handler(Looper.getMainLooper());
                }
            }
        }
        //noinspection ConstantConditions
        mMainHandler.post(runnable);
    }
    @Override
    public boolean isMainThread() {//检测是否在主线程具体实现
        return Looper.getMainLooper().getThread() == Thread.currentThread();
    }
  • setValue()中先检查是否主线程然后调用了dispatchingValue(),在dispatchingValue中遍历mObservers中所有的Observer,调用considerNotify()中.mObserver.onChanged(T)更新数据
    postValue(T value)(子线程)
 protected void postValue(T value) {
        boolean postTask;
        synchronized (mDataLock) {
            postTask = mPendingData == NOT_SET;
            mPendingData = value;
        }
        if (!postTask) {
            return;
        }
        ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
    }
...
  private final Runnable mPostValueRunnable = new Runnable() {
        @Override
        public void run() {
            Object newValue;
            synchronized (mDataLock) {
                newValue = mPendingData;
                mPendingData = NOT_SET;
            }
            //noinspection unchecked
            setValue((T) newValue);
        }
    };
  • postValue()中传递的Runnable 也是调用setValue() 只不过是通过handle在子线程向主线程发消息

5.展望

本次分享只是分析了ViewModel,LiveData 的大概源码,与简单示例,但其实在实际生产环境中,我们需要使用ViewModel与Repository连接网络层,同时如果需要数据持久化还需要连接Room数据库层,通过ViewModel生成LiveData,UI层订阅LiveData,真正的实现数据驱动界面

再看一次架构图

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

推荐阅读更多精彩内容