ViewModel簡(jiǎn)單使用與解析

在開始說ViewModel之前我們先來一些我們經(jīng)常要考慮問題:
1.Activity屏幕旋轉(zhuǎn)怎么處理保留和處理數(shù)據(jù)昆码?通過onSaveInstanceState鳍鸵?那如果數(shù)據(jù)比較大呢楞艾?
2.如果Acntivity/Fragment持有的后臺(tái)線程在未結(jié)束之前我們按了返回,任務(wù)結(jié)束后操作UI是不是各種NullPointerException?
3.如果我們正在執(zhí)行一個(gè)很耗時(shí)的任務(wù)历造,我們?cè)鯓幽茏龅皆谛D(zhuǎn)屏幕的時(shí)候不中斷這個(gè)任務(wù)而后能返回正解的結(jié)果呢直秆?

以上種種跟生命周期相關(guān)的問題都是比較常見又不得不面對(duì)的,那有沒有很優(yōu)雅的處理方式呢巧骚?
有赊颠,就是ViewModel

ViewModel官方介紹:

ViewModel類旨在以生命周期意識(shí)的方式存儲(chǔ)和管理與UI相關(guān)的數(shù)據(jù)。
ViewModel類允許數(shù)據(jù)在配置更改(例如屏幕旋轉(zhuǎn))后繼續(xù)存在劈彪。

我們先來看下應(yīng)用場(chǎng)景

我們?cè)谪Q屏狀態(tài)下啟動(dòng)了一個(gè)耗時(shí)任務(wù)竣蹦,然后翻轉(zhuǎn)手機(jī)到橫屏狀態(tài)

橫豎屏切換

通過圖片我們看到在切換到橫屏?xí)r耗時(shí)任務(wù)并沒有中斷,在Activity重新創(chuàng)建后還能收到它返回的數(shù)據(jù)沧奴。我們來看下這過程Activity生命周期的變化:


image.png

通過圖片我們看到Activity確實(shí)已經(jīng)Destroy然后又重新Cread了痘括。然后如果我們直接按返回鍵退出這個(gè)Activity


image.png

你會(huì)看到Activity被Destroy的同時(shí),ViewModel也跟著Clear了滔吠。
說明我們的ViewModel不僅僅能保存數(shù)據(jù)纲菌,能還保留任務(wù),而且對(duì)生命周期還是可感知的疮绷。

那ViewModel是怎么做到這些的呢翰舌?我們先從簡(jiǎn)單的使用開始:

導(dǎo)入ViewModel:

//androidx的版本,包含ViewModel和LiveData
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
//非androidx版本
implementation "android.arch.lifecycle:extensions:1.1.1"

創(chuàng)建一個(gè)繼承自ViewModel的類:

public class MViewModel extends ViewModel

獲取一個(gè)ViewModel實(shí)例:

//在Activity中
mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
//在Fragment中,傳this是獲取和此Fragment綁定的ViewModel,
//傳getActivity則獲取到的是跟activity中的是同一個(gè)ViewModel
mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
mViewModel = ViewModelProviders.of(getActivity).get(MViewModel.class);

我們看下完整的代碼

自定義MViewModel(提前用到了LiveData可以先不用管冬骚,后面會(huì)講到)

public class MViewModel extends ViewModel {

    MutableLiveData<String> mString;
    MutableLiveData<String> msgString;


    public MutableLiveData<String> getString(){
        if(mString==null){
            mString=new MutableLiveData<>();
        }
        return mString;
    }

    public MutableLiveData<String> getMsgString(){
        if(msgString==null){
            msgString=new MutableLiveData<>();
        }
        return msgString;
    }

    public void startTask(){
        new Thread(){
            @Override
            public void run() {
                //請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)椅贱、數(shù)據(jù)庫(kù)懂算、加載大圖等。
                //如果在Activity轉(zhuǎn)屏的時(shí)候取消這些任務(wù)庇麦,那恢復(fù)的時(shí)候就要重新加載犯犁,勢(shì)必浪費(fèi)資源
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //此處用的是LiveData,如果我們不用LiveData可能是通過EventBus之類的把數(shù)據(jù)傳遞出去的
                mString.postValue("我是來自3秒后的數(shù)據(jù)");
                super.run();
            }
        }.start();
    }


    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
        //做一些數(shù)據(jù)清理工作
        Log.e("MViewModel","onCleared");
    }

}

MainActivity中獲取了一個(gè)ViewModel

public class MainActivity extends AppCompatActivity {

    private MViewModel mViewModel;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView=findViewById(R.id.desc);
        mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
        
       //訂閱數(shù)據(jù)變化,可以暫時(shí)理解為EventBus的消息訂閱
        mViewModel.getString().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.e("MainActivity", "耗時(shí)任務(wù)結(jié)束返回?cái)?shù)據(jù)");
                mTextView.setText(s);
            }
        });

        mViewModel.getMsgString().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                Log.e("MainActivity", "Fragment1發(fā)過來的數(shù)據(jù)");
                mTextView.setText(s);
            }
        });

        getSupportFragmentManager().beginTransaction().replace(R.id.container, new TestFragment(),
                TestFragment.class.getName()).commit();
        getSupportFragmentManager().beginTransaction().replace(R.id.container2, new TestFragment2(),
                TestFragment.class.getName()).commit();

        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("MainActivity", "開始耗時(shí)任務(wù)");
                mViewModel.startTask();
            }
        });


    }
}

為了說明ViewModel的作用域女器,我們?cè)俳ㄒ粋€(gè)TestFragment

public class TestFragment extends Fragment {

    private static final String TAG = "TestFragment";

    private View view;
    private MViewModel mMViewModel;
    private Button mBtn;
    private TextView mDesc;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.fragment_test, container, false);
        mBtn = view.findViewById(R.id.btn);
        mDesc = view.findViewById(R.id.desc);
        return view;

    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //這里傳入了getActivity(),返回的是跟Activity同一個(gè)ViewModel.
        mMViewModel = ViewModelProviders.of(getActivity()).get(MViewModel.class);
        mMViewModel.getString().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                mDesc.setText(s);
            }
        });

        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mMViewModel.getMsgString().setValue("Fragment1 發(fā)送的數(shù)據(jù)");
            }
        });
    }

}

通過上面的代碼你會(huì)發(fā)現(xiàn)ViewModel使用起來非常的簡(jiǎn)單酸役,只需要繼承ViewModel即可,其余的生命周期之類的就完全不用我們考慮驾胆,它自己就能處理涣澡。除此之外同一Activity內(nèi)的Fragment還能不通過Activity直接進(jìn)行數(shù)據(jù)交互和共享,實(shí)現(xiàn)了真正意義上的解耦丧诺。

原理解析

1創(chuàng)建過程解析

1.1 ViewModelProviders.of()

ViewModelProviders 類提供了4個(gè)靜態(tài)工廠方法 of() 創(chuàng)建新的 ViewModelProvider 對(duì)象入桂。

    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) {
        Application application = checkApplication(activity);
        if (factory == null) {
            //默認(rèn)的factory
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }

這里主要是ViewModelProvider、Fragemtn/Activity下的ViewModelStrore驳阎、Factory(從上你可以看出默認(rèn)都是ViewModelProvider.AndroidViewModelFactory)

1.2 activity.getViewModelStore()和fragment.getViewModelStore()

      //ViewModelStoreOwner
      public interface ViewModelStoreOwner {
          ViewModelStore getViewModelStore();
      }

      //activity實(shí)現(xiàn)ViewModelStoreOwner
     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) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                //需要注意的是這里抗愁,優(yōu)先從NonConfigurationInstances中取出,這就是為什么Activity在轉(zhuǎn)屏重建之后還能拿到原來的ViewModel的原因呵晚,后面我們看是怎么存的
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    //fragment實(shí)現(xiàn)ViewModelStoreOwner
    public ViewModelStore getViewModelStore() {
        if (getContext() == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }

1.3 ViewModelStrore

我們看到上面的方法返回的都是一個(gè)ViewModelStore 對(duì)象蜘腌,并且是Fragemnt/Activity下的一個(gè)成員變量。ViewModelStore 類中維護(hù)一個(gè) Map<String, ViewModel> 對(duì)象存儲(chǔ)已創(chuàng)建的 ViewModel 對(duì)象饵隙,說明一個(gè)Activity或者Fragment下可以保存多個(gè)不同的ViewModel撮珠,并提供 put() 和 get() 方法。

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

1.4 ViewModelProvider.Factory

Factory 接口定義了一個(gè)創(chuàng)建 ViewModel 的接口 create()金矛,ViewModelProvider 在需要時(shí)調(diào)用該方法新建 ViewModel 對(duì)象芯急。

    public interface Factory {
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

Android 已經(jīng)內(nèi)置了2個(gè) Factory 實(shí)現(xiàn)類,分別是:

  • AndroidViewModelFactory 實(shí)現(xiàn)類驶俊,可以創(chuàng)建 ViewModel 和 AndroidViewModel 子類對(duì)象娶耍。
  • NewInstanceFactory 類,只可以創(chuàng)建 ViewModel 子類對(duì)象饼酿。
    它們的實(shí)現(xiàn)都是通過反射機(jī)制調(diào)用 ViewModel 子類的構(gòu)造方法創(chuàng)建對(duì)象榕酒。
public static class NewInstanceFactory implements Factory {
    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        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 繼承 NewInstanceFactory 類,是個(gè)單例嗜湃,支持創(chuàng)建 AndroidViewModel 子類對(duì)象奈应。

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    public static AndroidViewModelFactory getInstance(Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    public AndroidViewModelFactory(Application application) {
        mApplication = application;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            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.5 ViewModelProvider

ViewModelProviders.of()返回的是一個(gè)ViewModelProvider對(duì)象,這個(gè)類下面就兩個(gè)字段

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

通過上面的解析我們知道Factory是創(chuàng)建ViewModel的工廠购披,ViewModelStore是用來存儲(chǔ)ViewModel的,所以我們來看下其中的get方法

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

至此肩榕,一個(gè)ViewModel便通過ViewModelProviders.of(activity/fragment).get()創(chuàng)建完成刚陡。

2生命周期和保存活過程解析

ViewModel生命周期.png

從上圖可知ViewModel跟實(shí)例化時(shí)傳入的Activity/Fragment的生命周期是保持一致的惩妇,但是從文章一開始我們就說到在屏幕旋轉(zhuǎn)的時(shí)候Acticity已經(jīng)被Destroy掉的情況下ViewModel卻依然存活并正常執(zhí)行其內(nèi)部的任務(wù)。原因就在于Activity的Configuration Changes筐乳。

2.1 Configuration Change概述

Configuration 這個(gè)類描述了設(shè)備的所有配置信息歌殃,這些配置信息會(huì)影響到應(yīng)用程序檢索的資源。包括了用戶指定的選項(xiàng)(locale和scaling)也包括設(shè)備本身配置(例如input modes蝙云,screen size and screen orientation).可以在該類里查看所有影響Configuration Change 的屬性氓皱。

橫豎屏切換是我們最常見的影響配置變化的因素,還有很多其他影響配置的因素有語言的更改(例如中英文切換)勃刨、鍵盤的可用性(這個(gè)沒理解)等

常見的引發(fā)Configuration Change的屬性:
橫豎屏切換:android:configChanges="orientation"
鍵盤可用性:android:configChanges="keyboardHidden"
屏幕大小變化:android:configChanges="screenSize"
語言的更改:android:configChanges="locale"

在程序運(yùn)行時(shí)波材,如果發(fā)生Configuration Change會(huì)導(dǎo)致當(dāng)前的Activity被銷毀并重新創(chuàng)建 ,即先調(diào)用onDestroy緊接著調(diào)用onCreate()方法身隐。 重建的目的是為了讓應(yīng)用程序通過自動(dòng)加載可替代資源來適應(yīng)新的配置廷区。

2.2 ViewModelStore的存取

當(dāng)設(shè)備發(fā)生Configuration Change之后,Activity在Destroy之前會(huì)調(diào)用onRetainNonConfigurationInstance方法贾铝,F(xiàn)ragmentActivity#onRetainNonConfigurationInstance()源碼如下

   public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        //這地方是調(diào)用Fragment的處理方法隙轻,進(jìn)去之后也是類似的處理方式
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

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

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        //mViewModelStore將ViewModelStrore保存
        nci.viewModelStore = mViewModelStore;
        nci.fragments = fragments;
        return nci;
    }

我們可以看到此方法將ViewModelStore對(duì)象保存了下來,然后在onCreate的時(shí)候取回

protected void onCreate(Bundle savedInstanceState) {
        //...
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null && nc.viewModelStore != null && mViewModelStore == null) {
            mViewModelStore = nc.viewModelStore;
        }
        //...
}

此外回到我們之前說的FragmentActivity#getViewModelStore你會(huì)發(fā)現(xiàn)也做了相應(yīng)的取出操作

public ViewModelStore getViewModelStore() {
       //省略 ...
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

2.3 銷毀過程

Configuration Change的過程不銷毀垢揩,但是從文章開始我們發(fā)現(xiàn)當(dāng)我們按返回鍵主動(dòng)結(jié)束這個(gè)Activity的時(shí)候ViewModel也跟著執(zhí)行onClear()了玖绿。我們來看下系統(tǒng)是如何區(qū)分的:
FragmentActivity#onDestroy

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

FragmentActivity#onDestroy

public void onDestroy() {
        mCalled = true;
        FragmentActivity activity = getActivity();
        boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
        if (mViewModelStore != null && !isChangingConfigurations) {
            mViewModelStore.clear();
        }
    }

由此我們可以看出FragmentActivity跟Fragment的onDestroy方法中都對(duì)Configuration Change做了相應(yīng)的判斷。

最后的總結(jié)

  • 通過 ViewModelProviders 創(chuàng)建 ViewModelProvider 對(duì)象叁巨,調(diào)用該對(duì)象的 get() 方法獲取 ViewModel 對(duì)象镰矿。 當(dāng) ViewModelStore 里不存在想要的對(duì)象,ViewModelProvider 會(huì)使用 Factory 新建一個(gè)對(duì)象并存放到 ViewModelStore 里俘种。
  • 當(dāng)發(fā)生 發(fā)生 Configuration Changes 時(shí)秤标,F(xiàn)ragmentActivity 利用 getLastNonConfigurationInstance()、onRetainNonConfigurationInstance() 方法實(shí)現(xiàn) -ViewModelStore 的保留與恢復(fù)宙刘,進(jìn)而實(shí)現(xiàn) ViewModel 對(duì)象的辈越活。
  • 當(dāng) FragmentActivity 和 Fragment 被銷毀時(shí)悬包,會(huì)根據(jù)是否發(fā)生 Configuration Changes 來決定是否銷毀 ViewModel衙猪。
  • 最重要的一點(diǎn)是在發(fā)生 Configuration Changes時(shí),ViewModel的存活時(shí)間是會(huì)比Activity/Fragment的存活時(shí)間要長(zhǎng)布近,所以我們不能在ViewModel中持有Activity/Fragment引用垫释,否則會(huì)產(chǎn)生泄漏。所以我們不能通過接口的形式(MVP)將數(shù)據(jù)返回給Activity/Fragment使用撑瞧,為此官方給出的方案便是結(jié)合LiveData使用棵譬,我們將在下一篇文章中講到。
  • 如果ViewModel中要使用到Application可以通過繼承AndroidViewModel预伺,也是通過ViewModelProviders.of(this).get(MViewModel.class)方式實(shí)例化

示例源碼地址

下一篇文章LiveData使用與解析

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末订咸,一起剝皮案震驚了整個(gè)濱河市曼尊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脏嚷,老刑警劉巖骆撇,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異父叙,居然都是意外死亡神郊,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門趾唱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涌乳,“玉大人,你說我怎么就攤上這事鲸匿∫常” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵带欢,是天一觀的道長(zhǎng)运授。 經(jīng)常有香客問我,道長(zhǎng)乔煞,這世上最難降的妖魔是什么吁朦? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮渡贾,結(jié)果婚禮上逗宜,老公的妹妹穿的比我還像新娘。我一直安慰自己空骚,他們只是感情好纺讲,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著囤屹,像睡著了一般熬甚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上肋坚,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天乡括,我揣著相機(jī)與錄音,去河邊找鬼智厌。 笑死诲泌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铣鹏。 我是一名探鬼主播敷扫,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吝沫!你這毒婦竟也來了呻澜?” 一聲冷哼從身側(cè)響起递礼,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤惨险,失蹤者是張志新(化名)和其女友劉穎羹幸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辫愉,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡栅受,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了恭朗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屏镊。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖痰腮,靈堂內(nèi)的尸體忽然破棺而出而芥,到底是詐尸還是另有隱情,我是刑警寧澤膀值,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布棍丐,位于F島的核電站,受9級(jí)特大地震影響沧踏,放射性物質(zhì)發(fā)生泄漏歌逢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一翘狱、第九天 我趴在偏房一處隱蔽的房頂上張望秘案。 院中可真熱鬧,春花似錦潦匈、人聲如沸阱高。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赤惊。三九已至,卻和暖如春寒屯,著一層夾襖步出監(jiān)牢的瞬間荐捻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工寡夹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留处面,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓菩掏,卻偏偏與公主長(zhǎng)得像魂角,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子智绸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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