Dagger 2 : Component.Builder注解有什么用?

Dagger 2 : Component.Builder 这篇博客是系列博客Dagger 2 Android : Defeat the Dahaka的其中一部分。如果你想了解更多有关 Dahaka 的内容,可以去查看其它博客,它们教我们如何去打败 Dahaka 以及说明了写这些博客的原因。

这里说明下 Dahaka 是个什么鬼。达哈卡(Dahaka)是游戏《波斯王子:武者之心》中的一个怪物(在这个系列博客里面指的是各个对象之间的依赖关系),同时也是隐藏结局里的最终BOSS。反正两者都很让人头疼。里面还有个重要的道具--时之匕首,其实也有暗指 Dagger 的意思,看来原作者是这游戏的忠实粉,不过她的印式英语真是很难听懂(后面有YouTube的视频)。

我们从一个简单的例子开始。 定义一个 AppComponent 代码如下:

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

  void inject(MainActivity mainActivity);
  SharedPreferences getSharedPrefs();
}
复制代码

AppModule 的代码:

@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实例化 Component

@Override
 public void onCreate() {
     super.onCreate();
     AppComponent appComponent = DaggerAppComponent.builder()
             .appMoudle(new AppMoudle(this))
             .build();
 }
复制代码

下面是 Dagger 自动帮我们生成的 Builder类:

DaggerAppComponent

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 方法中:

AppComponent appComponent = DaggerAppComponent.builder()
        .appMoudle(new AppMoudle(this))
        .build();
复制代码

上面的代码都是Daggeer 自动生成的 DaggerAppComponent 类代码,可以在 app/build/generated/source/apt/debug/你的包名/DaggerAppComponent.java 目录下找到

回到本文的重点:@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 中使用。

视频

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值