Dagger2

介绍Dagger2

1.基础知识点

  • 依赖注入(Dependency Injection)
  • Java的注解 (Annotation)

1.1 依赖注入 DI

目标类(需要进行依赖初始化的类)中所依赖的其他类的初始化过程,不是通过手动编码方式创建的类,通过技术手段,把其他类已经初始化好的实例自动注入到目标类中。
依赖注入图解

1.2 注解 Annotation

它提供了一种安全的类似注释的机制,用来将任何的信息或元数据(metadata)与程序元素(类、方法、成员变量等)进行关联。
Annotation其实是一种接口。通过Java的反射机制相关的API来访问annotation信息。相关类(框架或工具中的类)根据这些信息来决定如何使用该程序元素或改变它们的行为。
Annotation是不会影响程序代码的执行,无论annotation怎么变化,代码都始终如一地执行。


2.Dagger2注解

2.1 @Inject

Inject主要有两个作用,一个是使用在构造函数上,通过标记构造函数让Dagger2来使用(Dagger2通过Inject标记可以在需要这个类实例的时候来找到这个构造函数并把相关实例new出来)从而提供依赖,另一个作用就是标记在需要依赖的变量让Dagger2为其提供依赖。

2.2 @Provider

用Provide来标注一个方法,该方法可以在需要提供依赖时被调用,从而把预先提供好的对象当做依赖给标注了@Inject的变量赋值。provide主要用于标注Module里的方法

2.3 @Module

用Module标注的类是专门用来提供依赖的。有的人可能有些疑惑,看了上面的@Inject,需要在构造函数上标记才能提供依赖,那么如果我们需要提供的类构造函数无法修改怎么办,比如一些jar包里的类,我们无法修改源码。这时候就需要使用Module了。Module可以给不能修改源码的类提供依赖,当然,能用Inject标注的通过Module也可以提供依赖。

2.4 @Component

Component一般用来标注接口,Component也叫组件,方法中有dependenies,跟modules两个参数,被标注了Component的接口在编译时会产生相应的类的实例来作为提供依赖方和需要依赖方之间的桥梁,把相关依赖注入到其中。

示例图

2.5 提供依赖的流程

提供依赖有两种方式:

  • 1.注解了@Inject的构造函数
  • 2.注解了@Module 类中的@Provider 的方法

所以在依赖的选择上就有一个流程。

Dagger2选择依赖的流程:

  • 步骤1:查找Module中是否存在创建该类的方法。
  • 步骤2:若存在创建类方法,查看该方法是否存在参数
  • 步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数
  • 步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
  • 步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
  • 步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
  • 步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束

3. 两个比较难理解的注解:@Scope @Qulifiter

3.1 @Qulifiter

依赖迷失:如果有Module中有两个一样的返回类型,该用谁呢。我们把这种场景叫做依赖迷失。
@Qulifiter:通过自定义Qulifier,可以告诉Dagger2去需找具体的依赖提供者。

3.2 @Qulifiter使用

1.创建自定义注解:

1
2
3
4
5
6
7
@Qulifiter
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}

通过@Qulifiter创建了两个自定义注解,@A 与 @B

2.在Module中使用@A 与 @B:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Module
public class SimpleModule {
@Provides
@A
Cooker provideCookerA(){
return new Cooker("James","Espresso");
}
@Provides
@B
Cooker provideCookerB(){
return new Cooker("Karry","Machiato");
}
}

3.具体使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ComplexMaker implements CoffeeMaker {
Cooker cookerA;
Cooker cookerB;
//通过自定义的Qulifiter注解标记对应的Provider提供的依赖
@Inject
public ComplexMaker(@A Cooker cookerA,@B Cooker cookerB){
this.cookerA = cookerA;
this.cookerB = cookerB;
}
@Override
public String makeCoffee() {
return cooker.make();
}
}

4.调用CookerA与CookerB

1
2
cookerA.make();//James make Espresso
cookerB.make();//Karry make Machiato

3.3 @Scope

Scope难理解,通过例子来解释:

####1.定义一个Scope

1
2
3
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}

2.把定义的PerActivity放到Module

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Module
public class ActivityModule {
@Provides
CoffeeShop provideCoffeeShop(){
return CoffeeShop.getInstance();
}
@Provides
@PerActivity
CookerFactory provideCookerFactory(){
return new CookerFactory();
}
@Provides
CookerFactoryMulty provideCookerFactoryMulty(){
return new CookerFactoryMulty();
}
}

3.这个Module提供了CoffeeShop,CookerFactory和CookerFacotryMulty的依赖

CoffShop.java

1
2
3
4
5
6
7
8
9
10
11
12
public class CoffeeShop {
private static CoffeeShop INSTANCE;
private CoffeeShop(){
Log.d("TAG","CoffeeShop New Instance");
}
public static CoffeeShop getInstance(){
if(INSTANCE == null){
INSTANCE = new CoffeeShop();
}
return INSTANCE;
}
}

CookerFactory.java

1
2
3
4
5
public class CookerFactory {
public CookerFactory(){
Log.d("TAG","CookerFactory New Instance");
}
}

CookerFactoryMulty.java

1
2
3
4
5
public class CookerFactoryMulty {
public CookerFactoryMulty(){
Log.d("TAG","CookerFactoryMulty New Instance");
}
}

我们在这三个对象的构造方法里都加了Log,当他们的实例产生时能看到相关的Log,再看我们用到的地方,在MainActivity里给每个类都写两个变量.

4.MainActivity中:

MainActivity.java

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
public class MainActivity extends Activity {
ActivityComponent activityComponent;
@Inject
CoffeeShop coffeeShop1;
@Inject
CoffeeShop coffeeShop2;
@Inject
CookerFactory cookerFactory1;
@Inject
CookerFactory cookerFactory2;
@Inject
CookerFactoryMulty cookerFactoryMulty1;
@Inject
CookerFactoryMulty cookerFactoryMulty2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
.activityModule(provideModule())
.applicationComponent(MyApplication.getComponent()).build();
activityComponent.inject(this);
coffeeFactory.run();
}
private ActivityModule provideModule(){
return new ActivityModule();
}
}

5.运行结果

1
2
3
4
07-11 16:53:27.978 1927-1927/? D/TAG﹕ CoffeeShop New Instance
07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactory New Instance
07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactoryMulty New Instance
07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactoryMulty New Instance

从Log中可以看到,CoffeeShop和CookerFactory的类都只new过一次,而CookerFactoryMulty被new了两次 再回头看我们的Module,其中CoffeeShop的依赖是通过单例模式提供的,只打一条Log很容易理解,而CookerFactory相对于CookerFactoryMulty来说内容几乎是一模一样,只多加一个@PerActivity的注解,但却比它少打了一次Log,这是为什么呢。哈哈,客官们,这就是@Scope神秘的地方,他通过自定义@Scope注解提供了单例,正如上面的CookerFactory,虽然并未用单例来提供依赖,但却和用单例提供依赖的CoffeeShop一样,两个对象的实例都是同一个,这就是Scope的作用,提供局部单例的功能,局部范围是啥,那就是它生命周期范围内。


4.学习过程中遇到的一些坑

4.1 代码:

1.AppModule.java

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
35
@Module
public final class AppModule {
private final MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
@Provides
@Singleton
@ForApplication
public Application provideApplication(){
return this.application;
}
@Provides
@Singleton
@ForApplication
public MyApplication provideApp(){
return this.application;
}
@Provides
@Singleton
@ForApplication
public Context provideContext(){
return this.application.getApplicationContext();
}
@Provides
public User provideUser(){
return new User("RootUser");
}
}

2.UserModule.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Module
public class UserModule {
// @Provides
// public User provideUser(){
// return new User("RootUser");
// }
// @Provides
// public User providername(User user){
// return user.getName();
// }
// @Provides
// public String provideString(User user){
// return user.getName();
// }
// @Provides
// public String provideName(MyApplication application){
// Log.e("Name" , " " + application.getPackageName());
// return application.getPackageName();
// }
}

3.RepositoryComponent.java

1
2
3
4
5
6
7
8
9
@Singleton
@Component(modules = {AppModule.class, UserModule.class})
public interface RepositoryComponent {
//下层的依赖需要用到就要返回 dependenciuce
User getUser();
@ForApplication
MyApplication getApplication();
}

4.ActivityComponent.java

1
2
3
4
5
@ActivityScope
@Component(dependencies = RepositoryComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MainActivity mainActivity);
}

4.2 上面代码解释:

1.AppModuleUserModule(UserModule.java是一个例子类,无实际作用)组成一个RepositoryComponent的组件,组件内的Provider可以互相提供依赖,注入实例到同一个组件需要的依赖里面。

2.ActivityComponent是提供给Activity类的组件,这个组件dependencies 依赖了RepositoryComponent组件,然后加入了ActivityModule

3.重点RepositoryComponent放出了两个方法User getUser();@ForApplication MyApplication getApplication(),在我看来,这两个方法相当于开放出两个接口,给依赖这个RepositoryComponent组件的下层组件(这里是ActivityComponent)来提供依赖。 例如, ActivityComponent组件里面需要依赖一个RepositoryComponent中的User,那么RepositoryComponent中就需要定义这个User getUser()方法。

如果需要开放的依赖有自定义的@Qulifiter注解,那么在开放这个依赖的时候,也要对应加上这个注解,例如@ForApplication MyApplication getAplication(), 对应的在下层依赖需要注入的时候也要带上这个@ForApplication注解。