概述
基础用法里讲了 Dagger 的一些基础用法,但实际项目开发中很多不会这么使用,比如在 Android 实际开发中,我们需要用到许多 Activity 与 Fragment, 当进行依赖注入时都需要进行许多重复性的操作,比如在 @Component
的组件中加入 inject 方法,并在对应的 Activity 或 Fragment 等中调用注入的方法。于是 Android 与 Dagger 团队一起为这些场景提供了更简便的实现方式,这在实际开发中更有实用价值。
另外这篇文章还会学习一下 Dagger 中一些注解的用法。
@Binds 之前讲过可以使用 @Module 和 @Provides 注解提供依赖项,当我们需要提供的依赖项是一个接口时,可以使用 @Binds 注解:
1 2 3 4 5 @Module interface TmpModule { @Binds fun provideOperate (impl: OperateImpl ) : IOperate }
注意这种方式上面的 OperateImpl 需要使用 @Inject 注解或者 @Module + @Provides 来告知 Dagger 如何提供依赖。当然也可以直接这样:
1 2 3 4 5 6 7 @Module class TmpModule { @Provides fun provideOperate(): IOperate { return OperateImpl() } }
@IntoSet 可以在 Module 中定义多个返回值类型相同的方法,用来提供 Set 集合的依赖项。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Module class TmpModule { @Provides @IntoSet fun provide1 () : String { return "A" } @Provides @IntoSet fun provide2 () : String { return "B" } }
接着可以使用:
1 2 3 4 5 6 7 8 9 10 class LoginActivity : AppCompatActivity () { @Inject lateinit var set : MutableSet<String> override fun onCreate (savedInstanceState: Bundle ?) { DaggerAppComponent.create().inject(this ) super .onCreate(savedInstanceState) println("Login: $set " ) } }
@IntoMap 跟 @IntoSet 类似,不过它用来给 Map 提供依赖项。
1 2 3 4 5 6 7 8 9 10 11 12 @Module class TmpModule { @Provides @IntoMap @IntKey(1) fun provide1 () : String = "A" @Provides @IntoMap @IntKey(2) fun provide2 () : String = "B" }
Dagger 提供了许多基础类型的 Key 注解,如 @IntKey, @StringKey 等,另外还可以自定义 Key 注解:
1 2 3 @Target(AnnotationTarget.FUNCTION) @MapKey annotation class GameKey (val value: KClass<out Game>)
AndroidInjector 注入Activity 可以用来在 Android 中简化注入操作,在使用前需要添加依赖:
1 2 3 4 5 6 dependencies { implementation 'com.google.dagger:dagger-android:2.x' implementation 'com.google.dagger:dagger-android-support:2.x' kapt 'com.google.dagger:dagger-android-processor:2.x' kapt 'com.google.dagger:dagger-compiler:2.x' }
1、首先把 AndroidInjectionModule 模块加入应用组件 ApplicationComponent 中,确保必要的基础类型可用(ensure that all bindings necessary for these base types are available
)。
2、创建一个继承自 AndroidInjector 的 SubComponent 子组件。
1 2 3 4 5 @Subcomponent interface LoginSubComponent : AndroidInjector <LoginActivity > { @Subcomponent .Factory interface Factory : AndroidInjector.Factory <LoginActivity? > }
3、接着将上面的子组件加入到应用组件图中。
1 2 3 4 5 6 7 8 9 10 11 12 @Module(subcomponents = [LoginSubComponent::class]) interface LoginModule { @Binds @IntoMap @ClassKey(LoginActivity::class) fun bindLoginAndroidInjectorFactory (factory: LoginSubComponent .Factory ?) : AndroidInjector.Factory<*>? } @Component(modules = [AndroidInjectionModule::class, LoginModule::class]) interface AppComponent { fun inject (application: MainApplication ) }
注意:这里每次增加 Activity 或 Fragment 等时都需要创建对应的 XXXSubComponent 和 XXXModule 类,可以通过使用 @ContributesAndroidInjector 注解来避免这种重复性的操作 。
于是可以将第二、三步改为:
1 2 3 4 5 6 7 8 9 10 11 @Module interface ActivityModule { @ContributesAndroidInjector(modules = [/* 需要引用的module */]) fun injectLoginActivity () : LoginActivity } @Component(modules = [AndroidInjectionModule::class, ActivityModule::class]) interface AppComponent { fun inject (application: MainApplication ) }
这样每次增加需要注入的 Activity 时只需要在 ActivityModule 里面增加对应的方法即可,其实就是 Dagger 通过这个注解为我们在编译期间生成了上面的重复模版类代码 。
看看 Dagger 为我们生成的代码:
1 2 3 4 5 6 @Module interface ActivityModule { @ActivityScope @ContributesAndroidInjector(modules = [TmpModule::class]) fun injectLoginActivity () : LoginActivity }
生成的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Module(subcomponents = ActivityModule_InjectLoginActivity.LoginActivitySubcomponent.class) public abstract class ActivityModule_InjectLoginActivity { private ActivityModule_InjectLoginActivity () {} @Binds @IntoMap @ClassKey(LoginActivity.class) abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory( LoginActivitySubcomponent.Factory builder); @Subcomponent(modules = TmpModule.class) @ActivityScope public interface LoginActivitySubcomponent extends AndroidInjector <LoginActivity > { @Subcomponent .Factory interface Factory extends AndroidInjector .Factory <LoginActivity > {} } }
4、让 Application 实现 HasAndroidInjector 接口,并注入一个 DispatchingAndroidInjector 实例:
1 2 3 4 5 6 7 8 9 10 11 12 class MainApplication : Application (), HasAndroidInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> override fun androidInjector () : AndroidInjector<Any> = dispatchingAndroidInjector override fun onCreate () { super .onCreate() DaggerAppComponent.create().inject(this ) } }
5、最后在 Activity 中调用 AndroidInjection.inject(this)
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class LoginInfo @Inject constructor () { override fun toString () : String { return "LoginInfo" } } class LoginActivity : AppCompatActivity () { @Inject lateinit var loginInfo: LoginInfo override fun onCreate (savedInstanceState: Bundle ?) { AndroidInjection.inject(this ) super .onCreate(savedInstanceState) println("loginInfo: $loginInfo " ) } }
注入Fragment 跟注入 Activity 类似,上面的 Key 换成了 Fragment, 不一样的地方在于 AndroidInjection.inject(this)
需要放到 onAttach 中执行。
另外,可以选择将我们的 Fragment Component 作为另一个 Fragment/Activity Component 的子组件,也可以跟上面 Activity 一样作为应用组件的子组件,这取决于我们的业务需要。
下面直接使用官方文档的例子说明当 Fragment 绑定到 YourActivitySubcomponent 的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class YourActivity extends Activity implements HasAndroidInjector { @Inject DispatchingAndroidInjector<Object> androidInjector; @Override public void onCreate (Bundle savedInstanceState) { AndroidInjection.inject(this ); super .onCreate(savedInstanceState); } @Override public AndroidInjector<Object> androidInjector () { return androidInjector; } } public class YourFragment extends Fragment { @Inject SomeDependency someDep; @Override public void onAttach (Activity activity) { AndroidInjection.inject(this ); super .onAttach(activity); } } @Subcomponent(modules = ...) public interface YourFragmentSubcomponent extends AndroidInjector <YourFragment > { @Subcomponent .Factory public interface Factory extends AndroidInjector .Factory <YourFragment > {} } @Module(subcomponents = YourFragmentSubcomponent.class) abstract class YourFragmentModule { @Binds @IntoMap @ClassKey(YourFragment.class) abstract AndroidInjector.Factory<?> bindYourFragmentInjectorFactory(YourFragmentSubcomponent.Factory factory); } @Subcomponent(modules = { YourFragmentModule.class, ... } public interface YourActivityOrYourApplicationComponent { ... }
工作原理
AndroidInjection.inject()
会从 Application 中获取到 DispatchingAndroidInjector 对象并调用其 DispatchingAndroidInjector.inject(activity)
方法。比较简单,不贴代码了。
DispatchingAndroidInjector.inject(activity)
方法会根据 activity 来寻找对应的 AndroidInjector.Factory(即 LoginSubComponent.Factory)。还记得刚才的 bindLoginAndroidInjectorFactory 方法不?它通过 @IntoMap 注解将 LoginActivity 作为 key, LoginSubComponent.Factory 作为 value, Dagger 会将这条记录存入 map 中,DispatchingAndroidInjector.inject(activity)
便是在这个 map 中找到 LoginSubComponent.Factory 的。贴出关键代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public final class DispatchingAndroidInjector <T > implements AndroidInjector <T > { @Override public void inject (T instance) { boolean wasInjected = maybeInject(instance); } public boolean maybeInject (T instance) { Provider<AndroidInjector.Factory<?>> factoryProvider = injectorFactories.get(instance.getClass().getName()); AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get(); AndroidInjector<T> injector = factory.create(instance); injector.inject(instance); return true ; } }
接着通过上面找到的 Factory 来创建 AndroidInjector(即 LoginSubComponent) 实例并调用其 inject 方法,该方法内部会对 LoginActivity 执行注入。
1 2 3 4 5 6 7 8 9 10 11 12 private final class LoginSubComponentImpl implements LoginSubComponent { @Override public void inject (LoginActivity arg0) { injectLoginActivity(arg0); } private LoginActivity injectLoginActivity (LoginActivity instance) { LoginActivity_MembersInjector.injectLoginInfo(instance, new LoginInfo()); return instance; } }
基础类型 Dagger 提供了一些基础类型诸如 DaggerApplication , DaggerActivity , DaggerFragment , DaggerService , DaggerBroadcastReceiver , DaggerContentProvider 等,可以直接继承它们来实现上面的那些行为,具体使用直接看它们的文档或者源码即可,比较简单。
总结 这篇文章介绍了几个常用的 Dagger 注解,以及 AndroidInjector 的用法,这可以减少写一些重复性代码,在实际开发中比较有帮助。Dagger 的用法看起来比较复杂,主要是太多注解了,其实它的原理还是比较简单的,通过注解处理器生成一系列的注入代码,主要还是多用用吧,习惯就好~
Dagger 的更多用法参考官方文档: https://dagger.dev/dev-guide/android