Android Dagger2使用小结

都说不用 dagger2mvp 模式都是耍流氓,但是,但是我之前一直都在耍流氓?所以这两天入门了一下 dagger2

参考的项目时googlesamples/android-architecturedagger 分支。还有就是 dagger 里面自带的example。

dagger的定义

A fast dependency injector for Android and Java.
Android和Java的依赖快速注入器。

定义是如此的简洁,那么到底是撒意思呢?应该如何理解?我现在的理解就是它可以快速自动的构建出我们所需要的依赖对象,这里的依赖对象可以理解为某一个成员变量。例如在 MVP 中,VP 层就是互相关联的, V 要依赖对应的 P,而 P 也要依赖对应的 Vdagger 能解决的就是这种依赖关系,通过注入的方式,将双方的耦合再次降低,在实际的使用中体现为一个注解想要的对象就创建好了,咱们不用再去管理所依赖对象的创建等情况了。

dagger 入门

注入,肯定涉及到 将什么 注入 到哪里 的三个问题,那接下来就围绕着三个方面展开咯!

自定义 Module 注入

Module 中使用 @Provides 提供相关的注入对象是第一种实现方式。具体做法是定义一个类,加上 @Moudule 的注解。然后在里面使用 @Provides 定义返回注入对象的方法。

@Module
public final class ApplicationModule {

    private final Context mContext;

    ApplicationModule(Context context) {
        mContext = context;
    }

    @Provides
    Context provideContext() {
        return mContext;
    }

    @Provides
    DailyApiService provideDailyApiService() {
        return BaseDataManager.getDailyApiService();
    }
    @Provides
    int provideAge() {
        return 500;
    }
}
Android Dagger2使用小结_第1张图片
Provider.png

关于 Module 中的方法,按照官方解释咱们需要有以下的注意事项,每个方法应该是无参的,返回的就是需要注入的类型的。

构造方法注入

如果我们可以访问对应的构造方法,那儿这里也提供了另外一种注入的方式,就是在构造方法是使用 @Inject 的注解了。

Android Dagger2使用小结_第2张图片
Inject相关.png

这个注解可以定义在构造方法上,方法上,字段上,但是这里有一个优先顺序,就是最先执行构造方法上的,其次是相关字段的,最后就是相关方法的。字段的权限不能是私有的。如果有多个构造方法,@Inject只能指定其中的一个。

@Inject
public Person(String birthday,  String name) {
    this.birthday = birthday;
    this.name = name;
}

到这里基本上差不多说完了第一部分 将什么 的问题。

注入 到哪里

对于 注入 到哪里 这两个问题,这里首先要引入 @Component 的注解了,举个场景,例如我们在 Activity 里经常会使用到全局的 Application ,在 Application 的初始化中也可能初始化好某些类( ApiService ),那么这个关系就是 Activity 依赖 Application , Application 依赖 ApiService

首先我们定义一个 AppComponent ,这是一个接口类,dagger 会自动生成具体实现类。 @Component 定义了需要注入的 Modules 对象 还有可以指定 所依赖的其他 Component 组件。对于 @Component 的使用,它应该定义在接口或者抽象类中,并且里面至少要有一个方法,至于每个方法的命名,没有特别的指定符合相关命名规范就好了。

@Singleton
@Component(modules = { ApplicationModule.class})
public interface AppComponent {

    Context context();  

    DailyApiService getdailyApiService();  

    int getAge();
}

通过以上步骤,就已经定义好了 Component 和需要注入的 Module 了,然后在 APP 中来试着注入我们定义的 age 字段看看。这里第一个问题是咱们怎么获取对应的 AppComponent 实现类呢?定义好了 ComponentModule 之后记得 rebuild 一下,然后 dagger 会自动生成对应的 Component 实现类,名字的话就是 Dagger+ComponentName (如果你是内部类的话似乎还有外部的 ClassName ),然后调用其的 builder() 方法,传入依赖的 Module 或者 Component ,最后就创建好了对应的实现类了。

但是到这里,你会发现 log 显示的 age 根本就不是我们指定的 500,为什么呢?怎么就没有注入成功呢?!因为我们还没有执行真正的注入这个动作啊,还缺了最后一个方法,执行我们的注入,上面只是把对应的Component 创建出来了而已,并没有注入呢!那么最后这一步怎么定义呢?

Android Dagger2使用小结_第3张图片
Inject_Method.png

根据文档,我们需要在 AppComponent 接口中再定义一个方法,这个方法只能有一个参数,这个参数就是指定我们之前所说的哪里,这个方法可以返回 void 或者该类型,咱们通常的写法都是返回 void 的。在接口中添加该方法,修改了 Component 记得要 rebuild 哟。

void inject(App app);

App 中最后调用 mAppComponent.inject(this) ,再次运行,这次就可以看到已经注入成功了,到这里,dagger 的入门就搞定了。用一个比喻就是 Component 相当于一个注射器,是个容器,还有一个针头,里面装的就是使用 @Provides 或者 @Inject 定义的需要注入的对象,通过inject(Type type) 的方法指定需要注入到指定的对象中,这样就完成了整个注入过程。

dagger进阶

相同类型多次注入

@Qualifier
如果我们在 Module 中有重复的类型返回,例如我定义两个 int 类型的providesModule 中的话,编译直接会报错:

xxx is bound multiple times:

那如果我们真的需要注入同一类型多次呢,这个问题总会有解决方案的吧?要是真的这么坑估计也没人用 dagger 了吧!哈哈。下面说明这个问题应该如何解决。

dagger 中具有相同类型返回的情况时,可以使用@Qualifier 的注解来区分,而 dagger 已经为我们提供了一个其子类 @Named("xx") 的注解。

例如在上面的 Module 中可以如果需要返回两个 int 类型的话需要这么声明:

@Singleton
@Provides
@Named("number")
int provideNumber() {
    return 5;
}

@Provides
@Named("age")
int provideAge() {
    return 500;
}

并且在调用的位置,也需要使用同样的 @Named("age") 的来指定你需要注入的是哪个具体的对象。

懒加载

在上面的比喻中,一针扎进去,是撒都给你打进去了,那么如果有些我想要在调用的时候才加载呢?这里 dagger 提供了 Lazy 的方式来注入。

对应的获取就是:

@Inject
Lazy str;//延迟加载

你没有看错,就是这么简单,获取时使用 Lazy 包装一下就行了,在真正需要加载的时候调用 str.get()来加载。

获取某一个注入对象多次

比如我们需要一次性创建出10个 Person 对象,这个怎么处理呢?这里就需要使用 Provider 的类来指定了。获取方式也是 get() 就好了,每次都会创建出行的对象。

Component 的依赖

如果我们定义了某一个 ActivityComponent ,并且它依赖咱们的 AppComponent 里面的 APIService 的话就要这样定义了:

@ActivityScope
@Component(dependencies = AppComponent.class, modules = ListStoryPresenterModule.class)
public interface ListStoryComponent {
  void inject(ListStory view);
}

AppComponent 中需要将获取 APIService 的方法暴露出来,不然还是无法注入成功的。

那如果我觉得暴露这些方法太麻烦了,那需要怎么办呢?最简单就是使用 @SubComponent ,在所属的父 Component 中定义一个 SubComponent,该 SubComponent 中将会包含父 Component 的所有方法。父 Component 不显示声明都可以。

@ActivityScope
@Component(dependencies = AppComponent.class, modules = ListStoryPresenterModule.class)
public interface ListStoryComponent {

    String getName(String name);

    ListPresenter.View getView();

    SubComponent getSubComponent(SubModule module);

}

@Subcomponent(modules = SubModule.class)
public interface SubComponent {

    void inject(ListStory context);
}

子类注入

void inject(Activity view) 注入的方法是可以接受该类型的子类的,但是在子类中定义的 @Inject 是无效的,比如说上面说的那个 AppComponent 的例子中如果最后申明的注入方法是:

void inject(Application app);

你会发现,age 的值一样不会注入成功的。再举官方的实例:我们定义了 void inject(Self self) 的方法,只有A 和 B 能注入,其子类中的C是无法注入的。

 class Parent {
    @Inject A a;
 }

 class Self extends Parent {
    @Inject B b;
 }

 class Child extends Self {
    @Inject C c;
 }

那么需要怎么解决这个问题呢?目前我的感觉只能在对应的 Component 中定义一系列需要实现的子类的注入方法咯。

void inject(App app);

void inject(SubActivityA activity);
void inject(SubActivityB activity);

作用域

最后还有一个问题就是 Component 的作用域,@Scope 就是其中来限定相关作用域,例如说我们需要初始化一些全局的工具类,像 GsonSharedPreferences 这些,那我们就需要一个单例就行了,那么 dagger 如何保证注入的对象就是单例的呢?那就需要使用这个@Scope的注解了。然后应该在基类里面初始化一个全局通用的 Component ,然后在每个子类里面一次调用注入的方法。另外这个注解 ComponentModule 是要成对使用的,不然会报错的。然后在具体的子类中实现initInject()的方法。 对了,@Scope 已经实现了的是 @Singleton,我们可以直接使用 。

public abstract class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AppComponent appComponent = ((App) getApplication()).getAppComponent();
        initInject(appComponent);
    }

    public abstract void initInject(AppComponent appComponent);
}

小结

使用 dagger 的好处就是在有依赖对象的时候,进一步不用去考虑所依赖对象的创建什么的,对我们就是隐藏了构造对应对象的过程,省去了创建的相关代码。当然我也只是这两天刚刚上手使用 dagger,所以不免有相关优势没有挖掘出来,或者自己有相关的理解偏差。另外建议大家多看看 dagger的相关注解,除了是英文这个缺点,真的很详尽的。

你可能感兴趣的:(Android Dagger2使用小结)