Android Jetpack之ViewModel源碼分析

Android Jetpack之ViewModel源碼分析

ViewModel 簡(jiǎn)介

在Android開發(fā)的時(shí)候,使用Activity、Fragment的生命周期的變動(dòng)有時(shí)候是不受開發(fā)人員控制的(比如橫豎屏切換,導(dǎo)致Activity銷毀并重新創(chuàng)建),各種因素導(dǎo)致Android界面或被系統(tǒng)重新創(chuàng)建。當(dāng)Activity需要重新創(chuàng)建的時(shí)候,之前與之綁定的數(shù)據(jù)也會(huì)丟失(比如EditText上輸入的數(shù)據(jù)或者網(wǎng)絡(luò)請(qǐng)求而沒有本地化存儲(chǔ)的數(shù)據(jù)等),導(dǎo)致用戶體驗(yàn)極差。
但是在實(shí)際開發(fā)中,我們可以使用Android中提供的onSaveInstanceState()方法來保存臨時(shí)數(shù)據(jù)從而可以恢復(fù)用戶現(xiàn)場(chǎng),然后重新執(zhí)行OnCreate的時(shí)候,通過Bundle參數(shù)來再次獲取保存的數(shù)據(jù)。這種實(shí)現(xiàn)方式有較大的局限性:只能保存數(shù)據(jù)量較小的情況(比如較大的圖片就不適合),并且數(shù)據(jù)需要被序列化(Parcelable)。
ViewMode的出現(xiàn)解決了onSaveInstanceState的不足,ViewMode將UI控制器和數(shù)據(jù)業(yè)務(wù)進(jìn)行分離,UI控制器只負(fù)責(zé)UI展示相關(guān)的工作,ViewMode用來管理和存儲(chǔ)與UI綁定的數(shù)據(jù),同時(shí)ViewMode還與UI的生命周期相關(guān)聯(lián)。比如在橫豎屏切換時(shí),與ViewMode相關(guān)聯(lián)的UI界面需要重新繪制創(chuàng)建時(shí)即執(zhí)行onCreate方法時(shí),就可以使用ViewMode存儲(chǔ)并恢復(fù)之前的用戶現(xiàn)場(chǎng)的數(shù)據(jù),而不會(huì)因?yàn)锳ctivity的銷毀而丟失,從而恢復(fù)用戶現(xiàn)場(chǎng)。ViewModel的還有一個(gè)好處就是可以實(shí)現(xiàn)Activity和Fragment之間的數(shù)據(jù)共享。

官方網(wǎng)址介紹:ViewModel介紹

ViewMode的使用

2.1 Gradle引入

implementation "android.arch.lifecycle:extensions:2.0.0"

2.2 創(chuàng)建自定義ViewModel

創(chuàng)建自定義ViewModel有兩種方式代碼如下。其中最大的區(qū)別是ViewMode中不能引用Activity、Fragment的實(shí)例。主要原因是:為了防止內(nèi)存泄漏。如果在某種場(chǎng)景下需要用到上下文Context,在繼承ViewMode的時(shí)候,可以選擇繼承AndroidViewMode。由于AndroidViewMode持有的是Application,Application在單進(jìn)程的應(yīng)用中是單例模式,而且生命周期是最長(zhǎng)的所以不在內(nèi)存泄漏的問題。

open class MyCustomViewModel : ViewModel(){
    var name: String = "demo "
}

class MyCustomAndroidViewModel(application: Application): AndroidViewModel(application){
    var lastName = "demo"
}

2.3 在Activity中的使用

(1)代碼塊1 :通過ViewModelProviders的of方法綁定activity的實(shí)例進(jìn)行初始化,然后通過get方法來獲取到MyViewMode的實(shí)例。
(2)代碼塊2:使用了lambda表達(dá)式,主要是對(duì)ViewModel里面的lastName變量進(jìn)行重新賦值。
(3)代碼塊3:使用了Kotlin的拓展包可以不用findViewById。如果旋轉(zhuǎn)屏幕,重新執(zhí)行onCreate方法,獲取到的還是之前的ViewModel實(shí)例。所以這個(gè)時(shí)候屏幕顯示的是“configChanged”文字。

class ViewModelActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        //代碼塊1
        val viewModel = ViewModelProviders.of(this).get(MyCustomViewModel::class.java)
        val androidViewModel = ViewModelProviders.of(this).get(MyCustomAndroidViewModel::class.java)
        //代碼塊3
        txt_view_model.text = androidViewModel.lastName
        //代碼快2
        txt_view_model.setOnClickListener { androidViewModel.lastName = "configChanged" }
    }
}

open class MyCustomViewModel : ViewModel(){
    var name: String = "demo "
}

class MyCustomAndroidViewModel(application: Application): AndroidViewModel(application){
    var lastName = "demo"
}

2.4 ViewMode的生命周期

ViewMode在Activity被finish后才會(huì)銷毀,否則在Activity的生命周期范圍內(nèi)會(huì)一直保存在內(nèi)存中,或者當(dāng)依附的Fragment detached后進(jìn)行銷毀。
使用官網(wǎng)的一張圖來標(biāo)注Activity的生命周期與ViewMode的生命周期關(guān)聯(lián),如下圖所示:


在這里插入圖片描述

源碼分析

3.1 Activity是如何實(shí)現(xiàn)狀態(tài)保留

在分析源碼之前我們先來學(xué)習(xí)Activity是如何實(shí)現(xiàn)狀態(tài)保留。Activity中的retainNonConfigurationInstances和getLastNonConfigurationInstance方法。在Android橫豎屏切換時(shí)會(huì)觸發(fā)onSaveInstanceState,而還原時(shí)會(huì)產(chǎn)生onRestoreInstanceState,但是Android的Activity類還有一個(gè)方法名為onRetainNonConfigurationInstance和getLastNonConfigurationInstance這兩個(gè)方法。 當(dāng)發(fā)生屏幕切換時(shí),將伴隨Destroying被系統(tǒng)調(diào)用。通過這個(gè)方法可以像onSaveInstanceState()的方法一樣保留變化前的Activity數(shù)據(jù)和狀態(tài),最大的不同在于這個(gè)方法可以返回一個(gè)包含有狀態(tài)信息的Object對(duì)象,其中甚至可以包含Activity Instance本身。用這個(gè)方法保存Activity State后,通過getLastNonConfigurationInstance()在新的Activity Instance中恢復(fù)原有狀態(tài)。比如: 在恢復(fù)窗口時(shí),我們可以不使用onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。下面的例子效果和ViewModel實(shí)現(xiàn)的一致的,都可以實(shí)現(xiàn)用戶現(xiàn)場(chǎng)的數(shù)據(jù)恢復(fù)。
(1)看一下Activity中NonConfigurationInstances的定義 。子類通過復(fù)寫onRetainNonConfigurationInstance返回的對(duì)象,其實(shí)被賦值給了NonConfigurationInstances.activity屬性。

static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }

(2)getLastNonConfigurationInstance方法返回的數(shù)據(jù)就是NonConfigurationInstances.activity屬性。

@Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

(3)下面來看一個(gè)具體的例子。繼承自Activity

  1. 代碼塊1復(fù)寫Activity的onRetainNonConfigurationInstance方法,給MyCustomViewModel的name賦值,并返回該對(duì)象,其實(shí)該對(duì)象被賦值給了NonConfigurationInstances.activity屬性
  2. 代碼塊2通過getLastNonConfigurationInstance方法獲取NonConfigurationInstances.activity屬性即MyCustomViewModel對(duì)象,然后取出name屬性賦值給TextView。
class ViewModelActivity : Activity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        //代碼塊2
        lastNonConfigurationInstance?.let {
            var data =  it as MyCustomViewModel
            txt_view_model.text = data.name
        }
    }
    //代碼塊1
    override fun onRetainNonConfigurationInstance(): Any {
        val model = MyCustomViewModel()
        model.name = "configChanged"
        return model
    }
}

(4)下面來看另一種具體的實(shí)例。但是是繼承自AppCompatActivity
代碼的效果和上訴一致,只不過復(fù)寫的是onRetainCustomNonConfigurationInstance方法,獲取用的是getLastCustomNonConfigurationInstance方法。

class ViewModelActivity : AppCompatActivity(){
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        lastCustomNonConfigurationInstance?.let {
            var data =  it as MyCustomViewModel
            txt_view_model.text = data.name
        }
        Log.e("activity","class :"+this.toString())

    }

    override fun onRetainCustomNonConfigurationInstance(): Any {
        val model = MyCustomViewModel()
        model.name = "configChanged"
        return model
    }
}

為什么會(huì)有上面的區(qū)別呢?我們來看一下原因:
在FragmentActivity中把onRetainNonConfigurationInstance寫成final了,不允許子類復(fù)寫。但是我們?cè)谄渲锌吹搅薿nRetainCustomNonConfigurationInstance方法,并把onRetainCustomNonConfigurationInstance的返回值賦值給NonConfigurationInstances.custom屬性,然后返回FragmentActivity中的NonConfigurationInstances賦值給Activity中的NonConfigurationInstances.activity屬性。注意這兩個(gè)NonConfigurationInstances要區(qū)分開來。

public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        if (fragments == null && mViewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }

NonConfigurationInstances類定義。這里的NonConfigurationInstances是定義在FragmentActivity里面的一個(gè)類。

  static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
        FragmentManagerNonConfig fragments;
    }

在來看一下getLastCustomNonConfigurationInstance方法:
(1)調(diào)用Activity中g(shù)etLastNonConfigurationInstance獲取FragmentActivity中的NonConfigurationInstances對(duì)象。
(2)獲取到FragmentActivity中的NonConfigurationInstances對(duì)象后在獲取custom屬性,custom屬性就是我們復(fù)寫的onRetainCustomNonConfigurationInstance返回對(duì)象。

 /**
     * Return the value previously returned from
     * {@link #onRetainCustomNonConfigurationInstance()}.
     */
    @SuppressWarnings("deprecation")
    public Object getLastCustomNonConfigurationInstance() {
        NonConfigurationInstances nc = (NonConfigurationInstances)
                getLastNonConfigurationInstance();
        return nc != null ? nc.custom : null;
    }

特別注意的點(diǎn)
1. onRetainNonConfigurationInstance()在onSaveInstanceState()之后被調(diào)用。
2. 調(diào)用順序同樣在onStop() 和 onDestroy()之間。

3.2 ViewModelProviders類

  1. ViewModelProviders.of方法
    在Activity中我們會(huì)調(diào)用如下代碼來獲取ViewModel實(shí)例
ViewModelProviders.of(this).get(MyCustomViewModel::class.java)

那么我們就看看在ViewModelProviders.of(this)都做了些什么?
在ViewModelProviders中有四個(gè)構(gòu)造方法,都是創(chuàng)建ViewModelProvider對(duì)象,只不過參數(shù)不同而已。
(1)ViewModelProviders里面的of()函數(shù)為我們構(gòu)建一個(gè)ViewModelProvider對(duì)象。而ViewModelProvider構(gòu)造方法接兩個(gè)變量ViewModelStore和Factory
(2)ViewModelStore顧名思義就是存儲(chǔ)ViewMoel的容器
(3)在代碼塊2:如果沒有傳入Factory,則內(nèi)部創(chuàng)建了默認(rèn)的Factory,F(xiàn)actory是ViewModelProvider的一個(gè)內(nèi)部接口,里面有一個(gè)create方法。Factory實(shí)現(xiàn)類是用來構(gòu)建ViewModel實(shí)例的。

public static ViewModelProvider of(@NonNull Fragment fragment) {
        return of(fragment, null);
    }
    
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }
    
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(fragment.getViewModelStore(), factory);
    }
 public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        //代碼塊1
        Application application = checkApplication(activity);
        if (factory == null) {
        //代碼塊2
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

在構(gòu)建ViewModelProvider的時(shí)候需要用到ViewModelStore和Factory,下面我們來分別介紹一下他們。

3.3 ViewModelStore類

ViewModelStore顧名思義就是存儲(chǔ)ViewMoel的容器
(1)在ViewModelStore中通過HashMap存儲(chǔ)ViewModel
(2)在Activity和Fragment的onDestroy方法中,會(huì)執(zhí)行clear方法,清空Map集合里面的ViewModel。并且會(huì)執(zhí)行ViewModel的onCleared方法。

 protected void onDestroy() {
        super.onDestroy();
        if (mViewModelStore != null && !isChangingConfigurations()) {
            mViewModelStore.clear();
        }
    }

在new ViewModelProvider(activity.getViewModelStore(), factory)的時(shí)候,會(huì)通過調(diào)用androidx.fragment.app.FragmentActivity的getViewModelStore()方法獲取ViewModelStore對(duì)象。這里就用到了我們?cè)?.1說的知識(shí)點(diǎn)了。
(1)如果mViewModelStore不是null,直接返回該對(duì)象
(2)如果是null,通過getLastNonConfigurationInstance獲屏幕旋轉(zhuǎn)等操作是保存的對(duì)象。注意返回的NonConfigurationInstances是FragmentActivity的。
(3)如果NonConfigurationInstances不為空,則獲取NonConfigurationInstances中的viewModelStore屬性。
(3)如果NonConfigurationInstances中的viewModelStore屬性為null,則創(chuàng)建一個(gè)ViewModelStore對(duì)象。

public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
           //代碼塊1
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

我們?cè)賮砜纯丛贏ctivity在橫豎屏切換時(shí)是如何保存viewModelStore對(duì)象的,在onRetainNonConfigurationInstance方法中將mViewModelStore賦值給了NonConfigurationInstances.viewModelStore屬性。這樣在橫豎屏切換時(shí)相當(dāng)于ViewModel實(shí)例被恢復(fù)了。

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    return nci;
}

通過上面的分析,我們知道在橫豎屏旋轉(zhuǎn)的時(shí)候,其實(shí)恢復(fù)的是ViewModelStore對(duì)象,而ViewModelStore里面通過鍵值對(duì)的形式存儲(chǔ)著ViewModel,所以相當(dāng)于ViewModel被恢復(fù)了。
下面附上ViewModelStore的代碼

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);
    }
    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

3.4 Factory類

在new ViewModelProvider(activity.getViewModelStore(), factory)的時(shí)候第二個(gè)參數(shù)是Factory的子類,F(xiàn)actory有2個(gè)實(shí)現(xiàn)類:是NewInstanceFactory, AndroidViewModelFactory 。通過ViewModelProvider.AndroidViewModelFactory.getInstance獲取單例的Factory對(duì)象。
NewInstanceFactory源代碼:

public static class NewInstanceFactory implements Factory {

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
    }

AndroidViewModelFactory 源代碼

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        /**
         * Retrieve a singleton instance of AndroidViewModelFactory.
         *
         * @param application an application to pass in {@link AndroidViewModel}
         * @return A valid {@link AndroidViewModelFactory}
         */
        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        /**
         * Creates a {@code AndroidViewModelFactory}
         *
         * @param application an application to pass in {@link AndroidViewModel}
         */
        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }

(1)AndroidViewModelFactory實(shí)例化構(gòu)造方法里面有參數(shù)的class,可以引用Context,其實(shí)是Appplication實(shí)例。
(2)如果是有application參數(shù),則通過newInstance(application)實(shí)例化。否則調(diào)用父類的create方法然后通過newInstance()實(shí)例化。
(3)如果通過newInstance(application)實(shí)例化。就可以在ViewModel里面拿到Context,由于Application是APP全局的生命周期最長(zhǎng),所以就不存在內(nèi)存泄露的問題了。
Factory類的定義非常簡(jiǎn)單,只有一個(gè)create方法,返回ViewModel實(shí)例。

    public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

3.5 ViewModelProvider類

通過上面的步驟,我們已經(jīng)構(gòu)建出了ViewModelProvider對(duì)象,那么接下來就是使用它了。還記得在Activity里面的ViewModelProviders.of(this).get(MyCustomViewModel::class.java)代碼嗎?通過ViewModelProvider的get方法,就可以獲取ViewModel的實(shí)例了。而這個(gè)實(shí)例是通過上面的Factory創(chuàng)建的。
下面我們來看一下get方法,里面生成了一個(gè)KEY,這個(gè)KEY就是ViewModelStore的HashMap的Key了。然后調(diào)用重載的get方法 get(@NonNull String key, @NonNull Class<T> modelClass)

  public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }

(1)根據(jù)之前的從FragmentActivity中獲取的ViewModelStore對(duì)象中獲取key對(duì)應(yīng)的ViewModel。如果獲取的ViewModel是傳入的Class 的對(duì)象,則直接返回該ViewModel。
(2)通過of函數(shù)創(chuàng)建的Factory生產(chǎn)一個(gè)ViewModel實(shí)例,可能是帶有Application實(shí)例的。
(3)將Factory實(shí)例put進(jìn)ViewModelStore的HashMap里面。

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;
    }

3.6 ViewModel類

ViewModel是一個(gè)抽象類,里面有一個(gè)onCleared方法,有一個(gè)實(shí)現(xiàn)類AndroidViewModel,唯一的不同點(diǎn)是AndroidViewModel里面有一個(gè)Application的屬性。
onCleared在Fragment和Activity的onDestory方法中通過ViewModelStore的clear調(diào)用。

public abstract class ViewModel {
    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}
public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}

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

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