[11]——MVP 模式之 先說說 Dagger (2)

轉(zhuǎn)載請標(biāo)明原文地址:http://www.reibang.com/p/dc163215bc7e


本來打算繼續(xù)寫 MVP 模式的莲蜘,但是看了網(wǎng)上的幾篇 Dagger 介紹的文章后,還是決定先寫寫 Dagger帘营,網(wǎng)上有些文章寫的不是過于簡單就是太過復(fù)雜票渠,或是不夠詳實,讓剛接觸 Dagger 的人容易看的云里霧里芬迄。正好也是剛學(xué)習(xí) Dagger 沒多久问顷,記錄下來對自己也是一個查缺補漏。文中如有錯誤薯鼠,請各位大佬予以斧正!

本文示例代碼:https://github.com/junerver/DaggerDemo


Dagger2

注:本文中的 Dagger 都是指 Google 推出的 Dagger2

Dagger 是 Square 公司推出的一個 DI(依賴注入)框架械蹋,后來項目被 Google 接手出皇,大家習(xí)慣性稱之為 Dagger2。

依賴注入:可能有的朋友看到依賴注入這四個字就迷惑了哗戈,這是什么gui郊艘?那你聽聽控制反轉(zhuǎn)呢?是不是更難懂唯咬,更加拗口纱注。其實在我們的程序中存在著大量的依賴,這里的依賴不是指我們的項目依賴第三方庫胆胰,而是指我們的對象依賴于其他的實例狞贱。只要實例 A 中用到了 B 的實例,我們就稱之為 A 依賴于 B蜀涨。比如StringBuffer stringBuffer = new StringBuffer("hello world");這里的 StringBuffer 類就依賴于 String 類瞎嬉。

在 J2EE 領(lǐng)域依賴注入使用很普及,對于大型項目而言存在著大量的實例厚柳,這些實例之間互相依賴氧枣,為了方便調(diào)用者使用,依賴注入順勢而生别垮,比如 Spring 框架中就包含了依賴注入的功能便监。

在 Android 中依賴注入起步較晚,其原因大概是因為早期的 Android 工程普遍不大碳想,而現(xiàn)在的 Android 工程動輒上百近千個頁面烧董,已經(jīng)可以視為大工程來看待的了毁靶,所以依賴注入框架也開始漸漸流行起來(同理像一些ORM框架也是這樣的);

釀酒大師教你釀酒

上面我們提到了為什么依賴注入開始流行起來解藻,我們來看看不使用依賴注入的一個示例代碼吧:

//制作白蘭地的流程
new Brandy(new Distiller(), new Wine(new Grape("解百納"), new FermentBarrel()));

看了這段代碼我估計你要罵街了老充,這是什么鬼!螟左?不是說要介紹依賴注入的優(yōu)勢么啡浊,這一大堆的 new 是什么東西?

別急別急胶背,沒看我的注釋是“制作白蘭地的流程”么巷嚣,我這是在制作一款用解百納釀造的白蘭地啊~,你看我們想要喝白蘭地需要先有酒液原漿吧钳吟,需要有蒸餾器吧廷粒,把原漿蒸餾了才能得到白蘭地嘛。而酒液原漿的獲得又需要用到葡萄和發(fā)酵桶嘛红且。

其實上面這段代碼我們就是模擬了一個相對復(fù)雜的實例化過程坝茎,可以看到其中的依賴關(guān)系如下

    |--- 蒸餾器
白蘭地  
    |       |--- 葡萄
    |--- 原漿
            |--- 發(fā)酵桶

那么在每次我們視圖實例化“白蘭地”對象時,都需要首先將其他的四個對象先行實例化完畢暇番,實例化一次還好嗤放,如果這個實例在上百個頁面中都需要使用到呢?但如果我們要是使用了依賴注入框架壁酬,將會使這一步驟變得十分容易次酌。

@Inject
Brandy mBrandy;
...
...
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ...
    ...
    mInjection.inject(this);
}

是的,我們只需要在申明對象時舆乔,加上一行注解即可岳服。然后在需要使用的地方直接使用mBrandy,是不是方便了很多希俩。

如何使用

添加依賴

看完上面的代碼對比吊宋,相比你已經(jīng)對 Dagger 產(chǎn)生了強烈的興趣了吧。如此優(yōu)雅颜武、高效的方式怎么會不吸引人呢贫母,下面我們來看看如何在項目中使用 Dagger 。

注意:我使用的環(huán)境是 AS 2.0盒刚,使用的 Dagger 版本是 2.4腺劣。如果你是使用 AS 2.2,請參照Dagger2官方的說明因块。

首先我們需要在項目中加入 Dagger 的依賴橘原,首先在項目的build.gradle文件中添加classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.0.0'
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'// <----添加這一句
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

然后在 app 模塊下的build.gradle文件中添加apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.0.1'

    //引入dagger2
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    //java注解
    provided 'org.glassfish:javax.annotation:10.0-b28'
}

然后選擇 Sync Now即可;

了解幾個小概念

  • @Inject
    @Inject 有兩個功能:
    1、注解類的構(gòu)造方法趾断,其作用可以理解為通過注解將這個類的實例化方法告訴 Dagger拒名;
public class Distiller {
    @Inject
    public Distiller() {
    }
    @Override
    public String toString() {
        return "蒸餾器";
    }
}

2芋酌、在需要使用該實例的地方注解同云,其作用是告訴調(diào)用者炸站,這個被注解的實例由 Dagger 來負(fù)責(zé)實例化旱易;

@Inject
Distiller mDistiller;
  • @Module
    @Module 可以理解為一個生產(chǎn)實例的工廠,他掌握各個需要注入的類的實例化方法腿堤,當(dāng) Dagger 需要為某個類注入實例時阀坏,會到 @Module 注解的類中,查找這個類的實例化方法笆檀。當(dāng)然這一過程是需要通過使用 @Provides 注解的有返回值的方法忌堂,來告知 Dagger 的。
@Module
public class BrandyModule {
    @Provides
    public Grape provideGrape() {
        return new Grape("解百納");
    }
}
  • @Component
    @Component 用來注解一個接口误债,在編譯的時候會生成 Dagger+文件名 的新Java文件浸船。Component可以理解為注射器妄迁,它是連接被注入的類與需要被注入的類之間的橋梁寝蹈。
@Component(modules = BrandyModule.class)    //這里的modules參數(shù)可以是多個,用于告訴“注射器”都有哪些實例可以被注射到目標(biāo)類中
public interface BrandyComponent {
    void inject(MainActivity mainActivity);
}
  • @Provides
    在提供實例的方法上注解登淘,用于告訴 Dagger 這是一個用于注入的實例箫老。方法名可以隨便黔州,Dagger 是通過方法的返回值來將其添加到依賴列表的。
@Provides
public Grape provideGrape() {
    return new Grape("解百納");
}

看個小例子

如果你按照上面的步驟寫下了代碼,運行代碼你會發(fā)現(xiàn)報了一個空指針錯誤涣达,這是因為我們還沒有為我們要注入的對象與被注入的類建立聯(lián)系。我們需要在使用被注入實例之前調(diào)用如下方法DaggerBrandyComponent.create().inject(this);,注意這里的參數(shù) this ,這個參數(shù)的值由我們剛剛定義的接口 BrandyComponent 中聲明的方法的參數(shù)決定谨设。為了方便測試素跺,我新建了一個 TestDagger 類,并在接口中添加了一個方法void inject(TestDagger testDagger);

public class TestDagger {
    @Inject
    Distiller mDistiller;

    public TestDagger() {
        DaggerBrandyComponent.create().inject(this);    //在代碼中我們并沒有對 Distiller 對象進行 new 操作來實例化
    }

    @Override
    public String toString() {
        return mDistiller.toString();
    }
}

這樣做是為了可以方便的在測試用例中進行測試,而不用將程序運行在手機或者模擬器上。


使用測試用例來方便的測試

可以看到,下面輸出了“蒸餾器”碴里,這說明我們對這一實例的注入成功了睡互。

不得不說的Module

Interesting

看了上面的例子,你是不是覺得有點意思了图贸?但是要注意的是撒汉,上面的那種方法適用于無參的構(gòu)造方法(當(dāng)然也可以有參數(shù),但是對應(yīng)的參數(shù)的構(gòu)造方法上也要有 @Inject 注解)。Talk is cheap隘谣,show me the code秩仆!

為了驗證剛剛提到的那一點忘衍,我們來為蒸餾器的構(gòu)造方法添加一個新的參數(shù) Heater 加熱器铅搓!

public class Distiller {

    private Heater mHeater;
    
    @Inject
    public Distiller(Heater heater) {
        mHeater = heater;
    }

    @Override
    public String toString() {
        return "蒸餾器";
    }
}

public class Heater {
    
    public Heater() {
    }

    @Override
    public String toString() {
        return "加熱器";
    }
}

注意上面的代碼,Heater 類的構(gòu)造方法沒有使用 @Inject 注解,我們運行一下看看效果。


Heater類的實例無法提供

程序拋出錯誤,錯誤的內(nèi)容是:在沒有使用 @Inject 注解構(gòu)造方法或者 @Provides 注解一個方法時無法提供 Heater 的實例。我們只需要在 Heater 類的構(gòu)造方法上也加上 @Inject 注解就可以了斑响。

通過上面的例子重绷,你應(yīng)該了解了我們可以為構(gòu)造方法參數(shù)的構(gòu)造方法添加 @Inject 注解來實現(xiàn)注入。(繞口令:八百標(biāo)兵奔北坡能颁,北坡炮兵并排跑敌土,炮兵怕把標(biāo)兵碰,標(biāo)兵怕碰炮兵炮)财剖。

但是如果我們的參數(shù)是第三方的類呢?比如參數(shù)是一個 String 呢策彤?我們不可能去 String 類的構(gòu)造方法中添加注解店诗。這時候就需要用到 Moudle 類了。

Module 的代碼上面我們已經(jīng)寫過了擦囊,我們來看一下

@Module
public class BrandyModule {

    @Provides
    public Grape provideGrape() {
        return new Grape("解百納");
    }
}

我們的 Grape 葡萄類的構(gòu)造方法有一個 String 參數(shù)涧郊,通過 @Provides 注解,我們可以告知 Dagger 當(dāng)需要用到 Grape 類的實例的時候幌陕,來 Module 類中獲取暇赤。再次運行代碼查看結(jié)果:


運行結(jié)果

可以看到我們輸出了正確的結(jié)果:我們來梳理一下 Dagger 注入實例的過程:

  • 步驟1:查找Module中是否存在創(chuàng)建該類的方法失暴。
  • 步驟2:若存在創(chuàng)建類方法古戴,查看該方法是否存在參數(shù)
    • 步驟2.1:若存在參數(shù)黍檩,則按從步驟1開始依次初始化每個參數(shù)
    • 步驟2.2:若不存在參數(shù),則直接初始化該類實例润文,一次依賴注入到此結(jié)束
  • 步驟3:若不存在創(chuàng)建類方法头谜,則查找Inject注解的構(gòu)造函數(shù)砖织,看構(gòu)造函數(shù)是否存在參數(shù)
    • 步驟3.1:若存在參數(shù)甲脏,則從步驟1開始依次初始化每個參數(shù)
    • 步驟3.2:若不存在參數(shù),則直接初始化該類實例,一次依賴注入到此結(jié)束

:同時存在 @Inject 與 Module 時哲鸳,Module 的優(yōu)先級高于 @Inject 注解。

本文示例代碼:https://github.com/junerver/DaggerDemo

酒鬼總是希望可以多喝幾種酒

現(xiàn)在你應(yīng)該理解了 Dagger 是怎么一個工作流程了吧!也許你會問了,紀(jì)然 Dagger 是通過被注解方法的返回值來將它添加到依賴列表的,那么我們?nèi)绻卸鄠€ Grape 實例可用,應(yīng)該怎么辦呢(1、如何為 Dagger 創(chuàng)建多個相同類的的實例;2疾渣、在需要注入時如何區(qū)分多個實例;)?

首先我們再添加一個 @Provides 注解方法試試看:


bound multiple times

可以看到梁沧,編譯器報錯,提示 bound multiple times(多次綁定)芝囤,難道說 Dagger 只能注入一種實例么??那他的局限性也太大了吧瘩扼?這時候 @Named 注解就需要粉墨登場啦~

@Named 注解用于給 @Provides 注解提供別名垃僚,在使用的時候也需要加上 @Named 注解集绰,Dagger 就知道我們需要的是具體哪個實例了。

@Provides
@Named("CabernetSauvignon")
public Grape provideOtherGrape() {
    return new Grape("赤霞珠");
}
默認(rèn)的注入實例
使用別名的注入實例

可以看到我們現(xiàn)在可以通過 @Named 注解來活動不同的葡萄了谆棺,那需要使用 Wine 類如果希望使用赤霞珠作為參數(shù)應(yīng)該怎么辦呢栽燕,如下所示:

@Provides
@Named("CabernetSauvignon")
public Wine provideOtherWine(@Named("CabernetSauvignon") Grape grape, FermentBarrel fermentBarrel) {
    return new Wine(grape, fermentBarrel);
}

我們新增加一個提供赤霞珠原漿的方法,在其參數(shù)中使用了@Named("CabernetSauvignon")來指定,這個參數(shù)是赤霞珠斧蜕。完整的代碼如下所示:

@Module
public class BrandyModule {

    @Provides
    public Grape provideGrape() {
        return new Grape("解百納");
    }

    @Provides
    @Named("CabernetSauvignon")
    public Grape provideOtherGrape() {
        return new Grape("赤霞珠");
    }

    @Provides
    @Named("CabernetSauvignon")
    public Wine provideOtherWine(@Named("CabernetSauvignon") Grape grape, FermentBarrel fermentBarrel) {
        return new Wine(grape, fermentBarrel);
    }

    @Provides
    @Named("CabernetSauvignon")
    public Brandy provideOtherBrandy(@Named("CabernetSauvignon") Wine wine, Distiller distiller) {
        return new Brandy(distiller, wine);
    }
}

現(xiàn)在我們就可以品嘗到使用赤霞珠葡萄制作的白蘭地啦~

品嘗美酒吧!

總結(jié)要點:

  1. @Inject 有兩種用途肛搬;
  2. 對于不能使用 @Inject 注解的類,將該類的實例化方法使用 @Provides 注解楼肪;
  3. 對于同一個類的不同實例化方法,使用 @Named 注解;
  4. @Named 注解還可以注解 Provides 方法的參數(shù);

我們不需要那么多的蒸餾器

我們將 Distiller 的 toString() 方法進行修改:

public class Distiller {

    private Heater mHeater;

    @Inject
    public Distiller(Heater heater) {
        mHeater = heater;
    }

    @Override
    public String toString() {
        return "有"+mHeater.toString()+"的蒸餾器"+super.toString();
    }
}

再次運行程序:


使用了不同的蒸餾器

發(fā)現(xiàn)問題了嗎嗽测?我們的白蘭地居然使用了不同的蒸餾器蜀变,這很不合理喂很,我們的釀酒作坊只需要一個蒸餾器就可以了,完全不需要對每瓶酒都使用一個新的蒸餾器湾盒。也就是說我們的 Distiller 類應(yīng)該是一個單例!

如果你看過其他文章你應(yīng)該會知道有一個注解 @Singleton,這個注解的字面就是單例,那么我們使用該注解來注釋我們的 Distiller 類以及我們的 BrandyComponent 接口墨坚。再次運行程序磷蛹,結(jié)果如下:

@Singleton
public class Distiller {

    private Heater mHeater;

    @Inject
    public Distiller(Heater heater) {
        mHeater = heater;
    }

    @Override
    public String toString() {
        return "有"+mHeater.toString()+"的蒸餾器"+super.toString();
    }
}

@Component(modules = BrandyModule.class)
@Singleton
public interface BrandyComponent {
    void inject(MainActivity mainActivity);
    void inject(TestDagger testDagger);
}
現(xiàn)在使用的是相同的蒸餾器了

你可能會驚嘆:我的天吶柿菩!真是魔法!Q恢隆!我們居然通過一個注解就實現(xiàn)了單例模式!误辑?還學(xué)什么7種單例模式的實現(xiàn)方式啊万俗,以后都用這個注解不就都搞定了嘛?

我們再新建一個測試類牧抵,同時在 @Component 中添加新的方法void inject(OtherTest otherTest);

public class OtherTest {
    @Inject
    Brandy mBrandy;

    @Inject
    @Named("CabernetSauvignon")
    Brandy mCSBrandy;

    public OtherTest() {
        DaggerBrandyComponent.create().inject(this);
    }

    @Override
    public String toString() {
        return mBrandy.toString()+"\n"+mCSBrandy.toString();
    }
}

在我們的測試用例中輸出System.out.println(new OtherTest().toString());,結(jié)果如下所示:

Scope的障眼法

可以看到我們又生成了一個新的蒸餾器,也就是說我們的 Distiller 類并不是一個真正的單例碳褒,但是在一個用例(一個被注入類)的范圍內(nèi),他確實是一個“單例”看疗。這也就是我所說的沙峻,@Scope 注釋的障眼法(@Singleton 的實質(zhì)就是一個 @Scope)。

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

@Scope 字面意思是范圍两芳,實際使用的效果我們可以看出在相同范圍內(nèi)专酗,只會存在一個該實例。那么這個范圍到底是什么盗扇?我的理解是:調(diào)用注入者的生命周期祷肯,就是這個標(biāo)注的范圍。比如 TestDagger 類調(diào)用了 DaggerBrandyComponent.create().inject(this); 進行了注入疗隶,在這個類的生命周期里佑笋,會復(fù)用 Distiller 類的實例。

可以很容易的證明我上面的這段文字斑鼻,我們自行實現(xiàn)一個 Scope 注解:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface Lalala {}

運行的結(jié)果與上面是完全相同的蒋纬!充分證明了 @Scope 注解的本質(zhì)就是在同生命周期中復(fù)用有注解的實例。
:提供實例的方法坚弱、或者含有@Inject的類蜀备,其 Scope 名稱必須與對應(yīng)的 Component 完全一致!

只要一個蒸餾器

上面說了荒叶,所謂的 Singleton 注解只是一個官方已經(jīng)定義好的 @Scope碾阁,那我們怎么才能真正的實現(xiàn)一個單例的蒸餾器呢?

首先我們在介紹一個 @Component 注解的參數(shù) dependencies (依賴)些楣,通過依賴我們可以將注射器進行“繼承”脂凶,Show me the code!

新建一個接口愁茁,這個接口中有一個方法蚕钦,返回值是 Distiller,注意其中的 Scope 注解鹅很,使用的是與 Distiller 類相同的注解嘶居。那么這個 Component 可以為我們提供 Distiller 類的注入。注意其中方法名是可以隨便寫的促煮,這跟 @Provides 注解是一樣的邮屁,Dagger 只關(guān)心返回值整袁。

@Component
@Singleton
public interface BaseComponent {
    Distiller anyName();
}

修改 BrandyComponent 類如下:

@Component(modules = BrandyModule.class,dependencies = BaseComponent.class)
@Lalala
public interface BrandyComponent {
    void inject(MainActivity mainActivity);
    void inject(TestDagger testDagger);
    void inject(OtherTest otherTest);
}

注意這里我使用的是剛剛自行創(chuàng)建的 Scope 注解,因為 Component 的 Scope 不能相同樱报。重新編譯代碼后會發(fā)現(xiàn)報錯了葬项,是因為我們原來使用的注入是DaggerBrandyComponent.create().inject(this);,當(dāng)我們?yōu)?BrandyComponent 添加依賴后迹蛤,就不能再使用 create 方法來生成 Component 的實例了民珍,只能使用 builder 方法來構(gòu)建,而且我們還必須要為 builder 添加 baseComponent(BaseComponent baseComponent) 這一方法盗飒;

前面我們已經(jīng)說到嚷量,@Scope 注解的本質(zhì)是在同生命周期內(nèi)復(fù)用實例。我們在一個單例中實現(xiàn) BaseComponent (單例模式的生命周期就是軟件的生命周期)逆趣,那么這個注射器可以注入的實例就將都是單例模式蝶溶。

為了驗證我們的說法:我們創(chuàng)建一個單例:

public class Singleton {

    private BaseComponent mBaseComponent;

    private Singleton() {
        mBaseComponent = DaggerBaseComponent.create();
    }

    private static class SingletonInstance {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonInstance.INSTANCE;
    }

    public BaseComponent getBaseComponent() {
        return mBaseComponent;
    }
}

將原來的注入方法DaggerBrandyComponent.create().inject(this);修改為

DaggerBrandyComponent
        .builder()
        .baseComponent(Singleton.getInstance().getBaseComponent())
        .build()
        .inject(this);

再次運行代碼:


實現(xiàn)了真正的單例

這次我們就真正實現(xiàn)了被注入對象單例了!

在 Android 中我們有一個現(xiàn)成的單例模式可用宣渗,那就是我們的 Application 類抖所,我們只要寫下如下代碼就可以實現(xiàn)上述效果:

public class MyApp extends Application {

    private BaseComponent mBaseComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        mBaseComponent = DaggerBaseComponent.create();
    }

    public BaseComponent getBaseComponent() {
        return mBaseComponent;
    }
}

如果 BaseComponent 需要使用 Module 的話,就將 BaseComponent 實例獲取方式修改為: mBaseComponent = DaggerBaseComponent.builder().baseModule(new BaseModule()).build();

總結(jié)要點:

  1. Component 與 Module 的 Scope 必須相同痕囱;
  2. Component 與 被依賴的 Component 的 Scope 必須不同田轧;
  3. 如果 Component 有依賴,則只能使用 builder 方式來構(gòu)建 Component 對象鞍恢,同時必須傳入被依賴的 Component傻粘;
  4. 被依賴的 Component 提供的能被注入的實例,需要在接口中用方法聲明帮掉。

好啦弦悉,本篇文章到此也就告一段落了,對于 Dagger 的使用蟆炊,相比你也已經(jīng)有了一定的了解了稽莉,本文示例代碼在DaggerDemo,大家可以參考著代碼閱讀本文盅称,會對理解有更好的幫助肩祥!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市缩膝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌岸霹,老刑警劉巖疾层,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贡避,居然都是意外死亡痛黎,警方通過查閱死者的電腦和手機予弧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湖饱,“玉大人掖蛤,你說我怎么就攤上這事【幔” “怎么了蚓庭?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長仅仆。 經(jīng)常有香客問我器赞,道長,這世上最難降的妖魔是什么墓拜? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任港柜,我火速辦了婚禮,結(jié)果婚禮上咳榜,老公的妹妹穿的比我還像新娘夏醉。我一直安慰自己,他們只是感情好涌韩,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布畔柔。 她就那樣靜靜地躺著,像睡著了一般贸辈。 火紅的嫁衣襯著肌膚如雪释树。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天擎淤,我揣著相機與錄音奢啥,去河邊找鬼。 笑死嘴拢,一個胖子當(dāng)著我的面吹牛桩盲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播席吴,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赌结,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了孝冒?” 一聲冷哼從身側(cè)響起柬姚,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庄涡,沒想到半個月后量承,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年撕捍,在試婚紗的時候發(fā)現(xiàn)自己被綠了拿穴。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡忧风,死狀恐怖默色,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情狮腿,我是刑警寧澤腿宰,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站蚤霞,受9級特大地震影響酗失,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜昧绣,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一规肴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧夜畴,春花似錦拖刃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至税灌,卻和暖如春均函,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背菱涤。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工苞也, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粘秆。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓如迟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親攻走。 傳聞我的和親對象是個殘疾皇子殷勘,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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