Dagger2 系列(二)Dagger2 進階使用

通過前面一節(jié)的介紹,我們學習了關于Dagger2的一些基本概念和簡單使用方法,對Dagger2有了一個初步的認識。而對于我們在工程中的實際使用來說,掌握基本用法是遠遠不夠的,接下來我們繼續(xù)介紹Dagger2的進階用法。

在介紹Dagger2進階用法之前,我們先回顧一下使用Dagger2的幾個關鍵步驟: 首先目標類里要使用 Inject表示出自己需要進行注入的對象;然后需要在依賴對象類中使用 Inject來標識構造方法,或者在Module中使用 Provides 標識對象的生成方法。最后需要一個 Component作為橋梁將生成的對象賦值給目標類的依賴成員。由此可見,Dagger中最關鍵的三要素如下圖所示,后續(xù)將要介紹的進階用法也是圍繞著這三要素來展開的:


Dagger2三要素

我們在實際的工程開發(fā)中,目標類對于依賴對象的需求是多種多樣的,比如很多類有多個構造方法,如下代碼示例,應該使用哪個構造方法以及如何標識不同的構造方法呢,這個就是接下來將要介紹的 Qualifier 標識符所起的作用。

@Module
public class BodyModule {

    @Provides
    public Body provideBody() {
        return  new Body();
    }

    @Provides
    public Body provideBody(Leg leg) {
        return  new Body(leg);
    }
    
    @Provides
    public Leg provideLeg() {
        return new Leg();
    }
}

Qualifier 是元注解,也就是注解的注解,用來定義不同的注解名字來對不同的構造方法進行區(qū)分,如下代碼所示,我們使用Qualifier標注了兩個注解,分別為ProvideBody ,ProvideNewBody :

@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ProvideBody {
}
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface ProvideNewBody {
}

我們需要將上述定義的兩個注解分別標識在 Module中兩個構造方法上,以此來標識兩個不同的構造方法。另外需要在目標類依賴實例的地方標識需要使用哪個構造方法,這樣就能使用指定的方法來創(chuàng)建依賴實例了,Qualifier的使用還是比較簡單的,代碼如下示例:

@Module
public class BodyModule {

    @Provides
    @ProvideBody
    public Body provideBody() {
        return  new Body();
    }

    @Provides
    @ProvideNewBody
    public Body provideNewBody(Leg leg) {
        return  new Body(leg);
    }

    @Provides
    public Leg provideLeg() {
        return new Leg();
    }
}

public class People {
    @Inject
    @ProvideBody
    Body mBody;

    public People() {
    }
}

假如在目標類中依賴的對象要求是單例的,在一定的生命周期內(nèi)使用同一個對象,使用Dagger2應該如何做呢。根據(jù)之前基礎使用方法中的介紹,每次我們調(diào)用 component 的 inject方法時,都會新創(chuàng)建一個對象來注入。如果我們想使用一個實例,那么就需要在創(chuàng)建了一個實例之后,后續(xù)每次使用都返回同一個對象而不是重新創(chuàng)建。如何達到這一目的呢,這里就需要用到 Scope 注解 。Scope 顧名思義是作用域,用于標注一個對象的作用域。Scope也是一個元注解,首先用Scope 來定義一個注解:

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

然后將這個定義好的注解 PeopleScope 標注在 Component 以及 Module 的構造方法上。

@Module
public class BodyModule {

    @Provides
    @ProvideBody
    @PeopleScope
    public Body provideBody() {
        return  new Body();
    }
    .....
}

@Component(modules = BodyModule.class)
@PeopleScope
public interface PeopleComponent {
    void injectPeople(People people);
}

這樣標注之后和之前有什么區(qū)別呢,這里就需要我們來看Dagger生成的DaggerPeopleComponent源碼了,為了精簡篇幅,這里我們只截取核心的源碼部分

public final class DaggerPeopleComponent implements PeopleComponent {
  private Provider<Body> provideBodyProvider;

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.provideBodyProvider =
        DoubleCheck.provider(BodyModule_ProvideBodyFactory.create(builder.bodyModule)); // 通過DoubleCheck進行一次包裝
  }
  @Override
  public void injectPeople(People people) {
    injectPeople2(people);
  }

  private People injectPeople2(People instance) {
    People_MembersInjector.injectMBody(instance, provideBodyProvider.get());
    return instance;
  }
  ......
  
  }

通過這段核心代碼,我們看到生成的 DaggerPeopleComponent 和之前不同的地方是調(diào)用了 DoubleCheck.provider 方法對provider進行了包裝,那么 DoubleCheck 做了什么工作呢,繼續(xù)看DoubleCheck的源碼:

public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
    private static final Object UNINITIALIZED = new Object();
    private volatile Provider<T> provider;
    private volatile Object instance;

    private DoubleCheck(Provider<T> provider) {
        this.instance = UNINITIALIZED;
        assert provider != null;
        this.provider = provider;
    }

    public T get() {   // 實現(xiàn)單例
        Object result = this.instance;
        if(result == UNINITIALIZED) {
            synchronized(this) {
                result = this.instance;
                if(result == UNINITIALIZED) {
                    result = this.provider.get();
                    this.instance = reentrantCheck(this.instance, result);
                    this.provider = null;
                }
            }
        }

        return result;
    }
.......
}

看到這段代碼,想必大家已經(jīng)快明白了,這個get方法不就是單例的寫法么,如果已經(jīng)創(chuàng)建過一次實例,后續(xù)每次調(diào)用get方法,返回的都是第一次創(chuàng)建的實例。如此在 DaggerPeopleComponent 中每次調(diào)用inject方法時,都會調(diào)用到這個get方法,從而達到使用同一實例的目的。另外強調(diào)一句,這里的單例只是對于同一個 Component對象來說的,在同一個 Component對象的生命周期里,持有的依賴對象為同一個。不同的Component對象中的自然就是不同的了。另外強調(diào)一下使用Scope的注意事項:Module 中 provide 方法的 Scope 注解和 與之綁定的 Component 的 Scope 需要保持一致,否則作用域不同會導致編譯時會報錯。

聽到這里可能還存在很多疑問,每個目標類依賴都有對應的 Component類,每個Component對象都持有相應的依賴對象,如果某個Component對象希望和另外一個Component對象共享依賴實例,應該如何做呢,接下來我們學習Component的組織關系,用于解決Component。Component擁有兩種關系依賴關系和繼承關系:一個Component可以依賴于一個或多個Component,依賴關系通過Component中的dependencies屬性來實現(xiàn),具體用法通過一個例子來看一下;

本例中 People 和 Animal中都要依賴一個 Body類型的對象,而且Animal 要和People 共用同一個Body對象。首先要在 PeopleComponent中增加一個 提供Body對象的接口,在AnimalComponent 中要使用dependencies 屬性標明 依賴 PeopleComponent , 最后注意 PeopleComponent 和 AnimalComponent都需要標注 Scope,而且兩者的Scope不能相同。

@Component(modules = BodyModule.class)
@PeopleScope
public interface PeopleComponent {
    void injectPeople(People people);

    Body getBody();
}

@Component(dependencies = PeopleComponent.class)
@AnimalScope
public interface AnimalComponent {
    void inject(Animal animal);
}

然后編譯一下,來詳看生成的DaggerAnimalComponent 中的代碼,出于篇幅考慮僅貼出部分核心代碼,首先我們看到Builder類中增加了一個成員 PeopleComponent, 用于傳入PeopleComponent 實例,在inject方法中,獲取依賴實例時調(diào)用的是傳入的peoplecomponent的get方法,因此AnimalComponent 和 PeopleComponent共享了同一個 Body的實例:

public final class DaggerAnimalComponent implements AnimalComponent {
  private PeopleComponent peopleComponent;

  ......
  @Override
  public void inject(Animal animal) {
    injectAnimal(animal);
  }

  private Animal injectAnimal(Animal instance) {
    Animal_MembersInjector.injectMBody(
        instance,
        Preconditions.checkNotNull(
            peopleComponent.getBody(), "Cannot return null from a non-@Nullable component method"));
    return instance;
  }

  public static final class Builder {
    private PeopleComponent peopleComponent;

    private Builder() {}

    public AnimalComponent build() {
      if (peopleComponent == null) {
        throw new IllegalStateException(PeopleComponent.class.getCanonicalName() + " must be set");
      }
      return new DaggerAnimalComponent(this);
    }

    public Builder peopleComponent(PeopleComponent peopleComponent) {
      this.peopleComponent = Preconditions.checkNotNull(peopleComponent);
      return this;
    }
  }
}

使用方法如下所示
PeopleComponent peopleComponent = DaggerPeopleComponent.builder().build();
AnimalComponent animalComponent = DaggerAnimalComponent.builder().peopleComponent(peopleComponent).build();
animalComponent.inject(this);

上面介紹的是Component 的依賴關系,Component還有一種繼承關系,也可達到 Component之間共享依賴實例的目的。如果使AnimalComponent繼承PeopleComponent,使用方式如下,繼承關系的使用相對復雜一些。首先 使用 SubComponent標注AnimalComponent,AnimalComponent 中增加一個 Builder接口并用@Subcomponent.Builder標注。PeopleComponent中增加一個方法返回類型為 AnimalComponent.Builder,用于外部創(chuàng)建AnimalComponent 對象使用。最后要在PeopleComponent所使用的module中標識 subcomponents = AnimalComponent.class,自此就建立起來 AnimalComponent 和 PeopleComponent之間的繼承關系:

@Component(modules = BodyModule.class)
@PeopleScope
public interface PeopleComponent {
    void injectPeople(People people);

    AnimalComponent.Builder animalComponent();
}

@Module(subcomponents = AnimalComponent.class)
public class BodyModule {

    @Provides
    @PeopleScope
    public Body provideBody() {
        return  new Body();
    }
}

@Subcomponent
@AnimalScope
public interface AnimalComponent {
    void inject(Animal animal);
    @Subcomponent.Builder
    interface Builder {
        AnimalComponent build();
    }
}

我們還是編譯一下看生成的核心源碼, 這里看到有個區(qū)別,只生成了 DaggerPeopleComponent 類,沒有生成 DaggerAnimalComponent類。創(chuàng)建AnimalComponent 對象的方法在 DaggerPeopleComponent 中,DaggerPeopleComponent中的AnimalComponentImpl 是AnimalComponent 接口的實現(xiàn)類,其中的injectAnimal方法 注入的對象是由DaggerPeopleComponent 中的provideBodyProvider提供的,由此實現(xiàn)了 AnimalComponent和PeopleComponent共享依賴實例的目的。

public final class DaggerPeopleComponent implements PeopleComponent {
  private Provider<Body> provideBodyProvider;
  ......

  public static PeopleComponent create() {
    return new Builder().build();
  }

  @Override
  public void injectPeople(People people) {
    injectPeople2(people);
  }

  @Override
  public AnimalComponent.Builder animalComponent() {
    return new AnimalComponentBuilder();
  }

  private People injectPeople2(People instance) {
    People_MembersInjector.injectMBody(instance, provideBodyProvider.get());
    return instance;
  }

  private final class AnimalComponentBuilder implements AnimalComponent.Builder {
    @Override
    public AnimalComponent build() {
      return new AnimalComponentImpl(this);
    }
  }

  private final class AnimalComponentImpl implements AnimalComponent {
    private AnimalComponentImpl(AnimalComponentBuilder builder) {}

    @Override
    public void inject(Animal animal) {
      injectAnimal(animal);
    }

    private Animal injectAnimal(Animal instance) {
      Animal_MembersInjector.injectMBody(
          instance, DaggerPeopleComponent.this.provideBodyProvider.get());
      return instance;
    }
  }
}

上面介紹了 Component 組織關系的兩種方式,在APP的開發(fā)中,有非常多的類都有依賴對象,總不能每一個目標類都配一個Component吧。應該以什么樣的原則來劃分Component呢,Component應該劃分為多小的粒度呢,一般會遵循如下的組織原則:

  1. 創(chuàng)建一個全局的Component(ApplicationComponent), 在application中對其進行實例化,一般會在這個component中用來管理APP中的全局類實例。

  2. 對于每個頁面創(chuàng)建一個Component,一個Activity頁面定義一個Component,一個Fragment定義一個Component,使這些component繼承自applicationComponent。
    這部分內(nèi)容可以參考一下 實例,https://github.com/googlesamples/android-architecture/tree/todo-mvp, 這個實例中展示了如何在APP中使用Dagger2,在此不再展開詳述。
    通過上面對Dagger2進階用法的介紹,可能大家心中依然存在一些疑惑,從入門到這里有點想放棄。Dagger2的使用需要在每個目標類里重復寫大量的模板代碼,這與Dagger2解耦,減少重復勞動的目標是有一定背離的。如何在APP開發(fā)中更加簡潔,方便的使用Dagger2呢,接下來將要繼續(xù)介紹的Dagger-android框架,將會繼續(xù)詳細闡述這一問題的解決方法,使Dagger2在APP開發(fā)中的使用更加簡潔。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容