[Android] Dagger2 入門 2

上一篇文章介紹了Dagger2的基本用法,這篇文章主要說一下Dagger2中@Scope的用法和原理。java

上一篇文章中提到:android

如上面例子所示,若是要求D對象爲單例,能夠經過@Singleton註解來實現。首先咱們須要在依賴圖中聲明對象是單例的:git

@Module
public class DModule {
    @Provides
    @Singleton
    public D provideD() {
        return new D();
    }
}

DComponent接口也須要聲明:github

@Singleton
@Component(modules = DModule.class)
public interface DComponent {
    D provideD();
}

如此,當咱們注入D對象時,可保證每次注入的是同一個D對象:app

DComponent dComponent = DaggerDComponent.create();
D d1 = dComponent.provideD(); 
D d2 = dComponent.provideD(); 
// d1 == d2

在咱們看來,只是多加了一個註解而已,便實現了單例模式。要知道其原理,要從Dagger2生成的源碼入手。ide

Dagger2生成的源碼

以以下例子爲例:工具

  1. 定義類:ui

    public class A {
        public A(){
        }
    }
    
    public class B {
        A a;
    
        public B(A a) {
            this.a = a;
        }
    }
    
    public class C {
        A a;
        B b;
    
        public C(A a, B b) {
            this.a = a;
            this.b = b;
        }
    }
  2. 定義Modulethis

    @Module
    public class ABCModule {
        @Provides
        public A provideA() {
            return new A();
        }
    
        @Provides
        public B provideB(A a) {
            return new B(a);
        }
    
        @Provides
        public C provideC(A a, B b) {
            return new C(a, b);
        }
    }
  3. 定義Component接口:.net

    @Component(module=ABCModule.class)
    public interface ABCComponent {
        public A provideA();
    
        public B provideB();
    
        public C provideC();
    
        void inject(Main main);
    }
  4. 依賴注入:

    public class Main {

    @Inject
    C c;
    
    public Main() {
        ABCComponent abcComponent = DaggerABCComponent
                .builder()
                .dComponent(dComponent)
                .build();
        A a = abcComponent.provideA();
        B b = abcComponent.provideB();
        abcComponent.inject(this);
    }

    }

編譯工程,Dagger2在項目路徑下生成了以下文件:

[dagger2]
┣━[di]
┃  ┣━[component]
┃  ┃  ┗━DaggerABCComponent.java
┃  ┗━[module]
┃      ┣━ABCModule_ProvideAFactory.java
┃      ┣━ABCModule_ProvideBFactory.java
┃      ┗━ABCModule_ProvideCFactory.java
┗━[model]
    ┗━Main_MembersInjector.java

(利用這個工具生成了文件結構圖)

注意,生成的文件在關聯的類相同路徑下。如DaggerABCComponent類生成在ABCComponent路徑下。

咱們先來看看實際接觸到的DaggerABCComponent類:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerABCComponent implements ABCComponent {
  private Provider<A> provideAProvider;
  private Provider<B> provideBProvider;
  private Provider<C> provideCProvider;
  private MembersInjector<Main> mainMembersInjector;

  private DaggerABCComponent(Builder builder) {  
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {  
    return new Builder();
  }

  public static ABCComponent create() {  
    return builder().build();
  }

  private void initialize(final Builder builder) {  
    this.provideAProvider = ABCModule_ProvideAFactory.create(builder.aBCModule);
    this.provideBProvider = ABCModule_ProvideBFactory.create(builder.aBCModule, provideAProvider);
    this.provideCProvider = ABCModule_ProvideCFactory.create(builder.aBCModule, provideAProvider, provideBProvider);
    this.mainMembersInjector = Main_MembersInjector.create(provideCProvider);
  }

  @Override
  public A provideA() {  
    return provideAProvider.get();
  }

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

  @Override
  public C provideC() {  
    return provideCProvider.get();
  }

  @Override
  public void inject(Main main) {  
    mainMembersInjector.injectMembers(main);
  }

  public static final class Builder {
    private ABCModule aBCModule;
  
    private Builder() {  
    }
  
    public ABCComponent build() {  
      if (aBCModule == null) {
        this.aBCModule = new ABCModule();
      }
      return new DaggerABCComponent(this);
    }
  
    public Builder aBCModule(ABCModule aBCModule) {  
      if (aBCModule == null) {
        throw new NullPointerException("aBCModule");
      }
      this.aBCModule = aBCModule;
      return this;
    }
  }
}

來看幾個關鍵點:

  1. DaggerABCComponent繼承於ABCComponent。因此咱們能夠直接調用ABCComponent的方法。

  2. DaggerABCComponent須要Builder來進行初始化。Builder的做用是提供對象的module。

  3. 對象經過Provider從依賴圖中取出。Provider由Factory生成時會有相似依賴注入的操做。

  4. 經過MembersInjector進行依賴注入。

這幾個關鍵類的關係可用下圖表示:

+---------------------------------------+
|         DaggerABCComponent            |
|                                       |
|  +----------+  create     +-----------+------------+
|  |  Factory +-----+-----> |  Provider<A>           |
|  +----+-----+     |       +----+------+------------+
|       ^           |       |    |      |
|       |           |       | +--v------+------------+
|       |           +-----> | |Provider<B>           |
|       | ABCModule |       | +--+------+------------+
|       |           |       |    |      |
|       |           |       +----v------+------------+
|  +----+----+      +-----> |  Provider<C>           |
|  | Builder |              +-----------+------------+
|  +---------+                          |
|                                       |
| +--------------------+    +-----------+------------+
| |Main_MembersInjector+--> |  MembersInjector<Main> |
| +--------------------+    +-----------+------------+
|                                       |
+---------------------------------------+

其中最最關鍵的是Factory和Provider。以B類爲例,從依賴圖中取出B對象,須要通過以下代碼:

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

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

其中ABCModule_ProvideBFactory的源碼以下所示:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class ABCModule_ProvideBFactory implements Factory<B> {
  private final ABCModule module;
  private final Provider<A> aProvider;

  public ABCModule_ProvideBFactory(ABCModule module, Provider<A> aProvider) { // 根據以前的依賴關係,注入ProviderA  
    assert module != null;
    this.module = module;
    assert aProvider != null;
    this.aProvider = aProvider;
  }

  @Override
  public B get() {  
    B provided = module.provideB(aProvider.get()); // 從ProviderA中取出A對象,再生成B對象
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<B> create(ABCModule module, Provider<A> aProvider) {  
    return new ABCModule_ProvideBFactory(module, aProvider);
  }
}

Factory和Provider接口以下所示:

public interface Factory<T> extends Provider<T> {
}

public interface Provider<T> {
    T get();
}

從使用者的角度看,無需關心對象是如何生成的,只需調用provider的get方法便可得到對象。並且對象應該是符合既定的規則而且初始化好能夠立刻用的。

從ABCModule_ProvideBFactory(或者某個Provider)的角度看,在初始化方法裏就明確了本身所需依賴的對象(這裏是ProviderA)。在get方法的實現裏,只需關心B對象的生成。當須要A對象時,直接從外部「注入」的providerA取出便可。

再來看一看Main_MembersInjector的實現:

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class Main_MembersInjector implements MembersInjector<Main> {
  private final Provider<C> cProvider;

  public Main_MembersInjector(Provider<C> cProvider) {  
    assert cProvider != null;
    this.cProvider = cProvider;
  }

  @Override
  public void injectMembers(Main instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.c = cProvider.get();
  }

  public static MembersInjector<Main> create(Provider<C> cProvider) {  
      return new Main_MembersInjector(cProvider);
  }
}

Dagger2在編譯時會分析module中inject方法的參數的類型(這裏是Main類),記錄下用@Inject註解標註的成員,而後生成對應的Injector。

理解Injector的關鍵在理解它的構造方法和injectMembers方法。instance.c = cProvider.get();一句實施了依賴注入。

@Singleton

修改ABCModule以下所示:

@Module
    public class ABCModule {
        ...

        @Provides
        @Singleton               // 添加Singleton註解
        public B provideB(A a) {
            return new B(a);
        }

        ...
    }

修改ABCComponent以下所示:

@Singleton
    @Component(module=ABCModule.class)
    public interface 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的源碼:

package dagger.internal;

import javax.inject.Provider;

public final class ScopedProvider<T> implements Provider<T> {
  private static final Object UNINITIALIZED = new Object();

  private final Factory<T> factory;
  private volatile Object instance = UNINITIALIZED;

  private ScopedProvider(Factory<T> factory) {
    assert factory != null;
    this.factory = factory;
  }

  @SuppressWarnings("unchecked") // cast only happens when result comes from the factory
  @Override
  public T get() {
    // double-check idiom from EJ2: Item 71
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          instance = result = factory.get();
        }
      }
    }
    return (T) result;
  }

  /** Returns a new scoped provider for the given factory. */
  public static <T> Provider<T> create(Factory<T> factory) {
    if (factory == null) {
      throw new NullPointerException();
    }
    return new ScopedProvider<T>(factory);
  }
}

理解上面的代碼關鍵在於:

  1. ScopedProvider在dagger.internal下,非Dagger2自動生成。

  2. ScopedProvider也是一個Provider

  3. 利用double-check,在instance上實現了單例模式。也就是說,在ScopedProvider的生命週期內,get返回的都是同一個對象。

以上3點實現了無入侵式的Singleton模式。但其實ScopedProvider並非專爲Singleton模式設計的,Singleton模式只是Dagger2中Scope功能的效果。

@Scope

@Singleton註解的源碼以下所示:

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

...

@Target(ANNOTATION_TYPE)
@Retention(RUNTIME)
@Documented
public @interface Scope {}

能夠看出,@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並保持引用。

相關文章
相關標籤/搜索