Android應用模板之MVP在塔, MVVM模式

應用模板代碼地址:https://github.com/thfhongfeng/AndroidAppTemplate

其實所謂架構(gòu)模式言秸,無非是一個通過接口化實現(xiàn)代碼解耦分層的過程吟秩。而接口的實質(zhì)就是一個輸入輸出的規(guī)范仆百。這個規(guī)范成為各個分層代碼的溝通橋梁,使得各個分層只關心自己要做的事堵腹,即告訴我你需要我提供什么(輸出)炸站,并且給我相關的證明(輸入),至于你要用來做什么疚顷,我不關心旱易。

MVP模式

先上通用圖

MVP

View發(fā)送指令給Presenter,Presenter獲取指令后腿堤,調(diào)用響應的Model進行業(yè)務邏輯處理阀坏,然后返回數(shù)據(jù)給Presenter,Presenter根據(jù)Model返回的數(shù)據(jù)再來調(diào)用相應的View笆檀。

View:我的服務對象是用戶忌堂,用戶告訴我要給它提供哪些顯示和響應服務,我給出相應的服務误债;
Presenter:我的服務對象是View浸船,View告訴我哪些UI界面需要顯示和響應妄迁,我根據(jù)它提供的內(nèi)容適時的給它提供這些View所需的數(shù)據(jù)內(nèi)容寝蹈;
Model:我的服務對象是Presenter,Presenter告訴我需要哪些業(yè)務數(shù)據(jù)登淘,我根據(jù)它提供的內(nèi)容適時給它提供業(yè)務數(shù)據(jù)箫老。


MVP基礎代碼目錄結(jié)構(gòu)

上圖是mvp架構(gòu)的各個基類,業(yè)務模塊通過繼承這些基類來實現(xiàn)mvp架構(gòu)黔州。


IContract接口集:規(guī)范了Ui(View層)接口和Presenter(Presenter層)接口耍鬓,這里沒有做model層的接口化,實際上這里的接口定義并不是說一定每層都必須要流妻,而應該是根據(jù)自己的項目來靈活處理牲蜀。所謂的架構(gòu)其實只是給了一個分層的規(guī)范,好比一個公司的員工組織架構(gòu):管理做規(guī)劃绅这,技術人員實現(xiàn)產(chǎn)品涣达,客戶經(jīng)理推廣產(chǎn)品,而助理就負責這些崗位的溝通工作。但有時候公司規(guī)模小或者公司性質(zhì)原因度苔,可能就不需要助理而是采用直接溝通的方式匆篓。所以這里接口化的處理是根據(jù)自己項目的性質(zhì)來決定的,選擇適合自己項目的接口化程度能減少項目的開發(fā)成本寇窑。

public interface IContract {
    interface Ui {
        Activity getContextActivity();

        void setLoadingUiVisibility(boolean visibility);
    }

    interface Presenter {

    }
}

IContract只是定義了基本的通用的一些接口鸦概,具體到業(yè)務模塊,通過繼承添加自己的業(yè)務接口甩骏。


IModelAsyncResponse數(shù)據(jù)響應接口:用于Presenter層響應Model層處理結(jié)果窗市。即告訴Model層,我需要T對象横漏。

public interface IModelAsyncResponse<T> {
    void onResponse(T t);

    boolean onFail(Exception e);

    void onCancel();
}



Presenter基類:規(guī)范與UI(View層)的綁定與解綁谨设,定義了一些通用的方法。業(yè)務模塊中通過繼承該類實現(xiàn)Presenter層缎浇。

public abstract class Presenter<V extends IContract.Ui> {
    protected final String TAG = LogUtils.makeLogTag(this.getClass());
    private UiState mUiState = UiState.UI_STATE_UNDEFINE;

    protected boolean mIsLoadProcessing;

    /**
     * UI的弱引用
     */
    private WeakReference<V> mUiRef;

    /**
     * 關聯(lián)UI
     *
     * @param ui
     */
    @CallSuper
    public void attachUi(V ui) {
        mUiRef = new WeakReference<V>(ui);
    }

    /**
     * 解除關聯(lián)
     */
    @CallSuper
    public void detachUi() {
        if (mUiRef != null) {
            onUiState(UiState.UI_STATE_ON_DETACH);
            mUiRef.clear();
        }
    }

    /**
     * 得到UI
     *
     * @return
     */
    public V getUi() {
        return mUiRef.get();
    }

    /**
     * 得到Application
     *
     * @return
     */
    public Application getApplication() {
        return AppUtils.getApplication();
    }

    /**
     * 得到Activity
     *
     * @return
     */
    public Activity getActivity() {
        return getUi().getContextActivity();
    }

    /**
     * 得到Context
     *
     * @return
     */
    public Context getContext() {
        return getUi().getContextActivity();
    }

    /**
     * 得到Intent
     *
     * @return
     */
    public Intent getIntent() {
        if (mUiRef.get() != null) {
            if (mUiRef.get() instanceof Activity) {
                return ((Activity) mUiRef.get()).getIntent();
            } else if (mUiRef.get() instanceof Fragment) {
                return ((Fragment) mUiRef.get()).getActivity().getIntent();
            } else if (mUiRef.get() instanceof android.support.v4.app.Fragment) {
                return ((android.support.v4.app.Fragment) mUiRef.get()).getActivity().getIntent();
            }
        }
        return new Intent();
    }

    public final boolean isUiAlive() {
        if (mUiRef.get() == null) {
            return false;
        }
        if (mUiRef.get() instanceof Activity) {
            return !((Activity) mUiRef.get()).isFinishing();
        }
        return true;
    }

    public final void finishUi() {
        if (mUiRef.get() != null)
            if (mUiRef.get() instanceof Activity) {
                ((Activity) mUiRef.get()).finish();
            } else if (mUiRef.get() instanceof Fragment) {
                ((Fragment) mUiRef.get()).getActivity().finish();
            } else if (mUiRef.get() instanceof android.support.v4.app.Fragment) {
                ((android.support.v4.app.Fragment) mUiRef.get()).getActivity().finish();
            }
    }

    public void setUiLoading(boolean uiLoading) {
        mIsLoadProcessing = uiLoading;
        if (!isUiAlive()) {
            return;
        }
        getUi().setLoadingUiVisibility(uiLoading);
    }

    /**
     * 用于分析傳入?yún)?shù)是否非法
     *
     * @return true表示非法扎拣, false表示合法
     */
    public boolean parseIntentData(@NonNull Bundle bundle) {
        return false;
    }

    /**
     * 用于分析傳入?yún)?shù)是否非法,在View init之后調(diào)用
     *
     * @return
     */
    public void afterViewInit() {

    }

    /**
     * UI狀態(tài)回調(diào)
     *
     * @param state UI_STATE_ON_CREATE,UI_STATE_ON_START,UI_STATE_ON_RESUME,UI_STATE_ON_PAUSE,
     *              UI_STATE_ON_STOP,UI_STATE_ON_DETACH
     */
    @CallSuper
    public void onUiState(UiState state) {
        mUiState = state;
    }

    public UiState getUiState() {
        return mUiState;
    }

    public void showShortToast(String message) {
        if (isUiAlive()) {
            Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
        }
    }

    public void showShortToast(@StringRes int resId) {
        if (isUiAlive()) {
            Toast.makeText(getContext(), resId, Toast.LENGTH_SHORT).show();
        }
    }

    public void showLongToast(String message) {
        if (isUiAlive()) {
            Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
        }
    }

    public void showLongToast(@StringRes int resId) {
        if (isUiAlive()) {
            Toast.makeText(getContext(), resId, Toast.LENGTH_LONG).show();
        }
    }

    public enum UiState {
        UI_STATE_UNDEFINE,
        UI_STATE_ON_CREATE,
        UI_STATE_ON_START,
        UI_STATE_ON_RESUME,
        UI_STATE_ON_PAUSE,
        UI_STATE_ON_STOP,
        UI_STATE_ON_DETACH
    }
}



MvpActivity基類:規(guī)范與Presenter(Presenter層)的綁定與解綁素跺,定義了一些通用的方法二蓝。業(yè)務模塊中通過繼承該類實現(xiàn)View層。
注意:該類繼承自筆者自定義的Activity類指厌,而非原生Activity類刊愚。自定義的Activity類實現(xiàn)的內(nèi)容是與架構(gòu)無關的,所以無需擔心踩验。如果直接繼承原生Activity類鸥诽,只需將beforeInitOnCreate, parseIntentData箕憾,afterInit三個方法的內(nèi)容直接按順序移到原生的Activity的onCreate方法中即可牡借。

public abstract class MvpActivity<V extends IContract.Ui, P extends Presenter<V>>
        extends Activity implements IContract.Ui {
    protected P mPresenter;

    @CallSuper
    @Override
    protected void beforeInitOnCreate(@Nullable Bundle savedInstanceState) {
        // 創(chuàng)建并綁定presenter
        mPresenter = createPresenter();
        if (mPresenter == null) {
            Class presenterClazz;
            Type type = getClass().getGenericSuperclass();
            if (type instanceof ParameterizedType) {
                presenterClazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
                try {
                    mPresenter = (P) presenterClazz.newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        if (mPresenter != null) {
            mPresenter.attachUi((V) this);
        } else {
            throw new RuntimeException("must initialize a presenter!");
        }
    }

    protected P createPresenter() {
        return null;
    }

    @Override
    protected final boolean parseIntentData() {
        if (mPresenter != null) {
            return mPresenter.parseIntentData(getIntent().getExtras() == null ?
                    new Bundle() : getIntent().getExtras());
        }
        return false;
    }

    @CallSuper
    @Override
    protected void afterInit() {
        if (mPresenter != null) {
            mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_CREATE);
            mPresenter.afterViewInit();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mPresenter != null) {
            mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_START);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mPresenter != null) {
            mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_RESUME);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mPresenter != null) {
            mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_PAUSE);
        }
    }

    @Override
    protected void onStop() {
        if (mPresenter != null) {
            mPresenter.onUiState(Presenter.UiState.UI_STATE_ON_STOP);
        }
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除綁定
        if (mPresenter != null) {
            mPresenter.detachUi();
        }
    }

    @Override
    public android.app.Activity getContextActivity() {
        return this;
    }
}


MVVM模式

先上通用圖


MVVM

MVVM是Model-View-ViewModel的簡寫,即模型-視圖-視圖模型袭异∧屏【模型】指的是后端傳遞的數(shù)據(jù),【視圖】指的是所看到的頁面御铃,【視圖模型】mvvm模式的核心碴里,它是連接view和model的橋梁。


MVVM模式相對于MVP模式上真,其實就是將接口化溝通變成了實體化(模型化)溝通咬腋。
簡單來說就是視圖和業(yè)務邏輯的溝通方式不再是命令響應(接口化)的方式,而是具體化的實體模型睡互。即VM提供給View的不再是響應型的數(shù)據(jù)根竿,而是顯示整個View的實體模型溜徙。View響應的是實體模型的改變,而不再是接口的回調(diào)犀填。這樣不僅簡化原先MVP模式下Presenter層與View層的溝通成本蠢壹,還將View的顯示更加組件化,VM與View的綁定可以形成更微小的顯示窗口九巡,以顯示窗口的聚合重用來復用代碼图贸。
其實從命名就可以看出了,VM即View-model冕广,也就是View的model疏日。

實現(xiàn)MVVM的兩個重要的技術:

  1. DataBinding:完成視圖實體模型與View綁定的利器。
  2. LiveData:完成視圖實體模型的狀態(tài)化撒汉,讓業(yè)務響應與視圖實體模型的狀態(tài)關聯(lián)起來沟优。



IModelAsyncResponse數(shù)據(jù)響應接口:用于VM層響應Model層處理結(jié)果。即告訴Model層睬辐,我需要T對象挠阁。
這部分與MVP類似


ViewModel視圖模型:定義了一些基本的顯示模型實體。業(yè)務模塊中通過繼承該類實現(xiàn)VM層溯饵。

public abstract class ViewModel extends android.arch.lifecycle.ViewModel {
    protected final String TAG = LogUtils.makeLogTag(this.getClass());
    private UiState mUiState = UiState.UI_STATE_UNDEFINE;

    /**
     * UI狀態(tài)回調(diào)
     *
     * @param state UI_STATE_ON_INIT,UI_STATE_ON_RESUME,UI_STATE_ON_PAUSE,
     *              UI_STATE_ON_STOP,UI_STATE_ON_DETACH
     */
    @CallSuper
    public void onUiState(UiState state) {
        mUiState = state;
    }

    public UiState getUiState() {
        return mUiState;
    }

    /**
     * 用于分析傳入?yún)?shù)是否非法侵俗,在View init之前調(diào)用
     *
     * @return true表示非法冻河, false表示合法
     */
    public boolean parseIntentData(@NonNull Bundle bundle) {
        return false;
    }

    /**
     * 在View init之后調(diào)用
     *
     * @return
     */
    public void afterViewInit() {

    }

    public void onCleared() {

    }

    MutableLiveData<Integer> observeSyncLiveData = new MutableLiveData<>();

    public MutableLiveData<Integer> getObserveSyncLiveDataData() {
        return observeSyncLiveData;
    }
    
    /**
     * 用于LiveData是其它功能操作返回(不是在VM中初始化賦值)的情況谁帕,
     * 在LiveData返回時通過調(diào)用setSyncLiveDataTag來告訴UI開始綁定Observer,
     * UI中的必須實現(xiàn)observeSyncLiveData破婆,同時所有其它功能操作返回的LiveData只能在此方法中進行綁定Observer
     *
     * @param liveDataObjTag 用來標識對應的LiveData(由調(diào)用者自己確定)
     */
    public void setSyncLiveDataTag(int liveDataObjTag) {
        observeSyncLiveData.setValue(liveDataObjTag);
    }

    // 重置UI
    MutableLiveData<Boolean> resetUiData = new MutableLiveData<>();

    public MutableLiveData<Boolean> getResetUiData() {
        return resetUiData;
    }

    public void resetUi() {
        resetUiData.setValue(true);
    }

    // 結(jié)束UI
    MutableLiveData<Boolean> finishData = new MutableLiveData<>();

    public MutableLiveData<Boolean> getFinishData() {
        return finishData;
    }

    public void finishUi() {
        finishData.setValue(true);
    }

    // 加載中ui顯示狀態(tài)
    MutableLiveData<Boolean> uiLoadingData = new MutableLiveData<>();

    public MutableLiveData<Boolean> getUiLoadingData() {
        return uiLoadingData;
    }

    public boolean isUiLoading() {
        return uiLoadingData.getValue();
    }

    public void setUiLoading(boolean isLoading) {
        uiLoadingData.setValue(isLoading);
    }

    // Toast ui顯示
    MutableLiveData<String> toastMsgData = new MutableLiveData<>();

    public MutableLiveData<String> getToastMsgData() {
        return toastMsgData;
    }

    public void setToastMsg(String msg) {
        toastMsgData.setValue(msg);
    }

    MutableLiveData<Integer> toastResIdData = new MutableLiveData<>();

    public MutableLiveData<Integer> getToastResIdData() {
        return toastResIdData;
    }

    public void setToastResId(@StringRes Integer id) {
        toastResIdData.setValue(id);
    }
}



MvvmActivity基類:規(guī)范與VM的綁定與解綁啄巧,定義了一些通用的方法寻歧。業(yè)務模塊中通過繼承該類實現(xiàn)View層。
注意:該類繼承自筆者自定義的Activity類秩仆,而非原生Activity類码泛。自定義的Activity類實現(xiàn)的內(nèi)容是與架構(gòu)無關的,所以無需擔心逗概。如果直接繼承原生Activity類弟晚,只需將beforeInitOnCreate忘衍,setContentView逾苫, findViewOnCreate,parseIntentData枚钓,afterInit五個方法的內(nèi)容直接按順序移到原生的Activity的onCreate方法中即可铅搓。

public abstract class MvvmActivity<T extends ViewDataBinding, VM extends ViewModel> extends Activity {
    protected T mBinding;
    protected VM mViewModel;

    private Observer<Integer> mSyncLiveDataObserver = new Observer<Integer>() {
        @Override
        public void onChanged(@Nullable Integer tag) {
            observeSyncLiveData(tag);
        }
    };

    private Observer<Boolean> mResetUiDataObserver = new Observer<Boolean>() {
        @Override
        public void onChanged(@Nullable Boolean aBoolean) {
            if (aBoolean) {
                finish();
                startActivity(getIntent());
            }
        }
    };

    private Observer<Boolean> mFinishDataObserver = new Observer<Boolean>() {
        @Override
        public void onChanged(@Nullable Boolean aBoolean) {
            if (aBoolean) {
                finish();
            }
        }
    };

    private Observer<Boolean> mUiLoadingDataObserver = new Observer<Boolean>() {
        @Override
        public void onChanged(@Nullable Boolean aBoolean) {
            setLoadingUiVisibility(aBoolean);
        }
    };

    private Observer<String> mToastMsgDataObserver = new Observer<String>() {
        @Override
        public void onChanged(@Nullable String msg) {
            showShortToast(msg);
        }
    };

    private Observer<Integer> mToastResIdDataObserver = new Observer<Integer>() {
        @Override
        public void onChanged(@Nullable Integer resId) {
            showShortToast(resId);
        }
    };

    @CallSuper
    @Override
    protected void beforeInitOnCreate(@Nullable Bundle savedInstanceState) {
        // 創(chuàng)建ViewModel{
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Class presenterClazz = (Class) ((ParameterizedType) type).getActualTypeArguments()[1];
            mViewModel = (VM) ViewModelProviders.of(this).get(presenterClazz);
            mViewModel.getUiLoadingData().setValue(false);
        }
        mViewModel.getObserveSyncLiveDataData().observe(this, mSyncLiveDataObserver);
        mViewModel.getResetUiData().observe(this, mResetUiDataObserver);
        mViewModel.getFinishData().observe(this, mFinishDataObserver);
        mViewModel.getUiLoadingData().observe(this, mUiLoadingDataObserver);
        mViewModel.getToastMsgData().observe(this, mToastMsgDataObserver);
        mViewModel.getToastResIdData().observe(this, mToastResIdDataObserver);
        observeInitLiveData();
    }

    /**
     * 用于在VM中初始化賦值的LiveData的進行監(jiān)聽觀察
     * 此方法在Activity onCreate的時候自動調(diào)用
     * (注意區(qū)別于observeSyncLiveData)
     * observeInitLiveData:用于在VM中初始化的LiveData的進行監(jiān)聽觀察。
     * observeSyncLiveData :用于對不是在VM中初始化賦值的LiveData的進行監(jiān)聽觀察搀捷,需要在VM中主動調(diào)用setSyncLiveDataTag星掰。
     */
    public abstract void observeInitLiveData();

    protected void setContentView(Bundle savedInstanceState) {
        mBinding = DataBindingUtil.setContentView(this, getActivityLayoutResId());
    }

    @Override
    protected final void findViewOnCreate() {

    }

    @CallSuper
    @Override
    protected final boolean parseIntentData() {
        if (mViewModel != null) {
            return mViewModel.parseIntentData(getIntent().getExtras() == null ?
                    new Bundle() : getIntent().getExtras());
        }
        return false;
    }

    @CallSuper
    @Override
    protected void afterInit() {
        if (mViewModel != null) {
            mViewModel.onUiState(UiState.UI_STATE_ON_INIT);
        }
        if (mViewModel != null) {
            mViewModel.afterViewInit();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mViewModel != null) {
            mViewModel.onUiState(UiState.UI_STATE_ON_RESUME);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mViewModel != null) {
            mViewModel.onUiState(UiState.UI_STATE_ON_PAUSE);
        }
    }

    @Override
    protected void onStop() {
        if (mViewModel != null) {
            mViewModel.onUiState(UiState.UI_STATE_ON_STOP);
        }
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //解除綁定
        if (mViewModel != null) {
            mViewModel.onUiState(UiState.UI_STATE_ON_DETACH);
        }
    }

    public void setLoadingUiVisibility(boolean visibility) {

    }

    /**
     * 對不是在VM中初始化賦值的LiveData的進行監(jiān)聽觀察(通過其它功能返回的LiveData)多望。
     * 此方法的調(diào)用需要在VM獲取到LiveData后中主動調(diào)用setSyncLiveDataTag方法。
     * (注意區(qū)別于observeInitLiveData)
     * observeInitLiveData:用于在VM中初始化的LiveData的進行監(jiān)聽觀察氢烘。
     * observeSyncLiveData :用于對不是在VM中初始化賦值的LiveData的進行監(jiān)聽觀察怀偷,需要在VM中主動調(diào)用setSyncLiveDataTag。
     *
     * @param liveDataObjTag 用來標識對應的LiveData(由調(diào)用者自己標識)
     */
    public abstract void observeSyncLiveData(int liveDataObjTag);
}



各種模式只是規(guī)范化編碼方式的一種總結(jié)播玖,千萬不要為了模式而模式椎工,要根據(jù)自己的項目特性和規(guī)模,選擇合適自己的模式蜀踏。一般來說模式的開發(fā)是針對業(yè)務模塊的维蒙,不同的業(yè)務模塊根據(jù)自己的需求可以使用不同的模式來開發(fā)。
比如:
業(yè)務重用較多的模塊果覆,可以使用MVP模式颅痊。
視圖重用較多或者業(yè)務較復雜的模塊,可以使用MVVM模式局待。
簡單的過渡模塊斑响,直接Android的默認開發(fā)模式就可以了。
不過钳榨,一般的模塊開發(fā)恋捆,還是建議使用MVVM模式。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末重绷,一起剝皮案震驚了整個濱河市沸停,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌昭卓,老刑警劉巖愤钾,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異候醒,居然都是意外死亡能颁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門倒淫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伙菊,“玉大人,你說我怎么就攤上這事敌土【邓叮” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵返干,是天一觀的道長兴枯。 經(jīng)常有香客問我,道長矩欠,這世上最難降的妖魔是什么财剖? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任悠夯,我火速辦了婚禮,結(jié)果婚禮上躺坟,老公的妹妹穿的比我還像新娘沦补。我一直安慰自己,他們只是感情好咪橙,可當我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布策彤。 她就那樣靜靜地躺著,像睡著了一般匣摘。 火紅的嫁衣襯著肌膚如雪店诗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天音榜,我揣著相機與錄音庞瘸,去河邊找鬼。 笑死赠叼,一個胖子當著我的面吹牛擦囊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播嘴办,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼瞬场,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涧郊?” 一聲冷哼從身側(cè)響起贯被,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎妆艘,沒想到半個月后彤灶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡批旺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年幌陕,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汽煮。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡搏熄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出暇赤,到底是詐尸還是另有隱情心例,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布翎卓,位于F島的核電站契邀,受9級特大地震影響摆寄,放射性物質(zhì)發(fā)生泄漏失暴。R本人自食惡果不足惜坯门,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望逗扒。 院中可真熱鬧古戴,春花似錦、人聲如沸矩肩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽黍檩。三九已至叉袍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刽酱,已是汗流浹背喳逛。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留棵里,地道東北人润文。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像殿怜,于是被迫代替她去往敵國和親典蝌。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,592評論 2 353