本文适合学习使用过Dagger后,对Scope概念还感到困惑的朋友,文中不会介绍Dagger的基本知识。
Dagger是一个优秀的依赖注入框架,使用它会大大降低项目各模块间的耦合,提高项目的可扩展性。我看到简书上讲Dagger的文章不少,但大都对其Scope概念语焉不详,显然作者本人也没有完全搞清楚。于是我决定写篇小文来解释一下Scope的含义及用法。
一、不同组件有不同的用途
使用Dagger,需要定义很多组件,不同的组件是为了完成不同的依赖注入功能。比如,有的组件是为了给某个Activity注入依赖,有的是为了给某个Service注入依赖。从组件里面的inject方法可以了解这个组件是给谁注入依赖的。举例如下:
//这个组件是用来给MainActivity注入依赖的
@Component(modules ={MainModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
//这个组件是用来给MainService注入依赖的
@Component(modules ={BackModule.class})
public interface BackComponent {
void inject(MainService service);
}
二、让MainComponent与MainActivity的生命周期保持同步
MainComponent用来给MainActivity注入依赖,我们当然希望MainComponent与MainActivity“同生共死”,即MainActivity创建的同时创建MainComponent,MainActivity销毁的同时销毁MainComponent。
那么怎么做到呢?
1、定义Scope
我们可以定义一个Scope注解,把它加到MainComponent头上。
定义Scope如下:
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface MainActivityScope {
}
2、为MainComponent添加Scope
现在,我们把上面自定义的Scope添加到MainComponent头上,如下:
//这个组件是用来给MainActivity注入依赖的
@MainActivityScope
@Component(modules ={MainModule.class})
public interface MainComponent {
void inject(MainActivity activity);
}
3、将MainComponent与MainActivity同步
很多初学者,误以为添加上自定义的Scope后,MainComponent就会获得一种神奇的能力,能够自动实现与MainActivity的“生死与共”。这种想法天真烂漫可爱,但却是错误的。
添加上自定义的Scope后,MainComponent并没有获得什么超能力,要想让它与MainActivity的生命周期同步,还需要我们手动完成,即:
- MainActivity的onCreate方法中创建MainComponent并完成依赖注入
- MainActivity的onDestroy方法中销毁MainComponent
三、Scope为我们带来的好处
有的人说,既然Scope根本无法决定MainComponent的创建销毁,最终还要靠我们手动完成,那我为什么要使用Scope?
原因有三。
1、方便管理
在MainComponent加上@MainActivityScope注解后,我们可以很清楚地知道,这个组件是用来给MainActivity注入依赖的,这会提醒我们将MainComponent的创建销毁与MainActivity关联起来。
2、不得不用
Dagger可以使用组件依赖,将不同的组件组合起来完成某个依赖注入任务,但它规定:父组件与子组件不能有相同的Scope,这时候就不得不自己定义Scope。
3、实现单例
这是我认为最重要的一点。还是以上面的MainComponent为例,它有一个模块,即MainModule,假设它包含如下内容:
@Module
public class MainModule {
Application mApp;
public MainModule(Application app){
this.mApp = app ;
}
@Provides
Application providesApplication(){
return mApp ;
}
@Provides
OkHttpClient providesOkHttpClient(){
return new OkHttpClient();
}
Dagger规定,一个组件的模块中的@Provides注解的方法,要不加上与组件相同的Scope,要不就不加。在我们的例子中,MainModule的两个被@Provides注解的方法都没有加@MainActivityScope注解。我们来看看这会造成什么后果。
第一个方法提供Application实例,它直接返回模块MainModule内缓存的Application实例,由此我们知道,组件MainComponent提供的Application实例只有一个,就是模块MainModule创建时传入的Application实例,这时我们说,MainComponent注入的Application实例是一个单例。
第二个方法提供OkHttpClient实例,在 providesOkHttpClient方法中,会new一个OkHtttpClient实例返回。这意味着,每当我们用MainComponent注入OkHttpClient实例时,都会重新创建一个崭新的OkHttpClient实例,如果你注入五次,那么就会有五个OkHttpClient实例。
这显然不是我们想要的结果,我们希望用MainComponent注入的OkHttpClient也是单例,怎么办?
我们可以给providesOkHttpClient方法加上@MainActivityScope注解,如下:
@Provides
@MainActivityScope
OkHttpClient providesOkHttpClient(){
return new OkHttpClient();
}
有了这个注解,当我们第一次用MainComponent注入OkHttpClient实例时,Dagger会调用providesOkHttpClient方法创建一个OkHttpClient实例返回,同时,Dagger会缓存这个新创建的实例。
以后,只要这个MainComponent没有销毁,再次使用MainComponent注入OkHttpClient实例时,Dagger都会直接返回缓存的OkHttpClient实例,而不是调用providesOkHttpClient方法创建新的OkHttpClient实例。这就确保了MainComponent注入的依赖都是一个实例,也就是实现了单例模式。
好了,希望你读完本文能够帮助你理解掌握Dagger中的Scope概念,在工作中更好地使用Dagger。