0%

我所理解的Dagger2

一、简述

Dagger2是一个Android目前较为主流的依赖注入框架,最早的版本Dagger1 由Square公司开发,现在Dagger2由谷歌接手开发,主要用于模块间解耦,提高代码的健壮性和可维护性。之前也断断续续看过好多文章,今天正式学习一下。并且额外介绍下如何在Kotlin中使用Dagger2,写的不好,还请见谅!

二、注解方法

  1. @Inject: 通常在需要依赖的地方使用这个注解。换句话说,你用它告诉Dagger这个类或者字段需要依赖注入。这样,Dagger就会构造一个这个类的实例并满足他们的依赖。

  2. @Module: Modules类里面的方法专门提供依赖,所以我们定义一个类,用 @Module注解,这样Dagger在构造类的实例的时候,就知道从哪里去找到需要的 依赖。modules的一个重要特征是它们设计为分区并组合在一起(比如说,在我们的app中可以有多个组成在一起的modules)。

  3. @Provides: 在modules中,我们定义的方法是用这个注解,以此来告诉Dagger我们想要构造对象并提供这些依赖。

  4. @Component: Components从根本上来说就是一个注入器,也可以说是@Inject@Module的桥梁,它的主要作用就是连接这两个部分。Components可以提供所有定义了的类型的实例,比如:我们必须用@Component注解一个接口然后列出所有的@Modules组成该组件,如 果缺失了任何一块都会在编译的时候报错。所有的组件都可以通过它的modules知道依赖的范围。

  5. @Named:当有多个构造方法时,可以使用@Named进行标记区分

  6. @Scope: Scopes可是非常的有用,Dagger2可以通过自定义注解限定注解作用域。后面会演示一个例子,这是一个非常强大的特点,因为就如前面说的一样,没必要让每个对象都去了解如何管理他们的实例

  7. @Singleton:单例

三、入门例子

  1. 创建两个实例类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    public class MoneyController {

    public MoneyController(String s) {
    Log.e("TAG", "MoneyController: 构造方法======" + s);
    }

    public String payMoney() {
    return "payMoney";
    }
    }
    // -------------------------------------------------
    public class OrderController {

    String s;

    public OrderController(String s) {
    this.s = s;
    }

    public void order() {
    Log.e("OrderController", "order" + s);
    }
    }
  2. 创建Component,连接Module与Activity之间的桥梁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // @Modules 类似于我们的模块,提供各种实例跟对象
    @Module
    public class UserModule {

    private Context mContext;
    private String s;

    public UserModule(Context context,String s) {
    this.mContext = context;
    this.s = s;
    }

    // @Provides 在Modules中,我们定义的方法是用这个注解,以此来告诉Dagger2我们想要提供哪些实例和对象
    @Provides
    public OrderController providerOrderController() {
    return new OrderController("lisi");
    }

    @Provides
    public MoneyController providerMoneyController() {
    return new MoneyController(s);
    }
    }
  3. Inject注入

    1
    2
    3
    4
    5
    // @Component:注入器,是@Inject和@Module的桥梁,它的主要作用就是连接这两个部分
    @Component(modules = {UserModule.class})
    public interface UserComponet {
    void inject(DaggerActivity daggerActivity);
    }
  4. 在Activity中使用(需要先Rebuild一下,会自动生成一个Dagger开头的类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Inject
OrderController orderController;
@Inject
MoneyController moneyController;
---------------------------------
// 需要传参必须要用builder构建
DaggerUserComponet.builder().userModule(new UserModule(this,"sss")).build().inject(this);

// 如果构造方法中不需要参数可以直接用
// DaggerUserComponet.create()

btnMakeCoffee.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvCoffee.setText(moneyController.payMoney());
orderController.order();
}
});
  1. 如果UserModule中没有提供providerMoneyController这个方法可以怎么做呢?

它会自动调用MoneyController类中带有@Inject注解的构造方法

四、区分不同实例

如果一个类有两个或多个构造方法该如何去区分使用呢?使用`@Named`
  1. 修改一下OrderController,给它添加一个构造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class OrderController {

    String s;
    int age;

    public OrderController(String s) {
    this.s = s;
    }

    public OrderController(String s, int age) {
    this.s = s;
    this.age = age;
    }

    public void order() {
    Log.e("OrderController", "order" + s);
    }

    public void age() {
    Log.e("OrderController", "order" + s + "age" + age);
    }
    }
  2. 修改UserModule,使用@Named方法进行标记

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // @Named 有多个构造方法时使用它标注不同的名字
    // @Provides 在Modules中,我们定义的方法是用这个注解,以此来告诉Dagger2我们想要提供哪些实例和对象
    @Named("debug")
    @Provides
    public OrderController providerOrderController() {
    return new OrderController("lisi");
    }

    @Named("release")
    @Provides
    public OrderController providerUserControllerNameAndAge() {
    return new OrderController("lisi", 15);
    }
  3. 在Activity中也要这样子标注一下

    1
    2
    3
    4
    5
    6
    @Named("debug")
    @Inject
    OrderController orderController1;
    @Named("release")
    @Inject
    OrderController orderController2;
  4. 如果不满足这样子处理,可以参照源码加一个自定义注解

    1
    2
    3
    4
    5
    @Qualifier
    @Documented
    @Retention(RUNTIME)
    public @interface Debug {
    }
  5. 用4中的注解取代@Named("debug")

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Debug
    @Provides
    public OrderController providerOrderController() {
    return new OrderController("lisi");
    }

    ----------------

    @Debug
    @Inject
    OrderController orderController1;

五、模块化的三种方式

  1. 假设单独创建一个网络请求的OkHttp模块,MoneyController需要使用这个OkHttpClient

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Module
    public class HttpModule {

    // 在这里创建OkHttpClient(应该是用单例的)
    @Provides
    public OkHttpClient providerOkHttpClient(){
    return new OkHttpClient().newBuilder().build();
    }
    }
  2. 使用@Module(includes = HttpModule.class)引入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Module(includes = HttpModule.class)
    public class UserModule {

    ... ...

    // OkHttpClient来自HttpModule
    @Provides
    public MoneyController providerMoneyController(OkHttpClient okHttpClient) {
    return new MoneyController(okHttpClient);
    }
    }
  3. 在Activity中使用

    1
    2
    3
    4
    DaggerUserComponet.builder()
    .userModule(new UserModule(this))
    .httpModule(new HttpModule()) //注意这个地方
    .build().inject(this);
  4. 第二种方法:可以将HttpModule.class添加到UserComponet的@Component注解的modules中

    1
    @Component(modules = {UserModule.class, HttpModule.class})
  5. 第三种方法是使用dependencies引入一个Component

    1
    @Component(dependencies =HttpComponent.class, modules = UserModule.class)

六、在Kotlin中使用Dagger2(修改项目build)

1
2
3
4
5
6
7
8
9
10
11
12
apply plugin: 'kotlin-kapt'
... ...
kapt {
generateStubs = true
}
... ...
dependencies {
// 使用kapt
// dagger2
compile "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
}

七、全局单例(使用Kotlin实现)

声明:例子比较复杂,可以看源码(KotlinAndroidSamples),这里简要列举一下代码

  1. AppComponent

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @Singleton
    @Component(modules = arrayOf(AppModule::class, HttpModule::class))
    interface AppComponent {

    fun getContext(): App // 提供App的Context

    fun getDataManager(): DataManager //数据中心

    fun retrofitHelper(): RetrofitHelper //提供http的帮助类

    }
  2. App(继承自Application)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    class App : Application() {

    // 伴生对象
    companion object {
    lateinit var appComponent: AppComponent
    }

    override fun onCreate() {
    super.onCreate()

    appComponent = DaggerAppComponent
    .builder()
    .appModule(AppModule(this))
    .httpModule(HttpModule())
    .build()
    }

    }
  3. 在其它地方使用appComponent

    1
    2
    3
    4
    5
    6
    DaggerFragmentComponent
    .builder()
    .appComponent(App.appComponent)
    .fragmentModule(FragmentModule(this))
    .build()
    .inject(this)
  4. FragmentComponent

    1
    2
    3
    4
    5
    6
    7
    8
    @FragmentScope
    @Component(dependencies = arrayOf(AppComponent::class), modules = arrayOf(FragmentModule::class))
    interface FragmentComponent {

    val activity: Activity

    fun inject(mingJiaFragment: MingJiaFragment)
    }

八、源码地址

例子源码:https://github.com/sdwfqin/AndroidSamples
Kotlin例子源码:https://github.com/sdwfqin/KotlinAndroidSamples