本篇的内容不涉及Dagger2的源码,只是为了更好的使用.
想进一步了解的话可以阅读以下文章:
dagger2让你爱不释手-基础依赖注入框架篇
dagger2让你爱不释手-重点概念讲解、融合篇
dagger2让你爱不释手-终结篇
Dagger2 这次入门就不用放弃了
Dagger2 使用正确姿势
Dagger2 彻底了解如何构建依赖关系
本着不重复造轮子的精神,理论上面几篇文章很详细了,那我就直接结合我的开发来谈谈吧
Dagger2的配置
目录添加apt支持,apt是用于自动生成代码来进行依赖注入的。
项目中的build.gradle添加:
dependencies {
//构建android项目的gradle
classpath 'com.android.tools.build:gradle:2.2.3'
//构建dragger2
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
在module的build.gradle添加:
apply plugin: 'com.neenbedankt.android-apt'
android{
...
}
dependencies {
provided 'org.glassfish:javax.annotation:10.0-b28'
compile 'com.google.dagger:dagger:2.0.2'
compile 'com.google.dagger:dagger-compiler:2.0.2'
}
第一步:编写Module
编写ActivityModule
@Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
@Provides
@ActivityScope
public Activity provideActivity() {
return mActivity;
}
@Provides
@ActivityScope
public List<DataListBean> provideList() {
return new ArrayList<>();
}
@Provides
@ActivityScope
public PlanManagerAdapter provideAdapter(List<DataListBean> data) {
return new PlanManagerAdapter(data);
}
}
类要用@Module注解来标示,可以看到我这个AcitivtyModule中定义了一个构造函数,需要传进来一个XXXActivity对象。
我们需要明确Module的作用是用来提供生成依赖对象的,比如我要注入PlanManagerAdapter,那么这个Module的作用就是需要生成一个PlanManagerAdapter 的对象,来让Dagger2注入到XXXActivity中。当然还可以是XXXPresenter对象,或者普通的bean对象等.
细心的同学应该看到了函数provideAdapter上面使用@Provides注解(@ActivityScope后面讲),用@Provides注解的函数名需要以provide开头,然后后面接什么内容都可以,看自己喜欢,事实上这里是根据返回值类型来标识的,方法名并不重要,只需要保证以provide开头即可。
然后这里需要传入一个List<DataListBean>参数,仔细看我上面的代码中还定义了两个函数,分别为provideList和provideActivity,这里provideAdapter的参数就是通过provideList这两个函数来获取的。剩下的provideActivity则是会出现在开发中的需要activity的地方,用@Inject注解标识就可以获取到activity的对象,如果没有声明provideList这个函数的话,编译期间会报错。
编写AppModule
我的项目中提供一个全局的RetrofitHelper对象来进行网络请求,他的生命周期是和APP一致的,这个时候我们就需要编写AppModule 了。
@Module
public class AppModule {
@Provides
@Singleton
RetrofitHelper provideRetrofitHelper() {
return new RetrofitHelper();
}
}
- 这里provide方法除了@Provides之外还添加了一个@Singleton注解,这里只是标注一下让大家看起来方便识别是全局单例的对象只是添加一个@Singleton注解并不能说明RetrofitHelper对象就是单例,这还要看在哪里初始化(后面讲)。
第二步:编写Component
编写ActivityComponent
@ActivityScope
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
void inject(HomeActivity homeActivity);
}
编写AppComponent了:
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
RetrofitHelper retrofitHelper(); //http的帮助类
}
- 我们编写的ActivityComponent需要用@Component注解来标识,同时声明了(modules = ActivityModule.class),并且依赖了AppComponent(dependencies = AppComponent.class),Component之间也可以依赖,然后提供了一个方法,叫做inject,用来在Activity中注入。这里的AppComponent提供了一个方法,用来暴露RetrofitHelper对象的。
第三步:在目标类中(activity)注入
Make Project,之后就会生成DaggerActivityComponent和DaggerAppComponent的类,之后我们在MyApplicaiotn中实例化DaggerAppComponent:
private static AppComponent appComponent;
@Override
public void onCreate() {
super.onCreate();
appComponent = DaggerAppComponent.builder()
.appModule(new AppModule())
.build();
}
//初始化AppComponent
public static AppComponent getAppComponent() {
return appComponent;
}
然后在Activity的onCreated函数中编写如下代码:
DaggerActivityComponent.builder()
.appComponent(MyApplication.getAppComponent())
.activityModule(new ActivityModule(this))
.build()
.inject(this);
这些工作做完之后接下来看下调用代码
- 首先在activity中注入PlanManagerAdapter 对象
这个对象就是来自ActivityModule中的provideAdapter方法返回的对象,此处再次强调因为PlanManagerAdapter构造需要传入参数,这个参数也要在ActivityModule中创建对象并返回.
-
其次就是在Presenter中注入了activity(可以作为上下文)和网络请求帮助类RetrofitHelper
-
大家会有疑问RetrofitHelper并没有在ActivityModule中啊,但是大家不要忘了,我在ActivityComponent中进行了依赖,这样就可以解决问题了
注入方式
- 注入实例的方式一种是通过编写Moudle,并且提供一些provideXXX()的方法,然后通过Component把这些对象进行注入。
-
其实在Dagger2中还有一种方式实现对象的注入,这种方式比较简单。就像上面我需要在activity中注入PlanManagerAdapter对象,除了编写Moudle,我们还可以在PlanManagerAdapter的构造函数中添加@Inject注解即可,代码如下:
这样就不需要在Moudle中添加provideXXX()的方法了,两种方式都可以.
当在目标类(activity)遇到
@Inject
类XXX XXX;
整个Dagger2的依赖注入的过程如下:
步骤1:查找Module中是否存在创建该类ProvidesXXX的方法。
步骤2:若存在创建类ProvidesXXX方法,查看该方法是否存在参数
步骤2.1:若存在参数,则按从步骤1开始依次初始化每个参数(上面讲过有参数的情况)
步骤2.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
步骤3:若不存在创建类方法,则查找Inject注解的构造函数,看构造函数是否存在参数
步骤3.1:若存在参数,则从步骤1开始依次初始化每个参数
步骤3.2:若不存在参数,则直接初始化该类实例,一次依赖注入到此结束
为什么ActivityComponent要提供一个inject方法
在XXXActivity中对对某些需要注入的成员添加@Inject注解的时候,Dagger2就会生成一个XXXActivity_MembersInjector的东西,这个是对成员变量进行注入的关键类,涉及到再深入的内容,我就没再研究了。我们只需要知道当我们需要向目标类(Activity)注入一些成员变量的时候,我们需要在ActivityComponent中提供一个方法:
void inject(XXXActivity xxxActivity);
并且在初始化的时候调用这个方法,才能成功注入。
Scope的使用,如何实现单例?
在我们的AppComponent中添加了一个注解为@Singleton
,@Singleton就是一个Scope,事实上@Sinleton中并没有创建单例的能力,那么AppComponent中提供的依赖注入是如何实现单例的呢。其实这个原理很简单。首先Module提供了创建实例的方法,接着AppComponent
中对Module进行管理,最后MyAppComponent在自定义Applicaiton中被实例化了一次。
其实@Singletop还有有一些作用的,首先一方面能让你直面的了解到这是一个单例,其次这个@Singletop能够更好的管理Modlue和Component之间的关系。
Dagger2需要保证Component和Module是匹配的,就需要用到这个注解。
为什么这样说,上文中我定义了一个ActivityScope
@Scope
public @interface ActivityScope {
}
因为是因为我在AppComponent中是有@Singletop,ActivityComponent中依赖了AppComponent
,所以我们需要使用一个Scope来匹配他们之间的关系,不然就会在编译期间报错。并不是说ActivityScope能让实例和Activity生命周期一致。和Activity生命周期一致是因为ActivityComponent是在Activity中生成实例的。
Qualifier 限定符的作用以及使用。
这也是一个很强大的注解,首先为什么需要用这么一个东西呢,之前说道过,在Module中的provide方法实际上是根据返回值来进行识别的。但是假设我需要根据不同的需求传入不同的构造参数的时候,如何区分呢?比如:一个Presenter,可能他有两个构造函数,分别对应不同的需求,这种情况下,provide方法的返回值都是Presenter,那么就需要使用Qualifier。具体怎么使用呢。
首先声明一个注解用@Qualifier修饰,然后在需要区别的地方添加就行了,下面是一个实例代码:
深入理解Java:注解(Annotation)自定义注解入门
@Module
public class AppModule {
private final App app;
public AppModule(App app) {
this.app = app;
}
@Provides
@Singleton
@ForApplication
Context provideAppContext() {
return app;
}
@Provides
@Singleton
Prefser providePrefser(@ForApplication Context context) {
return new Prefser(context);
}
@Provides
@Singleton
AccountManager provideAccountManager(@ForApplication Context context) {
return AccountManager.get(context);
}
首先在provide中添加一个注解@ForApplicition,接着在需要使用这个Context的地方再次标示即可。这样假设有其他一些提供了Activity的Context的地方和这里发生冲突的时候,Dagger2也能准确找到这个Applicaiton的Context。
总结
写完才发现,虽然对Dagger的使用心里很明白了但是写成文字叫大家理解还是火候不够,还有疑问的可以参考下开篇的文章或者留言一起交流。