Android知识Android技术知识Android开发

Dagger 2学习与探索(八)

2017-08-28  本文已影响0人  akak18183

上一期介绍了Component dependency方法,这一期介绍SubComponent方法,效果是类似的,只是实现手段有一点不一样。
SubComponent的声明不再是用@Component标识,而是使用@Subcomponent标识,不再有dependency参数,同时上级Component也无需再添加暴露函数。
那么SubComponent是如何与上级建立联系的呢?
有两种方式:

第一种方法

主体代码

和上一期的非常类似,稍微做一下修改:
AppComponent不用再暴露ClassA的方法,而加上ActivityComponent的构造方法:

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

  void inject(MyApp myApp);
  
  ActivityComponent getActivityComponent(ActivityModule activityModule);
}

注意和提供/暴露函数不太一样,这里虽然ActivityComponent自己有范围标注,但在这里不必标出。你也可以标出,但不会造成任何区别。ActivityComponent不是注入对象,并不会变成单例模式。

@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(MainActivity mainActivity);
}

Module部分没有变化。然后就是使用的时候有区别:

public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    MyApp.getAppComponent().getActivityComponent(new ActivityModule(4)).inject(this);
  }
}

现在ActivityComponent是由AppComponent产生。运行之前还是要rebuild一下。
可以看到,这种方法和上一期的难易程度相差不大,不过由于Dagger1的时代就有类似Component dependency的模式,因此可能Component dependency用的更多一些。

生成代码

还是来看看DaggerAppComponent吧。现在DaggerActivityComponent已经不存在了。

public final class DaggerAppComponent implements AppComponent {
  private Provider<Integer> provideIntAProvider;

  private Provider<Integer> provideIntBProvider;

  private Provider<ClassA> provideClassAProvider;

  private MembersInjector<MyApp> myAppMembersInjector;

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

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

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

    this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);

    this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);

    this.provideClassAProvider =
        DoubleCheck.provider(
            AppModule_ProvideClassAFactory.create(
                builder.appModule, provideIntAProvider, provideIntBProvider));

    this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);
  }

  @Override
  public void inject(MyApp myApp) {
    myAppMembersInjector.injectMembers(myApp);
  }

  @Override
  public ActivityComponent getActivityComponent(ActivityModule activityModule) {
    return new ActivityComponentImpl(activityModule);
  }

  public static final class Builder {
    private AppModule appModule;

    private Builder() {}

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

    public Builder appModule(AppModule appModule) {
      this.appModule = Preconditions.checkNotNull(appModule);
      return this;
    }
  }

  private final class ActivityComponentImpl implements ActivityComponent {
    private final ActivityModule activityModule;

    private Provider<Integer> provideCProvider;

    private Provider<ClassB> provideClassBProvider;

    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private ActivityComponentImpl(ActivityModule activityModule) {
      this.activityModule = Preconditions.checkNotNull(activityModule);
      initialize();
    }

    @SuppressWarnings("unchecked")
    private void initialize() {

      this.provideCProvider = ActivityModule_ProvideCFactory.create(activityModule);

      this.provideClassBProvider =
          DoubleCheck.provider(
              ActivityModule_ProvideClassBFactory.create(
                  activityModule, DaggerAppComponent.this.provideClassAProvider, provideCProvider));

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

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

可见其内部新生成了一个类,ActivityComponentImpl,效果就像把上一期的DaggerActivityComponent合并到这里来。

第二种方法

主体代码
  1. ActivityComponent
@PerActivity
@Subcomponent(modules = ActivityModule.class)
public interface ActivityComponent {

  void inject(MainActivity mainActivity);

  @Subcomponent.Builder
  interface Builder extends SubcomponentBuilder<ActivityComponent> {
    Builder withActivityModule(ActivityModule activityModule);
  }
}

现在AppComponent没有工厂方法了,ActivityComponent需要提供一个Builder来用参数构造自己。注意到@Subcomponent.Builder标注,看看究竟:

/**
 * A subcomponent that inherits the bindings from a parent {@link Component} or
 * {@link Subcomponent}. The details of how to associate a subcomponent with a parent are described
 * in the documentation for {@link Component}.
 *
 * @author Gregory Kick
 * @since 2.0
 */
@Retention(RUNTIME) // Allows runtimes to have specialized behavior interoperating with Dagger.
@Target(TYPE)
@Documented
public @interface Subcomponent {
  /**
   * A list of classes annotated with {@link Module} whose bindings are used to generate the
   * subcomponent implementation.  Note that through the use of {@link Module#includes} the full set
   * of modules used to implement the subcomponent may include more modules that just those listed
   * here.
   */
  Class<?>[] modules() default {};
  
  /**
   * A builder for a subcomponent.  This follows all the rules of {@link Component.Builder}, except
   * it must appear in classes annotated with {@link Subcomponent} instead of {@code Component}.
   * Components can have methods that return a {@link Subcomponent.Builder}-annotated type,
   * allowing the user to set modules on the subcomponent using their defined API.
   */
  @Target(TYPE)
  @Documented
  @interface Builder {}
}

正如注释文档描述的那样,Builder接口需要有一个方法来帮助设置ActivityModule,在这里就是withActivityModule方法,名字仍旧不重要。
SubcomponentBuilder是自己定义的一个接口,好在如果使用第二种方式,这个接口可以复用在其他Subcomponent里:

public interface SubcomponentBuilder<V> {
  V build();
}

名字不重要,重要的是要带有build()方法。这个接口与Builder配合来实现Builder.build()

  1. AppComponent
@PerApplication
@Component(modules = {AppModule.class, ActivityBinders.class})
public interface AppComponent {

  void inject(MyApp myApp);

  Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders();
}

首先介绍一下ActivityBinders,这个负责获取对应的Builder

@Module(subcomponents={ ActivityComponent.class })
public abstract class ActivityBinders {

  @Binds
  @IntoMap
  @SubcomponentKey(Builder.class)
  public abstract SubcomponentBuilder getActivityBuilder(Builder impl);
}

注意其标注,属于@Module而且要声明对应的Subcomponent
出现了三个新标注。
@Binds说是新标注,其实在其他地方也见过。
@IntoMap

/**
 * The method's return type forms the type argument for the value of a {@code Map<K, Provider<V>>},
 * and the combination of the annotated key and the returned value is contributed to the map as a
 * key/value pair. The {@code Map<K, Provider<V>>} produced from the accumulation of values will be
 * immutable.
 *
 * @see <a href="https://google.github.io/dagger/multibindings#map-multibindings">Map multibinding</a>
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface IntoMap {}

大意是标注的方法会和某个Map<K, Provider<V>>建立联系,准确地说是返回类型和标注类型会成为一个键值对。
返回类型这里自然是SubcomponentBuilder的实现类,标注类型就是下面@SubcomponentKey(Builder.class)标注的Builder.class,这个Builder就是ActivityComponent里面的Builder
SubcomponentKey也是要自己添加的一个标注接口,好在还是可以复用。

@MapKey
@Target({ElementType.METHOD}) 
@Retention(RetentionPolicy.RUNTIME)
public @interface SubcomponentKey {
  Class<?> value();
}

其实总的来说,现在AppComponent不直接产生ActivityComponent,而是生成一个Map,key就是Builder的类(由@SubcomponentKey规定,当然你也可以直接用@MapKey,不过具体实现有点不同),value自然就是对应的BuilderProvider封装的工厂。ActivityComponent自己查表获取Builder再传入ActivityModule完成建造,从而再注入。
当然了,这里使用了一些帮助的扩展接口/标注,从而使得添加多个Subcomoponent变得容易。不用也是完全可以的。

  1. MainActivity
    基于2的分析,这里的代码思路就很清晰了:
public class MainActivity extends AppCompatActivity {
  @Inject ClassB classB;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    ActivityComponent.Builder builder
        = (ActivityComponent.Builder) MyApp.getAppComponent()
        .subcomponentBuilders()
        .get(ActivityComponent.Builder.class)
        .get();
    builder.withActivityModule(new ActivityModule(4)).build().inject(this);
  }
}
生成代码

DaggerAppComponent

public final class DaggerAppComponent implements AppComponent {
  private Provider<Integer> provideIntAProvider;

  private Provider<Integer> provideIntBProvider;

  private Provider<ClassA> provideClassAProvider;

  private MembersInjector<MyApp> myAppMembersInjector;

  private Provider<ActivityComponent.Builder> activityComponentBuilderProvider;

  private Provider<SubcomponentBuilder> getActivityBuilderProvider;

  private Provider<Map<Class<?>, Provider<SubcomponentBuilder>>>
      mapOfClassOfAndProviderOfSubcomponentBuilderProvider;

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

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

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

    this.provideIntAProvider = AppModule_ProvideIntAFactory.create(builder.appModule);

    this.provideIntBProvider = AppModule_ProvideIntBFactory.create(builder.appModule);

    this.provideClassAProvider =
        DoubleCheck.provider(
            AppModule_ProvideClassAFactory.create(
                builder.appModule, provideIntAProvider, provideIntBProvider));

    this.myAppMembersInjector = MyApp_MembersInjector.create(provideClassAProvider);

    this.activityComponentBuilderProvider =
        new dagger.internal.Factory<ActivityComponent.Builder>() {
          @Override
          public ActivityComponent.Builder get() {
            return new ActivityComponentBuilder();
          }
        };

    this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;

    this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
        MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
            .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
            .build();
  }

  @Override
  public void inject(MyApp myApp) {
    myAppMembersInjector.injectMembers(myApp);
  }

  @Override
  public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
    return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
  }

  public static final class Builder {
    private AppModule appModule;

    private Builder() {}

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

    public Builder appModule(AppModule appModule) {
      this.appModule = Preconditions.checkNotNull(appModule);
      return this;
    }
  }

  private final class ActivityComponentBuilder implements ActivityComponent.Builder {
    private ActivityModule activityModule;

    @Override
    public ActivityComponent build() {
      if (activityModule == null) {
        throw new IllegalStateException(ActivityModule.class.getCanonicalName() + " must be set");
      }
      return new ActivityComponentImpl(this);
    }

    @Override
    public ActivityComponentBuilder withActivityModule(ActivityModule activityModule) {
      this.activityModule = Preconditions.checkNotNull(activityModule);
      return this;
    }
  }

  private final class ActivityComponentImpl implements ActivityComponent {
    private Provider<Integer> provideCProvider;

    private Provider<ClassB> provideClassBProvider;

    private MembersInjector<MainActivity> mainActivityMembersInjector;

    private ActivityComponentImpl(ActivityComponentBuilder builder) {
      assert builder != null;
      initialize(builder);
    }

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

      this.provideCProvider = ActivityModule_ProvideCFactory.create(builder.activityModule);

      this.provideClassBProvider =
          DoubleCheck.provider(
              ActivityModule_ProvideClassBFactory.create(
                  builder.activityModule,
                  DaggerAppComponent.this.provideClassAProvider,
                  provideCProvider));

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

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

首先有些新的东西是下游的Builder的实现细节,很典型的建造者模式,这里就不过多分析了,主要是探究Map是怎么生成的。
注意到

  @Override
  public Map<Class<?>, Provider<SubcomponentBuilder>> subcomponentBuilders() {
    return mapOfClassOfAndProviderOfSubcomponentBuilderProvider.get();
  }

这个Map同样也被Provider接口封装,具体在

this.mapOfClassOfAndProviderOfSubcomponentBuilderProvider =
        MapProviderFactory.<Class<?>, SubcomponentBuilder>builder(1)
            .put(ActivityComponent.Builder.class, getActivityBuilderProvider)
            .build();

这里进行初始化。MapProviderFactory实际上就是一个Map的工厂,把键值对放进去,然后使用get()来获取Map:

/**
 * A {@link Factory} implementation used to implement {@link Map} bindings. This factory returns a
 * {@code Map<K, Provider<V>>} when calling {@link #get} (as specified by {@link Factory}).
 细节省略……

那么.put(ActivityComponent.Builder.class, getActivityBuilderProvider)就是放键值对,getActivityBuilderProvider又从哪里来?this.getActivityBuilderProvider = (Provider) activityComponentBuilderProvider;
然后

    this.activityComponentBuilderProvider =
        new dagger.internal.Factory<ActivityComponent.Builder>() {
          @Override
          public ActivityComponent.Builder get() {
            return new ActivityComponentBuilder();
          }
        };

如此就把一个初始化好的ActivityComponentBuilder插入到了Map中。
之后发生的就很自然了。
总而言之,这种方法复杂度比第一种高得很明显。少写了一行ActivityComponent的工厂函数,代价是多了一个ActivityBinders以及配套的接口/注解,同时AppComponent还得添加其他东西。至于好处那就见仁见智了。

上一篇下一篇

猜你喜欢

热点阅读