Dagger2-Scope

Dagger2的@Scope的用法介绍

Scope–能为我们提供什么

几乎所有的项目都会使用单例-比如API Client,DatabaseHelper,Analytic Manager等。因为依赖注入,所以我们不用关心实例化。我们不应该在代码里考虑如何获取这些对象。取而代之的是@Inject会给我们提供应该要提供的实例。
在Dagger2的Scope机制使得让一个类保持单例让他的存活周期跟Scope一样长。例如在实际中一个在@ApplicationScope的实例的存货周期跟Application的生命周期是一样的。@ActivityScope的存活周期跟Activity的生命周期相同(比如我们可以在同一个Activity下所有的Fragment共享单例)。

简单说:Scopes给我们创建了一种”局部单例“,生命周期取决于Scope自己。

但是,Dagger2并没有提供@ActivityScope@ApplicationScope,这些都是要通过@Scope来自定义。Dagger2默认只提供@Singleton的Scope注解。

Scope实践

我们要实现比Application/Activity Scope更加复杂的Scope。用GithubClient项目做为解释,项目中用到了三个Scope:

  • @Singleton - ApplicationScope
  • @UserScope - 用来联系用户的实例类的Scope(已经登录的用户)
  • @ActivityScope - 用来做与Activity生命周期相同的类的Scope(presenters 在我们的例子中)

引入的@UserScope跟前一篇文章的方案有不同之处。从用户体验的角度来说他没有提供任何帮助。但是从架构角度来说,他帮助我们在不需要传入任何intent参数的情况下为我们提供User实例。
并且那些需要user数据的类(RepositoriesManager类在这个例子中)能通过把User作为构造器函数拿到User。并且是在需要的时候去初始化,而不是在App启动的时候去初始化。这意味着:RepositoriesManager会在我们通过GithubApi拿到用户之后去做初始化(在RepositoriesListActivity呈现之前)。
这是简单的一个我们应用的Scopes和Component的示意图:

Singleton(ApplicationScope)是存活周期最长的Scope。
UserScope作为ApplicationScope的子Scope,他可以访问访问父Scope的对象。
ActivityScope也是如此,可以拿到UserScope跟ApplicationScope的对象。

Scope 生命周期示例


单例的生命周期是从app启动后的App的存活时间。
UserScope的创建是从我们通过GithubApi拿到User开始(真实情况下,实在用户登录后),并在我们回到SplashActivity后被销毁(真实情况下,是在用户登出后)。当新登录一个用户,会产生另外一个UserScope.
每个ActivityScope存活时间跟他所对应的Activity是一样的。

实现

在Dagger2中,Scope的实现归结于对Component的正确设置。一般情况下有两种设置:

1.使用Subcomponent注解 2.使用Component依赖

两者最大的区别在于对象图的共享。Subcomponent可以访问他们Parenent Component的所有对象图,而Component依赖只能通过Component暴露对象接口来访问。
这里选择第一种实现AppComponent

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Singleton
@Component(
modules = {
AppModule.class,
GithubApiModule.class
}
)
public interface AppComponent {
UserComponent plus(UserModule userModule);
SplashActivityComponent plus(SplashActivityModule splashActivityModule);
}

他会是其他SubComponent的根Component:UserComponent跟ActivitysComponents。我们去掉了上一章的依赖对象的接口暴露。Subcomponent可以直接访问全部的对象。
作为代替,我们新增了两个方法:

UserComponent plus(UserModule userModule); SplashActivityComponent plus(SplashActivityModule splashActivityModule);

这表示我们可以从AppComponent创建两个子Components:UserComponentSplashActivityComponent。两个子Components都可以访问AppComponent的Module的依赖。
这些方法的命名规则是:返回类型是subcomponent类,方法名字随意,参数是这个subcomponent的module
比如:UserComponent需要一个module(他通过plus传入)。这样,我们通过增加一个新生成的对象module,集成AppComponent图标:

1
2
3
4
5
6
7
8
9
10
11
@UserScope
@Subcomponent(
modules = {
UserModule.class
}
)
public interface UserComponent {
RepositoriesListActivityComponent plus(RepositoriesListActivityModule repositoriesListActivityModule);
RepositoryDetailsActivityComponent plus(RepositoryDetailsActivityModule repositoryDetailsActivityModule);
}

UserComponent从AppComonent引用的对象都是单例,但是UserModule即UserComponent的那部分,创建的对象是”局部单例“。

在这里比较重要的是我们要负责UserComponent的生命周期。所以我们应该关心他的初始化和释放,在GithubClient的例子中,我们新增两个方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class GithubClientApplication extends Application {
private AppComponent appComponent;
private UserComponent userComponent;
//...
public UserComponent createUserComponent(User user) {
userComponent = appComponent.plus(new UserModule(user));
return userComponent;
}
public void releaseUserComponent() {
userComponent = null;
}
//...
}

createUserComponent()方法我们会从GithubApi(在SpliashActivity中)获取到User对象时调用;releaseUserComponent()方法会在我们从RepositoriesListActivity(这个时候我们不再需要user scope了)中退出时调用。