dagger2從入門到放棄-ActivityMultibindings
前面文章中講到Componnet繼承和依賴的三種方式時說到了在父Componnet中Module中指定SubComponent,這種方式主要用來實現Activity-Multibindings,現在就來了解下什麼是Activity-Multibindings
先說說什麼是Multibindings
簡單的說就是將多個物件放到一個集合中,讓依賴需求方可以通過一個集合找到需要的依賴而不用繫結具體的依賴
Set multibindings
在@Module中使用 @IntoSet或者 @ElementsIntoSet註解
@Module class MyModuleA { //提供一個元素 @Provides @IntoSet static String provideOneString(DepA depA, DepB depB) { return "ABC"; } } @Module class MyModuleB { //一次提供多個元素 @Provides @ElementsIntoSet static Set<String> provideSomeStrings(DepA depA, DepB depB) { return new HashSet<String>(Arrays.asList("DEF", "GHI")); } } //dagger可以提供一個Set<String> class Bar { @Inject Bar(Set<String> strings) { assert strings.contains("ABC"); assert strings.contains("DEF"); assert strings.contains("GHI"); } } //或者從Componnet中獲取Set<String> @Component(modules = {MyModuleA.class, MyModuleB.class}) interface MyComponent { Set<String> strings(); } @Test void testMyComponent() { MyComponent myComponent = DaggerMyComponent.create(); assertThat(myComponent.strings()).containsExactly("ABC", "DEF", "GHI"); }
Map multibindings
只介紹最簡單的方式,裡面用到@IntoMap(標記依賴是要新增到Map中)和@MapKey(定義Map的key)
@Module class MyModule { @Provides @IntoMap @StringKey("foo") static Long provideFooValue() { return 100L; } @Provides @IntoMap @ClassKey(Thing.class) static String provideThingValue() { return "value for Thing"; } } @Component(modules = MyModule.class) interface MyComponent { Map<String, Long> longsByString(); Map<Class<?>, String> stringsByClass(); } @Test void testMyComponent() { MyComponent myComponent = DaggerMyComponent.create(); assertThat(myComponent.longsByString().get("foo")).isEqualTo(100L); assertThat(myComponent.stringsByClass().get(Thing.class)) .isEqualTo("value for Thing"); }
還有其他複雜點的用法
- 使用enum作為MapKey
- 使用複雜的MapKey
- 將set轉換為map的用法
- SubComponent將元素繫結到父Componnet中的集合中
ofollow,noindex">具體見官方文件multibindings
Activities Subcomponents Multibinding
之前注入的過程基本是這樣
getAppCompoent().getActivityComponent2().inject(this); DaggerActivityComponent1.builder() .appComponent(getAppCompoent()) .build().inject(this);
都需要獲取到父Componnet的例項才能完成注入,也就是被注入的Activity需要了解注射器是如何組織的,這不符合依賴注入的核心原則:一個類不應該知道如何實現依賴注入
使用Activities Subcomponents Multibinding可以解決上述問題
先看看如何使用
step1
先定義一個ActivityInjector介面,可以用來完成對Activity的注入
public interface ActivityInjector<A extends Activity> extends MembersInjector<A> { }
step2
定義一個ActivityModule,用來傳入一個activity例項並作為依賴提供
@Module public abstract class ActivityModule<T> { protected final T activity; public ActivityModule(T activity) { this.activity = activity; } @Provides @ActivityScope public T provideActivity() { return activity; } }
step3
定義通用的ActivityComponentBuilder介面,用來傳入ActivityModule並構建ActivityInjector例項
public interface ActivityComponentBuilder<M extends ActivityModule, C extends ActivityInjector> { ActivityComponentBuilder<M, C> activityModule(M activityModule); C build(); }
step4
定義注入Activity的SubComponent
@ActivityScope @Subcomponent(modules = ActivityComponent3.SubcomponentActivityModule.class) public interface ActivityComponent3 extends ActivityInjector<SubComponentActivity3> { @Subcomponent.Builder interface Builder extends ActivityComponentBuilder<SubcomponentActivityModule, ActivityComponent3> { } @Module class SubcomponentActivityModule extends ActivityModule<SubComponentActivity3> { public SubcomponentActivityModule(SubComponentActivity3 activity) { super(activity); } } }
step5
定義一個module,將ActivityComponentBuilder作為一個元素新增到Map中,使用Activity的型別作為MapKey
@Module(subcomponents = ActivityComponent3.class) public abstract class ActivitisMultibindingModule { @Binds @IntoMap @ActivityKey(SubComponentActivity3.class) public abstract ActivityComponentBuilder bindSubComponentActivityBuilder(ActivityComponent3.Builder builder); }
step6
定義HasActivityComponentBuilder介面,傳入MapKey獲取ActivityComponentBuilder,這裡的MapKey是Activity的Class型別
public interface HasActivityComponentBuilder { ActivityComponentBuilder getActivityComponentBuilder(Class<? extends Activity> activityClass); }
step7
Application中實現HasActivityComponentBuilder介面,從注入到Application中的Map<Class<? extends Activity>, Provider<ActivityComponentBuilder>>
以MapKey獲取ActivityComponentBuilder
public class RealApplication extends BaseApplication implements HasActivityInjector, HasActivityComponentBuilder { private static final String TAG = "RealApplication"; @Inject Map<Class<? extends Activity>, Provider<ActivityComponentBuilder>> activityComponentBuilders; //只是為了拿到Application public static HasActivityComponentBuilder get(Context context) { return ((HasActivityComponentBuilder) context.getApplicationContext()); } //用activityClass去找對應的ActivityComponentBuilder @Override public ActivityComponentBuilder getActivityComponentBuilder(Class<? extends Activity> activityClass) { return activityComponentBuilders.get(activityClass).get(); } }
step8
在Activity中完成注入
private void injectMembers() { RealApplication.get(this).getActivityComponentBuilder(SubComponentActivity3.class) .activityModule(new ActivityComponent3.SubcomponentActivityModule(this)) .build() .injectMembers(this); }
個人理解的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