Dagger依賴注入框架(入門)

目錄

    1. Dagger簡介
    1. Dagger1.x,Dagger2.x簡單對比
    1. Dagger中的注解
    • @Inject
    • @Provide
    • @Module
    • @Component
    • @Binds
    • multibinds
    • 其他關鍵注解
  • 4.使用
  • 5.Dagger & Android
    • Activity的注入
    • Fragment的注入

Dagger官方文檔
Dagger&Android
Dagger分享(這一篇筆記也很好,內容全面易懂,排版也很清晰,可以參考學習)
DaggerDamo(文中的Dagger&Android的Demo都是參考這里寫的)

1. Dagger簡介:

依賴注入的框架。依賴注入的目的——解耦。

依賴注入的常規方式

如果class A中有class B的實例,就可以說A依賴于B,如MVP中,V(Activity)持有P(Presenter),V依賴于P,這個例子存在一個問題,如果有一天,B的構造方法變了,那我們就需要修改A中創建B的代碼,而且所有創建B實例的地方,都需要修改。
———— 如果使用Dagger來進行依賴注入,只需要用@Inject和其他注解就能實現。

依賴注入的幾種方式

  1. 構造函數注入
// Constructor
Client(Service service) {
    // Save the reference to the passed-in
    // service inside this client
    this.service = service;
}
  1. setter方法注入
// Setter method
public void setService(Service service) {
    // Save the reference to the passed-in
    // service inside this client
    this.service = service;
}
  1. 接口注入
// Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this
   // client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

2. Dagger1.x -> Dagger2.x

更好的性能:相較于Dagger1,它使用的預編譯期間生成代碼來完成依賴注入,而不是用的反射。大家知道反射對手機應用開發影響是比較大的,因為反射是在程序運行時加載類來進行處理所以會比較耗時,而手機硬件資源有限,所以相對來說會對性能產生一定的影響。
容易跟蹤調試:因為dagger2是使用生成代碼來實現完整依賴注入,所以完全可以在相關代碼處下斷點進行運行調試.

3. Dagger中的注解

@Inject

@Inject,兩個作用,其一:用來標記構造函數,Dagger可以調用它創建該類的實例,對外提供依賴。其二:標記依賴字段,表示該字段由Dagger提供實例。也就是標記依賴源和依賴宿主。

如果一個類有@Inject注解的字段,但是沒有@Inject注解的構造器(注入jar包中的類),可以使用方法注入——@Provide注解。

@Inject不生效的地方:

  1. 注解接口不生效,因為接口不能被實例化。
  2. 注入第三方jar包里的類不生效,因為這樣的類不能被@Inject注解。
  3. Configurable objects must be configured!
    對于這些不能使用@Inject進行注入的地方,可以使用@Provider注解的方法提供依賴。

@Provide

@Provide用來注解方法,該方法用于提供依賴。當需要Dagger提供依賴注入的時候,這個方法會被調用——創建依賴對象。

@Module

@Module用于注解類,該類用于提供依賴。

@Component

@Component一般用來標注接口,被標注了Component的接口,在編譯時會由Dagger生成接口的實例,作為提供依賴方(Module)和依賴宿主的橋梁,或者說注入器,把相關依賴注入到宿主容器中。

@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
    CoffeeMaker maker();
}

Component中有兩個方法:

  • Provision:Provision方法如上,不接收參數,返回值是被注入的依賴類型,這個class必須有@Inject注解的構造器,或者有module創建這個依賴的實例。
  • Member-injection: Member-injection方法有一個參數,用于傳入依賴的宿主,如果依賴宿主不能由Dagger實例化(如Activity),就可以使用這種方式進行注入。
void injectMembers(T)

@Binds

如果我們需要一個一般化的類型,而Dagger的object graph中已經一個該類型的子類,那么用Provide是這樣做的:

@Provides 
static Pump providePump(Thermosiphon pump) {
    return  pump;
}

Binds 注解可以這么做:

@Binds 
static abstract Pump providePump(Thermosiphon pump);

@Binds 用于注解方法聲明,如果 module 是接口,那么 binds 方法就是一個方法聲明,如果 module 是類,那么 binds 方法就是一個abstract方法。 binds 方法有且只有一個參數,這個參數可以賦值給返回值類型。也就是說參數是返回接口的子類。

注意 Module 不能同時用 Binds 方法和 Provide 非static方法,因為 Binds 方法只是一個方法聲明沒有實現,一旦 Module 有了 Provide 方法(非static),意味著這個 Module 必須實例化,所以方法聲明就必須得有實現,這便起了沖突。當然,如果 Provide 方法是static的,那也是可以的。

multibinds

multibinds不是一個注解,而是用于multibinds的一組注解。用于將對象綁定到集合中。

  1. 注解 Provider 方法,表示提供的對象會被注入 Set/Map。

Set:

  • @IntoSet 將一個元素注入 Set
  • @ElementsIntoSet 將一個集合注入 Set

Map
@IntoMap 表示該方法的返回值作為 Map 的 value 注入 Map 中,另外 Key 由下面的注解提供:

  • @Stringkey 提供字符串作為 key
  • @IntKey 提供 int
  • @ClassKey 提供 class
  • 也可用 @MapKey 自定義 key 的類型
    key 都是常量
    在dagger-android中我們會看到 @ActivityKey 注解,就是通過@MapKey 定義的。
  1. 注解集合Set/Map,@Multibinds
    對于 @Inject 注解的集合,找不到元素提供方的話,dagger 會在編譯期報錯:
java.util.Map<java.lang.String,java.lang.String> cannot be provided without an @Provides- or @Produces-annotated method.

如果有在 Module 中聲明了 @Multibinds,便不會報錯,而是得到一個空集合。具體例子可以看dagger-android中的Activity注入。

注意:Module注解的優先級高于構造器注解。

其他關鍵注解

@Scope: Dagger2可以通過自定義注解限定注解作用域,來管理每個對象實例的生命周期,在它的生命周期范圍內,提供單例。

@Qualifier: 有時只靠類型,不足以識別一個依賴,可能無法找到依賴提供方。比如我們注入的是一個特殊的接口實現,與默認實現有區別(可能是構造參數不同),這時,我們需要@Qualifiers聲明一個額外的注解,用于區分依賴。

例如:在Android中,我們會需要不同類型的context,所以我們就可以定義 Qualifier注解@perApp@perActivity,這樣當注入一個context的時候,我們就可以告訴 Dagger我們想要哪種類型的context。

@Singleton:可以在@Providers方法或依賴類聲明中使用該注解。可在全局范圍內提供注入類的單例。

@Provides 
@Singleton 
static Heater provideHeater() {
  return new ElectricHeater();
}

@Singleton
class CoffeeMaker {
  ...
}  

Component需要和Scope關聯起來,一個Component不能既有@Singleton的依賴,又有@RequestScoped的依賴,因為二者的作用域不同,生命周期也不同。

@Component(modules = DripCoffeeModule.class)
@Singleton
interface CoffeeShop {
  CoffeeMaker maker();
}

4.使用

背景:假設我們有一個咖啡機(CoffeeMaker),可以加熱咖啡(Heater),有泵可以抽出咖啡(Pump)。現在,我們需要在CoffeeMaker中注入這兩個裝置,完成功能組合。

1. 定義依賴接口

//加熱裝置
public interface Heater {
    void on();
    void off();
    boolean isHot();
}

public interface Pump {
    void pump();
}

2.定義依賴宿主CoffeeMaker,使用@Inject注入依賴。

注意:我們注入的依賴,除了有@Inject注解的字段之外,還需要通過構造函數參數傳入。

public class CoffeeMaker {
    @Inject Heater heater;
    @Inject Pump pump;

    CoffeeMaker(Heater heater, Pump pump) {
        this.heater = heater;
        this.pump = pump;
    }

    public void brew() {
        heater.on();
        pump.pump();
        System.out.println(" [_]P coffee! [_]P ");
        heater.off();
    }
}

3.使用@Module,定義依賴提供方。

ElectricHeater是Heater的實現類。Thermosiphon是Pump的實現類。

@Module
public class DripCoffeeModule {
    @Provides static Heater provideHeater() {
        return new ElectricHeater();
    }

    @Provides static Pump providePump(Thermosiphon pump) {
        return  pump;
    }
}

4.使用@Component定義注入器接口,完成Module和依賴宿主的關聯。

@Component(modules = DripCoffeeModule.class)
public interface CoffeeShop {
    CoffeeMaker maker();
}

返回的CoffeeMaker對象也由Dagger提供。修改CoffeeMaker的構造函數,添加@Inject

@Inject
CoffeeMaker(Heater heater, Pump pump) {
    this.heater = heater;
    this.pump = pump;
}

5.在Activity或Main方法中通過Component獲取CoffeeMaker實例。

可以發現,我們并沒有實現DaggerCoffeeShop,這里為什么可以調用呢?因為在編譯期間,Dagger自動為我們的Component接口生成實現類,DaggerCoffeeShop就是CoffeeShop接口的實例.
dripCoffeeModule (),該方法根據Component綁定的Module自動生成,傳入Module實例。

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
            .dripCoffeeModule(new DripCoffeeModule())
            .build();
coffeeShop.maker().brew();

以上,就完成了依賴注入。
但是存在一個問題:Module對應一個默認的構造器,Dagger在編譯期間生成的DaggerCoffeeShop,可以訪問Module,獲取依賴實例。ModuleProvide方法是靜態的,不需要我們手動構造Module實例就可以訪問其Provide方法。—— 所以,我們可以使用create()代替build()

CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();

我們使用Component關聯依賴提供方法(Module)和依賴宿主。并返回CoffeeMaker對象,這個CoffeeMaker對象如何獲取?

  1. 會通過Dagger。先找有沒有Module提供CoffeeMaker對象,如果沒有,再找有沒有@Inject注解的構造器。
  2. 找到構造器之后,發現CoffeeMaker構造器有兩個參數,那個這兩個參數也是注入的。
  3. 再找有沒有提供參數的Provider方法,因為之前聲明了Component接口,把ModuleCoffeeMaker關聯了,這里會去DripCoffeeModuleProvider方法。這里也就解釋了為什么還需要在CoffeeMaker中傳入依賴,最為構造函數的參數。
  4. 最終完成注入

5.Dagger & Android

Dagger相比于其他依賴注入框架,優點在于它完全靜態生成依賴。
Dagger在Android中使用的特點在于,Android中很多組件是由Framework進行實例化的。常規的注入方式,是通過構造函數參數傳遞依賴。但是在Android中,顯然不能由Dagger創建Activity/Fragment,無法訪問到其構造函數。所以,我們必須在組件的生命周期方法內進行成員的注入。

常規的注入方式,通過構造函數傳遞依賴

public class CoffeeMaker {
    //通過Module提供注入——DripCoffeeModule
    @Inject Heater heater;
    @Inject Pump pump;

    @Inject
    CoffeeMaker(Heater heater, Pump pump) {
        this.heater = heater;
        this.pump = pump;
    }
}

Android中的注入

/**
 * 給Application用的component,可以作為一個全局的component,如果其他的Activity需要使用,從Appliction中獲取,以此達到復用的目的。
 * 將依賴提供方AppModule和依賴宿主FeatureActivity關聯起來
 */
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {

    //需要我們自己聲明builder,通常builder都是Dagger自動生成的,但是在Android中注入Activity,需要我們自己聲明,提供component
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(App application);
        AppComponent build();//提供獲取組件的方法,這個組件bind到了Application上
    }

    /**
     * Member-injection方法,用于傳入依賴的宿主,對于Activity這樣的組件,不能由Dagger進行實例化,無法在其構造器上添加注入的參數,需要中這種方式進行注入
     */
    void inject(FeatureActivity featureActivity);
}

@Module
public class AppModule {
    @Provides
    Context provideContext(App application) {
        return application.getApplicationContext();
    }

    @Singleton
    @Provides
    SomeClientApi provideSomeClientApi() {
        return new SomeClientApiImpl();
    }
}

public class App extends Application {

    //將Component綁定到Application,Activity需要使用時,通過Application獲取該組件,達到復用的目的
    private AppComponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent
                .builder()
                .application(this)
                .build();
    }

    public AppComponent getAppComponent() {
        return appComponent;
    }
}

在Android中,framework為我們初始化了Activity,除了使用@Inject注解注入的變量之外,我們需要調用component的members injection方法完成注入。

public class FeatureActivity extends AppCompatActivity {

    //在Activity中注入api,通過Application獲取
    @Inject
    SomeClientApi mSomeClientApi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在Activity的生命周期方法內完成注入
        ((App) getApplication())
                .getAppComponent()
                .inject(this);
        //調用注入的依賴
        mSomeClientApi.report();
    }
}

以上是在Activity注入一個依賴的全部步驟,但是這樣的注入方式帶來的問題:

  • 在其他需要注入這個依賴的Activity中,會在onCreate()寫這樣的重復代碼。
  • 被注入方——Activity,它知道我們是如何注入一個依賴的,用什么注入器(Component),注入器怎么調用。這樣違反了依賴注入的原則:一個類不需要知道它如何被注入依賴的。

使用dagger-android module進行依賴注入

1. 添加dagger-android依賴
// Dagger core dependencies
implementation 'com.google.dagger:dagger:2.16'
annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
// Dagger Android dependencies
implementation 'com.google.dagger:dagger-android:2.16'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.16'
implementation 'com.google.dagger:dagger-android-support:2.16' // if you use the support libraries
2. 定義一個@Subcomponent接口

該component實現 AndroidInjector<YourActivity>接口,并聲明其內部抽象類Builder,繼承AndroidInjector.Builder<YourActivity>

@Subcomponent
public interface FeatureSubComponent extends AndroidInjector<FeatureActivity> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<FeatureActivity> {}
}
3. 定義一個Module,用來bind subComponent的Builder
@Module(subcomponents = FeatureSubComponent.class)
public abstract class BuildersModule {
    @Binds
    @IntoMap
    @ActivityKey(FeatureActivity.class)
    abstract AndroidInjector.Factory<? extends Activity> bindFeatureActivityInjectorFactory(FeatureSubComponent.Builder builder);

    // Add more bindings here for other sub components
}

把這個Module綁定到Application的注入容器(Component)中.此外還需要注入AndroidSupportInjectionModule

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class})
public interface AppComponent {

//    void inject(FeatureActivity featureActivity);
    //這里不再注入Activity,而是注入到Application中
    void inject(App app);
}
4.改造Application

實現HasActivityInjector接口,實現接口的activityInjector()方法返回注入的 DispatchingAndroidInjector<Activity>
最后在onCreate()中進行注入。

public class App extends Application implements HasActivityInjector{

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.create()
                .inject(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }
}
5. 在Activity中注入依賴

移除原來的注入代碼,改為在super.onCreate()前調用AndroidInjection.inject(this)

public class FeatureActivity extends AppCompatActivity {

    @Inject
    SomeClientApi mSomeClientApi;

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

        //調用依賴的方法
        mSomeClientApi.report();
    }
}

注入的依賴,需要宿主提供參數

有時我們注入的依賴,它的實例化參數需要宿主方提供,在沒有dagger-android 的時候,需要我們在Activity中調用inject()進行注入之前,就將Activity作為參數傳給module。如:在MVP模式中,實例化P的時候,需要傳入V的實例,需要我們在把Activity作為Module的構造函數的參數進行傳遞,再添加provider函數提供Activity。

@Module
class FeatureModule {
    private FeatureView view;
        //構造module的時候傳入view
    public FeatureModule(FeatureView view) {
        this.view = view;
    }
        //再把這個view提供出去
    @Provides FeatureView provideView() {
        return view;
    }
}

//presenter中使用構造函數注入
class FeaturePresenter {
    private final FeatureView view;
    
    @Inject
    Presenter(FeatureView view) {
        this.view = view;    
    }
    public void doSomething() {
    }
}

public class FeatureActivity extends AppCompatActivity implements FeatureView {
    @Inject FeaturePresenter presenter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //在Activity中實例化FeatureModule,并把Activity作為參數
        DaggerFeatureComponent.builder()
                .featureModule(FeatureModule(this)).build()
                .inject(this)
        
        // presenter ready to be used
        presenter.doNothing();
    }
}

但是在dagger-android中,Activity只調用AndroidInjection.inject(this)進行注入,不在Activity中初始化Module。應該怎么進行注入呢?使用dagger-android的時候,activity實例已經存在于dagger的對象圖中了,使用@Binds,我們可以從對象圖中,取出activity作為參數,具體代碼如下:

@Module
public abstract class FeatureModule {
    //使用Binds表示從Dagger的對象圖中獲取FeatureActivity實例,然后將其賦值給FeatureConstrant.View,參數是返回值的子類
    @Binds
    abstract FeatureConstrant.View provideFutureView(FeatureActivity featureActivity);
}

在presenter中添加inject構造器,view作為參數。

public class FeaturePresenter implements FeatureConstrant.Presenter {

    private FeatureConstrant.View mView;

    @Inject
    public FeaturePresenter(FeatureConstrant.View view) {
        this.mView = view;
    }
    //業務代碼
}

在component中加入這個module,最后在view中添加@Inject注解的presenter字段就OK。

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, AppModule.class, BuildersModule.class, FeatureModule.class})
public interface AppComponent {
    //...
}

注入Fragment

Fragment的注入和Activity的注入流程差不多。步驟如下:

  1. Activity實現HasFragmentInjector接口。提供DispatchingAndroidInjector<Fragment>對象。和Activity的注入有區別,Fragment是在Activity中加載的,所以在Activity中實現接口。
public class FeatureActivity extends AppCompatActivity HasFragmentInjector{

    @Inject
    DispatchingAndroidInjector<Fragment> fragmentInjector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
    }

    @Override
    public AndroidInjector<Fragment> fragmentInjector() {
        return fragmentInjector;
    }
    //other function
}
  1. 在Fragment中添加依賴,并調用AndroidInjection.inject(this)進行依賴注入。注意inject()需要在onAttach()super.onAttach(activity)前調用。
public class TextFragment extends Fragment {

    //注入的依賴
    @Inject
    SomeClientApi mSomeClientApi;

    @Override
    public void onAttach(Activity activity) {
        AndroidInjection.inject(this);
        super.onAttach(activity);
    }
    //...
}
  1. 定義subComponent,實現AndroidInjector<YourFragment>接口,定義內部類Builder。
@Subcomponent
public interface FragmentSubComponent extends AndroidInjector<TextFragment> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<TextFragment> {}
}

4.把SubComponent的Builder綁定到Module。使用FragmentKey注解內部方法。

@Module(subcomponents = {OtherSubComponent.class, FragmentSubComponent.class})
public abstract class BuildersModule {
    //add fragment sub component
    @Binds
    @IntoMap
    @FragmentKey(TextFragment.class)
    abstract AndroidInjector.Factory<? extends Fragment> bindFragmentInjectorFratory(FragmentSubComponent.Builder builder);

    // Add more bindings here for other sub components
}

5.把Module綁定到Component。

@Singleton
@Component(modules = {AndroidSupportInjectionModule.class, BuildersModule.class, OtherModule.class})
public interface AppComponent {

    //這里不再注入Activity,而是注入到Application中
    void inject(App app);
}

6.總結

這一部分先到這里,后續想到再補充。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內容