Dagger on Android-Dagger2详解
Dagger on Android-从Dagger1到Dagger2
以上三篇文章是前一段时间整理的,从本篇开始,就来跟我一起学习如何使用
Dagger2
吧。如果你迫不及待地想了解Dagger2的使用,那你可以先去看一下在Dagger2 Snapshot 版本发布后,我在CSDN上发的 Android 依赖注入: Dagger 2 实例讲解(一).
下面就跟我一起一步一步来学习 Dagger2
.
一、配置Gradle
参考GitHub上 Dagger2 Wiki中的介绍,将Maven格式的配置文件转化成Gradle格式,如下:
1 2 3 4 | dependencies { compile 'com.google.dagger:dagger:2.0.2' provided 'com.google.dagger:dagger-compiler:2.0.2' } |
提示:provided 表示仅在编译时用到。Dagger-compiler会在编译时生成相关代码
添加Dagger2的库之后,这还没有完成,还有两点需要注意:
- 1、Dagger2 依赖Java Annotation Processors 来分析和验证依赖。Android Studio默认不会识别
Dagger2
自动生成的代码,使用 android-apt plugin可以将这些生成的文件添加到IDE类路径中去,而且还可以让你Trace到这些代码。
dependencies {
// other classpath definitions here
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
apply plugin: 'com.neenbedankt.android-apt'
- 2、Dagger2 生成的代码中会使用
@Generated
注解,而javax.anotation.generated
在Java 6及以上版本的JDK中都有,在Android API中是没有的,所在在Android项目中要加入以下库的依赖:
provided 'org.glassfish:javax.annotation:10.0-b28'
如果不添加这个,在编译时会报以下错误:
1 2 | error: cannot find symbol class Generated error: package javax.annotation does not exist |
配置完成后的build.gradle
文件如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.fidroid.dagger2example" minSdkVersion 14 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } buildscript { repositories { jcenter() } dependencies { // replace with the current version of the Android plugin classpath 'com.android.tools.build:gradle:2.0.0-alpha5' // replace with the current version of the android-apt plugin classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' //Dagger compile 'com.google.dagger:dagger:2.0.2' provided 'com.google.dagger:dagger-compiler:2.0.2' provided 'org.glassfish:javax.annotation:10.0-b28' } |
配置完成之后,点击 这个按钮就可以引入Dagger2的库了。至此使用Dagger2的准备工作已经完成。
二、开始使用
1、相关Annotation说明
在前面的文章中讲解Dagger1时已经已经介绍过,由于Dagger2又加入了一些新的Annotation,这里在统一介绍一下:
@Module
: 表明这个类是一个Dagger module,类中的方法提供依赖。
injects
: 表明这个module所要注入的类,我们还说明这类想会被直接注入ObjectGraph,这点下面会说。
@Provides
: 表明这个方法是注入的提供者(injection provider).方法的名称是什么不重要,关键是返回的类型。在@Module
注解的类中使用。
@Singleton
: 使用了Singleton注解的方法总是会返回相同的实例化对象,这种方式比平时用的单例模式要好很多。每次注入,我们不会得到一个新的实例对象,而是返回一个已经存在,同时也解释了provider的作用。Application 对象是唯一的。
@Component
: 其注解的类是modules与injection之间的桥接接口(interface
),有自己的生命周期,生命周期结束,接口下提供的对象也会被回收。
@Subcomponet
: 用来扩展@Component,同Component基本有一样的属性。不同的是:
- 1、其注解的接口要在父Component中声明
- 2、可以访问父Component中的所有元素
@Scope
: 这个注解使Dagger持有Component中提供的对象的一个实例,这个实例随component有一定的生命周期,Provider 方法如果没有使用@Scope注解,它提供的对象在每次使用@Inject注入使用时都会新建一个新的对象。@Singleton是一个被Dagger定义的特殊的Scope,我们也可以定义自己的Scope。(之所以在介绍Dagger1中没有介绍Scope这个概念,一是因为刚接触Dagger的同学开始学习时本来就搞不清楚,如果再加入Scope恐怕会更难理解,之前的介绍只是为了入门。二是在Dagger1中实现这个相对Dagger2会复杂一点,也不容易懂,而且在使用中比较少用。)另外,一旦同时使用自己定义的@Scope与@Singleton,那后者将会失效。
另外需要注意的是:使用@Scope注解的Component不能被没有使用其注解的Component所依赖。[注1]
2、创建Module
这里我们创建两个Module类:AppModule.java 和 DomainModule.java。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | @Module public class AppModule { private MyApplication mApplication; public AppModule(MyApplication application) { mApplication = application; } @Provides @Singleton Application providesApplication() { return mApplication; } @Provides @Singleton Context providesContext() { return mApplication; } } |
1 2 3 4 5 6 7 8 | @Module public class DomainModule { @Provides @Singleton public AnalyticsManager provideAnalyticsManager(Context context) { return new AnalyticsManager(context); } } |
这个AppModule同使用Dagger1时基本没有差别,就是缺省了@Module的参数,DomainModule中只有一个提供AnalyticsManager对象的方法,这个大家应该都很熟悉了,这里就不再赘述。可对比Dagger on Android-Dagger1介绍。
3、创建AppComponent
@Component是Dagger2中的一个新的注解,它提供了连接Module和Injection的接口。这里创建AppComponent如下:
1 2 3 4 5 6 7 8 9 10 11 12 | @Component( modules = { AppModule.class, DomainModule.class } ) @Singleton public interface AppComponent { MyApplication inject(MyApplication app); //获取其他对象的方法 .... } |
@Component
中包含modules
和dependencies
两个参数,这个modules
和 Dagger1中的includes
作用基本是一样,dependencies
指向所依赖的外部类,下面使用时再具体说明。
AppComponent是一个接口(interface),包含了一个inject(MyApplication app)方法,这个方法不是必须的,原因在下面会详细说明。
@Component的作用是为Module和Injection提供接口,所以我们获取其他所依赖的对象,也要在这里添加相应的方法。不管现在你有没有明白这句话,这个问题将在下面重点说明,请继续往下看。
4、使用Dagger2
Module 和 Component都已经完成,下一步就要进行编译,使Dagger2在编译时生成代码。
编译:shitf+R
活着使用gradle build
再活着鼠标点击运行
按钮. <😊Android Studio 2.0 Preview 5 Instant Run 真是太好用了😊>
编译之后会生成一些代码,稍后会进行分析。
a、创建MyApplication并完成Dagger的初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class MyApplication extends Application { AppComponent appComponent = null; @Inject AnalyticsManager analyticsManager; @Override public void onCreate() { super.onCreate(); appComponent = DaggerAppComponent.builder() .appModule(new AppModule(this)) .domainModule(new DomainModule()) .build(); appComponent.inject(this); analyticsManager.register(); analyticsManager.send("start "); } public AppComponent getAppComponent() { return appComponent; } } |
DaggerAppComponent使Dagger自动生成的,是AppComponent的实例。
appComponent.inject(this)
这句是将Dagger注入到MyApplication这个类中,在这句之后,我们可以通过注入其它对象完成相应功能,这里注入了AnalyticsManager
对象。如果MyApplication中并不需要依赖其他对象,那么Dagger就不必在此注入,也就是说AppComponent就不用提供 inject(MyApplication app)
这个接口,可以提供一个MainActivity inject(MainActivity activity)
这样的接口,直接在Activity中注入(这解释了上面的相应问题),但是,在实际应用中,像上面代码一样,这个接口还是需要的。
b、创建一个ActiviyComponent
1 2 3 4 5 6 7 8 | @Component( dependencies = AppComponent.class ) @ScopeActivity public interface ActivityComponent { void inject(MainActivity activity); OSHelper getOSHelper(); } |
dependencies = AppComponent.class
表明这个Component依赖于AppComponent
.
这里看到有个 @ScopeActivity
注解,这个不是Dagger自带的,是自定义的。因为AppComponent使用@Singleton注解,上面说过@Singletion
是一个特殊的@Scope
,所以ActivityComponent如果没有使用 @Scope
注解,那么将无法依赖 AppComponent
. 看上面[注1]
自定义ScopeActivity:
1 2 3 4 | @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ScopeActivity { } |
ActivityComponent
提供了两个方法:
inject(MainActivity activity)
方法将Component注入到MainActivity
getOSHelper()
方法获取一个OSHelper的对象,这个对象随MainActivity有一样的生命周期
OSHelper代码如下:
1 2 3 4 5 6 7 8 9 | public class OSHelper { @Inject public OSHelper() { //inject 注解的构造函数是必要的 } public String getDeviceBrand() { return Build.BRAND; } } |
下面将Dagger注入到MainActivity,并调用OSHelper中的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; // @Inject OSHelper osHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获得ActivityComponent对象 ActivityComponent activityComponent = DaggerActivityComponent.builder() .appComponent(((MyApplication) getApplication()).getAppComponent()) .build(); //注入到MainActivity中 activityComponent.inject(this); //通过ActivityComponent中的依赖对象获取接口获得OSHelper对象 osHelper = activityComponent.getOSHelper(); Log.d(TAG, "onCreate: " + osHelper.getDeviceBrand()); } } |
至此,Daggger的使用已经讲解完毕,使用起来还是挺方便的。
另外,细心的你可能发现,上面代码中OSHelper对象声明处的上方 @Inject
给注释掉了,这是什么意思呢?其实看过 Android 依赖注入: Dagger 2 实例讲解(一) 这篇博文的应该发现,这篇最后谈到的问题是和目前这个一样的,就是:
直接使用 @Inject
注入依赖对象的方式 和 通过 Component
接口提供依赖对象的方式到底有什么不一样? 下面通过源码层面来回答这个问题。
c、深入理解
直接使用
@Inject
注入依赖对象的方式 和 通过Component
接口提供依赖对象的方式到底有什么不一样?
上面这句不知你是否明白?拿OSHelper这个类来说,直接使用
@Inject
注入依赖对象的方式
就是在MainActivity中通过如下代码实现:
1 2 | @Inject OSHelper osHelper; |
这样注入之后,在 activityComponent.inject(this);
这句后面执行的代码都可以直接使用osHelper对象。
通过
Component
接口提供依赖对象的方式
是指在ActivityComponent接口中添加 getOSHelper()
方法的方式,在使用的时候通过 activityComponent.getOSHelper()
得到OSHelper对象。
这样看来直接注入的方式更为简单一些,而且使用起来也较方便。没错,之前一直都是这样使用的。下面通过Dagger2生成的代码来简单分析这两种方式的不同。
先来看一下当前使用第二种方式下生成的ActivityComponent的源码:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerActivityComponent implements ActivityComponent { private DaggerActivityComponent(Builder builder) { assert builder != null; initialize(builder); } public static Builder builder() { return new Builder(); } private void initialize(final Builder builder) { } @Override public void inject(MainActivity activity) { MembersInjectors.noOp().injectMembers(activity); } @Override public OSHelper getOSHelper() { return OSHelper_Factory.create().get(); } public static final class Builder { private AppComponent appComponent; private Builder() { } public ActivityComponent build() { if (appComponent == null) { throw new IllegalStateException("appComponent must be set"); } return new DaggerActivityComponent(this); } public Builder appComponent(AppComponent appComponent) { if (appComponent == null) { throw new NullPointerException("appComponent"); } this.appComponent = appComponent; return this; } } }
生成的代码很清晰,通过 Builder.build()
得到一个ActivityComponent对象,再通过 getOSHelper()
就可以得到一个OSHelper对象。这个过程很简单,为了与第一种方式做对比,请注意以下两个方法:
1 2 3 4 5 6 7 | private void initialize(final Builder builder) { } @Override public OSHelper getOSHelper() { return OSHelper_Factory.create().get(); } |
再看直接注入的方式生成的ActivityComponent代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | @Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerActivityComponent implements ActivityComponent { private MembersInjector<MainActivity> mainActivityMembersInjector; private DaggerActivityComponent(Builder builder) { assert builder != null; initialize(builder); } public static Builder builder() { return new Builder(); } private void initialize(final Builder builder) { this.mainActivityMembersInjector = MainActivity_MembersInjector.create( (MembersInjector) MembersInjectors.noOp(), OSHelper_Factory.create()); } @Override public void inject(MainActivity activity) { mainActivityMembersInjector.injectMembers(activity); } public static final class Builder { private AppComponent appComponent; private Builder() { } public ActivityComponent build() { if (appComponent == null) { throw new IllegalStateException("appComponent must be set"); } return new DaggerActivityComponent(this); } public Builder appComponent(AppComponent appComponent) { if (appComponent == null) { throw new NullPointerException("appComponent"); } this.appComponent = appComponent; return this; } } |
对比一下,少了 getOSHelper()
方法,但 initialize
方法不在是一个空的方法:
private void initialize(final Builder builder) {
this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), OSHelper_Factory.create());
}
可以看到在这个方法进行了 OSHelper_Factory.create()
这个操作,这个方法返回的是 Provider<OSHelper> osHelperProvider
,用过Dagger1的同学也许对这个会比较熟悉,要得到OSHelper对象,只要调用Provider的 get()方法即可,osHelperProvider.get()
. 那么这个方法什么时候会调用呢,我们再来继续看MainActivity_MembersInjector
这个类:
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
private final MembersInjector<AppCompatActivity> supertypeInjector;
private final Provider<OSHelper> osHelperProvider;
public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<OSHelper> osHelperProvider) {
assert supertypeInjector != null;
this.supertypeInjector = supertypeInjector;
assert osHelperProvider != null;
this.osHelperProvider = osHelperProvider;
}
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
supertypeInjector.injectMembers(instance);
instance.osHelper = osHelperProvider.get();
}
public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<OSHelper> osHelperProvider) {
return new MainActivity_MembersInjector(supertypeInjector, osHelperProvider);
}
}
其中的injectMembers
方法有调用上述get()方法,这个方法在DaggerActivityComponent
的inject(MainActivity activity) 方法中被调用,也就说在注入时就会得到OSHelper对象。
两种方式的差异已经很清楚了,一个使用时创建对象,一个注入时就创建对象,前者符合Dagger2设计,避免了一开始就创建一堆对象。这就是我给出的结论,