淺談Android中MVP模式的運(yùn)用

演示效果:

MVP設(shè)計(jì)模式演示.gif

這里的演示效果是利用TabLayout+ViewPager+Fragment完成的空执,由于本篇文章不是講具體實(shí)現(xiàn)艰躺,所以和MVP思想不相關(guān)的具體實(shí)現(xiàn)代碼我會(huì)忽略一部分。

前言

MVP開發(fā)模式陷揪,算是老生常談的話題了,最近一段時(shí)間也比較熱門,網(wǎng)上相關(guān)的資料不少肠槽,這里就不再做重復(fù)的描述。
這里談一下個(gè)人的一些理解:


mvc模式.png

由于Android平臺(tái)的特殊性奢啥,從MVC的角度來講秸仙,XML布局文件作為視圖層(V),Activity作為控制層(C)桩盲,一些具體的業(yè)務(wù)操作寂纪,如讀寫數(shù)據(jù)庫,網(wǎng)絡(luò)訪問等作為業(yè)務(wù)邏輯層(M),單純的XML布局又不能滿足V層的需要捞蛋,例如我們需要對(duì)一個(gè)控件進(jìn)行動(dòng)態(tài)的隱藏和顯示孝冒,這點(diǎn)單純的XML是辦不到的,此時(shí)我們只能在Activity里去做實(shí)現(xiàn)了拟杉,這樣子下來庄涡,使得Activity既是V又是C,隨著業(yè)務(wù)的擴(kuò)展捣域,代碼邏輯也漸漸變得復(fù)雜起來啼染,加上MVC模式的設(shè)計(jì),Model層和View層又是直接交互的焕梅,導(dǎo)致Activity的職責(zé)會(huì)變得很重迹鹅,慢慢的變得臃腫不堪,不易擴(kuò)展贞言,更不要提測(cè)試了斜棚。

mvp模式.png

既然有問題的存在,自然就有解決問題的辦法该窗,此時(shí)MVP出現(xiàn)了弟蚀,在我看來MVP模式其實(shí)就是從并不標(biāo)準(zhǔn)的MVC模式演化而來的,它減輕了Activity的職責(zé)酗失,簡化了Activity的代碼义钉,把復(fù)雜的邏輯交給了P層來處理,較低了系統(tǒng)的耦合度规肴,更加方便了測(cè)試捶闸。

這樣的開發(fā)模式下來,也使得各層的職責(zé)更加單一了拖刃,V層只關(guān)心用戶的輸入請(qǐng)求動(dòng)作删壮,P層相當(dāng)于V層和M層的“信使”只關(guān)心轉(zhuǎn)發(fā)數(shù)據(jù),來自V層的請(qǐng)求數(shù)據(jù)兑牡,來自M層的響應(yīng)數(shù)據(jù)央碟,而M層則只需要關(guān)心的具體的業(yè)務(wù)邏輯操作即可。

言歸正傳

說了這么多均函,來點(diǎn)實(shí)戰(zhàn)吧亿虽,這里我演示一個(gè)比較常見的功能,列表的下拉刷新和上拉加載數(shù)據(jù)边酒。

所涉及的開源框架:
網(wǎng)絡(luò)加載框架:Retrofit
圖片加載框架:Picasso
響應(yīng)式編程框架:Rxjava
注解框架:ButterKnife
帶有下拉刷新经柴,上拉加載的RecyclerView:XRecyclerView
JSON數(shù)據(jù)的解析:Gson

數(shù)據(jù)來源

http://gank.io/api/data/%E7%A6%8F%E5%88%A9/10/1

這里的來源是來自GANK.IO的福利妹紙圖,然后利用GsonFormat生成JavaBean墩朦,代碼太長我就不貼了坯认。

關(guān)于基類

View層
/** * 基類View接口 
* Created by chenwei.li 
* Date: 2016-01-11
* Time: 00:22 
*/
public interface IBaseView {   
  void showLoadingDialog();  
  void cancelLoadingDialog();   
  void showErrorMsg(String errorMsg);
}

這個(gè)基礎(chǔ)View接口需要讓每個(gè)具體的業(yè)務(wù)View都去繼承實(shí)現(xiàn),里面封裝了一些常用的方法,例如我們?cè)诩虞d數(shù)據(jù)的時(shí)候都為了良好的用戶體驗(yàn)牛哺,都需要顯示進(jìn)度條陋气,或者彈出對(duì)話框來提醒用戶正在進(jìn)行,當(dāng)數(shù)據(jù)加載完畢都需要關(guān)閉這個(gè)提醒引润,當(dāng)加載出錯(cuò)的時(shí)候巩趁,需要提醒用戶出錯(cuò)信息。

/**
 * 基類Fragment
 * Created by chenwei.li
 * Date: 2016-01-03
 * Time: 23:52
 */
public abstract class BaseFragment extends Fragment implements IBaseFragment {

    //當(dāng)前Fragment視圖對(duì)象
    protected View mView;


    /**
     * Activity對(duì)象淳附,避免getActivity()出現(xiàn)null
     */
    protected Activity mActivity;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.mActivity = (Activity) context;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        /**
         * 綁定視圖
         */
        if (mView == null) {
            mView = inflater.inflate(bindLayout(), container, false);
        }
        /**
         * 依賴注入
         */
        ButterKnife.bind(this, mView);

        /**
         * 創(chuàng)建Fragment
         */
        createFragment(savedInstanceState);

        /**
         * 初始化視圖,默認(rèn)值
         */
        initView();
        /**
         * 獲取對(duì)應(yīng)數(shù)據(jù)(網(wǎng)絡(luò)议慰,數(shù)據(jù)庫)
         */
        getData();

        return mView;
    }



    @Override
    public void onDestroy() {
        super.onDestroy();
        /**
         * 解綁ButterKnife
         */
        ButterKnife.unbind(this);

    }
}
/**
 * 基類Fragment(懶加載)
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 09:15
 * Email: lichenwei.me@foxmail.com
 */
public abstract class BaseLazyFragment extends Fragment implements IBaseFragment {


    //當(dāng)前Fragment視圖對(duì)象
    protected View mView;

    /**
     * 是否加載布局
     */
    protected boolean isPrepare;

    /**
     * 是否顯示布局
     */
    protected boolean isVisiable;

    /**
     * 是否是第一次加載
     */
    protected boolean isFirst;


    /**
     * Activity對(duì)象,避免getActivity()出現(xiàn)null
     */
    protected Activity mActivity;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        this.mActivity = (Activity) context;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {


        /**
         * 綁定視圖
         */
        if (mView == null) {
            mView = inflater.inflate(bindLayout(), container, false);
            isPrepare = true;
            isFirst = true;
        }

        /**
         * 依賴注入
         */
        ButterKnife.bind(this, mView);

        /**
         * 創(chuàng)建Fragment
         */
        createFragment(savedInstanceState);

        /**
         * 初始化視圖,默認(rèn)值
         */
        initView();

        return mView;
    }

    /**
     * 判斷當(dāng)前Fragment是否已經(jīng)顯示
     *
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint()) {
            isVisiable = true;
            onVisible();
        } else {
            isVisiable = false;
        }
    }

    protected void onVisible() {
        lazy();
    }

    /**
     * 要進(jìn)行懶加載的數(shù)據(jù)
     */
    protected void lazy() {
        if (isPrepare && isVisiable && isFirst) {
            lazyLoadData();
            isFirst = false;
        }
    }

    /**
     * 需要進(jìn)行懶加載的數(shù)據(jù)
     */
    protected abstract void lazyLoadData();


    /**
     * 只需要空實(shí)現(xiàn)奴曙,所有的懶加載數(shù)據(jù)都在lazyLoadData里面執(zhí)行
     */
    @Override
    public void getData() {
    }
}


上面是基礎(chǔ)類BaseFragment的封裝别凹,實(shí)現(xiàn)了IBaseFragment接口里的方法,代碼并不復(fù)雜洽糟,大家根據(jù)注釋看下就可以理解了炉菲,這里因?yàn)槲业淖⑷肟蚣苡玫腂utterKnife,所以在創(chuàng)建Frgament的時(shí)候坤溃,我去綁定了View拍霜,當(dāng)Fragment銷毀的時(shí)候,去取消綁定薪介。
這里根據(jù)具體業(yè)務(wù)祠饺,我又多封裝了一個(gè)具有懶加載功能的Fragment,這個(gè)LazyBaseFragment和普通的Fragment區(qū)別在于汁政,當(dāng)我們使用ViewPager去嵌套Fragment的時(shí)候吠裆,由于ViewPaer的原因,默認(rèn)它會(huì)加載當(dāng)前頁面的左右各一個(gè)頁卡烂完,有時(shí)候用戶需要看當(dāng)前頁面并不執(zhí)行滑動(dòng)操作去看其他的,但是程序已經(jīng)加載了頁面诵棵,勢(shì)必會(huì)造成一些資源浪費(fèi)抠蚣,所以我根據(jù)setUserVisibleHint(boolean isVisibleToUser)和標(biāo)志位的判斷,來確定在頁面加載且用戶滑到當(dāng)前頁的時(shí)候才去加載數(shù)據(jù)的履澳。這點(diǎn)的想法起源來自簡書的APP嘶窄,大家看下圖就能明白:

簡書APP
簡書APP

很明顯,簡書也運(yùn)動(dòng)了Fragment的懶加載距贷,這里可能有朋友會(huì)說關(guān)于ViewPager里的setOffscreenPageLimit(int pgaeSize)方法柄冲,這個(gè)方法的作用是控制ViewPager保留當(dāng)前頁面的左右各幾個(gè)頁面的數(shù)據(jù)狀態(tài),而不讓它進(jìn)行滑動(dòng)銷毀忠蝗,也可以決定ViewPager一次性加載幾個(gè)頁卡现横,所以把它設(shè)置為0不就解決了,其實(shí)不是的,如果你用去看過ViewPager的源碼戒祠,你就會(huì)發(fā)現(xiàn)這樣的一段代碼:

    /**
     * Set the number of pages that should be retained to either side of the
     * current page in the view hierarchy in an idle state. Pages beyond this
     * limit will be recreated from the adapter when needed.
     *
     * <p>This is offered as an optimization. If you know in advance the number
     * of pages you will need to support or have lazy-loading mechanisms in place
     * on your pages, tweaking this setting can have benefits in perceived smoothness
     * of paging animations and interaction. If you have a small number of pages (3-4)
     * that you can keep active all at once, less time will be spent in layout for
     * newly created view subtrees as the user pages back and forth.</p>
     *
     * <p>You should keep this limit low, especially if your pages have complex layouts.
     * This setting defaults to 1.</p>
     *
     * @param limit How many pages will be kept offscreen in an idle state.
     */
    public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

而這里的

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

所以不管你是設(shè)置0骇两,或者更小的數(shù),默認(rèn)它都是為1的姜盈,也就是默認(rèn)加載3個(gè)頁卡低千,保留當(dāng)前頁卡的左右頁卡狀態(tài)。

Presenter層
/**
 * 基類Presenter接口
 * Create by: chenwei.li
 * Date: 2016-05-31
 * time: 14:25
 * Email: lichenwei.me@foxmail.com
 */
public interface IBasePresenter<V extends IBaseView> {

    /**
     * V層注入引用
     * @param mvpView
     */
    void attachView(V mvpView);

    /**
     * 銷毀V層引用
     */
    void detachView();

}

這里封裝了2個(gè)方法馏颂,用來使View層的Activity和Fragment和P層的生命狀態(tài)保持一致示血,當(dāng)頁面銷毀的時(shí)候,這次Context上下文的引用也就沒有理由存在了救拉,這里我們把它置為null难审,便于GC回收。

/**
 * 基類BasePresenter
 * Create by: chenwei.li
 * Date: 2016-05-31
 * time: 11:14
 * Email: lichenwei.me@foxmail.com
 */
public class BasePresenter<T extends IBaseView> implements IBasePresenter<T> {

    private T mMvpView;

    @Override
    public void attachView(T mvpView) {
        this.mMvpView = mvpView;
    }

    @Override
    public void detachView() {
        this.mMvpView = null;
    }

    public T getMvpView() {
        return mMvpView;
    }
}

基類Presenter近上,讓所有的P層繼承剔宪。

具體代碼

View層

/**
 * View層妹紙福利
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 22:06
 * Email: lichenwei.me@foxmail.com
 */
public interface IWealView extends IBaseView {

    void getWealList(int pageSize, int currentPage);

    void onRefreshData(List<WealEntity.ResultsBean> resultsBean);

    void onLoadMoreData(List<WealEntity.ResultsBean> resultsBean);

}

這里提供了操作接口,因?yàn)樵诒卷撁娴臉I(yè)務(wù)只是單純的加載列表數(shù)據(jù)壹无,刷新數(shù)據(jù)葱绒,所以這里定義了3個(gè)方法,用來滿足業(yè)務(wù)斗锭。

/**
 * 福利Fragment
 * Create by: chenwei.li
 * Date: 2016-05-25
 * time: 11:19
 * Email: lichenwei.me@foxmail.com
 */
public class WealFragment extends BaseLazyFragment implements IWealView {


    @Bind(R.id.rv_wealView)
    XRecyclerView mRvWealView;


    //福利適配器與數(shù)據(jù)源
    private WealAdapter mWealAdapter;
    private List<WealEntity.ResultsBean> mResultsBeans;

    //福利P層
    private WealPresenter mWealPresenter;

    //記錄當(dāng)前頁數(shù)地淀,默認(rèn)為1
    private int mCurrentPage = 1;


    /**
     * 懶加載數(shù)據(jù)
     */
    @Override
    protected void lazyLoadData() {
        getWealList(10, mCurrentPage);
    }

    @Override
    public int bindLayout() {
        return R.layout.fragment_weal;
    }

    @Override
    public void createFragment(Bundle savedInstanceState) {
    }



    @Override
    public void initView() {

        //注入P層
        mWealPresenter = new WealPresenter();
        mWealPresenter.attachView(this);

        //創(chuàng)建數(shù)據(jù)源,設(shè)置適配器
        mResultsBeans = new ArrayList<WealEntity.ResultsBean>();
        mWealAdapter = new WealAdapter(mResultsBeans);
        mRvWealView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
        mRvWealView.setAdapter(mWealAdapter);

        //設(shè)置動(dòng)畫岖是,刷新樣式
        mRvWealView.setItemAnimator(new DefaultItemAnimator());
        mRvWealView.setArrowImageView(R.drawable.iconfont_downgrey);

        //監(jiān)聽刷新數(shù)據(jù)
        mRvWealView.setLoadingListener(new XRecyclerView.LoadingListener() {
            @Override
            public void onRefresh() {
                mCurrentPage = 1;
                getWealList(10, mCurrentPage);
            }

            @Override
            public void onLoadMore() {
                getWealList(10, mCurrentPage);
            }
        });


    }


    @Override
    public void showLoadingDialog() {

    }

    @Override
    public void cancelLoadingDialog() {

    }

    /**
     * 顯示錯(cuò)誤信息
     *
     * @param errorMsg
     */
    @Override
    public void showErrorMsg(String errorMsg) {
        ToastUtil.showShort(MyApplication.getContext(), errorMsg);
    }

    /**
     * 獲取數(shù)據(jù)
     *
     * @param pageSize
     * @param currentPage
     */
    @Override
    public void getWealList(int pageSize, int currentPage) {
        mWealPresenter.getWealList(pageSize, currentPage);
        mCurrentPage++;
    }


    /**
     * 下拉刷新數(shù)據(jù)回調(diào)
     * @param resultsBean
     */
    @Override
    public void onRefreshData(List<WealEntity.ResultsBean> resultsBean) {
        mResultsBeans.clear();
        mResultsBeans.addAll(resultsBean);
        mWealAdapter.notifyDataSetChanged();
        mRvWealView.refreshComplete();
    }

    /**
     * 上拉加載數(shù)據(jù)回調(diào)
     * @param resultsBean
     */
    @Override
    public void onLoadMoreData(List<WealEntity.ResultsBean> resultsBean) {
        mResultsBeans.addAll(resultsBean);
        mWealAdapter.notifyDataSetChanged();
        mRvWealView.loadMoreComplete();
        if (resultsBean.size() < 10) {
            //當(dāng)加載新數(shù)據(jù)的條數(shù)不足10條帮毁,則關(guān)閉上拉加載
            mRvWealView.setLoadingMoreEnabled(false);
        }
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
        mWealPresenter.detachView();
    }


}

這里采用了懶加載LazyFragment,當(dāng)頁面加載完畢且用戶當(dāng)前界面處于它的時(shí)候豺撑,會(huì)調(diào)用執(zhí)行l(wèi)azyData加載數(shù)據(jù)烈疚,當(dāng)下拉刷新,上拉加載的時(shí)候會(huì)對(duì)應(yīng)的執(zhí)行方法聪轿,且改變當(dāng)前頁碼爷肝。這里在頁面初始化加載和銷毀的時(shí)候,分別去調(diào)用了P層的注入和銷毀方法陆错,用來保存生命狀態(tài)一致灯抛。

Presenter層

/**
 * 獲取福利Presenter層
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 22:20
 * Email: lichenwei.me@foxmail.com
 */
public class WealPresenter extends BasePresenter<IWealView> {

    private IWealModel iWealModel = new WealModel();


    @Override
    public void attachView(IWealView mvpView) {
        super.attachView(mvpView);
    }


    @Override
    public void detachView() {
        super.detachView();
    }

    /**
     * 根據(jù)加載數(shù)量和頁碼加載數(shù)據(jù)
     *
     * @param pageSize
     * @param currentPage
     */
    public void getWealList(int pageSize, final int currentPage) {
        iWealModel.getWealList(pageSize, currentPage, new Subscriber<WealEntity>() {
            @Override
            public void onCompleted() {
            }

            @Override
            public void onError(Throwable e) {
                getMvpView().showErrorMsg(e.getMessage());
            }

            @Override
            public void onNext(WealEntity wealEntity) {
                if (wealEntity.getResults() != null) {
                    if (currentPage == 1) {
                        getMvpView().onRefreshData(wealEntity.getResults());
                    } else {
                        getMvpView().onLoadMoreData(wealEntity.getResults());
                    }

                }
            }
        });

    }

}

這里沒什么好說的,只是作為“信使”音瓷,傳遞了View層的請(qǐng)求給Model層对嚼,并通過Rxjava的訂閱回調(diào)對(duì)應(yīng)的方法,再次傳遞數(shù)據(jù)給View層绳慎。

Model層

/**
 * 網(wǎng)絡(luò)接口包裝類
 * Create by: chenwei.li
 * Date: 2016-05-23
 * time: 00:43
 * Email: lichenwei.me@foxmail.com
 */
public class RetrofitWapper {

    private static RetrofitWapper mRetrofitWapper;
    private Retrofit mRetrofit;

    /**
     * 將構(gòu)造函數(shù)私有化
     */
    private RetrofitWapper() {
        mRetrofit = new Retrofit.Builder().baseUrl(Constant.BASE_URL).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
    }

    /**
     * 獲取RetrofitWapper實(shí)例(單例模式)
     *
     * @return
     */
    public static RetrofitWapper getRetrofitWapperInstance() {
        if (mRetrofitWapper == null) {
            synchronized (RetrofitWapper.class) {
            mRetrofitWapper = new RetrofitWapper();
        }
    }
        return mRetrofitWapper;
    }

    /**
     * 創(chuàng)建接口訪問入口
     * @param service
     * @param <T>
     * @return
     */
    public <T> T create(Class<T> service) {
        return mRetrofit.create(service);
    }


    public class Constant {
        public static final String BASE_URL = "http://gank.io/api/";
    }

}

這里寫了一個(gè)幫助類纵竖,用來提供Retrofit的實(shí)例(單例)漠烧,由于Retrofit底層默認(rèn)是提供Okhttp支持的,所以如果這里要對(duì)Okhttp進(jìn)行個(gè)性化定制也是很容易的磨确,例如:

    private RetrofitWapper() {
        OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
        okHttpClient.connectTimeout(5, TimeUnit.SECONDS);
        mRetrofit = new Retrofit.Builder().client(okHttpClient.build()).baseUrl(Constant.BASE_URL).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
    }

再來看下Retrofit的service:

/**
 * Gank.io接口類
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 16:11
 * Email: lichenwei.me@foxmail.com
 */
public interface GankApiService {

    /**
     * 獲取福利列表
     *
     * @param pageSize
     * @param currentPage
     * @return
     */
    @GET("data/福利/{pageSize}/{currentPage}")
    Observable<WealEntity> getWealList(@Path("pageSize") int pageSize, @Path("currentPage") int currentPage);
}

Model層的業(yè)務(wù)接口及實(shí)現(xiàn):

/**
 * 獲取福利Model層接口
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 22:31
 * Email: lichenwei.me@foxmail.com
 */
public interface IWealModel {
     void getWealList(int pageSize,int currentSize, Subscriber<WealEntity> wealEntitySubscriber);
}

/**
 * Create by: chenwei.li
 * Date: 2016-05-26
 * time: 22:28
 * Email: lichenwei.me@foxmail.com
 */
public class WealModel implements IWealModel {

    private GankApiService mGankApiService;

    /**
     * 獲取福利列表數(shù)據(jù)
     *
     * @param pageSize
     * @param currentSize
     * @param wealEntitySubscriber
     */
    @Override
    public void getWealList(int pageSize, int currentSize, Subscriber<WealEntity> wealEntitySubscriber) {
        mGankApiService = RetrofitWapper.getRetrofitWapperInstance().create(GankApiService.class);
        mGankApiService.getWealList(pageSize, currentSize).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(wealEntitySubscriber);
    }
}

這里的代碼很簡單沽甥,就不再做過多的文字描述了,對(duì)于Rxjava和Retrofit不熟悉的朋友可以網(wǎng)上查看下相關(guān)資料乏奥。

最后

關(guān)于軟件開發(fā)設(shè)計(jì)模式摆舟,其實(shí)并沒有最好的選擇,只有更適合的選擇邓了,很多時(shí)候我們拋開業(yè)務(wù)去談設(shè)計(jì)恨诱,架構(gòu)都是飄渺的,我認(rèn)為開發(fā)還是需要靈活點(diǎn)骗炉,不應(yīng)該被某模式某架構(gòu)所束縛照宝,這樣變得好像是為了目的而目的,已經(jīng)脫離了設(shè)計(jì)模式最初的設(shè)計(jì)意義句葵。
比如這個(gè)MVP厕鹃,對(duì)于業(yè)務(wù)很簡單的小型APP來說,有時(shí)候明明只需要幾個(gè)類就可以解決的事情乍丈,非要使用MVP剂碴,會(huì)使其多了一大堆接口,導(dǎo)致開發(fā)成本變大轻专,但也不是全然無用忆矛,至少它的代碼層次清晰了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末请垛,一起剝皮案震驚了整個(gè)濱河市催训,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宗收,老刑警劉巖漫拭,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異混稽,居然都是意外死亡嫂侍,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門荚坞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人菲盾,你說我怎么就攤上這事颓影。” “怎么了懒鉴?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵诡挂,是天一觀的道長碎浇。 經(jīng)常有香客問我,道長璃俗,這世上最難降的妖魔是什么奴璃? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮城豁,結(jié)果婚禮上苟穆,老公的妹妹穿的比我還像新娘。我一直安慰自己唱星,他們只是感情好雳旅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著间聊,像睡著了一般攒盈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哎榴,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天型豁,我揣著相機(jī)與錄音,去河邊找鬼尚蝌。 笑死迎变,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的驼壶。 我是一名探鬼主播氏豌,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼热凹!你這毒婦竟也來了泵喘?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤般妙,失蹤者是張志新(化名)和其女友劉穎纪铺,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碟渺,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鲜锚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苫拍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片芜繁。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情芦缰,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布榔袋,位于F島的核電站周拐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凰兑。R本人自食惡果不足惜妥粟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吏够。 院中可真熱鬧勾给,春花似錦、人聲如沸稿饰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喉镰。三九已至旅择,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間侣姆,已是汗流浹背生真。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留捺宗,地道東北人柱蟀。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像蚜厉,于是被迫代替她去往敵國和親长已。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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