第三方-Dagger2

dagger2相关

dagger2笔记:

是个依赖注入框架。Dagger2是第一个用生成的代码实现完整堆栈的。指导原则是生成代码,模仿用户可能已经手写的代码,以确保依赖注入尽可能简单、可跟踪和高性能。

例子(MVP):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainActivity extends AppCompatActivity implements MainContract.View {
@Inject
MainPresenter mainPresenter;
//...

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

DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
//调用Presenter方法加载数据
mainPresenter.loadData();
//...
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class MainPresenter {
//MainContract是个接口,View是他的内部接口,这里看做View接口即可
private MainContract.View mView;

@Inject
MainPresenter(MainContract.View view) {
mView = view;
}
public void loadData() {
//调用model层方法,加载数据
//...
//回调方法成功时
mView.updateUI();
}

@Module
public class MainModule {
private final MainContract.View mView;

public MainModule(MainContract.View view) {
mView = view;
}

@Provides
MainView provideMainView() {
return mView;
}
}

@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
}

说明:
@Module@privides 提供 –>提供给没有构造函数的类的依赖,这些类无法用@Inject标注(比如第三方库、系统类、以及MVPContract接口)
桥梁:@Component 接口(或抽象)类上,说明modules有哪些
@Inject声明的类成员属性不能用private修饰(MainActivityMainPresenter类对象用@Inject修饰)
某个类被@Inject标记时会自动到它的构造方法中,如果这个构造方法也被@Inject标记的话,就会自动初始化这个类,从而完成依赖注入。(当然他们之间需要个@Component当桥梁产生联系)
一个Component类可以包含多个Module类,用来提供依赖

注入过程:

首先MainActivity需要依赖MainPresenter,因此,我们在里面用@Inject对MainPresenter进行标注,表明这是要注入的类。然后,我们对MainPresenter的构造函数也添加注解@Inject,此时构造函数里有一个参数MainContract.View,因为MainPresenter需要依赖MainContract.View,所以我们定义了一个类,叫做MainModule,提供一个方法provideMainView,用来提供这个依赖,这个MainView是通过MainModule的构造函数注入进来的,接着我们需要定义Component接口类,并将Module包含进来,即

1
2
3
4
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}

同时里面声明了一个inject方法,方法参数为ManActivity,用来获取MainActivity实例,以初始化在里面声明的MainPresenter

1
2
3
4
DaggerMainComponent.builder()
.mainModule(new MainModule(this))
.build()
.inject(this);
  • @Inject 带有此注解的属性或构造方法将参与到依赖注入中Dagger2会实例化有此注解的类
  • @Module 带有此注解的类,用来提供依赖,里面定义一些用@Provides注解的以provide开头的方法,这些方法就是所提供的依赖,Dagger2会在该类中寻找实例化某个类所需要的依赖。
  • @Component用来将@Inject@Module联系起来的桥梁,从@Module中获取依赖并将依赖注入给@Inject

Dagger2注入原理

通过apt插件在编译阶段生成相应的注入代码

MainPresenter实例化过程,MainPresenter会有一个工厂类,在这个类的get()中进行MainPresenter创建,而MainPresenter所需要的View依赖,是由MainModule里定义的以provide开头方法所对应的工厂类提供的。

Dagger2基础知识及在Java中使用(1)

比喻:
@Component:相当于个注射器(是接口);
@Module相当于注射液,就是数据源(是类或抽象类);
要把注射液放入指定哪个注射器如:@Component(modules=...)
@Inject相当于标注被注射体。

首先添加依赖

1
2
implementation 'com.google.dagger:dagger:2.24'
annotationProcessor "com.google.dagger:dagger-compiler:2.24"

1. @Inject & @Component可以简单实现依赖注入(不带@Module)

1
2
3
4
5
//无参构造函数的类
public class Person{
@Inject
public Person(){}
}
1
2
3
4
5
//定义我们的Component
@Component
public interface AstudyActivityComponent{
void injectTo(AstudyActivity astudyActivity);
}

此时,点下AS》build》Make Project,让apt帮我们生成代码。一般生成代码为Dagger+你定义Component类名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//在我们的Activity中
public class AstudyActivity extends BaseActivity{
@Inject
Person person;

@Override
//这里是我封装的onCreate,省略部分代码,只为理解,之后都请忽略!
protected void processLogic(){
//第一种
DaggerAstudyActivityComponent.create().injectTo(this);
//第二种(使用module传值时一定是用这个)
//DaggerAstudyActivityComponent.builder().build().injectTo(this);
}
}

2. 带@Module的使用

无参构造函数的情况

假设Human类是第三方类库,里面没有@Inject,那么我们使用@Module

1
2
3
4
//假设这个类是第三方类库中的,里面没有@Inject
public class Human{
public Human(){}
}
1
2
3
4
5
6
7
8
//定义我们的数据源Module
@Module
public class BstudyActivityModule{
@Provides
Human humanProvides(){
return new Human();
}
}
1
2
3
4
5
//定义我们的Component。带上了参数
@Component(modules = BstudyActivityModule.class)
public interface BstudyActivityModuleComponent{
void injectTo(BstudyActivity bstudyActivity);
}
  1. 改不了的第三方库等类对象要注入到指定位置则需要用到 @Module+@Component+@Inject。而且**@Module标记的对象的优先级比@Inject标记的高**
  2. 通过Module传参
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Module
public class CstudyModule {
private int money;

public CstudyModule(int money) {
this.money = money;
}

@Provides
Soul providesSoul() {
Soul soul = new Soul();
soul.setMoney(this.money);
return soul;
}

@Provides
Woman providesWoman(Soul soul) {
return new Woman(soul);
}
}

providesWoman@Provides标注,它会先去找Soul的初始化,先通过@ProvidesSoul,找到了providesSoul。假如此时没有providesSoul,则会去Soul类中找有没有被@Inject标记的构造方法,如果还没有,那么报错。

注意:

  • Module的构造函数带有参数且参数被使用的情况下,所生产的Component类就没有create()方法了
  • 在这里的module如果没有providesSoul()方法的话,还有一种情况只要在Soul的构造方法有@Inject也是可以的。
  1. 自己在Component中写@Component.Builder,并在其标记的接口内加@BindInstance,可以避免在DaggerXXXComponent.builder().xxx调用的时候再创建对象,直接传参即可

  2. Componet依赖Component,使用dependence

    1
    2
    3
    FragmentComponent依赖于ActivityComponent
    FragmentComponent中 injectTo 在Activity中实例化并提供对外的activityComponent对象
    在Fragment中通过DaggerFragmentComponent.builder().activityComponent(ActivityComponent对象).build().injectTo(this);
  3. 使用@SubComponent跟使用dependence效果一样

    1
    2
    3
    4
    ActivityComponent中获取FragmentComponent对象
    FragmentComponent标记@Subcomponent,并加injectTo
    在Activity中实例化并提供对外的activityComponent对象
    在Fragment中通过ActivityComponent.fragmentComponent().injectTo(this);
  4. 使用@SubComponent@Subcomponent.Builder,效果与5、6一样

    1
    2
    3
    4
    ActivityComponent中获取FragmentComponent.Builder对象
    FragmentComponent标记@Subcomponent,加injectTo,并加@Subcomponent.Builder里面只有个build()方法
    在Activity中实例化并提供对外的activityComponent对象
    在Fragment中通过ActivityComponent.sonbuilder().build().injectTo(this);

Dagger2基础知识及在Java中使用(2)

Dagger2在Android中的使用

例子:

1
2
3
4
5
implementation 'com.google.dagger:dagger-android:2.24'
// if you use the support libraries(就是你需要v4.Fragment就需要加上support包)
implementation 'com.google.dagger:dagger-android-support:2.24'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.24'
1
2
3
4
5
6
public class Children {
@Inject
public Children() {

}
}
1
2
public class SurperMan {
}
1
2
3
4
5
6
7
@Module
public class SurperManModule {
@Provides
SurperMan surperManProvides(){
return new SurperMan();
}
}

dagger.android包提供了一个@ContributesAndroidInjector标注是为了省去我们每次去Activity里初始化Component

1
2
3
4
5
6
7
@Module
//抽象类,名字自定义,
public abstract class NeedInjectModules {
//这个方法的意思标识要注入到ForAndroidActivity页面。如果需要在MainActivity页面的话。继续加这个方法,返回值为MainActivity即可
@ContributesAndroidInjector
abstract ForAndroidActivity inject();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//这里AndroidSupportInjectionModule是系统的,必须加上
//NeedInjectModules是我们要注册到Activity的
//SurperManModule,surperMan的
@Component(modules = {
AndroidSupportInjectionModule.class,
NeedInjectModules.class,
SurperManModule
})

//继承AndroidInjector<T>,泛型就是我们的application
public interface AppComponent extends AndroidInjector<MyApplication> {

//还记得@Component.Builder的用法吗,可以看之前的。
@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}
1
2
3
4
5
6
7
8
9
public class MyApplication extends DaggerApplication {

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
//将我们的AppComponent.Builder返回出去
return DaggerAppComponent.builder().application(MyApplication.this).build();
}

}

在Activity里使用或者在Fragment里使用,只需继承我们的DaggerActivity&DaggerFragment。直接看我们的Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ForAndroidActivity extends DaggerAppCompatActivity{
@Inject
Children children;
@Inject
SuperMan superMan;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_astudy);
LogUtils.i("已经生成实例",children.hashCode()+"");
LogUtils.i("已经生成实例",surperMan.hashCode()+"");
}
}

Dagger2华丽使用在MVP框架中

RxJava + Retrofit + MVP + Dagger2

1
2
3
4
5
6
//引入dagger.android库
implementation 'com.google.dagger:dagger-android:2.24'
// if you use the support libraries
implementation 'com.google.dagger:dagger-android-support:2.24'
annotationProcessor 'com.google.dagger:dagger-compiler:2.24'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.24'
1
2
3
4
5
@Module
public abstract class NeedInjectModules {
@ContributesAndroidInjector
abstract POSTFragment injectPOSTFragment();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@Component(modules = {
AndroidSupportInjectionModule.class,
NeedInjectModules.class,
})
public interface AppComponent extends AndroidInjector<MyApplication> {

@Component.Builder
interface Builder {
@BindsInstance
Builder application(Application application);
AppComponent build();
}
}

初始化Presenter:

无参可以直接用@Inject,若有参数则要用Module

1
2
3
4
5
6
7
8
public class PostPresenter extends BasePresenter<PostContract.View> implements PostContract.Prensenter {

@Inject
public PostPresenter(){

}
...//省略部分代码,便于理解
}

Application中:

1
2
3
4
5
6
7
8
9
10
11
12
public class MyApplication extends DaggerApplication {

@Override
public void onCreate() {
super.onCreate();
}

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.builder().application(this).build();
}
}

因为我们的Activity要继承DaggerActivity、DaggerFragment,所以我们新建BaseDaggerActivity和BaseDaggerFragment,里面有DaggerActivity和RxActivity的一些内容:

1
2
3
4
5
6
7
8
public abstract class BaseDaggerActivity<T extends BasePresenter> extends RxFragmentActivity implements BaseView, HasAndroidInjector {
@Inject
public T mPresenter;

//public abstract T cretaePresenter();

...//省略部分代码,便于理解
}

各注解用法

Dagger2各属性了解

  • 必要属性

@inject //注入 , @Component , @Module , @Provider

要用Dagger2这几个属性是绕不开的

  • 高级属性

@Named , @Qualifier , @Scope , @Singleton

可分两组,@Named底层实现是@Qualifier@Singleton底层实现是@Scope

Dagger2各属性使用及分析

@Inject

注解构造函数:

通过标记构造函数,告诉Dagger2可以创建该类的实例(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖关系。

若注解了构造函数,没有注解依赖变量,将不创建实例。

用@Inject注解应该用来创建类实例的构造函数

注解依赖变量:

通过标记依赖变量,Dagger2提供依赖关系,注入变量。

若注解了依赖变量,而没注解构造函数,将获得一个null对象。

用@Inject注解构造函数,再用@Inject注解类实例

@Module

通过注解类,为Dagger提供依赖关系

@Provides

是一种工厂模式

@Provides依然是为了提供依赖关系,是对@Inject的补充,弥补了@Inject对于第三方框架和接口类的盲区。

@Provides方法本身可能也拥有自身的依赖关系。

@Provides方法本身是不能独立存在的,它必须依附于一个Module

1
2
3
4
5
6
7
8
9
10
11
12
13
public class LstudyActivity extends BaseActivity {
@Inject
Provider<Pig> pig_spec;
@Inject
Lazy<Sheep> sheep_spec;

@OnClick(R.id.btn)
public void specOnClick() {
LogUtils.i("看看hashCode值", "Provider pig_spec不通过get的hashCode ==> " + pig_spec.hashCode());
LogUtils.i("看看hashCode值", "Provider pig_spec的hashCode ==> " + pig_spec.get().hashCode());
LogUtils.i("看看hashCode值", "Lazy sheep_spec的hashCode ==> " + sheep_spec.get().hashCode());
}
}

不调用get()方法的Provider的HashCode值。一直不变

调用get()方法的Provider的HashCode值。一直在改变

调用get()方法的Lazy的HashCode值。一直不变

provider是一种工厂模式,每次调用get(),都会生成不同的实例。不调用不会生成实例(返回的是容器的HashCode)。

@Component

通过注解接口类,作为提供依赖和所需依赖之间的桥梁(链接ModuleContainer),在编译时生成相关实例,将相关依赖注入完成使命。

依赖规则

@Inject@Module都提供了依赖对象

  1. 查找Module中是否存在创建该类的方法
  2. 若存在,初始化参数,完成实例创建
  3. 若不存在,用@Inject创建实例

@Singleton

是@Scope的一种。是作用域单例。Component在哪build就跟那个类的生命周期绑定,即只在那个绑定的实例类里起作用

1
2
3
4
@Scope  //注明是Scope
@Documented //标记文档提示
@Retention(RUNTIME) //运行时候级别
public @interface Singleton {}

1、Component在Activity里build,那么这个单例只限于在这个Activity里。意思退出这个Activity,再进这个Activity时,这个单例会被重新new
2、如果Component在Application里build,那么这个单例就是全局的。注意这里涉及到了我们之前的Component依赖Component。

使用Dagger2要有局部单例全局单例的意识。(变量地址是否相同)

两者的基本定义是一样的,要看Component的作用域,若Component的作用域是全局的,那么对应@Singleton或者@Scope的对应module就全局的,反之亦然。

1
2
3
4
5
6
7
8
@Module
public class UrlConstant{
@Singleton
@Provides
public UrlConstant instance()[
return new UrlConstant();
]
}

局部单例:Component传入具体的某个子类Activity

1
2
3
4
5
@Singleton
@Component(modules={UrlConstant.class})
public interface MainComponent{
void inject(MainActivity mainActivity);
}

全局单例:步骤:

  1. Component传入基类的Activity
1
2
3
4
5
@Singleton
@Component(modules={UrlConstant.class})
public interface GlobalComponent{
void inject(BaseActivity baseActivity);
}
  1. 在Application中实例化它

利用率Application的唯一特性,也可以另起一个全局唯一的类承载,但没必要

1
2
3
4
5
6
7
8
9
10
11
12
public class MyApplication extends Application{
private GlobalComponent mGlobalComponent;
/**
* 获取全局单例Global
*/
public GlobalComponent getGlobalComponent(){
if(mGlobalComponent == null){
mGlobalComponent = DaggerGlobalComponent.builder().build();
}
return mGlobalComponent;
}
}
  1. Client中调用

在基类中进程初始化,然后加标记进行控制,对子类暴露,这样子类想要的时候可以直接拿,更重要的是可以避免Component中inject(XXXActivity)新增类的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public abstract class BaseActivity extends NetObserverActivity{
@Inject
UrlConstant mUrlConstant;

@Override
public void onCreate(@Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
if(!notInitGlobalComponent()){
((MyApplication) getApplication()).getGlobalComponent().inject(this);
}
}

protected abstract boolean notInitGlobalComponent();

public UrlConstant getUrlConstant(){
return mUrlConstant;
}
}

@Scope

  1. Scope是Singleton的底层实现,一般在工程实施中,它作为提高可读性的手段之一让程序员可以一眼就看出哪些是全局哪些是局部的生命周期
  2. 来标识范围的注解,并控制如何重复使用类的实例。仅用@inject注解,未用@Scope注解,在每次依赖注入的时候都会创建新的实例。使用@Scope注解会缓存第一次创建的实例,然后重复注入缓存的实例,不会创建新的实例。
  3. 如果有类注入实例的类被@Scope注解,那么其Component必须被相同的Scope注解。
  4. 如果依赖实例的注入来源是@Provides方法时,@Provides方法必须被@Scope注解;
  5. 如果依赖实例的注入来源是@Inject注解的构造函数时,实例类必须被@Scope注解。@Scope实际上是对注入器的控制。

另外在看其它博客文章的时候,你会经常看到 @ActivityScope 可以声明一个Activity生命周期的对象 ,@ApplicationScope 可以声明一个Application生命周期的对象 , 难道这些Scope这么神? 定义一个名字就可以控制对象的生命周期了? 其实这和 @Singleton一样的,都是代码是否通过同一个 component 对象来控制的。比如 @ActivityScope 定义的对象 ,其在Activity创建了component对象 ,那这个component对象肯定在这个Activity的生命周期内啊,依赖创建出来的对象也肯定是这个Activity啊。还有@ApplicationScope 中的component 对象是在 Application中的,那依赖创建出来的对象的生命周期也肯定是和 @Singleton的一样的,单例的生命周期不就是整个 Application 吗。

@Qualifier

限定符,它可以标记不同的构造方法让外部调用者获取期望的构造对象

1
2
3
4
5
6
7
8
9
/**
* 自定义一个限定符
*/
@Qualifier//限定符
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Type {
String value() default "";//默认值为""
}

如果把Type改成Named那么就跟@Named一样了

首先自定义一个注解@interface,刨开2个元注解不看 @Documented @Retention,顶部定义一个 @Qualifier限定符标识,然后这一句String value() default “”的意思是,Type注解可以传参,默认为一个空字符串。
定义好了这个注解就相当于定义好了一个可接收参数的标签,接下来我们要将这个标签传入不同的参数然后分别贴到不同的构造函数之上(记住其中的关系,**@Provide + @Type(“”)区分不同构造函数Type又被@Qualifier修饰**),这样,就相当于让每个构造函数有了辨识的途径,若要使用不同的构造函数,则只需去揭开不同的标签即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 定义Module
*/
@Module public class SalaModule {
//.........

@Type("normal")//使用限定符来区别使用哪个构造函数返回对象
@Provides public Apple provideNormalApple() {
return new Apple();
}

@Type("color")
//使用限定符来区别使用哪个构造函数返回对象
@Provides public Apple provideColorApple(String color) {
return new Apple(color);
}

//由于我们的Apple构造函数里使用了String,所以这里要管理这个String(★否则报错)
//int等基本数据类型是不需要这样做的
@Provides public String provideString(){
return new String();
}
}

Module传入不同参数分别标记不同的构造方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 编写Component
*/
@Component(modules = {SaladModule.class})
//指明要在那些Module里寻找依赖
public interface SaladComponent {
//.........

@Type("normal")
//关键靠这个限定符来区分调用哪个构造函数
Apple provideNormalApple();
//这个方法名并不重要,只要是provide开头就行,但是建议和module里一致
@Type("color")
Apple provideColorApple();
String provideString();
//注意:下面的这个方法,表示要将以上的三个依赖注入到某个类中
// 这里我们把上面的三个依赖注入到Salad中
// 因为我们要做沙拉
void inject(Salad salad);
}

Component通过刚才自定义的@Type注解标注对外暴露的接口,当这个接口方法被调用时,它会去Moudle内找对应的相同的@Type参数列表一致的构造与provider方法。

@Named

Named底层实现是@Qualifier,是@Qualifier的一种

源码

1
2
3
4
5
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface Named {
String value() default "";
}

简介 原文链接

Dagger2是个依赖注入框架

依赖注入:是种实现控制反转用于解决依赖性设计模式。一个依赖关系指的是可被利用的一种对象(即服务提供端)。依赖注入是将所依赖的传递给将使用的从属对象(即客户端)。该服务是将会变成客户端的状态的一部分。传递服务给客户端,而非允许客户端来建立或寻找服务,是本设计模式的基本要求。–》简单说就是将实例对象传入到另一个对象中去(给一个对象传入一个实例变量)。

依赖注入的实现

  • 构造函数注入
1
2
3
4
5
6
public class Chef{
Menu menu;
public Man(Menu menu){
this.menu = menu;
}
}
  • setter方法注入
1
2
3
4
5
6
public class Chef{
Menu menu;
public setMenu(Menu menu){
this.menu = menu;
}
}
  • 接口注入
1
2
3
4
5
6
7
8
9
10
11
12
public interface MenuInject{
void injectMenu(Menu menu);
}

public class Chef implements MenuInject{
Menu menu;

@Override
public injectMenu(Menu menu){
this.menu = menu;
}
}
  • 依赖注入框架
1
2
3
4
5
6
7
8
public @Inject class Menu{
...
}

public class Chef{
@Inject
Menu menu;
}

Dagger2 实现依赖注入

Dagger2(2.17之后的版本)最简单的使用方式就是下面这种:

1
2
3
4
5
6
7
8
9
10
11
public class A{
@Inject
public A(){

}
}

public class B{
@Inject A a;
...
}

假如,A还需要依赖其他的类,且这个类是第三方库中提供的。又或者A实现了C接口,我们就需要用依赖倒置原则来加强我们的代码的可维护性等等。

例子:做个餐饮系统,把点好的菜单发给厨师,让厨师负责做菜。

1
2
3
4
5
6
//引入一些Dagger For Android的依赖库
implementation 'com.google.dagger:dagger-android:2.17'
implementation 'com.google.dagger:dagger-android-support:2.17' // if you use the support libraries
implementation 'com.google.dagger:dagger:2.17'
annotationProcessor 'com.google.dagger:dagger-compiler:2.17'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.17'

Cooking接口

1
2
3
public interface Cooking{
String cook();
}

Chef

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 Chef implements Cooking{

Menu menu;

@Inject
public Chef(Menu menu){
this.menu = menu;
}

@Override
public String cook(){
//key菜名, value是否烹饪
Map<String,Boolean> menuList = menu.getMenus();
StringBuilder sb = new StringBuilder();
for (Map.Entry<String,Boolean> entry : menuList.entrySet()){
if (entry.getValue()){
sb.append(entry.getKey()).append(",");
}
}

return sb.toString();
}
}

Menu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Menu {

public Map<String,Boolean> menus;

@Inject
public Menu( Map<String,Boolean> menus){
this.menus = menus;
}

Map<String,Boolean> getMenus(){
return menus;
}

}

现在我们写一个Activity,作用是在onCreate方法中使用Chef对象实现cooking操作。我们先来看看不使用Dagger2和使用Dagger2的代码区别。

MainActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
Map<String, Boolean> menus = new LinkedHashMap<>();
menus.put("酸菜鱼", true);
menus.put("土豆丝", true);
menus.put("铁板牛肉", true);
Menu menu = new Menu(menus);
Chef chef = new Chef(menu);
System.out.println(chef.cook());
}
}

DaggerMainActivity

1
2
3
4
5
6
7
8
9
10
11
public class DaggerMainActivity extends DaggerActivity {
@Inject
Chef chef;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG,chef.cook());
}
}

在DaggerMainActivity上实现依赖注入还需要哪些代码。

CookModules

1
2
3
4
5
6
7
8
9
10
11
12
13
@Module
public class CookModules {

@Singleton
@Provides
public Map<String, Boolean> providerMenus(){
Map<String, Boolean> menus = new LinkedHashMap<>();
menus.put("酸菜鱼", true);
menus.put("土豆丝", true);
menus.put("铁板牛肉", true);
return menus;
}
}

ActivityModules

1
2
3
4
5
6
@Module
abstract class ActivityModules {

@ContributesAndroidInjector
abstract MainActivity contributeMainActivity();
}

CookAppComponent

1
2
3
4
5
6
7
8
9
10
11
@Singleton
@Component(modules = {
AndroidSupportInjectionModule.class,
ActivityModules.class,
CookModules.class})
public interface CookAppComponent extends AndroidInjector<MyApplication> {

@Component.Builder
abstract class Builder extends AndroidInjector.Builder<MyApplication>{}

}

MyApplication

1
2
3
4
5
6
7
8
9
10
11
12
public class MyApplication extends DaggerApplication{

@Override
public void onCreate() {
super.onCreate();
}

@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerCookAppComponent.builder().create(this);
}
}

Dagger2 For Android 使用要点分析

  1. CookModules
    CookModule很简单,它的目的就是通过@Providers注解提供Menu对象需要的数据。因为Menu是需要依赖一个Map对象的,所以我们通过CookModules给它构造一个Map对象,并自动把它注入到Menu实例里面。
  2. ActivityModules
    ActivityModules的主要作用就是通过@ContributesAndroidInjector来标记哪个类需要使用依赖注入功能,这里标记的是ManActivity,所以MainActivity能通过@Inject注解来注入Chef对象。
  3. CookAppComponent
    CookAppComponent相当于一个注射器,我们前面定义的Modules就是被注射的类,使用@Inject注入对象的地方就是接收者类。
  4. MyApplication
    MyAppliction的特点是继承了DaggerAppliction类,并且在applicationInjector方法中构建了一个DaggerCookAppComponent注射器。

这就是Dagger 2在Android中的使用方案了,在这里我们可以看到,接收这类(MainActivity)中的代码非常简单,实现依赖注入只使用了:

1
2
@Inject
Chef chef;

在接收类里面完全没有多余的代码,如果我们要拓展可以SecondsActivity的话,在SecondsActivity我们要用到Menu类。

那么我们只需要在ActivityModules中增加:

1
2
@ContributesAndroidInjector
abstract SecondsActivity contributeSecondsActivity();

然后在SecondsActivity注入Menu:

1
2
@Inject
Menu menu;

可以看到,对于整个工程来说,实现使用Dagger2 For Android实现依赖注入要写的模版代码其实非常少,非常简洁。只需要进行一次配置就可以,不需要频繁写一堆模版代码。总的来说,Dagger2造成模版代码增加这个问题已经解决了。

Dagger2的优势

在这里我们总结下使用Dagger2带来的优点。

  1. 减少代码量,提高工作效率
    例如上面的例子中,我们构建一个Chef对象的话,不使用Dagger2的情况下,需要在初始化Chef对象之前进行一堆前置对象(Menu、Map)的初始化,并且需要手工注入到对应的实例中。你想像下,如果我们再加一个Restaurant( 餐馆 )对象,并且需要把Chef注入到Restaurant中的话,那么初始化Restaurant对象时,需要的前置步骤就更繁琐了。
    可能有人会觉得,这也没什么啊,我不介意手工初始化。但是如果你的系统中有N处需要初始化Restaurant对象的地方呢?使用Dagger2 的话,只需要用注解注入就可以了。
  2. 自动处理依赖关系
    使用Dagger2的时候,我们不需要指定对象的依赖关系,Dagger2会自动帮我们处理依赖关系(例如Chef需要依赖Menu,Menu需要依赖Map,Dagger自动处理了这个依赖关系)。
  3. 采用静态编译,不影响运行效率
    因为Dagger2是在编译期处理依赖注入的,所以不会影响运行效率在一定的程度上还能提高系统的运行效率(例如采用Dagger2实现单例,不用加锁效率更高)。
  4. 提高多人编程效率
    在多人协作的时候,一个人用Dagger2边写完代码后,其它所有组员都能通过@Inject注解直接注入常用的对象。加快编程效率,并且能大大增加代码的复用性。
  • @ContributesAndroidInjector
    你可以把它看成Dagger2是否要自动把需要的用到的Modules注入到DishesFragment中。这个注解是Dagger2 For Android简化代码的关键
  • @Module
    被这个注解标记的类可以看作为依赖对象的提供者,可以通过这个被标记的类结合其它注解来实现依赖关系的关联。
  • @Provides
    主要作用就是用来提供一些第三方类库的对象或提供一些构建非常复杂的对象在Dagger2中类似工厂类的一个角色。
  • @Binds
    主要作用就是确定接口与具体的具体实现类。

Dagger2 For Android是如何注入依赖的?

我们在用Dagger2的时候是通过一些模版代码来实现依赖注入的( DaggerXXXComponent.builder().inject(xxx) 这种模版代码),但是在Demo中的DishesFragment根本没看到类似的代码啊,那么这些对象是什么时候注入到DishesFragment重的呢?

答案就是**@ContributesAndroidInjector**注解

我们先来看看Dagger2是通过什么方式来实现自动把依赖注入到DishesActivity中的。

ActivityModules

1
2
3
4
5
6
7
8
9
10
@Module
public abstract class ActivityModules {

@ContributesAndroidInjector(modules = DishesModules.class)
abstract public DishesActivity contributesDishActivity();

@ContributesAndroidInjector(modules = AddEditModules.class)
abstract public AddEditDishActivity contributesAddEditDishActivity();

}

没错,就是@ContributesAndroidInjector这个注解,modules就代表这个DishesActivity需要依赖哪个Modules。

Dagger2 For Android使用要点

我们现在来总结下,简化版的Dagger实现依赖注入的几个必要条件:

  1. 第三方库通过Modules的@provides注解来提供依赖
  2. 提供一个全局唯一的Component,并且Modules中需要添加AndroidSupportInjectionModule类,它的作用时关联需求与依赖之间的关系
  3. Application需要继承DaggerApplication类,并且在applicationInjector构建并返回全剧唯一的Component实例
  4. 其它需要使用依赖注入的组建都需要继承Dagger组件名字类,并且需要在相应的Modules中通过@ContributesAndroidInjector注解标记需要注入依赖的组建。