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閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容