Dagger2 基本使用

目录:
  1. 基本使用
  2. 添加 Module 使用
  3. Component 依赖
  4. Scope作用域——Singleton 单例
导入依赖

导入 Dagger2 直接参看github,Kotlin 中导入 Dagger 依赖,需要将 annotationProcessor 更改为 kapt,同时添加 kotlin-kapt 插件:

apply plugin: 'kotlin-kapt'
// ...
dependencies {
    // ...
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

    implementation 'com.google.dagger:dagger:2.15'
    kapt 'com.google.dagger:dagger-compiler:2.15'
}
基本使用

直接看栗子,简单的实现依赖注入创建 Product 实例。

首先创建一个 POJO 类 Product,并使用 @Inject 注解 Product 的无参构造方法。

class Product @Inject constructor()

然后创建一个以 @Component 注解标注 Component 接口, 并且添加一个无参的, 带参数的方法(方法名通过习惯命名为 inject)。

@Component
interface MainActivityComponent {
    fun inject(mainActivity: MainActivity)
}

创建完成 Component 接口以后,需要手动 build 一下项目, 然后 Dagger 框架将会自动生成一个名字以 Dagger 开头的 Component 接口为后缀的实现类。这里为 DaggerMainActivityComponent。

最后在MainActivity 类的 onCreate() 方法中, 将 MainActivity 注入到 Component 中.

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var product: Product
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 两种注入方式
        DaggerMainActivityComponent.create().inject(this)
//        DaggerMainActivityComponent.builder().build().inject(this)
        Log.i("MainActivity", product.toString())
    }
}

这样,我们 Dagger 的基本依赖注入的实现就完成了。

上面实现的是 Product 类的构造方法不带参数的注入,接下来看看实现构造方法带参数的注入。

再创建一个 POJO 类 Factory,该类创建一个需要 Product 实例的构造方法。

class Factory @Inject constructor(val product: Product)

MainActivityComponent 接口不需要改变,修改 MainActivity 如下:

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var factory: Factory
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // 两种注入方式
        DaggerMainActivityComponent.create().inject(this)
//        DaggerMainActivityComponent.builder().build().inject(this)
        Log.i("MainActivity", "product: ${factory.product}, factory: $factory")
    }
}

总结一下:

1. 将 MainActivity 注入到 Component 中.
2. 查找 MainActivity 是否存在被 Inject 注解的变量, 找到 factory 变量.
3. 准备实例化 Factory, 然后在 Factory 类中查找被 Inject 注解的构造方法.
4. 找到被注解的构造方法后, 调用该构造方法时, 发现需要 Product 实例才能调用构造方法.
5. Dagger 框架又去 Product 类中去查找被 Inject 注解的构造方法, 然后创建一个 Product 实例.
6. 最后将 Product 的实例交给 Factory 去创建 factory 实例.
添加 Module 使用

使用 @Inject 注入的时候,不是无所不能的,比如对于第三方框架,我们是不能去为其构造方法添加 @Inject 注解的,也不能构造接口。

那么这个时候,我就需要使用 @Module 注解了,那么接下来还是通过一个栗子来说明其用法。

首先模拟两个第三方类 OkHttpClient 和 RetrofitManager, 表示我们不可更改的类。

// OkHttpClient.kt
class OkHttpClient {
    var cacheSize: Int = 0
}
// RetrofitManager.kt
class RetrofitManger(val client: OkHttpClient)

然后创建一个 Module 类,在其内创建一个被 @Provides 注解的,并且返回值为 OkHttpClient 类型的无参方法。同样的,还可以创建一个返回值为 RetrofitManager 类型,但是有参方法,参数为 OkHttpClient 实例。

@Module
class HttpActivityModule {
    // 方法名无关紧要, 主要是返回值
    @Provides
    fun provideHttpClient(): OkHttpClient {
        Log.i("HttpActivity", "HttpActivityModule...")
        return OkHttpClient()
    }

    @Provides
    fun provideRetrofitManager(client: OkHttpClient): RetrofitManger {
        return RetrofitManger(client)
    }
}

然后创建 Component,将 Module 类添加到注解 @Component 的 modules 属性上,其作用是将 Module 类注册到 Component 类中,表示在进行依赖注入时,Component 首先去该 Module 中查找提供依赖注入的具体对象。

@Component(modules = [HttpActivityModule::class])
interface HttpActivityComponent {
    fun inject(httpActivity: HttpActivity)
}

最后,将 HttpActivity 注入到 Component 接口实现类中,HttpActivity 的代码如下:

class HttpActivity : AppCompatActivity() {
    @Inject
    lateinit var retrofitManger: RetrofitManger

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_http)
        // Module 无构造参数时, 注入的写法可以有 3 种.
//        DaggerHttpActivityComponent.create().inject(this)
//        DaggerHttpActivityComponent.builder().build().inject(this)
        DaggerHttpActivityComponent.builder()
                .httpActivityModule(HttpActivityModule())
                .build().inject(this)
        Log.i("HttpActivity", "OkHttpClient: ${retrofitManger.client}, " +
                "RetrofitManager: $retrofitManger")
    }
}

这样就实现了第三方类的依赖注入。但是如果我们需要传入一些配置去构造第三方类的时候,我们可以通过使用 Module 的构造参数传入。

对 HttpActivityModule 进行修改,添加 Module 的构造方法,为第三方类 OkHttpClient 添加一个 缓存大小 的配置参数。

@Module
class HttpActivityModule(private val cacheSize: Int) {
    @Provides
    fun provideHttpClient(): OkHttpClient {
        Log.i("HttpActivity", "HttpActivityModule...")
        val client = OkHttpClient()
        client.cacheSize = this.cacheSize
        return client
    }

    @Provides
    fun provideRetrofitManager(client: OkHttpClient): RetrofitManger {
        return RetrofitManger(client)
    }
}

这样在 HttpActivity 注入的时候,就需要传入一个 缓存大小 的实参了。

class HttpActivity : AppCompatActivity() {
    @Inject
    lateinit var retrofitManger: RetrofitManger

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_http)
        // Module 有构造参数时,只有这种写法
        DaggerHttpActivityComponent.builder()
                .httpActivityModule(HttpActivityModule(100))
                .build().inject(this)
        Log.i("HttpActivity", "OkHttpClient: ${retrofitManger.client}, " +
                "RetrofitManager: $retrofitManger")
    }
}

总结:

1. 将 HttpActivity 注入到 Component 中.
2. 查找 HttpActivity 是否存在被 Inject 注解的变量, 找到 retrofitManger 变量.
3. 准备实例化 RetrofitManger, 这个时候因为在 Component 应用了 Module.
4. 所以会先去 Module 中查找被 Provides 注解, 并且返回值为 RetrofitManger 的方法.
5. 如果有查找到该方法, 但发现该方法需要 OkHttpClient 参数.
     5.1 将继续在 Module 中查找被 Provides 注解, 并且返回值为 OkHttpClient 的方法.
     5.2 如果查找到该方法, 那么直接调用该方法实例化 OkHttpClient.
     5.3 最后将 OkHttpClient 的实例交给 步骤5 中找到的方法, 实例化 retrofitManger 对象, 结束.
     5.4 如果没有找到该该方法, 将去 OkHttpClient 类中查找被 Inject 注解了构造方法.
     5.5 找到构造方法之后, 跳转到 步骤 5.3, 结束.
6. 如果在 Module 中没有找到, 那么将会去 RetrofitManger 类中查找被 Inject 注解了构造方法.
7. 找到构造方法之后, 跳转到 步骤 5.
Component 依赖

当两个 Component 都可以实现同样的功能,那么我们就没必要再去重复写一遍该功能,这个时候我们可以使用 Component 依赖来解决这个问题,类似继承的作用。

Component 依赖可以有两种写法:

a. 使用注解 @Component 中的 dependence 属性

b. 使用 @Subcomponent 注解

a. 使用注解 @Component 中的 dependence 属性

这里也直接举个栗子来说明其用法,我们实现在 Activity 和 Fragment 中都依赖注入 Hello 对象。

首先创建一个 Hello 类.

class Hello

然后创建 Module 类,在其内提供 Hello 实例的依赖注入方法。

@Module
class HelloActivityModule {
    @Provides
    fun provideHello(): Hello {
        return Hello()
    }
}

创建父 Component 接口 HelloActivityComponent,在其内注册 Module,添加需要注入的目标位置方法 inject(target),并且还需暴露给子 Component 共享的依赖注入方法 provideHello()。

@Component(modules = [HelloActivityModule::class])
interface HelloActivityComponent {
    // 需要注入的位置, 这里为 HelloActivity.
    fun inject(helloActivity: HelloActivity)
    // 暴露给子 Component 使用, 名字不必须和 Module 中相同, 
    // 但返回值必须是子 Component 需要的依赖注入对象.
    fun provideHello(): Hello
}

然后在 HttpActivity 添加依赖注入的变量 hello,并且提供父 Component 实例给子 Component 使用,代码如下:

class HelloActivity : AppCompatActivity() {
    @Inject
    lateinit var hello: Hello
    lateinit var helloActivityComponent: HelloActivityComponent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_hello)
        helloActivityComponent = DaggerHelloActivityComponent.create()
        helloActivityComponent.inject(this)

        supportFragmentManager.beginTransaction().add(R.id.fl_hello, HelloFragment()).commit()

        Log.i("HelloActivity", hello.toString())
    }
}

接着创建子 Component 接口 HelloFragmentComponent,该 Component 通过 dependencies 属性依赖于 HelloActivityComponent,代码如下:

@Component(dependencies = [HelloActivityComponent::class])
interface HelloFragmentComponent {
    fun inject(fragment: HelloFragment)
}

最后在 HelloFragment 类中,获取父 Component 实现类,并且将父 Component 实现类设置到子 Component 实现类中,已达到需要注入 hello 对象时,通过父 Component 实现类调用 provideHello() 方法, 获取到共享 Module 实现类去获取 hello 实例的效果。

class HelloFragment : Fragment() {
    @Inject
    lateinit var hello: Hello
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_hello, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 获取父 Component 实现类.
        val helloActivityComponent = (context as HelloActivity).helloActivityComponent
        // 将父 Component 实现类添加到子 Component 实现类中, 以间接的获取 hello 实例.
        DaggerHelloFragmentComponent.builder()
                .helloActivityComponent(helloActivityComponent)
                .build().inject(this)

        Log.i("HelloFragment", hello.toString())
    }
}

最后运行项目,将在 HelloActivity 和 HelloFragment 都能创建两个不同的 Hello 实例并打印出来。

需要注意点:

1、父 Component 中要显式的写出需要暴露可提供给子 Component 的方法;
2、子 Component 在注解中使用 dependencies= 来连接父 Component;
3、注意子 Component 实例化方式。
b. 使用 @Subcomponent 注解

将上面的栗子改造一下,新创建一个父 Component 接口 SubHelloActivityComponent 和子 Component 接口 SubHelloFragmentComponent.

首先定义子 Component 接口,以注解 @Subcomponent 标注。但是通过 @Subcomponent 注解标注的接口 Dagger 框架不会为该 Component 自动生成实现类,当在其父 Component 中被定义时,将会以内部类的形式实现该子 Component 接口:

// SubHelloFragmentComponent.kt
@Subcomponent
interface SubHelloFragmentComponent {
    fun inject(helloFragment: HelloFragment)
}

然后定义父 Component 接口,在其内声明有 @Subcomponent 注解的 Component 作用返回值的方法(即声明获取子 Component 的方法)。

// SubHelloActivityComponent.kt
@Component(modules = [HelloActivityModule::class])
interface SubHelloActivityComponent {
    fun inject(helloActivity: HelloActivity)
    // 定义返回值为子 Component 的方法。在 Dagger 框架为该 Component
    // 创建实现类 DaggerSubHelloActivityComponent 时, 将会同时为
    // 其子 Component 接口以内部类的形式创建实例类并返回。
    fun subHelloFragmentComponent(): SubHelloFragmentComponent
}

HelloActivity 将改为:

class HelloActivity : AppCompatActivity() {
    @Inject
    lateinit var hello: Hello
    lateinit var subHelloActivityComponent: SubHelloActivityComponent

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_hello)
        subHelloActivityComponent = DaggerSubHelloActivityComponent.create()
        subHelloActivityComponent.inject(this)

        supportFragmentManager.beginTransaction().add(R.id.fl_hello, HelloFragment()).commit()
        Log.i("HelloActivity", hello.toString())
    }
}

HelloFragment 将改为:

class HelloFragment : Fragment() {
    @Inject
    lateinit var hello: Hello

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_hello, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        // 通过父 Component 获取子 Component
        (context as HelloActivity).subHelloActivityComponent
                .subHelloFragmentComponent().inject(this)

        Log.i("HelloFragment", hello.toString())
    }
}

实现的效果和使用 dependencies 一样。

使用 @Subcomponent 注解需要注意点的:

1、先定义子 Component,使用 @Subcomponent 标注(不可同时再使用 @Component);
2、父 Component 中定义获得子 Component 的方法;
3、注意子 Component 实例化方式。
添加配置(参数)

上面实现的是直接在父 Component 中提供了返回子 Component 的方法,忽略了子 Component 构建时需要传入参数的情况,当然不需要传入参数也可以用这种方法;[重点:子 Component 构建时传入参数的话就需要在子 Component 中使用 @Subcomponent.Builder 注解(接口或抽象类)去添加]。

那么还等什么,直接上代码。

创建 POJO 类 AppBean 和 ActivityBean.

class AppBean
class ActivityBean

然后创建 Module 类 AppModule 和 MainActivityModule.

@Module
class AppModule {
    // 提供 AppBean 实例的依赖注入
    @Provides
    fun provideAppBean(): AppBean {
        return AppBean()
    }
}

@Module
class MainActivityModule(private val activityBean: ActivityBean) {
    // // 提供 ActivityBean 实例的依赖注入
    @Provides
    fun provideActivityBean(): ActivityBean {
        return activityBean
    }
}

接着创建 Component 接口 APPComponent 和 MainActivityComponent.

@Component(modules = [AppModule::class])
interface AppComponent {
    // 添加返回值为被 @Subcomponent 注解的子 Component 类型的方法
    fun mainBuilder(): MainActivityComponent.MainBuilder
}

@Subcomponent(modules = [MainActivityModule::class])
interface MainActivityComponent {
    fun inject(mainActivity: MainActivity)

    // 注意, 这是 @Subcomponent.
    // 通过 @Subcomponent.Builder 注解创建一个 Builder 接口/抽象类,
    // 为其注册的 Module 添加配置参数.
    @Subcomponent.Builder
    interface MainBuilder {
        fun mainActivityModule(module: MainActivityModule): MainBuilder
        fun build(): MainActivityComponent
    }
}

接着在 Application 中创建父 Component 接口 AppComponent 实例。

// 需要将其配置到清单文件中.
class App : Application() {
    lateinit var appComponent: AppComponent
    override fun onCreate() {
        super.onCreate()
        appComponent = DaggerAppComponent.create()
    }
}

最后在 MainActivity 中对 AppBean 和 ActivityBean 进行依赖注入:

class MainActivity : AppCompatActivity() {
    @Inject lateinit var appBean: AppBean
    @Inject lateinit var activityBean: ActivityBean
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        // 获取 DaggerAppComponent 实现类实例, 并获取其子 Component 的 Builder 实例,
        // 通过该 Builder 实例为其注册的 Module 添加配置参数.
        (application as App).appComponent.mainBuilder()
                .mainActivityModule(MainActivityModule(ActivityBean()))
                .build()
                .inject(this)
        Log.e("MainActivity", "appBean: $appBean, activityBean: $activityBean")
    }
}
Scope作用域——Singleton 单例

如果需要同一个 Component 注入的变量为单例,需要为其 依赖注入类 和 Component 接口都添加 @Singleton 注解。

首先看看没有使用 @Singleton 注解的栗子:

创建 依赖注入类 Book 和 Component 接口 BookActivityComponent.

class Book @Inject constructor()

@Component
interface BookActivityComponent  {
    fun inject(bookActivity: BookActivity)
}

最后在 BookActivity 对 book1 和 book2 进行依赖注入,打印出来查看是否为同一个实例。

class BookActivity : AppCompatActivity() {
    @Inject lateinit var book1: Book
    @Inject lateinit var book2: Book
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_book)
        DaggerBookActivityComponent.create().inject(this)
        Log.i("BookActivity", "book1: $book1, book2: $book2")
    }
}

运行程序,结果如下:

I/BookActivity: book1: com.np.daggerproject.singleton.Book@8d8f8d2, book2: com.np.daggerproject.singleton.Book@49e1da3

有运行结果可得,book1 和 book2 是两个不同的实例.

接下来,我们为 Book 类和 Component 接口添加 @Singleton 注解。

@Singleton
class Book @Inject constructor()

@Singleton
@Component
interface BookActivityComponent  {
    fun inject(bookActivity: BookActivity)
}

BookActivity 代码不变,运行程序,结果如下:

I/BookActivity: book1: com.np.daggerproject.singleton.Book@8d8f8d2, book2: com.np.daggerproject.singleton.Book@8d8f8d2

可以看出,book1 和 book2 为同一个对象。

但是,这样只能使在同一个注入目标类中的 Book 对象保持单例, 也就是说该单例范围只在该注入目标类中(如在 BookActivity 中保持单例, 但是在 Book2Activity 中就不能保持单例了)。

那么怎样才能使依赖注入对象 Book 全局保持单例呢。这个时候需要将其 Component 接口在 Application 中初始化, 然后在 BookActivity 中获取该 Component 实例注入 BookActivity,在 KotlinBookActivity 中也获取同一个 Component 实例注入 KotlinBookActivity,这样他们获取的 Book 对象将会是同一个。

将 BookActivityComponent 接口在 Application 中实例化。

class App : Application() {
    lateinit var bookActivityComponent: BookActivityComponent
    override fun onCreate() {
        super.onCreate()
        bookActivityComponent = DaggerBookActivityComponent.create()
    }
}

然后在 BookActivity 中获取该 Component 实例。

class BookActivity : AppCompatActivity() {
    @Inject lateinit var book1: Book
    @Inject lateinit var book2: Book
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_book)
        (application as App).bookActivityComponent.inject(this)
        Log.e("BookActivity", "book1: $book1, book2: $book2")

        button.setOnClickListener { startActivity(
            Intent(this, KotlinBookActivity::class.java)) }
    }
}

再在另一个 Activity 类 KotlinBookActivity 中也做同样的操作。

// 在 BookActivityComponent 添加一个 KotlinBookActivity 类为注入目标的方法
@Singleton
@Component
interface BookActivityComponent  {
    fun inject(bookActivity: BookActivity)
    fun inject(kotlinActivity: KotlinBookActivity)
}
// KotlinBookActivity.kt
class KotlinBookActivity : AppCompatActivity() {
    @Inject lateinit var book3: Book
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin_book)
        (application as App).bookActivityComponent.inject(this)
        Log.e("BookActivity", "book3: $book3")
    }
}

最后运行程序,然后点击按钮跳转到 KotlinBookActivity 界面,结果如下:

E/BookActivity: book1: com.np.daggerproject.singleton.Book@42817ef, book2: com.np.daggerproject.singleton.Book@42817ef
E/BookActivity: book3: com.np.daggerproject.singleton.Book@42817ef

从结果可以看出,不同的注入目标中的依赖注入对象 Book 为同一个实例。

参考文章链接

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,417评论 5 465
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,317评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,319评论 0 327
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,039评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,951评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,803评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,003评论 3 389
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,621评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,909评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,942评论 2 315
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,752评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,529评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,021评论 3 301
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,149评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,454评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,064评论 2 343
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,260评论 2 339

推荐阅读更多精彩内容