Dagger2是一个实现注入的框架,相信大家都听说过ButterKnife,Dagger2和ButterKnife的作用是一样的,但是实现的功能更加强大。ButterKnife只能注入View和事件,而Dagger2可以注入任何一个对象。本节主要介绍一下Dagger2的实用及实现的原理。
一、Dagger2的使用
首先我先用张图来简单说明一下Dagger2使用的流程
图中的Module是应用中常用的功能模块,比如说网络访问,数据存储等。这个类会有一个@Module的注解,具体的代码我会在后面详细介绍。其实这个Module的作用是提供各种功能对象,而这些Module会放到一个有@Component的容器类中,也就是图中Component类。当我们想使用各种功能对象进行业务操作的时候,只需要这个容器就能得到被注册了的功能对象,图中的意思是在Activity中使用对象进行业务操作,当然也不仅限于Activity。
上图相对来说还是太简略了,并没有完整的表达出Dagger2的原理,下面我们直接从代码中感受一个Dagger2的强大。
1.引入Dagger2
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
2.创建网络访问对象及其Module模块
首先创建一个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这个注解放在了方法的上面,从上面的代码可以看出来,主要就是创建功能对象。
3.创建容器Component
@Component(modules = {HttpModule.class})
public interface MyComponent {
void injectMainActivity(MainActivity mainActivity);
}
容器这个类被@Component所注解,而且是一个接口类,@Component中的modules参数接收的类型是一个数组,表示被装入容器的Module有哪些。injectMainActivity方法表示这个容器中的功能对象(例如HttpObject)会在哪个类使用,我这里使用的MainActivity做的测试,所以参数写的是MainActivity。
4.在类中使用HttpObject
在配置完上述的代码之后,一定先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的优点
有的小伙伴会问,你整这么一大堆是为了啥?要不然直接创建对象,要不然创建一个单例,多省事,可比Dagger2的这种方式方便多了。在中大型项目中,类似于HttpObject这种对象会被大量的应用,如果突然有一天这个类的初始化方法改变了,你岂不是要修改每一处吗。即便是使用单例getInstance方法也避免不了这种问题,因为如果在创建对象的时候需要在构造器中添加一个参数,每一处的getInstance也需要被修改。而Dagger2完美的避免了这种问题
二、源码分析
下面我们从源码中分析Dagger2的实现流程
上面的代码我们使用了create方法来创建MyComponent,这次我们使用建造者的方式来创建,Dagger2中提供了这种方式,如下代码
DaggerMyComponent
.builder()
.httpModule(new HttpModule())
.build()
.injectMainActivity(this);
其实我们从create的方法中进入就可以看到,其实create中包装了建造者的方式。
builder方法其实没什么说的,点进去看,其实就是Builder的创建,代码我就不贴出来了。
httpModule这个方法肯定就是相应Module的创建了,代码如下,我们可以看出来,源代码中只是做了一个对象非空的检查
public Builder httpModule(HttpModule httpModule) {
this.httpModule = Preconditions.checkNotNull(httpModule);
return this;
}
而build方法主要就DaggerMyComponent的创建,代码如下:
public MyComponent build() {
if (httpModule == null) {
this.httpModule = new HttpModule();
}
return new DaggerMyComponent(this);
}
以上的代码都没什么特别的,主要就是建造者模式的只用,下面我们来分析一下injectMainActivity方法的逻辑。
我们点击进入injectMainActivity这个方法,会进入到我们所写的MyComponent这个接口方法中,点击图中红框的位置会出现接口的实现类或者直接进入到实现类
MyComponent的实现类其实我们已经见到了,就是DaggerMyComponent,其中injectMainActivity的实现方法如下:
@Override
public void injectMainActivity(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
mainActivityMembersInjector的类是MembersInjector<T>这个接口类,同样的我们查看这个方法的实现类方法:
我们发现实现类有两个,那我们查看哪个呢?我们来这样看:首先将项目切换到project模式,依次进入如下目录:app->build->generated->source->apt->debug,到这里,我们就发现了我们项目的包,可以看见生成了几个类,如图:
而图中红框标注的正是MembersInjector<T>的实现类,我们想要找的就是它,我们来查看injectMembers方法的实现:
private final Provider<HttpObject> mHttpObjectProvider;
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.mHttpObject = mHttpObjectProvider.get();
}
代码中的instance是MainActivity,而mHttpObject是MainActivity中的mHttpObject(从这里我们可以看出来,想要被注解的对象修饰符一定是public),mHttpObject是通过mHttpObjectProvider.get()获得的,mHttpObjectProvider的类是Provider,而Provider是一个接口,我们如果还是使用上面的方法来找实现类,你会发现实现类有四五十个,那怎么办呢?
从上面系统帮我们生成的类中有如下这么一个类,而这个类这是我们想要找的Provider的实现类,我们点击去查看get方法的实现:
@Override
public HttpObject get() {
return Preconditions.checkNotNull(
module.providerHttpObject(), "Cannot return null from a non-@Nullable @Provides method");
}
get方法所返回的是module通过providerHttpObject方法创建的,这个module是HttpModule,点击去查看providerHttpObject:
@Module
public class HttpModule {
@Provides
public HttpObject providerHttpObject(){
return new HttpObject();
}
}
这个不就是我们的HttpModule中的方法吗?是的,Dagger框架绕了一大圈创建了HttpObject这个对象。
以上就是Dagger创建对象的流程