Dagger2是一个基于Java依赖注入的标准的依赖注入框架,他能够在编译器自动生成代码,负责依赖对象的创建。Dagger2是Dagger的升级版第一代由Square公司共享出来,第二代则是由谷歌接手开发出来,现在来学习它的使用方法。
首先在gradle的dependencies中加入:
implementation 'com.google.dagger:dagger:2.7'
annotationProcessor 'com.google.dagger:dagger-compiler:2.7'
在使用之前先来看一个例子。创建一个Worker类:
public class Worker {
public void work(){
Log.e("TAG","工人干活");
}
}
然后在Activity中使用:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Worker worker = new Worker();
worker.work();
}
}
这样使用没有任何问题,但是MainActivity持有Worker的对象,这样解耦不彻底,那么现在就是要使用Dagger2进行彻底的解耦。
一、@Inject与@Component
1、改写Worker类:
在Worker2的构造方法上面加上@Inject表明Dagger2可以使用Worker2的构造方法创建对象。
public class Worker2 {
@Inject
public Worker2(){
}
public void work(){
System.out.println("工人干活");
}
}
2、定义MainActivityComponent接口
使用@Component来完成依赖注入,需要定义一个接口,接口名称一般为目标名称+Component,在编译后就会自动生成一个辅助类,他会把依赖的目标的实例注入到目标类中。这里定义的是MainActivityComponent,方法名称是inject,要注入的类是MainActivity,代码如下:
@Component
public interface MainActivityComponent {
void inject(MainActivity activity);
}
3、完成注入
@Inject有两种注入方式,一个是成员变量注入,另一个是构造方法注入,在MainActivity中这两种方式都采用,如下:
public class MainActivity extends AppCompatActivity {
@Inject
Worker2 mWorker2; //成员变量注入
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this); //构造方法注入
mWorker2.work();
}
}
@Inject 有两个作用:
- 标记Worker2的构造方法,通知 Dagger2 在需要该类实例时可以通过该构造函数 new 出相关实例从而提供依赖。
- 标记 MainActivity 的 Worker2 变量,通知 Dagger2 该变量实例需要由它来提供,也就是上述的需要Dagger2 去 new 出 Worker2 的实例 mWorker2 。
@Component作用:
- 是一个中间件去连接依赖提供方和依赖使用方。
二、@Module与@Provides
@Inject 标记构造方法来生成依赖对象的方式有它的局限性,如:
- 接口(Interface),没有构造方法,自然无处标记。
- 第三方库提供的类,不适合去直接修改源码,标记构造方法。
对于上述的问题,就需要 @Provide 来提供依赖。
1、编写Module类
如果项目中使用的第三方库,这样使用就会报错,比如说Gson。这个时候就可以使用@Module与@Provides来处理。首先创建Module类:
@Module
public class GsonModule {
@Provides
public Gson provideGson() {
return new Gson;
}
}
将@Module标注在内类上,用来告诉Component,可以从这个类中获取依赖对象,也就是Gson类,将@Provides标注在方法上面表示可以通过这个方法进行获取Gson实例,两者都是一起使用的。需要注意的是:
- @Provides 只能用于标记非构造方法,并且该方法必须在 @Moudle 内部。
- @Provides 修饰的方法的方法名建议以 provide 开头。
2、定义MainActivityComponent接口
@Component(modules = GsonModule.class)
public interface MainActivityComponent2 {
void inject(MainActivity activity);
}
这里和前面的而不一样的地方是modules = GsonModule.class,表示用来指定Module,需要注意的是可以指定多个Module。
3、完成注入
public class MainActivity extends AppCompatActivity {
@Inject
Gson mGson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
String jsonObj = "{'age': '32','name': '张三'}";
Man man = mGson.fromJson(jsonObj, Man.class);
Log.e("TAG",man.age);
Log.e("TAG",man.name);
}
}
如果使用的类是抽象的则@Inject无法使用,因为抽象类不能被实例化,此时也是需要使用@Module与@Provides。
三、@Named与@Qualifier
@Qualifier是限定符,@Named是 @Qualifier的一种实现。当我们有两个依赖的时候,高层的Component就不知道我们使用的是哪个,例如新建抽象类Animals,实现类Dog,Cat,如下:
public class AnimalsModule {
@Provides
public Animals provideDog(){
return new Dog();
}
@Provides
public Animals provideCat(){
return new Cat();
}
}
public class Zoo {
private Animals mAnimals;
@Inject
public Zoo(Animals animals) {
mAnimals = animals;
}
public void eatFood(){
mAnimals.eat();
}
}
public class MainActivity extends AppCompatActivity {
@Inject
Zoo mZoo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainActivityComponent.create().inject(this);
mZoo.eatFood();
}
}
运行之后报错了,原因是我们提供了多个@Provides,Component不知道到底是用哪个,这个时候可以使用@Named,在AnimalsModule和Zoo的构造参数上面分别指明所需要的对象,假设使用猫,则需要Zoo的构造对象中指定@Named("Cat"),如下:
@Module
public class AnimalsModule {
@Provides
@Named("Dog")
public Animals provideDog(){
return new Dog();
}
@Provides
@Named("Cat")
public Animals provideCat(){
return new Cat();
}
}
public class Zoo {
private Animals mAnimals;
@Inject
public Zoo(@Named("Cat") Animals animals) {
mAnimals = animals;
}
public void eatFood(){
mAnimals.eat();
}
}
四、@Singleton与@Scope
@Scope是用来自定义注解的,而@Singleton是用来配合实现局部单例和全局单例的,需要要注意的是@Singlton本身不具备创建单例的能力为了实现全局单例这里使用@Scope和Application实现。
1、定义注解
@ApplicationScope
@Component(modules = GsonModule.class)
public interface ActivityComponet {
void inject(MainActivity mainActivity);
}
2、定义Module
@Module
public class GsonModule {
@ApplicationScope
@Provides
public Gson provideGson(){
return new Gson();
}
}
3、定义MainActivityComponent
@ApplicationScope
@Component(modules = GsonModule.class)
public interface MainActivityComponent {
void inject(MainActivity activity);
}
4、定义Application
public class App extends Application {
private static MainActivityComponent mActivityComponent;
@Override
public void onCreate() {
super.onCreate();
mActivityComponent = DaggerMainActivityComponent.builder().build();
}
public static MainActivityComponent getActivityComponet(){
return mActivityComponent;
}
}
5、完成注入
public class MainActivity extends AppCompatActivity {
@Inject
Gson mGson1;
@Inject
Gson mGson2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
App.getActivityComponet().inject(this);
Log.e("TAG",mGson1.toString());
Log.e("TAG",mGson2.toString());
}
}
看一下二者内存地址完全一致:
参考文献:https://segmentfault.com/a/1190000008677663#articleHeader7