以下内容为原创,欢迎转载,转载请注明
来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5095426.html

使用Dagger 2依赖注入 - 自定义Scope

原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-custom-scopes/

这章是展示使用Dagger 2在Android端实现依赖注入的系列中的一部分。今天我会花点时间在自定义Scope(作用域)上面 - 它是很实用,但是对于刚接触依赖注入的人会有一点困难。

Scope - 它给我们带来了什么?

几乎所有的项目都会用到单例 - 比如API clients,database helpers,analytics managers等。因为我们不需要去关心实例化(由于依赖注入),我们不应该在我们的代码中考虑关于怎么得到这些对象。取而代之的是@Inject注解应该提供给我们适合的实例。

在Dagger 2中,Scope机制可以使得在scope存在时保持类的单例。在实践中,这意味着被限定范围为@ApplicationScope的实例与Applicaiton对象的生命周期一致。@ActivityScope保证引用与Activity的生命周期一致(举个例子我们可以在这个Activity中持有的所有fragment之间分享一个任何类的单例)。

简单来说 - scope给我们带来了“局部单例”,生命周期取决于scope自己。

但是需要弄清楚的是 - Dagger 2默认并不提供@ActivityScope 或者/并且 @ApplicationScope 这些注解。这些只是最常用的自定义Scope。只有@Singleton scope是默认提供的(由Java自己提供)。

Scope - 实践案例

为了更好地去理解Dagger 2中的scope,我们直接进入实践案例。我们将要去实现比Application/Activity scope更加复杂一点的scope。为此我们将使用 上一文章 中的 GithubClient 例子。我们的app应需要三种scope:

  • @Sigleton - application scope
  • @UserScope - 用于与被选中的用户联系起来的类实例的scope(在真实的app中可以是当前登录的用户)。
  • @ActivityScope - 生命周期与Activity(在我们例子中的呈现者)一致的实例的scope

讲解的@UserScope是今天方案与以前文章之间的主要的不同之处。从用户体验的角度来说它没有带给我们任何东西,但是从架构的观点来说它帮助我们在不传入任何意图参数的情况下提供了User实例。使用方法参数获取用户数据的类(在我们的例子中是RepositoriesManager)中我们可以通过构造参数(它将通过依赖图表提供)的方式来获取User实例并在需要的时候被初始化,而不是在app启动的时候创建它。这意味着RepositoriesManager可以在我们从Github API获取到用户信息(在RepositoriesListActivity呈现之前)之后被创建。

这里有个我们app中scopes和components呈现的简单图表。

单例(Application scope)是最长的scope(在实践中是与application一样长)。UserScope作为Application scope的一个子集scope,它可以访问它的对象(我们可以从父scope中得到对象)。ActivityScope(生命周期与Activity一致)也是如此 - 它可以从UserScope和ApplicationScope中得到对象。

Scope生命周期的例子

这里有一个我们app中scope生命周期的案例:

单例的生命周期是从app启动后的所有的时期,当我们从Github API(在真实app中是用户登录之后)得到了User实例时UserScope被创建了,然后当我们回退到SplashActivity(在真实app中是用户退出之后)时被销毁。

实现

在Dagger 2中,Scope的实现归结于对Components的一个正确的设置。一般情况下我们有两种方式 - 使用Subcomponent注解或者使用Components依赖。它们两者最大的区别就是对象图表的共享。Subcomponents可以访问它们parent的所有对象图表,而Component依赖只能访问通过Component接口暴露的对象。

我选择第一种使用 @Subcomponent 注解,如果你之前使用过Dagger 1,它几乎与从ObjectGraph创建一个subgraphs(子图表)是一样的。此外,对于创建一个subgraphs的方法我们会使用类似的命名法则(但这不是强制性的)。

我们从AppComponent的实现开始:

@Singleton
@Component(modules = {AppModule.class,GithubApiModule.class}
)
public interface AppComponent {UserComponent plus(UserModule userModule);SplashActivityComponent plus(SplashActivityModule splashActivityModule);}

它将会是其它subcomponents的根Components:UserComponent和Activities Components。正如你注意到的那样(尤其如果你在前面的文章中看过的AppComponent 实现)所有的返回依赖图表对象的公开方法全部消失了。因为我们有subcomponents了,我们不需要去公开去暴露依赖了 - 无论如何subgraphs都可以访问它们全部了。

作为替代,我们新增了两个方法:

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

这表示,我们可以从AppComponent创建两个子Components(subcomponents):UserComponentSplashActivityComponent。因为它们都是AppComponent的subcomponents,所以它们两者都可以访问AppModuleGithubApiModule创建的实例。

这些方法的命名法则是:返回类型是subcomponent类,方法名字随意,参数是这个subcomponent需要的modules。

如你所见,UserComponent需要另一个module(它通过plus()方法的参数传入)。这样,我们通过增加一个新的用于生成对象的module,继承AppComponent图表。UserComponent类看起来这样:

@UserScope
@Subcomponent(modules = {UserModule.class}
)
public interface UserComponent {RepositoriesListActivityComponent plus(RepositoriesListActivityModule repositoriesListActivityModule);RepositoryDetailsActivityComponent plus(RepositoryDetailsActivityModule repositoryDetailsActivityModule);
}

当然@UserScope注解是我们自己创建的:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}

我们可以从UserComponent创建另外两个subcomponents:RepositoriesListActivityComponentRepositoryDetailsActivityComponent

@UserScope
@Subcomponent(modules = {UserModule.class}
)
public interface UserComponent {RepositoriesListActivityComponent plus(RepositoriesListActivityModule repositoriesListActivityModule);RepositoryDetailsActivityComponent plus(RepositoryDetailsActivityModule repositoryDetailsActivityModule);
}

并且更重要的是所有scope的东西都发生在这里。所有UserComponent中从AppComponent继承过来的仍然shi是单例的(是 Applicaton scope)。但是UserModuleUserComponent的那部分)创建的对象将会是“局部单例”,它的生命周期跟UserComponent实例是一样的。

所以,每次一创建另一个UserComponent实例将会调用:

UserComponent appComponent = appComponent.plus(new UserModule(user))

UserModule中获取的对象将是不同的实例。

但是这里很重要的一点是 - 我们要负责UserComponent的生命周期。所以我们应该关心它的初始化和释放。在我们的例子中,我为它增加了两个额外的方法:

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()方法会在我们从Github API(在SplashActivity中)获取到User对象时调用。releaseUserComponent()方法会在我们从RepositoriesListActivity(这个时候我们不再需要user scope了)中返回时调用。

Dagger 2中的Scope - 内部实现

查看它的内部的工作原理是很不错的。通常在这种情况下可以确定,在Dagger 2的scope机制下并不存在什么魔法。

我们从UserModule.provideRepositoriesManager()方法开始研究。它提供了RepositoriesManager实例,它应该使用@UserScopeScope。我们来检验这个方法哪里被调用(第8行):

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class UserModule_ProvideRepositoriesManagerFactory implements Factory<RepositoriesManager> {//...@Overridepublic RepositoriesManager get() {  RepositoriesManager provided = module.provideRepositoriesManager(userProvider.get(), githubApiServiceProvider.get());if (provided == null) {throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");}return provided;}public static Factory<RepositoriesManager> create(UserModule module, Provider<User> userProvider, Provider<GithubApiService> githubApiServiceProvider) {  return new UserModule_ProvideRepositoriesManagerFactory(module, userProvider, githubApiServiceProvider);}
}

UserModule_ProvideRepositoriesManagerFactory仅仅是一个工厂模式的现实,它从UserModule中获取到RepositoriesManager实例。我们应该往更深层次挖掘。

UserModule_ProvideRepositoriesManagerFactoryUserComponentImpl中被使用 - 我们component的实现(line 15):

private final class UserComponentImpl implements UserComponent {//...private UserComponentImpl(UserModule userModule) {if (userModule == null) {throw new NullPointerException();}this.userModule = userModule;initialize();}private void initialize() {this.provideUserProvider = ScopedProvider.create(UserModule_ProvideUserFactory.create(userModule));this.provideRepositoriesManagerProvider = ScopedProvider.create(UserModule_ProvideRepositoriesManagerFactory.create(userModule, provideUserProvider, DaggerAppComponent.this.provideGithubApiServiceProvider));}//...}

provideRepositoriesManagerProvider对象在我们每次请求它时负责提供RepositoriesManager实例。如我们所见,provider是通过ScopedProvider实现的。来看下它的部分代码:

public final class ScopedProvider<T> implements Provider<T> {//...private ScopedProvider(Factory<T> factory) {assert factory != null;this.factory = factory;}@SuppressWarnings("unchecked") // cast only happens when result comes from the factory@Overridepublic T get() {// double-check idiom from EJ2: Item 71Object result = instance;if (result == UNINITIALIZED) {synchronized (this) {result = instance;if (result == UNINITIALIZED) {instance = result = factory.get();}}}return (T) result;}//...}

再简单不过了吧?第一次调用ScopedProvider从factory(我们的例子中是UserModule_ProvideRepositoriesManagerFactory)中获取实例并像单例模式一样存储起来。我们的scoped provider只是UserComponentImpl中的一个属性,所以简单说就是ScopedProvider返回一个与依赖于Component的单例。

在这里你可以查看 ScopedProvider 的所有的实现。

就是这样。我们弄清楚了Dagger 2中Scope底层是怎么工作的。现在我们知道,它们没有以任何方式于Scope注解连接。自定义注解只是给了我们一个简单的方式来进行编译时代码校验和标记一个类是单例/非单例。所有的scope相关东西都是与Component的生命周期相关联。

以上就是今天的全部内容。我希望从现在开始scopes会变得更加容易使用。感谢阅读!

代码:

以上描述的完整代码可见Github repository。

作者

Miroslaw Stanek

Head of Mobile Development @ Azimo

[Android]使用Dagger 2依赖注入 - DI介绍(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5092083.html

[Android]使用Dagger 2依赖注入 - API(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5092525.html

[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5095426.html

[Android]使用Dagger 2依赖注入 - 图表创建的性能(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5098943.html

[Android]Dagger2Metrics - 测量DI图表初始化的性能(翻译):

http://www.cnblogs.com/tiantianbyconan/p/5193437.html

[Android]使用Dagger 2进行依赖注入 - Producers(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6234811.html

[Android]在Dagger 2中使用RxJava来进行异步注入(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6236646.html

[Android]使用Dagger 2来构建UserScope(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6237731.html

[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译):

http://www.cnblogs.com/tiantianbyconan/p/6266442.html

[Android]使用Dagger 2依赖注入 - 自定义Scope(翻译)相关推荐

  1. [Android]使用Dagger 2依赖注入 - DI介绍(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5092083.html 使用Dagger 2依赖注入 - DI介 ...

  2. [Android]使用Dagger 2进行依赖注入 - Producers(翻译)

    使用Dagger 2进行依赖注入 - Producers 原文:http://frogermcs.github.io/dependency-injection-with-dagger-2-produc ...

  3. Dagger Hilt - Android官方推荐的依赖注入框架

    Dagger Hilt Android端有不少DI框架可供选择 – 例如用于控件注入的ButterKnife.用于Kotlin的Koin等,但唯有Dagger才称得上是谷歌官方认可的DI方案. Dag ...

  4. Android神匕首—Dagger2依赖注入框架详解

    简介 Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(没错!还有一把黄油刀,唤作ButterKnife) Dagger2 是一个Android依赖注入框架,由谷歌开发,最早的版本Dagg ...

  5. Android 使用dagger2进行依赖注入(基础篇)

    0. 前言 Dagger2是首个使用生成代码实现完整依赖注入的框架,极大减少了使用者的编码负担, 本文主要介绍如何使用dagger2进行依赖注入.如果你不还不了解依赖注入,请看这一篇. 1. 简单的依 ...

  6. Java 控制反转和依赖注入模式【翻译】【整理】

    Inversion of Control Containers and the Dependency Injection pattern --Martin Fowler 本文内容 Component ...

  7. 使用Angular依赖注入自定义SAP Spartacus的ProductAdapter

    新建一个MyProductAdapter,继承自SAP Spartacus的ProductAdapter,返回一些fake数据: import { Injectable } from '@angula ...

  8. [Android]在Dagger 2中使用RxJava来进行异步注入(翻译)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/6236646.html 在Dagger 2中使用RxJava来进 ...

  9. Android 依赖注入框架 Dagger2使用

    前言 Dagger 2这个匕首确实很难上手,上手后又比较难瞬间掌握,可以这么说,刚开始使用就是用来尝(zhuang)鲜(X)的,但相信随着使用的加深,会不断体会到它对于整个项目架构的极强辅助作用,能使 ...

最新文章

  1. 在PyTorch中进行双线性采样:原理和代码详解
  2. “编程能力差!90%输在这点上!”谷歌AI专家:其实都是瞎努力!
  3. java重新初始化吗_Java中为何已经重新赋值的变量在输出后会初始化?
  4. 第1次作业:这是我的一个响亮的标题X!
  5. 【飞秋】进一步完善 -- GEF创建助手工具条
  6. 2017职称英语和计算机考试,2017年职称英语考试取消了吗
  7. python灰度图生成g代码_Python打造一个在线G代码生成器
  8. 5000并发_彻底理解 jmeter 的线程数与并发数之间的关系
  9. python遗传算法最短路径问题有几种类型_用遗传算法求解最短路径问题.pdf
  10. JavaWeb(二)框架搭建篇
  11. 8086CPU各寄存器及其简介
  12. leetcode *207. 课程表(拓补排序)(2020.8.4)
  13. 华为usg系列防火墙-密码重置
  14. 房地产微信营销方案微信“危”与“机”
  15. Gitter---高颜值GitHub小程序客户端诞生记,2021年安卓社招面试题精选
  16. ViewPager标签栏滑条
  17. 计算机中数据的表现形式
  18. java set驱虫_由分子轨道理论可知, H 2 + 的键级为 0.5 ,并具有顺磁性_学小易找答案...
  19. 软件测试人事自我介绍,软件测试面试自我介绍范文
  20. 招聘帖 | 全国全品类职位列表整理,有需要的加入!

热门文章

  1. 了解计算机网络拓扑结构,认识计算机网络拓扑结构
  2. html显示日期时间代码,JS全中文显示日期时间代码
  3. 护壁桩嵌入深度_钻孔灌注桩嵌岩深度最少多少
  4. 如何在本机安装mysql_机器人之如何在本机安装MySQL,并配置电脑为数据库服务器...
  5. Java线程与Android线程,Android线程篇(三):深入理解Java线程池(一)
  6. kettle使用_ETL工具(kettle)使用系列(二)
  7. idea自动整理代码快捷键_MDK进阶使用教程,快捷启动任意软件,自动整理格式化代码,方便代码整理可以很好 的提高效率...
  8. PLOS_ONE_Genome-Wide Analysis of Long Noncoding RNA (lncRNA) Expression in Hepatoblastoma Tissues
  9. php逆波兰表达式,PHP逆波兰示意与表达式计算
  10. sql递归查询上级_递归的实际业务场景之MySQL 递归查询