[Android] Dagger2 入门 2

上一篇文章介绍了Dagger2的基本用法,这篇文章主要说一下Dagger2中@Scope的用法和原理。

上一篇文章中提到:

如上面例子所示,如果要求D对象为单例,可以通过@Singleton注解来实现。首先我们需要在依赖图中声明对象是单例的:

DComponent接口也需要声明:

如此,当我们注入D对象时,可保证每次注入的是同一个D对象:

在我们看来,只是多加了一个注解而已,便实现了单例模式。要知道其原理,要从Dagger2生成的源码入手。

Dagger2生成的源码

以如下例子为例:

  1. 定义类:

  2. 定义Module

  3. 定义Component接口:

  4. 依赖注入:

    public class Main {

    }

编译工程,Dagger2在项目路径下生成了如下文件:

(利用这个工具生成了文件结构图)

注意,生成的文件在关联的类相同路径下。如DaggerABCComponent类生成在ABCComponent路径下。

我们先来看看实际接触到的DaggerABCComponent类:

来看几个关键点:

  1. DaggerABCComponent继承于ABCComponent。所以我们可以直接调用ABCComponent的方法。

  2. DaggerABCComponent需要Builder来进行初始化。Builder的作用是提供对象的module。

  3. 对象通过Provider从依赖图中取出。Provider由Factory生成时会有类似依赖注入的操作。

  4. 通过MembersInjector进行依赖注入。

这几个关键类的关系可用下图表示:

其中最最关键的是Factory和Provider。以B类为例,从依赖图中取出B对象,需要经过如下代码:

其中ABCModule_ProvideBFactory的源码如下所示:

Factory和Provider接口如下所示:

从使用者的角度看,无需关心对象是如何生成的,只需调用provider的get方法即可获得对象。而且对象应该是符合既定的规则并且初始化好可以马上用的。

从ABCModule_ProvideBFactory(或者某个Provider)的角度看,在初始化方法里就明确了自己所需依赖的对象(这里是ProviderA)。在get方法的实现里,只需关心B对象的生成。当需要A对象时,直接从外部“注入”的providerA取出即可。

再来看一看Main_MembersInjector的实现:

Dagger2在编译时会分析module中inject方法的参数的类型(这里是Main类),记录下用@Inject注解标注的成员,然后生成对应的Injector。

理解Injector的关键在理解它的构造方法和injectMembers方法。instance.c = cProvider.get();一句实施了依赖注入。

@Singleton

修改ABCModule如下所示:

修改ABCComponent如下所示:

我们来看看Dagger2生成的代码有什么不同:

@Generated(“dagger.internal.codegen.ComponentProcessor”)
public final class DaggerABCComponent implements ABCComponent {

private Provider provideBProvider;

private void initialize(final Builder builder) {

this.provideBProvider = ScopedProvider.create(ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider));

}

@Override
public B provideB() {
return provideBProvider.get();
}

}

可以看到唯一的不同是用ScopedProvider将ABCModule_ProvideBFactory包裹起来。来看一下ScopedProvider的源码:

理解上面的代码关键在于:

  1. ScopedProvider在dagger.internal下,非Dagger2自动生成。
  2. ScopedProvider也是一个Provider
  3. 利用double-check,在instance上实现了单例模式。也就是说,在ScopedProvider的生命周期内,get返回的都是同一个对象。

以上3点实现了无入侵式的Singleton模式。但其实ScopedProvider并不是专为Singleton模式设计的,Singleton模式只是Dagger2中Scope功能的效果。

@Scope

@Singleton注解的源码如下所示:

可以看出,@Singleton只是一个标记,表明这是一个Scope。那么Scope是什么呢?源代码中有如此注释:

A scope annotation applies to a class
containing an injectable constructor and governs how the injector reuses
instances of the type. By default, if no scope annotation is present, the
injector creates an instance (by injecting the type’s constructor), uses
the instance for one injection, and then forgets it. If a scope annotation
is present, the injector may retain the instance for possible reuse in a
later injection.

简单地说,@Scope决定了注射器从依赖图中取出对象的行为。如果节点有Scope标签,那么注入时将重用上次生成的对象。

依赖图中某个节点标注了@Scope后,便拥有了与当前Component相同的生命周期。也就是说,如果要实现全局(Application范围内)的Singleton,必需要有全局的Component。这就是为什么许多其他关于Dagger2的例子中,要在Application中保持ApplicationComponent的引用的原因。

至于在许多例子中看到的@PerActivity(对象在当前Activity的生命周期内唯一),@PerUser(对象在当前用户态销毁前唯一),它们的实现也是依赖于Component的生命周期。所以需要在Activity的onCreate中新建SomeActivityComponent并保持引用,在UserManager的login中新建UserComponent并保持引用。

发表评论

电子邮件地址不会被公开。