Dagger 2 : Component.Builder註解有什麼用?

Dagger 2 : Component.Builder 這篇博客是系列博客Dagger 2 Android : Defeat the Dahaka的其中一部分。若是你想了解更多有關 Dahaka 的內容,能夠去查看其它博客,它們教咱們如何去戰勝 Dahaka 以及說明了寫這些博客的緣由。html

這裏說明下 Dahaka 是個什麼鬼。達哈卡(Dahaka)是遊戲《波斯王子:武者之心》中的一個怪物(在這個系列博客裏面指的是各個對象之間的依賴關係),同時也是隱藏結局裏的最終BOSS。反正二者都很讓人頭疼。裏面還有個重要的道具--時之匕首,其實也有暗指 Dagger 的意思,看來原做者是這遊戲的忠實粉,不過她的印式英語真是很難聽懂(後面有YouTube的視頻)。java

咱們從一個簡單的例子開始。 定義一個 AppComponent 代碼以下:android

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {

  void inject(MainActivity mainActivity);
  SharedPreferences getSharedPrefs();
}
複製代碼

AppModule 的代碼:git

@Module
public class AppModule {

   Application application;

   public AppModule(Application application) {
      this.application = application;
   }

   @Provides
   Application providesApplication() {
      return application;
   }
   @Provides
   @Singleton
   public SharedPreferences providePreferences() {
       return application.getSharedPreferences(DATA_STORE,
                             Context.MODE_PRIVATE);
   }

}
複製代碼

能夠在咱們的App類中的onCreate實例化 Componentgithub

@Override
 public void onCreate() {
     super.onCreate();
     AppComponent appComponent = DaggerAppComponent.builder()
             .appMoudle(new AppMoudle(this))
             .build();
 }
複製代碼

下面是 Dagger 自動幫咱們生成的 Builder類:json

DaggerAppComponentapi

public final class DaggerAppComponent implements AppComponent {

  public static Builder builder() {
    return new Builder();
  }
  ...
  public static final class Builder {
    private AppMoudle appMoudle;

    private Builder() {}

    public AppComponent build() {
      if (appMoudle == null) {
        throw new IllegalStateException(AppMoudle.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    public Builder appMoudle(AppMoudle appMoudle) {
      this.appMoudle = Preconditions.checkNotNull(appMoudle);
      return this;
    }
  }
  ...
}
複製代碼

其中 Builder 類的 appMoudle 成員變量。它經過appMoudle 方法傳遞進來的。 在 AppOnCreate 方法中:app

AppComponent appComponent = DaggerAppComponent.builder()
        .appMoudle(new AppMoudle(this))
        .build();
複製代碼

上面的代碼都是Daggeer 自動生成的 DaggerAppComponent 類代碼,能夠在 app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java 目錄下找到ide

回到本文的重點:@Component.Builder 能幹嘛? 它可讓咱們自定義上面 Dagger 生成的 Builer 類。 咱們先看下 @Component.Builder 的定義。函數

A builder for a component. Components may have a single nested static abstract class or interface annotated with @Component.Builder. If they do, then the component's generated builder will match the API in the type.--source

讓咱們在 AppComponent 中使用 @Component.Builder

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {

   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs();
   @Component.Builder
   interface Builder {
        AppComponent build();
        Builder appModule(AppModule appModule);
    }
}
複製代碼

經過使用 Component.Builder 來註解接口,Dagger 會自動生成跟上面徹底相同的 Builder 類。能夠本身去查看生成的 DaggerAppComponent 類。

這樣還達不到自定義的目的。如何實現自定義Builder類? 這時候咱們就須要使用到 @BindsInstance 註解了。

什麼是 @BindsInstance? 下面是它的定義:

Marks a method on a component builder or subcomponent builder that allows an instance to be bound to some type within the component.

WHAAT? 這是什麼鬼,其實我也不能理解,哈哈。

@BindsInstance methods should be preferred to writing a @Module with constructor arguments and immediately providing those values.--source

這句話大概意思:咱們常常經過構造函數來給某些成員變量賦值,在上面的 AppModule 也是經過構造函數來給成員 application 初始化。@BindsInstance 它可讓咱們省去寫這類構造函數,經過它可以爲類的成員變量賦值。

public AppModule(Application application) {
        this.application = application;
}
複製代碼

如今咱們去掉 AppModule 的構造函數以及 @Provides 註解的 providesApplication 函數:

@Module
 public class AppModule {

     @Provides
     @Singleton
     public SharedPreferences providePreferences( Application application) {
         return application.getSharedPreferences(
                                    "store", Context.MODE_PRIVATE);
     }
 }
複製代碼

這是咱們經過 @Component.Builder 自定義的 AppComponent 類:

@Singleton
@Component(modules = {AppModule.class})
public interface AppComponent {
   void inject(MainActivity mainActivity);
   SharedPreferences getSharedPrefs();
   @Component.Builder
   interface Builder {

      AppComponent build();
      @BindsInstance Builder application(Application application);      
  }
}
複製代碼

跟前面的代碼相比,咱們去掉了 Builder appModule(AppModule appModule); 函數,並添加了一行 @BindsInstance Builder application(Application application); 經過它咱們就能夠爲 AppModule 類裏面的成員方法提供 application 參數。下面會一步步說明是如何實現的。

首先看下初始化的改變:

初始化 DaggerAppComponent

DaggerAppComponent appComponent = DaggerAppComponent.builder()
           .application(this)
           .build();
複製代碼

注意:這裏咱們少了 appModule(AppModule appModule), 並經過 AppModule 默認構造函數(即無參構造函數)去生成 AppModule 實例(該實例是 Builder 類的成員變量)。

下面是修改後 Dagger 生成 DaggerAppComponent 類的代碼:

public final class DaggerAppComponent implements AppComponent {
  ...

  private static final class Builder implements AppComponent.Builder {
    private AppMoudle appMoudle;

    private Application application;

    @Override
    public AppComponent build() {
      if (appMoudle == null) {
        this.appMoudle = new AppMoudle();
      }
      if (application == null) {
        throw new IllegalStateException(Application.class.getCanonicalName() + " must be set");
      }
      return new DaggerAppComponent(this);
    }

    @Override
    public Builder application(Application application) {
      this.application = Preconditions.checkNotNull(application);
      return this;
    }
  }
}
複製代碼

這時咱們發現Builder 類有兩個成員變量了,其中 this.appMoudle = new AppMoudle(); ,因此成員變量 appMoudle 經過默認構造函數生成, 另外一個成員變量 application是並經過 @BindsInstance 註解過的方法 public Builder application(Application application) 來初始化的。

注意:前面的Builder類是沒有 application 成員變量的,它是 AppMoudle 類的成員變量。

如今 AppMoudle 類沒有了 application 成員變量了,那麼 AppMoudle 類裏面的成員方法 public SharedPreferences providePreferences(Application application)application 參數如何來的?

下面看 Dagger 根據 AppMoudle 自動生成的 *_ProvidePreferencesFactory 類: AppMoudle_ProvidePreferencesFactory

public final class AppMoudle_ProvidePreferencesFactory implements Factory<SharedPreferences> {
  private final AppMoudle module;

  private final Provider<Application> applicationProvider;

  public AppMoudle_ProvidePreferencesFactory( AppMoudle module, Provider<Application> applicationProvider) {
    assert module != null;
    this.module = module;
    assert applicationProvider != null;
    this.applicationProvider = applicationProvider;
  }

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

  public static Factory<SharedPreferences> create( AppMoudle module, Provider<Application> applicationProvider) {
    return new AppMoudle_ProvidePreferencesFactory(module, applicationProvider);
  }
}
複製代碼

還有 DaggerAppComponent 類:

private Provider<Application> applicationProvider;

private Provider<SharedPreferences> providePreferencesProvider;

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

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

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

  this.applicationProvider = InstanceFactory.create(builder.application);

  this.providePreferencesProvider =
      DoubleCheck.provider(
          AppMoudle_ProvidePreferencesFactory.create(builder.appMoudle, applicationProvider));
}

@Override
public void inject(MainActivity mainActivity) {
  MembersInjectors.<MainActivity>noOp().injectMembers(mainActivity);
}

@Override
public SharedPreferences getShasredPrefs() {
  return providePreferencesProvider.get();
}
複製代碼
  1. 能夠發現 application 參數是經過成員 applicationProviderget() 方法拿到的。
  2. applicationProvider 又是經過 create 方法傳遞進來的。
  3. create 方法是在 DaggerAppComponent 類的 initialize 方法中被調用了。
  4. DaggerAppComponent 類的 applicationProvider 則是經過Builder類的application成員變量生成的(this.applicationProvider = InstanceFactory.create(builder.application);)。
  5. Builder類的application成員變量是經過 @BindsInstance 註解的方法傳入的。

而後一切就通了。

總結:

  1. 若是使用 @Component.Builder@BindsInstance 來自定義 Builder 類,那麼被@BindsInstance註解方法裏面的參數在 Builder 類中都有對應的成員變量。
  2. 能夠經過 Builder 類中的成員變量在 Component 中生成對應的 Provider<T> **Provider 成員,經過 **Provider 成員的get()就能夠拿到依賴的實例了。

使用場景:

  1. 若是咱們的 Moudle 某個被 @Provides 註解的 provide**(xxx obj) 函數使用了某種類型的對象做爲參數(好比 application),而這個參數若是不想經過 Moudle 構造函數傳遞進來,那麼就可使用 @Component.Builder 來自定義 Builder 類,並經過 @BindsInstance 註解的方法來提供這個參數 obj
  2. 或者直接給 Component 直接添加一個對象依賴的提供函數。

根據這個總結好比咱們添加一個全局的 Gson 對象: 1.AppComponent

void inject(MainActivity mainActivity);

SharedPreferences getShasredPrefs();
Gson getGson();

@Singleton
@Component.Builder
interface Builder{
      AppComponent build();
      @BindsInstance
      Builder application(Application application);
      @BindsInstance
      Builder gson(Gson gson);
複製代碼

2.App 類裏面:

@Override
  public void onCreate() {
      super.onCreate();
      appComponent = DaggerAppComponent.builder()
              .application(this)
              .gson(new Gson())
              .build();
  }
複製代碼

3.而後在咱們的MainActivity中:

String json = "{\"name\":\"Pikachu\", \"id\":25}";

@Inject
Gson mgson;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    DaggerLearnApp.getAppComponent().inject(this);
}

@Override
protected void onResume() {
    super.onResume();
    Pokemon pokemon = mgson.fromJson(json, Pokemon.class);
    Log.i(TAG, "pokemon name:" + pokemon.getName());
}
複製代碼

經過這種方式能夠去掉 @Module 註解 AppMoudle 類裏面的 provideGson 方法,同時還去掉了 @Singleton 避免了 Lazy 加載,可是效果倒是同樣的。

@Module
abstract class ExecutorModule {
@Provides
@Singleton
@Background Executor provideGson() {
    return Executors.newCachedThreadPool(4);
    }
}
複製代碼

Subcomponent.Builder

@Subcomponent.Builder 也是相似的,不過它是在 subcomponents 中使用。

視頻

相關文章
相關標籤/搜索