Day2 當(dāng)前最火爆的APP架構(gòu)及其實(shí)現(xiàn)

點(diǎn)此進(jìn)入目錄:[干貨] 十天 教你從創(chuàng)意到上線APP

一、架構(gòu)設(shè)計的目的

通過設(shè)計使程序模塊化专筷,做到模塊內(nèi)部的高聚合和模塊之間的低耦合强霎。這樣做的好處是使得程序在開發(fā)的過程中,開發(fā)人員只需要專注于一點(diǎn)哼转,提高程序開發(fā)的效率,并且更容易進(jìn)行后續(xù)的測試以及定位問題槽华。但設(shè)計不能違背最初的目的壹蔓,對于不同量級的工程,具體架構(gòu)的實(shí)現(xiàn)方式必然是不同的猫态,切忌犯為了設(shè)計而設(shè)計庶溶,為了架構(gòu)而架構(gòu)的毛病。

下面我們詳細(xì)介紹下Android中常用的兩種架構(gòu)類型懂鸵,然后結(jié)合實(shí)例講解具體實(shí)現(xiàn)流程,最后根據(jù)業(yè)務(wù)場景選取合適我們的架構(gòu)模式行疏。

舉個簡單的例子:

一個Android App如果只有幾個Java文件匆光,那只需要做點(diǎn)模塊和層次的劃分就可以,引入框架或者架構(gòu)反而提高了工作量酿联,降低了生產(chǎn)力终息;

但如果當(dāng)前開發(fā)的App最終代碼量在10W行以上夺巩,本地需要進(jìn)行復(fù)雜操作,同時也需要考慮到與其余的Android開發(fā)者以及后臺開發(fā)人員之間的同步配合周崭,那就需要在架構(gòu)上進(jìn)行一些思考柳譬!

二、MVC設(shè)計架構(gòu)

1续镇、MVC簡介

MVC全名是Model View Controller美澳,如圖,是模型(model)-視圖(view)-控制器(controller)的縮寫摸航,一種軟件設(shè)計典范制跟,用一種業(yè)務(wù)邏輯、數(shù)據(jù)酱虎、界面顯示分離的方法組織代碼雨膨,在改進(jìn)和個性化定制界面及用戶交互的同時,不需要重新編寫業(yè)務(wù)邏輯读串。

其中M層處理數(shù)據(jù)聊记,業(yè)務(wù)邏輯等;V層處理界面的顯示結(jié)果恢暖;C層起到橋梁的作用排监,來控制V層和M層通信以此來達(dá)到分離視圖顯示和業(yè)務(wù)邏輯層。

2胀茵、Android中的MVC

視圖層(View)

一般采用XML文件進(jìn)行界面的描述社露,這些XML可以理解為AndroidApp的View。使用的時候可以非常方便的引入琼娘。同時便于后期界面的修改峭弟。邏輯中與界面對應(yīng)的id不變化則代碼不用修改,大大增強(qiáng)了代碼的可維護(hù)性脱拼。

控制層(Controller)

Android的控制層的重任通常落在了眾多的Activity的肩上瞒瘸。這句話也就暗含了不要在Activity中寫代碼,要通過Activity交割Model業(yè)務(wù)邏輯層處理熄浓,這樣做的另外一個原因是Android中的Actiivity的響應(yīng)時間是5s情臭,如果耗時的操作放在這里,程序就很容易被回收掉赌蔑。

模型層(Model)

我們針對業(yè)務(wù)模型俯在,建立的數(shù)據(jù)結(jié)構(gòu)和相關(guān)的類,就可以理解為AndroidApp的Model娃惯,Model是與View無關(guān)跷乐,而與業(yè)務(wù)相關(guān)的,比如:對數(shù)據(jù)庫的操作趾浅、對網(wǎng)絡(luò)等的操作都應(yīng)該在Model里面處理愕提,當(dāng)然對業(yè)務(wù)計算等操作也是必須放在的該層的馒稍。就是應(yīng)用程序中二進(jìn)制的數(shù)據(jù)。

3浅侨、MVC框架在Activity中的體現(xiàn)

我們這里以一個按鈕點(diǎn)擊后進(jìn)行網(wǎng)絡(luò)請求然后將請求數(shù)據(jù)同步更新到界面的Demo為例纽谒,向大家展示MVC在Android中具體的應(yīng)用場景和控制流程:
(1)Controller控制器&View:
public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {

    private WeatherModel weatherModel;
    private EditText cityNOInput;
    private TextView city;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherModel = new WeatherModelImpl();
        initView();
    }

    // 初始化View
    private void initView() {
        cityNOInput = findView(R.id.et_city_no);
        city = findView(R.id.tv_city);
        findView(R.id.btn_go).setOnClickListener(this);
    }

    // 顯示結(jié)果
    public void displayResult(Weather weather) {
        WeatherInfo weatherInfo = weather.getWeatherinfo();
        city.setText(weatherInfo.getCity());
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_go:
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);
                break;
        }
    }

    @Override
    public void onSuccess(Weather weather) {
        displayResult(weather);
    }

    @Override
    public void onError() {
        Toast.makeText(this, 獲取天氣信息失敗, Toast.LENGTH_SHORT).show();
    }

    private T findView(int id) {
        return (T) findViewById(id);
    }
}

從上面的代碼可以看到,Activity持有了WeatherModel模型的對象如输,當(dāng)用戶有點(diǎn)擊Button的時候鼓黔,Activity作為Controller控制層讀取View視圖層EditTextView的數(shù)據(jù)然后向Model模型發(fā)起數(shù)據(jù)請求,也就是調(diào)用WeatherModel對象的getWeather()方法挨决。當(dāng)Model模型處理數(shù)據(jù)結(jié)束后请祖,通過接口OnWeatherListener通知View視圖層數(shù)據(jù)處理,然后View視圖層調(diào)用displayResult()方法更新UI脖祈。至此肆捕,整個MVC框架流程就在Activity中體現(xiàn)出來了。

(2)WeatherModelImpl代碼實(shí)現(xiàn)&Model模型
public interface WeatherModel {
    void getWeather(String cityNumber, OnWeatherListener listener);
}

public class WeatherModelImpl implements WeatherModel {
    @Override
    public void getWeather(String cityNumber, final OnWeatherListener listener) {
        // 數(shù)據(jù)層操作
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html, Weather.class, new Response.Listener<weather>() {
            @Override
            public void onResponse(Weather weather) {
                if (weather != null) {
                    listener.onSuccess(weather);
                } else {
                    listener.onError();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                listener.onError();
            }
        });
    }
}

通過以上代碼看出盖高,這里設(shè)計了一個WeatherModel模型接口慎陵,然后實(shí)現(xiàn)了接口WeatherModelImpl類,Controller控制器Activity調(diào)用WeatherModelImpl類中的方法發(fā)起網(wǎng)絡(luò)請求喻奥,然后通過實(shí)現(xiàn)OnWeatherListener接口來獲得網(wǎng)絡(luò)請求的結(jié)果通知View視圖層更新UI席纽。至此,Activity就將View視圖顯示和Model模型數(shù)據(jù)處理隔離開了撞蚕。Activity擔(dān)當(dāng)Contronller完成了Model和View之間的協(xié)調(diào)作用润梯。

至于這里為什么不直接設(shè)計成類里面的一個getWeather()方法直接請求網(wǎng)絡(luò)數(shù)據(jù)呢?我們考慮下面這種情況:現(xiàn)在代碼中的網(wǎng)絡(luò)請求是使用Volley框架來實(shí)現(xiàn)的甥厦,如果哪天老板非要你使用Afinal框架實(shí)現(xiàn)網(wǎng)絡(luò)請求纺铭,問題要怎么解決呢?難道是修改getWeather()方法的實(shí)現(xiàn)嗎刀疙?這樣當(dāng)然不行舶赔,因為這樣修改不僅破壞了以前的代碼而且還不利于維護(hù)∏恚考慮到以后代碼的擴(kuò)展和維護(hù)性竟纳,我們選擇設(shè)計接口的方式來解決這個問題:我們實(shí)現(xiàn)另外一個WeatherModelWithAfinalImpl類,繼承自WeatherModel重寫里面的方法疚鲤,這樣不僅保留了以前的WeatherModelImpl類請求網(wǎng)絡(luò)方式锥累,還增加了WeatherModelWithAfinalImpl類的請求方式,這樣Activity調(diào)用代碼無需要任何修改集歇。

三揩悄、MVP設(shè)計架構(gòu)

1、MVP的誕生

在App開發(fā)過程中經(jīng)常出現(xiàn)的問題就是某一部分的代碼量過大鬼悠,雖然做了模塊劃分和接口隔離删性,但也很難完全避免,而且從實(shí)踐中看出焕窝,這更多的出現(xiàn)在UI部分蹬挺,也就是Activity里。不過Activity內(nèi)容過多的原因也很好解釋它掂,因為Activity本身需要擔(dān)負(fù)與用戶之間的操作交互巴帮、界面的展示,所以它不是單純的Controller或View虐秋,這就造成了Activity的臃腫榕茧。所以為了解決這個問題,讓我們引入MVP框架客给。

2用押、MVC的缺點(diǎn)

正如剛才所說,在Android開發(fā)中Activity并不是一個標(biāo)準(zhǔn)的MVC模式中的Controller靶剑,它的首要職責(zé)是加載應(yīng)用的布局和初始化用戶界面蜻拨,并接受和處理來自用戶的操作請求,進(jìn)而作出響應(yīng)桩引。隨著界面及其邏輯的復(fù)雜度不斷提升缎讼,Activity類的職責(zé)不斷增加,以致變得龐大臃腫坑匠,這便是MVC的缺點(diǎn)所在血崭。

3、什么是MVP厘灼?

MVP是從MVC框架演變過來的夹纫,當(dāng)然也就與MVC有一定的相似性:Controller/Presenter負(fù)責(zé)邏輯的處理,Model提供數(shù)據(jù)手幢,View負(fù)責(zé)顯示捷凄。


MVP框架由3部分組成:View負(fù)責(zé)顯示;Presenter負(fù)責(zé)邏輯處理围来;Model提供數(shù)據(jù)跺涤。在MVP模式里通常包含3個要素(加上View interface是4個):

  • View:負(fù)責(zé)繪制UI元素、與用戶進(jìn)行交互(在Android中體現(xiàn)為Activity)监透;
  • Model:負(fù)責(zé)存儲桶错、檢索、操縱數(shù)據(jù)(有時也實(shí)現(xiàn)一個Model interface用來降低耦合)胀蛮;
  • Presenter:作為View與Model交互的中間紐帶院刁,處理與用戶交互的負(fù)責(zé)邏輯;
  • View interface:需要View實(shí)現(xiàn)的接口粪狼,View通過View interface與Presenter進(jìn)行交互退腥,以此降低耦合并且方便進(jìn)行單元測試任岸;
Tips:View interface的必要性

回想一下你在開發(fā)Android應(yīng)用時是如何對代碼邏輯進(jìn)行單元測試的?是否每次都要將應(yīng)用部署到Android模擬器或真機(jī)上狡刘,然后通過模擬用戶操作進(jìn)行測試享潜?然而由于Android平臺的特性,每次部署都耗費(fèi)了大量的時間嗅蔬,這直接導(dǎo)致開發(fā)效率的降低剑按。而在MVP模式中,處理復(fù)雜邏輯的Presenter是通過interface與View(Activity)進(jìn)行交互的澜术,這說明我們可以通過自定義類實(shí)現(xiàn)這個interface來模擬Activity的行為對Presenter進(jìn)行單元測試艺蝴,省去了大量的部署及測試的時間。

4鸟废、MVC → MVP

當(dāng)我們將Activity復(fù)雜的邏輯處理移至另外的一個類(Presenter)中時猜敢,Activity其實(shí)就是MVP模式中的View,它負(fù)責(zé)UI元素的初始化侮攀,建立UI元素與Presenter的關(guān)聯(lián)(Listener之類)锣枝,同時自己也會處理一些簡單的邏輯(復(fù)雜的邏輯交由Presenter處理)。

MVP的Presenter是框架的控制者兰英,承擔(dān)了大量的邏輯操作撇叁,而MVC的Controller更多時候承擔(dān)一種轉(zhuǎn)發(fā)的作用。因此在App中引入MVP的原因畦贸,是為了將此前在Activty中包含的大量邏輯操作放到控制層中唆缴,避免Activity的臃腫奴饮。

因此我們可以發(fā)現(xiàn)MVP的優(yōu)點(diǎn)如下:
  • 模型與視圖完全分離梁丘,我們可以修改視圖而不影響模型缝龄;
  • 可以更高效地使用模型,因為所有的交互都發(fā)生在一個地方 —— Presenter內(nèi)部胶坠;
  • 我們可以將一個Presenter用于多個視圖君账,而不需要改變Presenter的邏輯。這個特性非常的有用沈善,因為視圖的變化總是比模型的變化頻繁乡数;
  • 如果我們把邏輯放在Presenter中,那么我們就可以脫離用戶接口來測試這些邏輯(單元測試)闻牡。

具體到Android App中净赴,一般可以將App根據(jù)程序的結(jié)構(gòu)進(jìn)行縱向劃分,根據(jù)MVP可以將App分別為模型層(M)罩润、UI層(V)和邏輯層(P)玖翅。UI層一般包括Activity、Fragment等直接和UI相關(guān)的類,UI層的Activity在啟動之后實(shí)例化相應(yīng)的Presenter金度,App的控制權(quán)后移应媚,由UI轉(zhuǎn)移到Presenter,兩者之間的通信通過BroadCast猜极、Handler或者接口完成珍特,只傳遞事件和結(jié)果。

舉個簡單的例子魔吐,UI層通知邏輯層(Presenter)用戶點(diǎn)擊了一個Button,邏輯層(Presenter)自己決定應(yīng)該用什么行為進(jìn)行響應(yīng)莱找,該找哪個模型(Model)去做這件事酬姆,最后邏輯層(Presenter)將完成的結(jié)果更新到UI層。

MVP的變種:Passive View

MVP的變種有很多奥溺,其中使用最廣泛的是Passive View模式辞色,即被動視圖。在這種模式下浮定,View和Model之間不能直接交互相满,而是通過Presenter與Model打交道。Presenter接受View的UI請求并完成簡單的UI處理邏輯并調(diào)用Model進(jìn)行業(yè)務(wù)處理桦卒,然后調(diào)用View將相應(yīng)的結(jié)果反映出來立美。View直接依賴Presenter,但是Presenter間接依賴View方灾,它直接依賴的是View實(shí)現(xiàn)的接口建蹄。

相對于View的被動,Presenter就是主動的一方:
  • Presenter是整個MVP體系的控制中心裕偿,而不是單純的處理View請求的人洞慎;
  • View僅僅是用戶交互請求的匯報者,對于響應(yīng)用戶交互相關(guān)的邏輯和流程嘿棘,View不參與決策劲腿,真正的決策者是Presenter;
  • View向Presenter發(fā)送用戶交互請求應(yīng)該采用這樣的口吻:“我現(xiàn)在將用戶交互請求發(fā)送給你鸟妙,你看著辦焦人,需要我的時候我會協(xié)助你”,不應(yīng)該是這樣:“我現(xiàn)在處理用戶交互請求了圆仔,我知道該怎么辦垃瞧,但是我需要你的支持,因為實(shí)現(xiàn)業(yè)務(wù)邏輯的Model只信任你”坪郭;
  • 對于綁定到View上的數(shù)據(jù)个从,不應(yīng)該是View從Presenter上“拉”回來的,應(yīng)該是Presenter主動“推”給View的;
  • View盡可能不維護(hù)數(shù)據(jù)狀態(tài)嗦锐,因為其本身僅僅實(shí)現(xiàn)單純的嫌松、獨(dú)立的UI操作;
  • Presenter才是整個體系的協(xié)調(diào)者奕污,它根據(jù)處理用于交互的邏輯給View和Model安排工作萎羔。

5、MVP架構(gòu)存在的問題與解決辦法

  • 加入模板方法(Template Method)
    轉(zhuǎn)移邏輯操作之后可能部分較為復(fù)雜的Activity內(nèi)代碼量還是不少碳默,于是需要在分層的基礎(chǔ)上再加入模板方法(Template Method)贾陷。
    具體做法是在Activity內(nèi)部分層,其中最頂層為BaseActivity嘱根,不做具體顯示而是提供一些基礎(chǔ)樣式:Dialog髓废、ActionBar在內(nèi)的內(nèi)容等。展現(xiàn)給用戶的Activity繼承BaseActivity该抒,重寫B(tài)aseActivity預(yù)留的方法慌洪。如有必要再進(jìn)行二次繼承,App中Activity之間的繼承次數(shù)最多不超過3次凑保。
    “愛閱”中采用的即是這樣的方法冈爹,加入模板方法不僅省去了很多重復(fù)的邏輯工作量,也更方便不同子視圖層的個性化定制欧引;

  • Model內(nèi)部分層
    模型層中的整體代碼量是最大的频伤,一般由大量的Package組成,針對這部分需要做的就是在程序設(shè)計的過程中维咸,做好模塊的劃分剂买,進(jìn)行接口隔離,在內(nèi)部進(jìn)行分層癌蓖。

  • 強(qiáng)化Presenter
    強(qiáng)化Presenter的作用瞬哼,將所有邏輯操作都放在Presenter內(nèi)也容易造成Presenter內(nèi)的代碼量過大,對于這點(diǎn)有一個方法是在UI層和Presenter之間設(shè)置中介者M(jìn)ediator租副,將例如數(shù)據(jù)校驗坐慰、組裝在內(nèi)的輕量級邏輯操作放在Mediator中;在Presenter和Model之間使用代理Proxy用僧;通過上述兩者分擔(dān)一部分Presenter的邏輯操作结胀,但整體框架的控制權(quán)還是在Presenter手中。Mediator和Proxy不是必須的责循,只在Presenter負(fù)擔(dān)過大時才建議使用糟港。

四 、MVP代碼實(shí)例

接下來我們看看MVP在Android開發(fā)中是怎么應(yīng)用的院仿!
(1)建立Bean
public class UserBean {
     private String mFirstName;
     private String mLastName;
     public UserBean(String firstName, String lastName) {
            this. mFirstName = firstName;
            this. mLastName = lastName;
     }
     public String getFirstName() {
            return mFirstName;
     }
     public String getLastName() {
            return mLastName;
     }
}
(2)建立Model
public interface UserModel {
     void setID(int id);

     void setFirstName(String firstName);

     void setLastName(String lastName);

     int getID();

     UserBean load(int id);// 通過id讀取user信息,返回一個UserBean
}
(3)Presenter控制器

建立presenter(通過iView和iModel接口操作Model和view)秸抚,Activity可以把所有邏輯給Presenter處理速和,這樣Java邏輯就從手機(jī)的Activity中分離出來。

public class UserPresenter {
     private IUserView mUserView;
     private IUserModel mUserModel;

     public UserPresenter(IUserView view) {
            mUserView = view;
            mUserModel = new UserModel();
     }

     public void saveUser(int id, String firstName, String lastName) {
            mUserModel.setID(id);
            mUserModel.setFirstName(firstName);
            mUserModel.setLastName(lastName);
     }

     public void loadUser(int id) {
           UserBean user = mUserModel.load(id);
            mUserView.setFirstName(user.getFirstName()); // 通過調(diào)用IUserView的方法來更新顯示
            mUserView.setLastName(user.getLastName());
     }
}
(4)View視圖

建立view(更新ui中的view狀態(tài))剥汤,這里列出需要操作當(dāng)前view的方法颠放,也是接口

public interface IUserView {
     int getID();

     String getFristName();

     String getLastName();

     void setFirstName(String firstName);

     void setLastName(String lastName);
}
(5)MainActivity
public class MainActivity extends Activity implements OnClickListener, IUserView {

     UserPresenter presenter;
     EditText id, first, last;

     @Override
     protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout. activity_main);

           findViewById(R.id. save).setOnClickListener( this);
           findViewById(R.id. load).setOnClickListener( this);
           id = (EditText) findViewById(R.id. id);
           first = (EditText) findViewById(R.id. first);
           last = (EditText) findViewById(R.id. last);

           presenter = new UserPresenter( this);
     }

     @Override
     public void onClick(View v) {
            switch (v.getId()) {
            case R.id. save:
                 presenter.saveUser(getID(), getFristName(), getLastName());
                 break;
            case R.id. load:
                 presenter.loadUser(getID());
                 break;
            default:
                 break;
           }
     }

     @Override
     public int getID() {
            return new Integer( id.getText().toString());
     }

     @Override
     public String getFristName() {
            return first.getText().toString();
     }

     @Override
     public String getLastName() {
            return last.getText().toString();
     }

     @Override
     public void setFirstName(String firstName) {
            first.setText(firstName);
     }

     @Override
     public void setLastName(String lastName) {
            last.setText(lastName);
     }
}

因此,Activity及從MVC中的Controller中解放出來了吭敢,這會Activity主要做顯示View的作用和用戶交互碰凶。每個Activity可以根據(jù)自己顯示View的不同實(shí)現(xiàn)View視圖接口IUserView。

五鹿驼、“愛閱”中的架構(gòu)設(shè)計

上面介紹了Android應(yīng)用中目前最火熱的兩種架構(gòu)模式欲低,那么“愛閱”中采用的架構(gòu)是什么呢?是MVC和MVP共存的架構(gòu)模式畜晰!大家可能有些奇怪伸头,難道MVC和MVP是可以共存的嗎?那是當(dāng)然的了舷蟀,只是我們在具體的業(yè)務(wù)場景中要根據(jù)不同的情況做出恰當(dāng)?shù)倪x擇就好。

比如在“愛閱”的整個架構(gòu)當(dāng)中面哼,MainActivity扮演了整個APP主要的業(yè)務(wù)角色野宜,在APP運(yùn)行的過程中,無論是側(cè)滑菜單的打開魔策、搜索界面的呈現(xiàn)匈子、收藏界面的展現(xiàn)等等,MainActivity都屬于常駐內(nèi)存的角色闯袒,所以對于MainActivity中的一些基本功能而言采用MVC模式最為合適虎敦,比如:左右兩側(cè)菜單欄的初始化、主界面ViewPager的更新和顯示等政敢。而在每個Fragment當(dāng)中其徙,選擇MVP架構(gòu)最為合適,因為涉及到了許多相似的界面設(shè)計(比如主頁面中的頁面點(diǎn)選和滑動等)喷户,這樣不僅減少了界面重復(fù)邏輯的代碼編寫唾那,而且把相同的業(yè)務(wù)邏輯處理抽取到Presenter中進(jìn)行處理后,更容易實(shí)現(xiàn)不同界面的個性化定制并減少模塊之間的耦合褪尝。

另外闹获,在MainActivity的設(shè)計中,對于一些長連接的邏輯處理同樣采用了MVP的架構(gòu)設(shè)計河哑。比如:對數(shù)據(jù)庫和網(wǎng)絡(luò)獲取的原始數(shù)據(jù)解析完成后避诽,我采用了EventBus對事件進(jìn)行分發(fā)并在MainActivity中進(jìn)行捕獲,這樣就實(shí)現(xiàn)了高度的業(yè)務(wù)邏輯解耦璃谨,即前面所說的 —— 數(shù)據(jù)不是從MainActivity中拉取沙庐,而是把數(shù)據(jù)推給MainActivity鲤妥;MainActivity只關(guān)心要什么、接受什么轨功,而不關(guān)心具體是怎么獲得的旭斥;MainActivity不是整個APP的業(yè)務(wù)邏輯承擔(dān)者,而僅僅輕量化的實(shí)現(xiàn)與用戶的UI交互功能古涧。

這樣垂券,我們就根據(jù)具體的業(yè)務(wù)場景做出了合理的架構(gòu)選擇和設(shè)計,在下一篇的課程當(dāng)中羡滑,我們就根據(jù)此架構(gòu)框架進(jìn)行主頁面的開發(fā)工作菇爪,See You!

聯(lián)系方式:

簡書:WillFlow
GitHub:愛閱

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末柒昏,一起剝皮案震驚了整個濱河市凳宙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌职祷,老刑警劉巖氏涩,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異有梆,居然都是意外死亡是尖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門泥耀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來饺汹,“玉大人,你說我怎么就攤上這事痰催《荡牵” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵夸溶,是天一觀的道長逸吵。 經(jīng)常有香客問我,道長缝裁,這世上最難降的妖魔是什么胁塞? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮压语,結(jié)果婚禮上啸罢,老公的妹妹穿的比我還像新娘。我一直安慰自己胎食,他們只是感情好扰才,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著厕怜,像睡著了一般衩匣。 火紅的嫁衣襯著肌膚如雪蕾总。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天琅捏,我揣著相機(jī)與錄音生百,去河邊找鬼。 笑死柄延,一個胖子當(dāng)著我的面吹牛蚀浆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播搜吧,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼市俊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了滤奈?” 一聲冷哼從身側(cè)響起摆昧,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蜒程,沒想到半個月后绅你,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昭躺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年勇吊,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窍仰。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖礼殊,靈堂內(nèi)的尸體忽然破棺而出驹吮,到底是詐尸還是另有隱情,我是刑警寧澤晶伦,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布碟狞,位于F島的核電站,受9級特大地震影響婚陪,放射性物質(zhì)發(fā)生泄漏族沃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一泌参、第九天 我趴在偏房一處隱蔽的房頂上張望脆淹。 院中可真熱鬧,春花似錦沽一、人聲如沸盖溺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烘嘱。三九已至昆禽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蝇庭,已是汗流浹背醉鳖。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哮内,地道東北人盗棵。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像牍蜂,于是被迫代替她去往敵國和親漾根。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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