tags: [dagger2,架构,android]
categories: dagger2
top: false
toc: true
cover: false
img: /featureImg/dagger.jpg
summary: dagger2最绕的概念之一
—-

前面文章中讲到Componnet继承和依赖的三种方式时说到了在父Componnet中Module中指定SubComponent,这种方式主要用来实现Activity-Multibindings,现在就来了解下什么是Activity-Multibindings

先说说什么是Multibindings

简单的说就是将多个对象放到一个集合中,让依赖需求方可以通过一个集合找到需要的依赖而不用绑定具体的依赖

Set multibindings

在@Module中使用 @IntoSet或者 @ElementsIntoSet注解

  1. @Module
  2. class MyModuleA {
  3. //提供一个元素
  4. @Provides @IntoSet
  5. static String provideOneString(DepA depA, DepB depB) {
  6. return "ABC";
  7. }
  8. }
  9. @Module
  10. class MyModuleB {
  11. //一次提供多个元素
  12. @Provides @ElementsIntoSet
  13. static Set<String> provideSomeStrings(DepA depA, DepB depB) {
  14. return new HashSet<String>(Arrays.asList("DEF", "GHI"));
  15. }
  16. }
  17. //dagger可以提供一个Set<String>
  18. class Bar {
  19. @Inject Bar(Set<String> strings) {
  20. assert strings.contains("ABC");
  21. assert strings.contains("DEF");
  22. assert strings.contains("GHI");
  23. }
  24. }
  25. //或者从Componnet中获取Set<String>
  26. @Component(modules = {MyModuleA.class, MyModuleB.class})
  27. interface MyComponent {
  28. Set<String> strings();
  29. }
  30. @Test void testMyComponent() {
  31. MyComponent myComponent = DaggerMyComponent.create();
  32. assertThat(myComponent.strings()).containsExactly("ABC", "DEF", "GHI");
  33. }

Map multibindings

只介绍最简单的方式,里面用到@IntoMap(标记依赖是要添加到Map中)和@MapKey(定义Map的key)

  1. @Module
  2. class MyModule {
  3. @Provides @IntoMap
  4. @StringKey("foo")
  5. static Long provideFooValue() {
  6. return 100L;
  7. }
  8. @Provides @IntoMap
  9. @ClassKey(Thing.class)
  10. static String provideThingValue() {
  11. return "value for Thing";
  12. }
  13. }
  14. @Component(modules = MyModule.class)
  15. interface MyComponent {
  16. Map<String, Long> longsByString();
  17. Map<Class<?>, String> stringsByClass();
  18. }
  19. @Test void testMyComponent() {
  20. MyComponent myComponent = DaggerMyComponent.create();
  21. assertThat(myComponent.longsByString().get("foo")).isEqualTo(100L);
  22. assertThat(myComponent.stringsByClass().get(Thing.class))
  23. .isEqualTo("value for Thing");
  24. }

还有其他复杂点的用法

Activities Subcomponents Multibinding

之前注入的过程基本是这样

  1. getAppCompoent().getActivityComponent2().inject(this);
  2. DaggerActivityComponent1.builder()
  3. .appComponent(getAppCompoent())
  4. .build().inject(this);

都需要获取到父Componnet的实例才能完成注入,也就是被注入的Activity需要了解注射器是如何组织的,这不符合依赖注入的核心原则:一个类不应该知道如何实现依赖注入
使用Activities Subcomponents Multibinding可以解决上述问题

先看看如何使用

step1

先定义一个ActivityInjector接口,可以用来完成对Activity的注入

  1. public interface ActivityInjector<A extends Activity> extends MembersInjector<A> {
  2. }

step2

定义一个ActivityModule,用来传入一个activity实例并作为依赖提供

  1. @Module
  2. public abstract class ActivityModule<T> {
  3. protected final T activity;
  4. public ActivityModule(T activity) {
  5. this.activity = activity;
  6. }
  7. @Provides
  8. @ActivityScope
  9. public T provideActivity() {
  10. return activity;
  11. }
  12. }

step3

定义通用的ActivityComponentBuilder接口,用来传入ActivityModule并构建ActivityInjector实例

  1. public interface ActivityComponentBuilder<M extends ActivityModule, C extends ActivityInjector> {
  2. ActivityComponentBuilder<M, C> activityModule(M activityModule);
  3. C build();
  4. }

step4

定义注入Activity的SubComponent

  1. @ActivityScope
  2. @Subcomponent(modules = ActivityComponent3.SubcomponentActivityModule.class)
  3. public interface ActivityComponent3 extends ActivityInjector<SubComponentActivity3> {
  4. @Subcomponent.Builder
  5. interface Builder extends ActivityComponentBuilder<SubcomponentActivityModule, ActivityComponent3> {
  6. }
  7. @Module
  8. class SubcomponentActivityModule extends ActivityModule<SubComponentActivity3> {
  9. public SubcomponentActivityModule(SubComponentActivity3 activity) {
  10. super(activity);
  11. }
  12. }
  13. }

step5

定义一个module,将ActivityComponentBuilder作为一个元素添加到Map中,使用Activity的类型作为MapKey

  1. @Module(subcomponents = ActivityComponent3.class)
  2. public abstract class ActivitisMultibindingModule {
  3. @Binds
  4. @IntoMap
  5. @ActivityKey(SubComponentActivity3.class)
  6. public abstract ActivityComponentBuilder
  7. bindSubComponentActivityBuilder(ActivityComponent3.Builder builder);
  8. }

step6

定义HasActivityComponentBuilder接口,传入MapKey获取ActivityComponentBuilder,这里的MapKey是Activity的Class类型

  1. public interface HasActivityComponentBuilder {
  2. ActivityComponentBuilder getActivityComponentBuilder(Class<? extends Activity> activityClass);
  3. }

step7

Application中实现HasActivityComponentBuilder接口,从注入到Application中的Map<Class<? extends Activity>, Provider<ActivityComponentBuilder>>以MapKey获取ActivityComponentBuilder

  1. public class RealApplication extends BaseApplication implements HasActivityInjector, HasActivityComponentBuilder {
  2. private static final String TAG = "RealApplication";
  3. @Inject
  4. Map<Class<? extends Activity>, Provider<ActivityComponentBuilder>> activityComponentBuilders;
  5. //只是为了拿到Application
  6. public static HasActivityComponentBuilder get(Context context) {
  7. return ((HasActivityComponentBuilder) context.getApplicationContext());
  8. }
  9. //用activityClass去找对应的ActivityComponentBuilder
  10. @Override
  11. public ActivityComponentBuilder getActivityComponentBuilder(Class<? extends Activity> activityClass) {
  12. return activityComponentBuilders.get(activityClass).get();
  13. }
  14. }

step8

在Activity中完成注入

  1. private void injectMembers() {
  2. RealApplication.get(this).getActivityComponentBuilder(SubComponentActivity3.class)
  3. .activityModule(new ActivityComponent3.SubcomponentActivityModule(this))
  4. .build()
  5. .injectMembers(this);
  6. }

个人理解的Activities Subcomponents Multibinding

实质上是将Activity的Subcomponnet/Subcomponent.Builder作为Map的value,使用时不从最底层的Component一级级的构建Component,而是用自身的Class作为key来获取Subcomponnet/Subcomponent.Builder,最终完成注入

优点

可以看到在最后一步的注入过程中,Activity只是获取Application-传入自身作为Module的参数,完成注入;整个过程都没有Componnet的身影,这就是Activities Subcomponents Multibinding的好处了
而且以上步骤不仅适用于Activity,对Fragment,View,ViewModel等都可以用同样的套路去屏蔽上层对如何完成注入的了解

缺点

当然有得有舍

  • 整个实现过程多了很多步骤
  • ActivityComponentBuilder是一个接口,导致只能将当前Activity实例作为参数,想要扩展其他参数作为依赖就没有办法了
    对于第一个缺点,仅仅为了不去了解依赖注入框架的结构就多写那么多代码,有些得不偿失,不过所幸这些都是套路,满眼都是模板代码,所以google推出了dagger.android来简化android中四大组件以及Fragment中的Subcomponents Multibinding
    下一章就来介绍dagger.android

参考资料

[Android]在Dagger 2中Activities和Subcomponents的多绑定(翻译)

相关文章

dagger2从入门到放弃-概念
dagger2从入门到放弃-最基础的用法介绍
dagger2从入门到放弃-Component的继承体系、局部单例
dagger2从入门到放弃-ActivityMultibindings
dagger2从入门到放弃-dagger.android
dagger2从入门到放弃-其他用法
dagger2从入门到放弃-多模块项目下dagger的使用
dagger2从入门到放弃-为何放弃

示例代码

DaggerInAction 欢迎star master分支上最新的代码可能会比当前文章的示例代码稍微复杂点,提交记录里包含了每一步的迭代过程,可以顺藤摸瓜