可能是東半球最好的dagger2文章

部分內容參考自:
[Android]使用Dagger 2依賴注入 - DI介紹(翻譯)
[Android]使用Dagger 2依賴注入 - API(翻譯)

爲什麼網上這麼多dagger2教程,我還寫了這篇文章。

  1. 找了很多Dagger2相關的博客,我看的腦漿炸裂……
  2. Dagger2給我們帶來了什麼,大多數博文也沒有說明
  3. 手動寫寫,加深印象,騙騙粉絲 (手動滑稽)
  4. 部分Dagger2的運作機制是我個人的臆測,比如Dagger2編譯入口,不過應該八九不離十吧,測試了挺多次的,沒有@Component的話是不會編譯的=。=

一、Dagger2使用Q&A

Q1:dagger2是什麼,有什麼用?
A1:dagger2是一個基於JSR-330標準的依賴注入框架,在編譯期間自動生成代碼,負責依賴對象的創建。

Q2:什麼是JSR-330
A2:JSR即Java Specification Requests,意思是java規範提要。
而JSR-330則是 Java依賴注入標準
關於JSR-330可以閱讀這篇文章Java 依賴注入標準(JSR-330)簡介,隨便看下就好了,不是重點。

Q3:用dagger2提供依賴有什麼好處
A:3:爲了進一步解耦和方便測試,我們會使用依賴注入的方式構建對象。(可以看這篇文章使用Dagger2前你必須瞭解的一些設計原則
但是,在Activity中有可能出現這樣的情況。

public class LoginActivity extends AppCompatActivity {

    LoginActivityPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        OkHttpClient okHttpClient = new OkHttpClient();
        RestAdapter.Builder builder = new RestAdapter.Builder();
        builder.setClient(new OkClient(okHttpClient));
        RestAdapter restAdapter = builder.build();
        ApiService apiService = restAdapter.create(ApiService.class);
        UserManager userManager = UserManager.getInstance(apiService);

        UserDataStore userDataStore = UserDataStore.getInstance(
                getSharedPreferences("prefs", MODE_PRIVATE)
        );

        //Presenter is initialized here
        presenter = new LoginActivityPresenter(this, userManager, userDataStore);
    }
}

其實我們需要的只是LoginActivityPresenter對象,但是因爲使用依賴注入的原因,我們不得不在LoginActivity中初始化一大堆Presenter所需要的依賴。

現在不僅依賴於LoginActivityPresenter,還依賴OkHttpClient ,UserManager ,RestAdapter等。它們之中任何一個的構造改變了,或者Presenter構造改變了,我們都需要反覆修改LoginActivity中的代碼。

而dagger框架就解決了這種問題,使用dagger2框架後相同代碼如下:

public class LoginActivity extends AppCompatActivity {

    @Inject
    LoginActivityPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //Satisfy all dependencies requested by @Inject annotation
        getDependenciesGraph().inject(this);
    }
}

LoginActivity瞬間清爽了。dagger2框架可以讓依賴注入獨立於組件之外,不管Presenter的依賴怎麼改,都不會對LoginActivity的代碼照成任何影響,這就是dagger2框架的好處了

二、Dagger2 API

public @interface Component {
    Class<?>[] modules() default {};
    Class<?>[] dependencies() default {};
}

public @interface Subcomponent {
    Class<?>[] modules() default {};
}

public @interface Module {
    Class<?>[] includes() default {};
}

public @interface Provides {
}

public @interface MapKey {
    boolean unwrapValue() default true;
}

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

還有在Dagger 2中用到的定義在 JSR-330 (Java中依賴注入的標準)中的其它元素:

public @interface Inject {
}

public @interface Scope {
}

public @interface Qualifier {
}

三、@Inject和@Component

先來看一段沒有使用dagger的依賴注入Demo
MainActivity依賴Pot, Pot依賴Rose

public class Rose {
    public String whisper()  {
        return "熱戀";
    }
}
public class Pot {

    private Rose rose;

    @Inject
    public Pot(Rose rose) {
        this.rose = rose;
    }

    public String show() {
        return rose.whisper();
    }
}
public class MainActivity extends AppCompatActivity {

    private Pot pot;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Rose rose = new Rose();
        pot = new Pot(rose);

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

使用Dagger2進行依賴注入如下:

public class Rose {

    @Inject
    public Rose() {}

    public String whisper()  {
        return "熱戀";
    }
}
public class Pot {

    private Rose rose;

    @Inject
    public Pot(Rose rose) {
        this.rose = rose;
    }

    public String show() {
        return rose.whisper();
    }
}
@Component
public interface MainActivityComponent {
    void inject(MainActivity activity);
}
public class MainActivity extends AppCompatActivity {

    @Inject
    Pot pot;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 這個類是重新編譯後Dagger2自動生成的,所以寫這行代碼之前要先編譯一次
        // Build --> Rebuild Project
        DaggerMainActivityComponent.create().inject(this);
        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

Dagger2生成的代碼保存在這裏:


Dagger2 apt.png

源碼待會分析,現在先來了解下@Inject@Component兩個API,想要使用Dagger2進行依賴注入,至少要使用到這兩個註解。
@Inject用於標記需要注入的依賴,或者標記用於提供依賴的方法。
@Component則可以理解爲注入器,在注入依賴的目標類MainActivity使用Component完成注入。

@Inject

依賴注入中第一個並且是最重要的就是@Inject註解。JSR-330標準中的一部分,標記那些應該被依賴注入框架提供的依賴。在Dagger 2中有3種不同的方式來提供依賴:

  1. 構造器注入,@Inject標註在構造器上其實有兩層意思。
    ①告訴Dagger2可以使用這個構造器構建對象。如Rose
    ②注入構造器所需要的參數的依賴。 如Pot類,構造上的Rose會被注入。
    構造器注入的侷限:如果有多個構造器,我們只能標註其中一個,無法標註多個。

  2. 屬性注入
    MainActivity類,標註在屬性上。被標註的屬性不能使用private修飾,否則無法注入。
    屬性注入也是Dagger2中使用最多的一個注入方式。

  3. 方法注入

    public class MainActivity extends AppCompatActivity {
    
     private Pot pot;
    
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         DaggerMainActivityComponent.create().inject(this);
         String show = pot.show();
         Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
     }
    
     @Inject
     public void setPot(Pot pot) {
         this.pot = pot;
     }
    }

    標註在public方法上,Dagger2會在構造器執行之後立即調用這個方法。
    方法注入和屬性注入基本上沒有區別, 那麼什麼時候應該使用方法注入呢?
    比如該依賴需要this對象的時候,使用方法注入可以提供安全的this對象,因爲方法注入是在構造器之後執行的。
    比如google mvp dagger2中,給View設置Presenter的時候可以這樣使用方法注入。

    /**
      * Method injection is used here to safely reference {@code this} after the object is created.
      * For more information, see Java Concurrency in Practice.
      */
     @Inject
     void setupListeners() {
         mTasksView.setPresenter(this);
     }

@Component

@Inject註解只是JSR-330中定義的註解,在javax.inject包中。
這個註解本身並沒有作用,它需要依賴於注入框架才具有意義,用來標記需要被注入框架注入的方法,屬性,構造。

而Dagger2則是用Component來完成依賴注入的,@Component可以說是Dagger2中最重要的一個註解。

@Component
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

以上是定義一個Component的方式。使用接口定義,並且@Component註解。
命名方式推薦爲:目標類名+Component,在編譯後Dagger2就會爲我們生成DaggerXXXComponent這個類,它是我們定義的xxxComponent的實現,在目標類中使用它就可以實現依賴注入了。

Component中一般使用兩種方式定義方法。

  1. void inject(目標類 obj);Dagger2會從目標類開始查找@Inject註解,自動生成依賴注入的代碼,調用inject可完成依賴的注入。
  2. Object getObj(); 如:Pot getPot();
    Dagger2會到Pot類中找被@Inject註解標註的構造器,自動生成提供Pot依賴的代碼,這種方式一般爲其他Component提供依賴。(一個Component可以依賴另一個Component,後面會說)

Component和Inject的關係如下:


Jsr330和Dagger2.png

Dagger2框架以Component中定義的方法作爲入口,到目標類中尋找JSR-330定義的@Inject標註,生成一系列提供依賴的Factory類和注入依賴的Injector類。
而Component則是聯繫Factory和Injector,最終完成依賴的注入。

我們看下源碼(請對應上面的Dagger2 apt圖一起看):

Rose_Factory和Pot_Factory分別對應Rose類和Pot類的構造器上的@Inject註解。
而Factory其實是個Provider對象

public interface Provider<T> {

    /**
     * Provides a fully-constructed and injected instance of {@code T}.
     *
     * @throws RuntimeException if the injector encounters an error while
     *  providing an instance. For example, if an injectable member on
     *  {@code T} throws an exception, the injector may wrap the exception
     *  and throw it to the caller of {@code get()}. Callers should not try
     *  to handle such exceptions as the behavior may vary across injector
     *  implementations and even different configurations of the same injector.
     */
    T get();
}
public interface Factory<T> extends Provider<T> {}

爲什麼這裏要使用枚舉作爲提供Rose對象的Provide我也不太清楚,反正能提供就對了=。=

public enum Rose_Factory implements Factory<Rose> {
  INSTANCE;

  @Override
  public Rose get() {
    return new Rose();
  }

  public static Factory<Rose> create() {
    return INSTANCE;
  }
}

Pot對象依賴Rose,所以直接將RoseProvide作爲參數傳入了。

public final class Pot_Factory implements Factory<Pot> {
  private final Provider<Rose> roseProvider;

  public Pot_Factory(Provider<Rose> roseProvider) {
    assert roseProvider != null;
    this.roseProvider = roseProvider;
  }

  @Override
  public Pot get() {
    return new Pot(roseProvider.get());
  }

  public static Factory<Pot> create(Provider<Rose> roseProvider) {
    return new Pot_Factory(roseProvider);
  }
}

MainActivity上的@Inject屬性或方法註解,則對應MainActivity_MembersInjector類

public interface MembersInjector<T> {

  /**
   * Injects dependencies into the fields and methods of {@code instance}. Ignores the presence or
   * absence of an injectable constructor.
   *
   * <p>Whenever the object graph creates an instance, it performs this injection automatically
   * (after first performing constructor injection), so if you're able to let the object graph
   * create all your objects for you, you'll never need to use this method.
   *
   * @param instance into which members are to be injected
   * @throws NullPointerException if {@code instance} is {@code null}
   */
  void injectMembers(T instance);
}
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<Pot> potProvider;

  public MainActivity_MembersInjector(Provider<Pot> potProvider) {
    assert potProvider != null;
    this.potProvider = potProvider;
  }

  public static MembersInjector<MainActivity> create(Provider<Pot> potProvider) {
    return new MainActivity_MembersInjector(potProvider);
  }

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

  public static void injectPot(MainActivity instance, Provider<Pot> potProvider) {
    instance.pot = potProvider.get();
  }
}

最後是DaggerMainActivityComponent類,對應@Component註解就不多說了。這是Dagger2解析JSR-330的入口。
它聯繫Factory和MainActivity兩個類完成注入。

public final class DaggerMainActivityComponent implements MainActivityComponent {
  private Provider<Pot> potProvider;

  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.potProvider = Pot_Factory.create(Rose_Factory.create());

    this.mainActivityMembersInjector = MainActivity_MembersInjector.create(potProvider);
  }

  @Override
  public void inject(MainActivity activity) {
    mainActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private Builder() {}

    public MainActivityComponent build() {
      return new DaggerMainActivityComponent(this);
    }
  }
}

只使用幾個註解,Dagger2就默默中爲我們做了這麼多事情,太感動了……
看完這個,相信大家已經完全理解了@Inject和@Component兩個註解的作用了,要區分的是,@Inject是JSR330定義的,而@Component是Dagger2框架自己定義的。

四、@Module和@Provides

使用@Inject標記構造器提供依賴是有侷限性的,比如說我們需要注入的對象是第三方庫提供的,我們無法在第三方庫的構造器上加上@Inject註解。
或者,我們使用依賴倒置的時候,因爲需要注入的對象是抽象的,@Inject也無法使用,因爲抽象的類並不能實例化,比如:

public abstract class Flower {
    public abstract String whisper();
}
public class Lily extends Flower {

    @Inject
    Lily() {}

    @Override
    public String whisper() {
        return "純潔";
    }
}
public class Rose extends Flower {

    @Inject
    public Rose() {}

    public String whisper()  {
        return "熱戀";
    }
}
public class Pot {

    private Flower flower;

    @Inject
    public Pot(Flower flower) {
        this.flower = flower;
    }

    public String show() {
        return flower.whisper();
    }
}

修改下Demo,遵循依賴倒置規則。但是這時候Dagger就報錯了,因爲Pot對象需要Flower,而Flower是抽象的,無法使用@Inject提供實例。


抽象的依賴.png

這時候就需要用到Module了。

清除Lily和Rose的@Inject

public class Lily extends Flower {

    @Override
    public String whisper() {
        return "純潔";
    }
}
public class Rose extends Flower {

    public String whisper()  {
        return "熱戀";
    }
}

@Module標記在類上面,@Provodes標記在方法上,表示可以通過這個方法獲取依賴。

@Module
public class FlowerModule {
    @Provides
    Flower provideFlower() {
        return new Rose();
    }
}

在@Component中指定Module

@Component(modules = FlowerModule.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

其他類不需要更改,這樣就完成了。

那麼Module是幹嘛的,我們來看看生成的類。


Module.png


可以看到,被@Module註解的類生成的也是Factory。

public final class FlowerModule_FlowerFactory implements Factory<Flower> {
  private final FlowerModule module;

  public FlowerModule_FlowerFactory(FlowerModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Flower get() {
    return Preconditions.checkNotNull(
        module.provideFlower(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Flower> create(FlowerModule module) {
    return new FlowerModule_FlowerFactory(module);
  }
}

@Module需要和@Provide是需要一起使用的時候才具有作用的,並且@Component也需要指定了該Module的時候。

@Module是告訴Component,可以從這裏獲取依賴對象。Component就會去找被@Provide標註的方法,相當於構造器的@Inject,可以提供依賴。

還有一點要說的是,@Component可以指定多個@Module的,如果需要提供多個依賴的話。
並且Component也可以依賴其它Component存在。

五、@Qualifier和@Named

@Qualifier是限定符,而@Named則是基於String的限定符。

當我有兩個相同的依賴(都繼承某一個父類或者都是先某一個接口)可以提供給高層時,那麼程序就不知道我們到底要提供哪一個依賴,因爲它找到了兩個。
這時候我們就可以通過限定符爲兩個依賴分別打上標記,指定提供某個依賴。

接着上一個Demo,例如:Module可以提供的依賴有兩個。

@Module
public class FlowerModule {

    @Provides
    Flower provideRose() {
        return new Rose();
    }

    @Provides
    Flower provideLily() {
        return new Lily();
    }
}

多個Provider

這時候就可以用到限定符來指定依賴了,我這裏用@Named來演示。

@Module
public class FlowerModule {

    @Provides
    @Named("Rose")
    Flower provideRose() {
        return new Rose();
    }

    @Provides
    @Named("Lily")
    Flower provideLily() {
        return new Lily();
    }
}

我們是通過@Inject Pot的構造器注入Flower依賴的,在這裏可以用到限定符。

public class Pot {

    private Flower flower;

    @Inject
    public Pot(@Named("Rose") Flower flower) {
        this.flower = flower;
    }

    public String show() {
        return flower.whisper();
    }
}

而@Qualifier的作用和@Named是完全一樣的,不過更推薦使用@Qualifier,因爲@Named需要手寫字符串,容易出錯。

@Qualifier不是直接註解在屬性上的,而是用來自定義註解的。

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface RoseFlower {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface LilyFlower {}
@Module
public class FlowerModule {

    @Provides
    @RoseFlower
    Flower provideRose() {
        return new Rose();
    }

    @Provides
    @LilyFlower
    Flower provideLily() {
        return new Lily();
    }
}
public class Pot {

    private Flower flower;

    @Inject
    public Pot(@RoseFlower Flower flower) {
        this.flower = flower;
    }

    public String show() {
        return flower.whisper();
    }
}

我們也可以使用Module來管理Pot依賴,當然還是需要@Qualifier指定提供哪一個依賴

@Module
public class PotModule {

    @Provides
    Pot providePot(@RoseFlower Flower flower) {
        return new Pot(flower);
    }
}

然後MainAcitivtyComponent需要增加一個Module

@Component(modules = {FlowerModule.class, PotModule.class})
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

六、@Component的dependence和@SubComponent

參考:SubComponent和Dependence區別

上面也說過,Component可以依賴於其他Component,可以使用@Component的dependence,也可以使用@SubComponent,這樣就可以獲取其他Component的依賴了。

如:我們也用Component來管理FlowerModule和PotModule,並且使用dependence聯繫各個Component。
這次我就將代碼貼完整點吧。

public abstract class Flower {
    public abstract String whisper();
}
public class Lily extends Flower {

    @Override
    public String whisper() {
        return "純潔";
    }
}
public class Rose extends Flower {

    public String whisper()  {
        return "熱戀";
    }
}
@Module
public class FlowerModule {

    @Provides
    @RoseFlower
    Flower provideRose() {
        return new Rose();
    }

    @Provides
    @LilyFlower
    Flower provideLily() {
        return new Lily();
    }
}

Component上也需要指定@Qualifier

@Component(modules = FlowerModule.class)
public interface FlowerComponent {
    @RoseFlower
    Flower getRoseFlower();

    @LilyFlower
    Flower getLilyFlower();
}
public class Pot {

    private Flower flower;

    public Pot(Flower flower) {
        this.flower = flower;
    }

    public String show() {
        return flower.whisper();
    }
}

PotModule需要依賴Flower,需要指定其中一個子類實現,這裏使用RoseFlower

@Module
public class PotModule {

    @Provides
    Pot providePot(@RoseFlower Flower flower) {
        return new Pot(flower);
    }
}
@Component(modules = PotModule.class,dependencies = FlowerComponent.class)
public interface PotComponent {
    Pot getPot();
}
@Component(dependencies = PotComponent.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

而在MainActivity則需要創建其依賴的Component

public class MainActivity extends AppCompatActivity {

    @Inject
    Pot pot;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(DaggerPotComponent.builder()
                        .flowerComponent(DaggerFlowerComponent.create())
                        .build())
                .build().inject(this);

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

這就是Component的dependencies的用法了,我們Component不需要重複的指定Module,可以直接依賴其它Component獲得。

分析下源碼,看下Component的dependencies做了什麼事情。

public final class DaggerPotComponent implements PotComponent {
  private Provider<Flower> getRoseFlowerProvider;

  private Provider<Pot> providePotProvider;

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

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

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.getRoseFlowerProvider =
        new Factory<Flower>() {
          private final FlowerComponent flowerComponent = builder.flowerComponent;

          @Override
          public Flower get() {
            return Preconditions.checkNotNull(
                flowerComponent.getRoseFlower(),
                "Cannot return null from a non-@Nullable component method");
          }
        };

    this.providePotProvider =
        PotModule_ProvidePotFactory.create(builder.potModule, getRoseFlowerProvider);
  }

  @Override
  public Pot getPot() {
    return providePotProvider.get();
  }

  public static final class Builder {
    private PotModule potModule;

    private FlowerComponent flowerComponent;

    private Builder() {}

    public PotComponent build() {
      if (potModule == null) {
        this.potModule = new PotModule();
      }
      if (flowerComponent == null) {
        throw new IllegalStateException(FlowerComponent.class.getCanonicalName() + " must be set");
      }
      return new DaggerPotComponent(this);
    }

    public Builder potModule(PotModule potModule) {
      this.potModule = Preconditions.checkNotNull(potModule);
      return this;
    }

    public Builder flowerComponent(FlowerComponent flowerComponent) {
      this.flowerComponent = Preconditions.checkNotNull(flowerComponent);
      return this;
    }
  }
}

PotComponent依賴FlowerComponent,其實就是將FlowerComponent的引用傳遞給PotComponent,這樣PotComponent就可以使用FlowerComponent中的方法了。
注意看getRoseFlowerProvider這個Provider,是從 flowerComponent.getRoseFlower()獲取到的


如果使用Subcomponent的話則是這麼寫, 其他類不需要改變,只修改Component即可

@Component(modules = FlowerModule.class)
public interface FlowerComponent {

    PotComponent plus(PotModule potModule);
}
@Subcomponent(modules = PotModule.class)
public interface PotComponent {
    MainActivityComponent plus();
}
@Subcomponent
public interface MainActivityComponent {
    void inject(MainActivity activity);
}
public class MainActivity extends AppCompatActivity {

    @Inject
    Pot pot;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerFlowerComponent.create()
                .plus(new PotModule())  // 這個方法返回PotComponent
                .plus()                 // 這個方法返回MainActivityComponent
                .inject(this);

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

FlowerComponent管理了PotComponent和MainActivityComponent,看起來不符合常理。

先來說說Component中的方法的第三種定義方式(上面說了兩種)。

@Component
class AComponpent {
    XxxComponent plus(Module... modules)
}
@Subcomponent(modules = xxxxx)
class XxxComponent {

}

xxxComponent是該AComponpent的依賴,被@Subcomponent標註。
而modules參數則是xxxComponent指定的Module。
在重新編譯後,Dagger2生成的代碼中,Subcomponent標記的類是Componpent的內部類。
像上面的Demo,MainActivityComponent是PotComponent的內部類,而PotComponent又是FlowerComponent的內部類。


但是用Subcomponent怎麼看怎麼彆扭,各個Component之間聯繫太緊密,不太適合我們Demo的使用場景。
那什麼時候該用@Subcomponent呢?
Subcomponent是作爲Component的拓展的時候。
像我寫的Demo中,Pot和Flower還有MainActivity只是單純的依賴關係。就算有,也只能是Flower作爲Pot的Subcomponent,而不是Demo中所示,因爲我需要給大家展示Dagger的API,強行使用。

比較適合使用Subcomponent的幾個場景:
很多工具類都需要使用到Application的Context對象,此時就可以用一個Component負責提供,我們可以命名爲AppComponent。
需要用到的context對象的SharePreferenceComponent,ToastComponent就可以它作爲Subcomponent存在了。

而且在AppComponent中,我們可以很清晰的看到有哪些子Component,因爲在裏面我們定義了很多XxxComponent plus(Module... modules)

每個ActivityComponent也是可以作爲AppComponent的Subcomponent,這樣可以更方便的進行依賴注入,減少重複代碼。

Component dependencies和Subcomponent區別

  1. Component dependencies 能單獨使用,而Subcomponent必須由Component調用方法獲取。
  2. Component dependencies 可以很清楚的得知他依賴哪個Component, 而Subcomponent不知道它自己的誰的孩子……真可憐
  3. 使用上的區別,Subcomponent就像這樣DaggerAppComponent.plus(new SharePreferenceModule());
    使用Dependence可能是這樣DaggerAppComponent.sharePreferenceComponent(SharePreferenceComponent.create())

Component dependencies和Subcomponent使用上的總結

Component Dependencies:

  1. 你想保留獨立的想個組件(Flower可以單獨使用注入,Pot也可以)
  2. 要明確的顯示該組件所使用的其他依賴

Subcomponent:

  1. 兩個組件之間的關係緊密
  2. 你只關心Component,而Subcomponent只是作爲Component的拓展,可以通過Component.xxx調用。

Dagger 2 subcomponents vs component dependencies

七、@Scope和@Singleton

@Scope是用來管理依賴的生命週期的。它和@Qualifier一樣是用來自定義註解的,而@Singleton則是@Scope的默認實現。

/**
 * Identifies a type that the injector only instantiates once. Not inherited.
 *
 * @see javax.inject.Scope @Scope
 */
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}

Component會幫我們注入被@Inject標記的依賴,並且可以注入多個。
但是每次注入都是重新new了一個依賴。如

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Inject
    Pot pot;

    @Inject
    Pot pot2;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(DaggerPotComponent.builder()
                                .flowerComponent(DaggerFlowerComponent.create()).build())
                .build().inject(this);

        Log.d(TAG, "pot = " + pot.hashCode() +", pot2 = " + pot2.hashCode());

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

打印的地址值不一樣,是兩個對象。
D/MainActivity: pot = com.aitsuki.architecture.pot.Pot@240f3ff5, pot2 = com.aitsuki.architecture.pot.Pot@2c79118a

假設我們需要Pot對象的生命週期和app相同,也就是單例,我們需要怎麼做?這時候就可以用到@Scope註解了。

我們來使用默認的@Scope實現——@Singleton
需要在@Provide和@Component中同時使用才起作用,爲什麼呢,待會會說明。

@Module
public class PotModule {

    @Provides
    @Singleton
    Pot providePot(@RoseFlower Flower flower) {
        return new Pot(flower);
    }
}
@Singleton
@Component(modules = PotModule.class, dependencies = FlowerComponent.class)
public interface PotComponent {
    Pot getPot();
}

然後我們再運行下項目,報錯了


@Scope報錯


那是因爲我們的MainActivityComponent依賴PotComponent,而dagger2規定子Component也必須標註@Scope。
但是我們不能給MainActivityComponent也標註@Singleton,並且dagger2也不允許。因爲單例依賴單例是不符合設計原則的,我們需要自定義一個@Scope註解。

定義Scope是名字要起得有意義,能一眼就讓你看出這個Scope所規定的生命週期。
比如ActivityScope 或者PerActivity,生命週期和Activity相同。

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {}
@ActivityScope
@Component(dependencies = PotComponent.class)
public interface MainActivityComponent {
    void inject(MainActivity activity);
}

D/MainActivity: pot = com.aitsuki.architecture.pot.Pot@240f3ff5, pot2 = com.aitsuki.architecture.pot.Pot@240f3ff5
這時候我們看到兩個pot對象的地址值是一樣的,@Scope註解起作用了。

那麼我再新建一個Activity,再次注入pot打印地址值。

public class SecondActivity extends AppCompatActivity {

    private static final String TAG = "SecondActivity";

    @Inject
    Pot pot3;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerSecondActivityComponent.builder()
                .potComponent(DaggerPotComponent.builder().flowerComponent(DaggerFlowerComponent.create()).build())
                .build().inject(this);

        Log.d(TAG, "pot3 = " + pot3);
    }
}
@ActivityScope
@Component(dependencies = PotComponent.class)
public interface SecondActivityComponent {
    void inject(SecondActivity activity);
}

在MainActivity初始化時直接跳轉到SecondActivity

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Inject
    Pot pot;

    @Inject
    Pot pot2;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(DaggerPotComponent.builder()
                                .flowerComponent(DaggerFlowerComponent.create()).build())
                .build().inject(this);

        Log.d(TAG, "pot = " + pot +", pot2 = " + pot2);

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();

        startActivity(new Intent(this, SecondActivity.class));
    }
}

D/MainActivity: pot = com.aitsuki.architecture.pot.Pot@240f3ff5, pot2 = com.aitsuki.architecture.pot.Pot@240f3ff5
D/SecondActivity: pot3 = com.aitsuki.architecture.pot.Pot@1b7661c7

可以看到,在SecondActivity中,Pot對象地址和MainActivity中的不一樣了。
爲什麼呢?不是叫@Singleton麼,爲什麼使用了它Pot還不是單例的,Dagger2你逗我!


那麼現在我可以說說@Scope的作用了,它的作用只是保證依賴在@Component中是唯一的,可以理解爲“局部單例”。
@Scope是需要成對存在的,在Module的Provide方法中使用了@Scope,那麼對應的Component中也必須使用@Scope註解,當兩邊的@Scope名字一樣時(比如同爲@Singleton), 那麼該Provide方法提供的依賴將會在Component中保持“局部單例”。
而在Component中標註@Scope,provide方法沒有標註,那麼這個Scope就不會起作用,而Component上的Scope的作用也只是爲了能順利通過編譯,就像我剛剛定義的ActivityScope一樣。

@Singleton也是一個自定義@Scope,它的作用就像上面說的一樣。但由於它是Dagger2中默認定義的,所以它比我們自定義Scope對了一個功能,就是編譯檢測,防止我們不規範的使用Scope註解,僅此而已。

在上面的Demo中,Pot對象在PotComponent中是“局部單例”的。
而到了SecondActivity,因爲是重新Build了一個PotComponent,所以Pot對象的地址值也就改變了。

那麼,我們如何使用Dagger2實現單例呢?
很簡單,做到以下兩點即可。

  1. 依賴在Component中是單例的(供該依賴的provide方法和對應的Component類使用同一個Scope註解。)
  2. 對應的Component在App中只初始化一次,每次注入依賴都使用這個Component對象。(在Application中創建該Component)

如:

public class App extends Application {

    private PotComponent potComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        potComponent = DaggerPotComponent.builder()
                .flowerComponent(DaggerFlowerComponent.create())
                .build();
    }

    public PotComponent getPotComponent() {
        return potComponent;
    }
}

然後修改MainActivity和SecondActivity的Dagger代碼如下

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Inject
    Pot pot;

    @Inject
    Pot pot2;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(((App) getApplication()).getPotComponent())
                .build().inject(this);

        Log.d(TAG, "pot = " + pot +", pot2 = " + pot2);

        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();

        startActivity(new Intent(this, SecondActivity.class));
    }
}
public class SecondActivity extends AppCompatActivity {

    private static final String TAG = "SecondActivity";

    @Inject
    Pot pot3;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerSecondActivityComponent.builder()
                .potComponent(((App) getApplication()).getPotComponent())
                .build().inject(this);

        Log.d(TAG, "pot3 = " + pot3);
    }
}

運行後的log輸出
D/MainActivity: pot = com.aitsuki.architecture.pot.Pot@240f3ff5, pot2 = com.aitsuki.architecture.pot.Pot@240f3ff5
D/SecondActivity: pot3 = com.aitsuki.architecture.pot.Pot@240f3ff5
現在Pot的生命週期就和app相同了。

你也可以試試自定義一個@ApplicationScope,替換掉@Singleton,結果是一樣的,這裏就不演示了。

稍微總結下@Scope註解:
Scope是用來給開發者管理依賴的生命週期的,它可以讓某個依賴在Component中保持 “局部單例”(唯一),如果將Component保存在Application中複用,則可以讓該依賴在app中保持單例。 我們可以通過自定義不同的Scope註解來標記這個依賴的生命週期,所以命名是需要慎重考慮的。
@Singleton告訴我們這個依賴時單例的
@ActivityScope告訴我們這個依賴的生命週期和Activity相同
@FragmentScope告訴我們這個依賴的生命週期和Fragment相同
@xxxxScope ……

八、MapKey和Lazy

@MapKey

這個註解用在定義一些依賴集合(目前爲止,Maps和Sets)。讓例子代碼自己來解釋吧:
定義:

@MapKey(unwrapValue = true)
@interface TestKey {
    String value();
}

提供依賴:

@Provides(type = Type.MAP)
@TestKey("foo")
String provideFooKey() {
    return "foo value";
}

@Provides(type = Type.MAP)
@TestKey("bar")
String provideBarKey() {
    return "bar value";
}

使用:

@Inject
Map<String, String> map;

map.toString() // => „{foo=foo value, bar=bar value}”

@MapKey註解目前只提供兩種類型 - String和Enum。

Lazy

Dagger2還支持Lazy模式,通過Lazy模擬提供的實例,在@Inject的時候並不初始化,而是等到你要使用的時候,主動調用其.get方法來獲取實例。
比如:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    @Inject
    Lazy<Pot> potLazy;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        DaggerMainActivityComponent.builder()
                .potComponent(((App) getApplication()).getPotComponent())
                .build().inject(this);

        Pot pot = potLazy.get();
        String show = pot.show();
        Toast.makeText(MainActivity.this, show, Toast.LENGTH_SHORT).show();
    }
}

九、項目實戰

略……

233333333,直接去看Google的MVP模式吧,上面有例子,也可以去看看其他博客。
我也不知道寫不寫哈,有點小忙,就算寫也可能是國慶過後了。

十、完結

看完這篇博文之後,感覺如何?博主表示寫的算是很詳細,很清晰易懂了。不懂的可以跟着思路敲一下哦,不動手,永遠不會知道Dagger2其實並沒有想象的那麼難用……



============================================

原文鏈接 http://www.jianshu.com/p/24af4c102f62

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章