Dagger 2 是 Google 开源的一款依赖注入框架,它的前身是 square 的 Dagger 1,Dagger 2 在 Android 中有着较为广泛的应用。
Dagger 2 基于 Java 注解,采用 annotationProcessor(注解处理器) 在项目编译时动态生成依赖注入需要的 Java 代码,然后我们在合适的位置手动完成最终的依赖注入,而不是 Dagger 1 中基于反射的解决方案,所以在性能上是有保障的。
图中的Module是应用中常用的功能模块,比如说网络访问,数据存储等。这个类会有一个@Module的注解,具体的代码我会在后面详细介绍。其实这个Module的作用是提供各种功能对象,而这些Module会放到一个有@Component的容器类中,也就是图中Component类。当我们想使用各种功能对象进行业务操作的时候,只需要这个容器就能得到被注册了的功能对象,图中的意思是在Activity中使用对象进行业务操作,当然也不仅限于Activity。
上图相对来说还是太简略了,并没有完整的表达出Dagger2的原理,下面我们直接从代码中感受一个Dagger2的强大。
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
首先创建一个HttpObject,我们假设这个HttpObject中有各种网络的操作,get,post,put等
public void get(){
Log.i("Dagger2","这里是get方法");
}
public void post(){
Log.i("Dagger2","这里是post方法");
}
}
创建HttpModule
@Module
public class HttpModule {
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
}
HttpModule的两个注解是需要注意的地方:@Module这个注解相当于给当前类打了一个标记,表明了这个类的类型,便于注入到容器中;@Provides这个注解放在了方法的上面,从上面的代码可以看出来,主要就是创建功能对象。
@Component(modules = {HttpModule.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
}
容器这个类被@Component所注解,而且是一个接口类,@Component中的modules参数接收的类型是一个数组,表示被装入容器的Module有哪些。injectMainActivity方法表示这个容器中的功能对象(例如HttpObject)会在哪个类使用,我这里使用的MainActivity做的测试,所以参数写的是MainActivity。
在配置完上述的代码之后,一定先rebuild!
public class MainActivity extends AppCompatActivity {
@Inject
HttpObject mHttpObject;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMyComponent.create().injectMainActivity(this);
mHttpObject.get();
mHttpObject.post();
}
}
因为我们要使用的类是HttpObject,所以在MainActivity创建这个类的对象,然后被@Inject所注解。要注意的是DaggerMyComponent这个类是rebuild之后生成的,调用DaggerMyComponent.create().inkectMainActivity(this)这句话来生成mHttpObject对象,调用HttpObject中的get和post方法就会有相应的输出。
相比机械性的记住这些注解以及使用流程,Dagger 2 背后的工作原理更加重要,这样我们将明白各个模块的职责,以及框架是如何将它们关联起来帮助我们完成依赖注入的,明白了原理后使用 Dagger 2 就会更加得心应手了。
所以我们这里先了解 Dagger 2 背后的基本原理,然后再学习其它内容。
以前边的demo为例,项目编译后会在app\build\generated\source\apt\debug\包名目录下生成依赖注入的相关类,如下:
按照之前说法,DaggerMainComponent是完成依赖注入的核心,所以从这个类开始分析,它的源码如下:
public final class DaggerMainComponent implements MainComponent {
private MainModule mainModule;
private DaggerMainComponent(Builder builder) {
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainModule = builder.mainModule;
}
// 重写inject方法,完成依赖注入
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
// 完成依赖注入的重要方法
private MainActivity injectMainActivity(MainActivity instance) {
// 给 MainActivity 中的 cat 成员变量赋值
MainActivity_MembersInjector.injectCat(instance, new Cat());
// 给 MainActivity 中的 flower 成员变量赋值
MainActivity_MembersInjector.injectFlower1(
instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
return instance;
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
// 完成DaggerMainComponent对象的创建
public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}
// 设置 mainModule 对象
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
}
}
典型的 Builder 构建模式,再结合之前 DaggerMainComponent 的用法来分析:
DaggerMainComponent.builder()
.mainModule(new MainModule())
.build()
.inject(this);
先通过builder()方法创建一个 Builder 对象,再通过其mainModule()方法设置mainModule 对象,接下来用 build()方法就是创建DaggerMainComponent 对象,这样它里边也有了一个mainModule对象。
在 DaggerMainComponent 中还重写了 MainComponent 接口的inject()方法,里边调用的injectMainActivity()方法是完成依赖注入的关键:
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectCat(instance, new Cat());
MainActivity_MembersInjector.injectFlower1(
instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
return instance;
}
首先是调用MainActivity_MembersInjector类的injectCat()方法直接创建一个 Cat 对象,完成 MainActivity 中 cat 的赋值,injectCat()方法声明如下:
public static void injectCat(MainActivity instance, Cat cat) {
instance.cat = cat;
}
然后是调用injectFlower()方法,完成 MainActivity 中 flower 的赋值,那么 flower 对象的值从哪里来呢?这里调用了MainModule_ProvideRedRoseFactory的proxyProvideRedRose()方法:
public static Flower proxyProvideRedRose(MainModule instance) {
return Preconditions.checkNotNull(
instance.provideRedRose(), "Cannot return null from a non-@Nullable @Provides method");
}
里边最终是调用了我们在 MainModule 中声明的 provideRedRose() 方法,所以在 DaggerMainComponent 内部是通过 MainActivity_MembersInjector 完成了最终的依赖注入。所以当在 Activity 中执行inject(this)方法时,就是开始创建依赖对象,并完成注入工作。
到这里整个依赖注入的流程就结束了,从源码的角度来看,整个过程更像是医生给患者注射药物。我们可以把依赖注入组件 DaggerMainComponent 看做“医生”,把 MainActivity_MembersInjector 看做“注射器”,MainModule 就是“药物”,MainActivity 就是“患者”,医生用注射器把药物送到患者体内。
有的小伙伴会问,你整这么一大堆是为了啥?要不然直接创建对象,要不然创建一个单例,多省事,可比Dagger2的这种方式方便多了。在中大型项目中,类似于HttpObject这种对象会被大量的应用,如果突然有一天这个类的初始化方法改变了,你岂不是要修改每一处吗。即便是使用单例getInstance方法也避免不了这种问题,因为如果在创建对象的时候需要在构造器中添加一个参数,每一处的getInstance也需要被修改。而Dagger2完美的避免了这种问题。
以上就是Android中Dagger 2的基本使用以及原理的学习,有关Android开发中还有更多的学习技术;这里推荐参考《Android核心技术手册》这个大文档里面记录了30多个技术板块。几千个小知识点带你引进更高的技术层面。
Dagger 2 的主要内容就这些了,使用 Dagger 2 必然要编写相关的辅助类、接口、使用各种注解,虽然没有直接 new 一个对象或者传统的依赖注入方式简单,但 Dagger 2 带来的是更好的代码解耦,更有利于后期的扩展维护,对于那些需要长期维护的项目这一点是更加重要的。