dagger2入门指南
配置Dagger2
参考Dagger2 Wiki,在Gradle中添加以下依赖
dependencies {
provided 'javax.annotation:javax.annotation-api:1.2'
compile 'com.google.dagger:dagger:2.0.2'
apt 'com.google.dagger:dagger-compiler:2.0.2'
}
apt
是一个Gradle插件,协助Android Studio 处理Annotation Processors
,所以在Gradle中还必须添加以下内容(关于Android-apt
更多内容参考)
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
apply plugin: 'com.neenbedankt.android-apt'
开始使用
Dagger2是实现依赖注入
的一种手段,关于依赖注入,这里就不多介绍,想了解的可以参考, Dagger2中需要使用注解来完成依赖注入,我们来一个个介绍这些注解
@Inject
我们可以使用@Inject
这个注解来标注目标类中所依赖的其他类,也可以用来标注所依赖的其他类的构造函数。举个例子
public class Human{
@Inject
Father father;
}
public class Father{
@Inject
public Father(){ //如果标注有参数的构造方法,在调用构造方法前会去获取参数对应的对象
}
}
当Human
需要通过@Inject
注入Father
这个类的时候,会先调用Father
中标注的构造函数,生成相应的对象。这样Human
和Father
之间就有了一种无形的联系,但是仅仅这样还不够,我们还需要用到Component
来使他们连接起来
@Component
@Component
的作用就是连接目标类和目标类依赖实例(可以通过上面说的@Inject
标注构造函数也可以通过@Module
来产生)。那Component
是怎么工作的呢?
查找目标类中用
@Inject
标注的属性查找对应属性依赖实例
将依赖实例进行赋值
目标类需要初始化自己依赖的其他类还需要调用Component
的inject(Object object)方法开始注入
@Singleton @Component(modules = { ApplicationModule.class},dependencies={xxxComponent.class})
public interface ApplicationComponent {
Context getContext();
void inject(MyApplication mApplication);
}
比如在MyApplication
中需要注入其他类,则需要在MyApplication
中调用ApplicationComponent
的inject
方法开始注入。
-
modules
对应的就是提供依赖的Module
类,可以有多个 -
dependencies
表示该Component还依赖其他Component -
@Singleton
是一种Scope
注解,下面会说到
@Module
介绍@Inject
的时候我们说过,可以通过@Inject
标注构造函数生成对应的对象,但是如果是第三方类库,我们无法添加@Inject
注解的时候该怎么办,这时候就需要用到@Module
了。举个列子
@Module
public class Module{
//A是第三方类库中的一个类
@Provides
A provideA(){
return A();
}
}
通过
provideA()
这个方法我们就可以生成一个A
的实例,目标类中可以通过@Inject
来注入@provides
标注的方法直接返回以创建或者新创建的对象,只在Module
中使用Component
会首先从Module
中查找需要注入的实例,如果找到了,则停止,如果没有找到则是继续从Inject
标注的构造函数查找
@Qualifier
通过上面我们知道,创建类的实例有两种方式,这两个方式有先后之分,但是如果一个类在同一方式下有多个创建类的方法的时候,Component
会选取哪个方法来创建这个类的实例呢?举个例子
@Module
public class Module{
@Provides
A provideA(){
return A();
}
@provides
A provideOtherA(){
return A();
}
}
如果我们按照上面这样写,Dagger2在编译的时候就会报错,那么我们改如何解决呢?这时候就需要用到@Qualifier
了,我们可以用这个注解给不同的创建实例的方法进行标识并加以区分。
@Module
public class Module{
@Named("firstA")
@Provides
A provideFirstA(){
return A();
}
@Named("secondA")
@provides
A provideSecondA(){
return A();
}
}
@Named
是Dagger2对于@Qualifier
一个默认实现,我们也可以自定义,比如@ForApplication
和@ForAcitivity
来标识不同的Context
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {
/** The name. */
String value() default "";
}
这样,通过@Named
我们就可以区分不同的实例了
public class Human{
@Inject
@Named("firstA")
A firstA;
@Named("secondA")
@provides
A secondA;
}
@Scope
Scope
是一个注解作用域,通过自定义注解限定对象的作用范围。通过这个注解能够解决不同对象生命周期不一致的问题,比如Application
,ToastHelper
等存在于应用的整个生命周期,Adapter
,Presenter
等则随着Activity的销毁而销毁。我们可以通过自定义不同的Scope
来区分。举个例子
@Scope
@Documented
@Retention(RUNTIME)
public @interface PerApp{}
@PerApp @Component(modules = { ApplicationModule.class})
public interface ApplicationComponent {
Context getContext();
void inject(MyApplication mApplication);
}
@Module
public class ApplicationModule {
private final Context context;
public ApplicationModule(Context context) {
this.context = context;
}
@Provides @PerApp public Context provideApplicationContext() {
return context.getApplicationContext();
}
}
我们在ApplicationModule
中定义了创建Context
实例的方法,在ApplicationComponent
中管理ApplicationModule
,因为ApplicationComponent
只有在Application
中实例化一次,所以Context
的生命周期也就和Application
一样了,也就是Dagger
并不会帮你管理生命周期,只能自己来控制。那么PerApp
这个注解有什么用呢?
- 更好的管理
Component
与Module
之间的匹配关系,Dagger2在编译的时候会检查Component
管理的Module
,若发现Component
所标注的自定义Scope
注解与Module
中不一样,就会报错。 - 提高可读性,通过
PerApp
就知道是在整个Application
有小范围内
注意点:
- 一个
Module
中只能存在一种Scope
-
Scope
标注的Component
和所依赖的Component
的Scope
不能一样
实例讲解
创建ApplicationComponent
@PerApp
@Component(modules = { ApplicationModule.class })
public interface ApplicationComponent {
Context getContext();
ToastUtils getToastUtils();
}
ApplicationComponent
是一个全局的Component
,负责管理整个App的全局类实例,Context getContext();
和ToastUtils getToastUtils();
是将Context
和ToastUtils
暴露给子Component。
创建ApplicationModule
@Module
public class ApplicationModule {
private final MyApplication myApplication;
public ApplicationModule(MyApplication myApplication) {
this.myApplication = myApplication;
}
@PerApp @Provides Context provideContext() {
return myApplication.getApplicationContext();
}
@PerApp @Provides ToastUtils provideToastUtils(Context mContext) {
return new ToastUtils(mContext);
}
}
ApplicationModule
生成全局Context
和ToastUtils
实例
在Application中创建ApplicationComponent实例
public class MyApplication extends Application {
private ApplicationComponent mApplicationComponent;
@Override public void onCreate() {
super.onCreate();
mApplicationComponent =
DaggerApplicationComponent.builder().applicationModule(new ApplicationModule(this)).build();
}
public ApplicationComponent getApplicationComponent() {
return mApplicationComponent;
}
}
DaggerApplicationComponent
这个是有Dagger2生成的类,因为ApplicationComponent
只初始化一次,所以注入的类都是单例的,这就是Dagger2真正创建单例的方法。
划分Component
一个应用应该包含一个全局的Component
,负责管理整个全局类的实例,其他Componet建议按照页面来划分。
@PerActivity
@Component(dependencies = ApplicationComponent.class, modules = {
MainModule.class, ActivityModule.class
})
public interface MainComponent {
void inject(MainActivity mActivity);
}
@Module
public class MainModule {
@PerActivity @Provides GetUser getUser() {
return new GetUser();
}
}
@PerActivity
@Component(modules = { ActivityModule.class })
public interface ActivityComponent {
Activity getActivity();
}
@Module
public class ActivityModule {
private final Activity mActivity;
public ActivityModule(Activity mActivity) {
this.mActivity = mActivity;
}
@Provides @PerActivity Activity provideActivity() {
return mActivity;
}
}
上面是Main
这个页面的划分
DEMO
demo