Dagger2官网:http://google.github.io/dagger/
Githup地址:https://github.com/google/dagger
Dagger2是一个依赖注入框架,其目的就是将模块之间依赖性降低,也就是解耦。以MVP模式为例,我们把V的业务逻辑都写在P中,通常的做法在V对象中new P,向P的构造方法传入参数。如果P的构造方法改变了,就会影响了到V中的代码,相应也得修改,V与P之间的依赖性太强导致了这种问题。使用dagger2的话,就不会出现这样的问题。声明P的时候用@Inject注解,就会在V中自动注入P对象。即使改变了P的构造方法,也不会影响到V。
在Android 项目上,Daager2有两种使用方式。第一种方式,是适用于所有的Java项目的,Android工程因为是用Java编写的嘛(当然,新的语言Kotlin就先不管了),所以也适用。
dependencies {
compile 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
}
第二种方式,就只适用于Android工程。
dependencies {
compile 'com.google.dagger:dagger:2.x'
annotationProcessor 'com.google.dagger:dagger-compiler:2.x'
compile 'com.google.dagger:dagger-android:2.x'
compile 'com.google.dagger:dagger-android-support:2.x'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.x'
}
这两种方式,都有annotationProcessor(注解处理器),其作用是在编译时通过注解生成相应的类文件,生成的类文件就在相应的build\generated\source\apt目录下;其相应的jar包在哪?选中一个依赖包,右击选中Library Properties,如下图:
这两种方式相对比的话,第二种方式更简便。第一种方式必须为每个需要注入的类写一个对应的Component接口,为了不这么麻烦,可以写一个基类来处理;但是,这种写法是不好的,在之后会说明。今天,先介绍第一种方式。
@Inject:在需要依赖的地方使用这个注解,可以用在构造、字段、方法。
@Inject
HitMvpPresenter mHitMvpPresenter;
在HitFragment类中,mHitMvpPresenter在声明的时候,用@Inject标识了;仅仅是这样,编译是会报错的,我们还得在其构造方式也得用@Inject标识,如下:
@Inject
public HitMvpPresenter(DataManager dataManager, BaseActivity baseActivity,@ActivityContext Context context) {
mRequest = dataManager.getRequest();
mLocationUtil = dataManager.getLocationManagerService();
mBaseActivity = baseActivity;
mContext = context;
}
@Module: 提供依赖的类,必须用这个注解。在构用@Inject标识的类实例的时候,dagger2才知道从哪里去找到构建该类实例所需要的依赖类。比如,上面的HitMvpPresenter的构造方法所需baseActivity、context两个参数。
@Module
public class ActivityModule {
private BaseActivity mActivity;
public ActivityModule(BaseActivity activity){
mActivity = activity;
}
@Provides
BaseActivity provideActivity() {
return mActivity;
}
@Provides
@ActivityContext
Context providesContext() {
return mActivity;
}
}
@Provide: 在用@Module标识的类中,方法上用这个注解标识,以提供构造对象需要的依赖参数实例。
@Component: 在用@Component标识的接口中,实例化用@Inject标识的类,所需要的依赖就会在@Module标识的类或@Component标识的接口中找。比如,上面的HitMvpPresenter的构造方法所需dataManager就是在@Component标识的接口中找到的。
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
DataManager dataManager();
}
也就是说@Component的作用就是将@Inject和@Module连接起来。
简单地说了下,这几个常用注解的作用;接下来就是介绍怎么使用了。
demo地址:https://github.com/568240761/Dagger2Demo
@Component
@Retention(RUNTIME)
@Target(TYPE)
@Documented
public @interface Component {
Class>[] modules() default {};
Class>[] dependencies() default {};
@Target(TYPE)
@Documented
@interface Builder {}
}
点开@Component,里面有两个方法和一个注解(在下篇博客的说明)。两个方法都是返回Class数组,也就是说modules、dependencies的赋值可以是多个类文件。
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
DataManager dataManager();
}
@ActivityScope
@Component(dependencies = ApplicationComponent.class,modules = ActivityModule.class)
public interface ActivityComponent {
void inject(MovieActivity movieActivity);
void inject(FilmmakerActivity filmmakerActivity);
void inject(SortActivity sortActivity);
void inject(SearchActivity searchActivity);
}
ActivityComponent是依赖于ApplicationComponent的。这有什么用呢?比如,我在ActivityComponent中注入MovieActivity,MovieActivity所依赖的实例会在ActivityModule中找;找不到的时候,就会到ApplicationComponent中找。比如,上面的HitMvpPresenter的构造方法所需dataManager实例就是在ApplicationComponent中找到的
@Singleton与@ActivityScope
@Singleton是由Java提供的,@ActivityScope是我自定义的注解。打开@Singleton与@ActivityScope,如下:
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}
基本一样。它们都有个注解@Scope,@Scope是用来给依赖划定作用域的。在这里@Singleton与@ActivityScope也有这样的作用;还有一个用处,就是用@Scope标识的类或方法,会在注入的Component只存在一个实例,有点相当于单例,后面代码分析。
需要注意的是一个@Scope只能注解一个Component,不能再注解其他的Component,不然会编译不通过。也就是说,@Singleton用在了ApplicationComponent上,就不能再用在ActivityComponent上;而且因为ActivityComponent依赖于ApplicationComponent,所以ActivityComponent需要一个新的@Scope注解,不然也会编译不通过。
@Module
@Module
public class ApplicationModule {
protected final Application mApplication;
public ApplicationModule(Application application) {
mApplication = application;
}
@Provides
Application provideApplication() {
return mApplication;
}
@Provides
@ApplicationContext
Context provideContext() {
return mApplication;
}
@Provides
@Singleton
Request provideRequest() {
return new NetworkManagerService().createRetrofit();
}
}
结合上面的ApplicationComponent类,在ApplicationComponent中就提供了DataManager、Application、Context、Request这四个类的实例;而且在这里提供给所需类的四个实例都是一样的,并不会新生成实例。如果把ApplicationComponent类和provideRequest()
上的@Singleton去掉,Application、Context类提供的实例不会改变,而DataManager和Request提供的实例会每次都不同,也就是新生成一个实例;原因会在之后的代码分析说,其实上面也提到了,原因就在于@Scope。
@Module
public class ActivityModule {
private BaseActivity mActivity;
public ActivityModule(BaseActivity activity) {
mActivity = activity;
}
@Provides
BaseActivity provideActivity() {
return mActivity;
}
@Provides
@ActivityContext
Context providesContext() {
return mActivity;
}
}
在ActivityComponent中注入的类所需的依赖实例,会在ActivityModule和ApplicationComponent查找。
@ApplicationContext与@ActivityContext
在ApplicationModule和ActivityModule中出现了两个自定义的注解,是用来标识Context是来自Application还是Activity的。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ApplicationContext {
}
它们都有@Qualifier,当类的类型不足以鉴别时候,我们就可以使用这个注解标示。
参考博客: 深入理解Java注解类型(@Annotation)
到这,dagger2的配置工作就完成了,在用之前,一定要先编译项目,通过我们下载的注解编译器来自动生成类文件。
先来看项目中MyMovieApplication类
public class MyMovieApplication extends Application {
private ApplicationComponent mApplicationComponent;
public static MyMovieApplication get(Context context){
return (MyMovieApplication) context.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
}
public ApplicationComponent getApplicationComponent(){
if(mApplicationComponent == null){
mApplicationComponent = DaggerApplicationComponent.builder()
.applicationModule(new ApplicationModule(this))
.build();
}
return mApplicationComponent;
}
}
代码中的DaggerApplicationComponent就是自动生成的类文件。将ApplicationComponent写在Application中,是为了让它的生命周期就和Application是一样的,就不用担心ApplicationComponent被GC清理掉了。
在我的工程中,除了ActivityComponent,还有一个FragmentComponent;它们都分别对应Activity和Fragment。在这,我就以Activity为例来说明。
在官网上,给出的建议是为每一个需要依赖注入的类,新写一个Component。也就说,你有5个Activity需要依赖注入,就需要写5个Component,是不是很麻烦。为了不复制粘贴,我写了个基类来处理(其实,这种写法是不可行的,在后面会进行说明)。
public class DaggerActivity extends BaseActivity {
private ActivityComponent mActivityComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mActivityComponent = DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(MyMovieApplication.get(this).getApplicationComponent())
.build();
}
public ActivityComponent activityComponent() {
return mActivityComponent;
}
}
因为有的Activity需要依赖注入,有的又不需要,所以又新建了一个类。在代码中DaggerActivityComponent也是自动生成的,其中有个applicationComponent()
方法,可以看出ActivityComponent依赖于ApplicationComponent。下面再看个SearchActivity类的代码。
public class SearchActivity extends DaggerActivity implements SearchMvpView {
@Inject
SearchMvpPresenter mSearchMvpPresenter;
@Inject
MovieRecyclerViewAdapter mMovieRecyclerViewAdapter;
...............
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
bindView(this);
activityComponent().inject(this);
........
}
.......
}
通过执行activityComponent().inject(this)
,将SearchActivity注入到ActivityComponent中。代码中mSearchMvpPresenter和mMovieRecyclerViewAdapter的声明都用了注解@Inject,也就是说这两个实例生成所需的依赖都会在ActivityComponent和ApplicationComponent中查找;需要注意的是这两个类的构造器也需要用@Inject标识。以SearchMvpPresenter为例,代码如下:
public class SearchMvpPresenter extends BasePresenter<SearchMvpView> {
private final Request mRequest;
@Inject
public SearchMvpPresenter(DataManager dataManager) {
mRequest = dataManager.getRequest();
}
.......
}
代码中DataManager 的实例是可以在ApplicationComponent中找到的。
第一种方式就介绍完了,但是这种写法是有问题的。从我们写的代码上看是没有问题,而且复用性又好,但如果打开DaggerActivityComponent类文件,你就会发现问题,这个稍后说明。
程序启动后,如下图:
每打开一个继承于DaggerActivity或DaggerFragment的页面,都会新建一个ActivityComponent或FragmentComponent。这两个组件的生命周期都与Activity或Fragment一样;Activity或Fragment销毁了,相应的组件也会被回收。
先回顾一下,出现的两个问题:
如何实现在Component中提供的实例对象不变或者可变的
为什么在基类中统一处理注入类的方式不行
为了说清出第一个问题,稍微改了一下代码,如下:
@Module
public class ApplicationModule {
.......
@Provides
Request provideRequest() {
return new NetworkManagerService().createRetrofit();
}
}
@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
DataManager dataManager();
Request request();
}
public class SearchMvpPresenter extends BasePresenter<SearchMvpView> {
private final Request mRequest;
@Inject
public SearchMvpPresenter(Request request){
mRequest = request;
}
.........
}
在ApplicationModule中,将provideRequest()
上的@Singleton去掉(Component中提供的实例对象变与不变,其实就跟@Scope有关;DataManager类上有@Singleton,而provideRequest()
上没有,就是为了看代码的差异性);在ApplicationComponent中,新增request()
(SearchMvpPresenter需要的依赖实例是不会在ApplicationModule中查找的,所有需要在ApplicationComponent中新增request()
)
DaggerApplicationComponent类文件源码:
public final class DaggerApplicationComponent implements ApplicationComponent {
private Provider provideRequestProvider;
private Provider dataManagerProvider;
private DaggerApplicationComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideRequestProvider =
ApplicationModule_ProvideRequestFactory.create(builder.applicationModule);
this.dataManagerProvider =
DoubleCheck.provider(
DataManager_Factory.create(
provideRequestProvider,
ImageManagerService_Factory.create(),
LocationManagerService_Factory.create()));
}
@Override
public DataManager dataManager() {
return dataManagerProvider.get();
}
@Override
public Request request() {
return provideRequestProvider.get();
}
public static final class Builder {
private ApplicationModule applicationModule;
private Builder() {}
public ApplicationComponent build() {
if (applicationModule == null) {
throw new IllegalStateException(
ApplicationModule.class.getCanonicalName() + " must be set");
}
return new DaggerApplicationComponent(this);
}
public Builder applicationModule(ApplicationModule applicationModule) {
this.applicationModule = Preconditions.checkNotNull(applicationModule);
return this;
}
}
}
在MyMovieApplication中,通过调用静态builder()
,生成一个静态内部类Builder实例;再调用applicationModule()
,传入一个ApplicationModule实例;最后调用build()
生成DaggerApplicationComponent实例。
DaggerApplicationComponent实现了ApplicationComponent接口,重写了dataManager()
和request()
。这个两个方法分别返回DataManager和Request类性,也就是说依赖于ApplicationComponent的组件需要request与dataManager实例时,就调用这两个方法来获取所需的实例。
@Override
public Request request() {
return provideRequestProvider.get();
}
private Provider provideRequestProvider;
Provider是一个接口
public interface Provider<T> {
T get();
}
provideRequestProvider赋值在initialize()
方法中:
private void initialize(final Builder builder) {
this.provideRequestProvider =
ApplicationModule_ProvideRequestFactory.create(builder.applicationModule);
.......
}
进入create(builder.applicationModule)
中:
public static Factory create(ApplicationModule module) {
return new ApplicationModule_ProvideRequestFactory(module);
}
返回了ApplicationModule_ProvideRequestFactory实例:
public final class ApplicationModule_ProvideRequestFactory implements Factory<Request> {
.......
@Override
public Request get() {
return Preconditions.checkNotNull(
module.provideRequest(), "Cannot return null from a non-@Nullable @Provides method");
}
......
}
ApplicationModule_ProvideRequestFactory实现了Factory,Factory又继承于Provider:
public interface Factory<T> extends Provider<T> {}
所以provideRequestProvider.get()
,就是ApplicationModule_ProvideRequestFactory.get()
。在get()
中又调用了module.provideRequest()
,这个module就是我们在MyMovieApplication中,通过applicationModule()
,传入ApplicationModule实例,也就说调用的是ApplicationModule类中的provideRequest()
。
@Provides
Request provideRequest() {
return new NetworkManagerService().createRetrofit();
}
所以,每次调用provideRequest()
,都会new个新实例。我们再来看看dataManager()
;
@Override
public DataManager dataManager() {
return dataManagerProvider.get();
}
dataManagerProvider赋值与provideRequestProvider有两处不一样;
this.dataManagerProvider =
DoubleCheck.provider(
DataManager_Factory.create(
provideRequestProvider,
ImageManagerService_Factory.create(),
LocationManagerService_Factory.create()));
多了个DoubleCheck.provider()
方法(待会说明);在DataManager_Factory类中get()
方法的实现也不同。
@Override
public DataManager get() {
return new DataManager(
requestProvider.get(),
imageManagerServiceProvider.get(),
locationManagerServiceProvider.get());
}
直接调用DataManager的构造方法来构建实例;为什么会这样,其原因是DataManager的构造方法上用了@Inject。
@Singleton
public class DataManager {
@Inject
public DataManager(Request request, ImageManagerService imageManagerService, LocationManagerService locationManagerService) {
mRequest = request;
mImageManagerService = imageManagerService;
mLocationManagerService = locationManagerService;
}
}
也就是说在dagger2有两种方式提供实例,一种是通过@Provides标识的方法返回实例,一种是用@Inject标识的构造方法来构造实例。如果,这两种方式都没有,就会编译报错。
来看看DoubleCheck.provider()
是干什么?为了说明,我们再把@Singleton加上在provideRequest()
上,看看代码有什么不一样。
@Provides
@Singleton
Request provideRequest() {
return new NetworkManagerService().createRetrofit();
}
clean项目后,再重新编译。
this.provideRequestProvider =
DoubleCheck.provider( ApplicationModule_ProvideRequestFactory.create(builder.applicationModule));
除了provideRequestProvider赋值有点变化,也多了个DoubleCheck.provider()
方法。其它地方一模一样,这个DoubleCheck是什么?
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
private static final Object UNINITIALIZED = new Object();
private volatile Provider provider;
private volatile Object instance = UNINITIALIZED;
private DoubleCheck(Provider provider) {
assert provider != null;
this.provider = provider;
}
@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
@Override
public T get() {
Object result = instance;
if (result == UNINITIALIZED) {
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
Object currentInstance = instance;
if (currentInstance != UNINITIALIZED && currentInstance != result) {
throw new IllegalStateException("....");
}
instance = result;
provider = null;
}
}
}
return (T) result;
}
public static Provider provider(Provider delegate) {
checkNotNull(delegate);
if (delegate instanceof DoubleCheck) {
return delegate;
}
return new DoubleCheck(delegate);
}
........
}
DoubleCheck也实现Provider,所以调用provideRequestProvider.get()
,就是调用DoubleCheck中的get()
。在DoubleCheck中的get()
中,又对Request实例进行了缓存;所以多次调用DaggerApplicationComponent中request(),
返回的都是同一个实例。要想Component中提供的实例对象不变,就使用@Scope;提供的实例对象变化,就不用@Scope就行。
对于第二问题,我们先看生成的DaggerActivityComponent类文件的源码:
public final class DaggerActivityComponent implements ActivityComponent {
private Provider provideActivityProvider;
private Provider dataManagerProvider;
private Provider movieMvpPresenterProvider;
private Provider filmmakerRecyclerViewAdapterProvider;
private MembersInjector movieActivityMembersInjector;
private Provider filmmakerMvpPresenterProvider;
private Provider movieRecyclerViewAdapterProvider;
private MembersInjector filmmakerActivityMembersInjector;
private Provider
movieRecyclerViewAdapterProvider2;
private Provider sortMvpPresenterProvider;
private MembersInjector sortActivityMembersInjector;
private Provider searchMvpPresenterProvider;
private MembersInjector searchActivityMembersInjector;
private DaggerActivityComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.provideActivityProvider =
ActivityModule_ProvideActivityFactory.create(builder.activityModule);
this.dataManagerProvider =
new com_ly_mymovie_injection_component_ApplicationComponent_dataManager(
builder.applicationComponent);
this.movieMvpPresenterProvider =
MovieMvpPresenter_Factory.create(
MembersInjectors.noOp(),
provideActivityProvider,
dataManagerProvider);
this.filmmakerRecyclerViewAdapterProvider =
FilmmakerRecyclerViewAdapter_Factory.create(
MembersInjectors.noOp(),
dataManagerProvider,
provideActivityProvider);
this.movieActivityMembersInjector =
MovieActivity_MembersInjector.create(
movieMvpPresenterProvider, filmmakerRecyclerViewAdapterProvider);
this.filmmakerMvpPresenterProvider =
FilmmakerMvpPresenter_Factory.create(
MembersInjectors.noOp(),
provideActivityProvider,
dataManagerProvider);
this.movieRecyclerViewAdapterProvider =
MovieRecyclerViewAdapter_Factory.create(
MembersInjectors.noOp(),
dataManagerProvider,
provideActivityProvider);
this.filmmakerActivityMembersInjector =
FilmmakerActivity_MembersInjector.create(
filmmakerMvpPresenterProvider, movieRecyclerViewAdapterProvider);
this.movieRecyclerViewAdapterProvider2 =
com.ly.mymovie.ui.adapter.MovieRecyclerViewAdapter_Factory.create(
MembersInjectors.noOp(),
provideActivityProvider,
dataManagerProvider);
this.sortMvpPresenterProvider =
SortMvpPresenter_Factory.create(
MembersInjectors.noOp(), dataManagerProvider);
this.sortActivityMembersInjector =
SortActivity_MembersInjector.create(
movieRecyclerViewAdapterProvider2, sortMvpPresenterProvider);
this.searchMvpPresenterProvider =
SearchMvpPresenter_Factory.create(
MembersInjectors.noOp(), dataManagerProvider);
this.searchActivityMembersInjector =
SearchActivity_MembersInjector.create(
searchMvpPresenterProvider, movieRecyclerViewAdapterProvider2);
}
@Override
public void inject(MovieActivity movieActivity) {
movieActivityMembersInjector.injectMembers(movieActivity);
}
@Override
public void inject(FilmmakerActivity filmmakerActivity) {
filmmakerActivityMembersInjector.injectMembers(filmmakerActivity);
}
@Override
public void inject(SortActivity sortActivity) {
sortActivityMembersInjector.injectMembers(sortActivity);
}
@Override
public void inject(SearchActivity searchActivity) {
searchActivityMembersInjector.injectMembers(searchActivity);
}
public static final class Builder {
private ActivityModule activityModule;
private ApplicationComponent applicationComponent;
private Builder() {}
public ActivityComponent build() {
if (activityModule == null) {
throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
}
if (applicationComponent == null) {
throw new IllegalStateException(
ApplicationComponent.class.getCanonicalName() + " must be set");
}
return new DaggerActivityComponent(this);
}
public Builder activityModule(ActivityModule activityModule) {
this.activityModule = Preconditions.checkNotNull(activityModule);
return this;
}
public Builder applicationComponent(ApplicationComponent applicationComponent) {
this.applicationComponent = Preconditions.checkNotNull(applicationComponent);
return this;
}
}
private static class com_ly_mymovie_injection_component_ApplicationComponent_dataManager
implements Provider<DataManager> {
private final ApplicationComponent applicationComponent;
com_ly_mymovie_injection_component_ApplicationComponent_dataManager(
ApplicationComponent applicationComponent) {
this.applicationComponent = applicationComponent;
}
@Override
public DataManager get() {
return Preconditions.checkNotNull(
applicationComponent.dataManager(),
"Cannot return null from a non-@Nullable component method");
}
}
}
这个类文件中,有13个成员变量。每打开一个Activity页面,都会new一个DaggerActivityComponent,在initialize()
方法会为这13个成员变量赋值,13个成员变量在某个Acitivity中,能用到的只有几个。比如,在SearchActivity中,只有searchMvpPresenterProvider、movieRecyclerViewAdapterProvider2、searchActivityMembersInjector这3个成员变量有用,其他的根本用不到。我这个工程,将所有的Activity都写在ActivityComponent中,随Activity的增多,成员变量会越来越多,这样会很占内存;所以在基类中统一处理注入类的方式是不可取。
那如何避免这个问题?最简单的,就是为每一个Activity新建一个Component。
DaggerActivityComponent.builder()
.activityModule(new ActivityModule(this))
.applicationComponent(MyMovieApplication.get(this)
.getApplicationComponent())
.build()
.inject(this);
以上这样的相似代码就复制粘贴呗;但是这样做的话,代码在以后是很难重构;最严重的是它违背了依赖注入的核心原则:一个类不应该知道它是如何被注入的。所以,第一种方式用在Android工程上是不可行的。第二种方式的介绍,我会过几天码。