Dagger 2 完全解析(四),在Android中的使用
[Dagger 2 完全解析(一),基本使用与原理
Dagger 2 完全解析(二), 进阶使用
Dagger 2 完全解析(三), Component 与 SubComponent
Dagger 2 完全解析(四),在Android中的使用
本系列文章是基于 Google Dagger 2.23.2 版本, Kotlin 1.3.21版本
本文:https://blog.csdn.net/xiaowu_zhu/article/details/95933256
在Android项目中使用Dagger2时,像 Activity
和Fragment
这种类型的初始化操作都是有Android系统提供的,如果要往其注入对象,不免会有这样的写法:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var activity: MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerMainActivityComponent.builder()
.mainActivityModule(MainActivityModule(this))
.appComponent(
(application as MyApplication)
.daggerAppComponent
).build()
.inject(this)
}
}
上述写法,会有以下几个我们几乎无法避免的问题:
-
上述Dagger部分的代码存在模板代码,在每个Activity中都会这么写到,即便抽取到BaseActivity中,也有一些特殊部分需要在每个Activity中单独处理,随着业务的增加后期维护不易;
-
从上面的代码可以看出,
MainActivityModule
中持有了MainActivity
实例,并且需要在MainActivity
传入参数,它打破了依赖注入的核心原则:类不应该知道它是如何注入的。
因此为了解决上面的问题,谷歌官方推出了dagger.android
。
下面是基本的使用方式,包括如何注入Activity
和Fragment
,本篇只讲如何使用,后续再分析其原理。
引入Dagger.android
依赖
在build.gradle
中添加:
implementation 'com.google.dagger:dagger-android:2.23.2'
implementation 'com.google.dagger:dagger-android-support:2.23.2'
kapt 'com.google.dagger:dagger-android-processor:2.23.2'
注入Activity
以MainActivity
为例
Dagger2中的写法
class MainActivity : AppCompatActivity() {
// 这里只是做了一个例子
@Inject
lateinit var activity: MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
DaggerMainActivityComponent.builder()
.mainActivityModule(MainActivityModule(this))
.appComponent(
(application as MyApplication)
.daggerAppComponent
).build()
.inject(this)
}
}
@ActivityScope
@Component(modules = [MainActivityModule::class], dependencies = [AppComponent::class])
interface MainActivityComponent {
fun inject(activity: MainActivity)
}
@Module
class MainActivityModule(private val activity: MainActivity) {
@Provides
fun provideMainActivity(): MainActivity = activity
}
@Scope
annotation class ActivityScope
Dagger2-Android中的写法
修改MainActivityComponent
@ActivityScope
@Subcomponent
interface MainActivityComponent : AndroidInjector<MainActivity> {
// 声明MainActivity创建的工厂接口
@dagger.Subcomponent.Factory
interface Factory : AndroidInjector.Factory<MainActivity>
}
修改MainActivityModule
@Module(subcomponents = [MainActivityComponent::class])
abstract class MainActivityModule {
// module中提供绑定工厂方法
@Binds
@IntoMap
@ClassKey(MainActivity::class)
abstract fun bind(
factory: MainActivityComponent.Factory
): AndroidInjector.Factory<*>
}
修改onCreate()
中的Dagger注入
override fun onCreate(savedInstanceState: Bundle?) {
// 在super.onCreate前添加
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
修改MyApplication
通过dagger.android
注入时,Application中也发生了相应的变化
class MyApplication : Application(), HasAndroidInjector {
@Inject
lateinit var dispatchingAndroidInjector: DqispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
DaggerAppComponent.factory().create(this).inject(this)
}
override fun androidInjector(): AndroidInjector<Any> {
return dispatchingAndroidInjector
}
}
@dagger.Component(
modules = [AndroidInjectionModule::class,// 引入框架提供的InjectionModule
AndroidSupportInjectionModule::class,// 如果使用了support相关的类,也需要引入
MainActivityModule::class // 这是我们自定义的Module
]
)
interface AppComponent : AndroidInjector<MyApplication> {
@dagger.Component.Factory
interface Factory : AndroidInjector.Factory<MyApplication>
}
我们需要将我们在MainActivity
中的MainActivityModule
加入到Application
中的AppComponent
的module
,然后Make Project
或Make App
或build project
,如果未报错即成功。
简洁写法
如果相应的ActivityComponent
中只有以下操作时:
@ActivityScope
@Subcomponent
interface MainActivityComponent : AndroidInjector<MainActivity> {
// 声明MainActivity创建的工厂接口
@dagger.Subcomponent.Factory
interface Factory : AndroidInjector.Factory<MainActivity>
}
我们可以新建一个module类, 将满足以上条件的Component 集中到一起,并删除对应的Component,如:
@Module
abstract class ActivityBindingModule {
@ActivityScoped
@ContributesAndroidInjector(modules = [MainActivityModule.class])
abstract fun mainActivity() : MainActivity
@ActivityScoped
@ContributesAndroidInjector(modules = [AddEditTaskModule.class])
abstract fun addEditTaskActivity(): AddEditTaskActivity
}
删除 原有module中的bind*
方法
@Module
public class MainActivityModule{
}
注入Fragment
注入Fragment
与注入Activity
类似,唯一不同的地方在于我们需要在onAttach
方法中执行AndroidSupportInject.inject(this)
class BlankFragment : Fragment() {
override fun onAttach(context: Context?) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
}
}
@dagger.Module
abstract class FragmentBindModule {
@ContributesAndroidInjector
abstract fun blankFragment(): BlankFragment
}
@dagger.Component(
modules = [AndroidInjectionModule::class,
AndroidSupportInjectionModule::class,
ActivityBinder::class,
FragmentBindModule::class
]
)
interface AppComponent : AndroidInjector<MyApplication> {
@dagger.Component.Factory
interface Factory : AndroidInjector.Factory<MyApplication>
}
对于FragmentBindModule
不仅可以放入到AppComponent
中,也可以放入到MainActivityComponent
,或者FragmentComponent
如:
@dagger.Subcomponent(modules = [FragmentBindModule::class])
interface MainActivityComponent : AndroidInjector<MainActivity>{
@dagger.Subcomponent.Factory
interface Factory: AndroidInjector.Factory<MainActivity>
}
@dagger.Module(subcomponents = [
MainActivityComponent::class // 将fragment放入到了activity对应的Component
])
abstract class MainActivityModule{
@Binds
@IntoMap
@ClassKey(MainActivity::class)
abstract fun bind(factory:MainActivityComponent.Factory):AndroidInjector.Factory<*>
}
一些问题
@Model 注解的是抽象类时 @provides 标注的必须是静态方法
A @Module may not contain both non-static @Provides methods and abstract @Binds or @Multibinds declarations
这个错误提示是将Module
定义成了抽象类,这在java
中只需要将@Provide
标注的方法设置为静态方法即可,但是在Kotlin
中是行不通的,因为在kotlin
中静态方法是写在companion object
代码块内,所以解决方法有两种:
-
用
Module
标注companion object
@dagger.Module abstract class MainActivityModule() { @dagger.Module companion object { @JvmStatic @Provides fun provideFragment() = BlankFragment() } @Binds abstract fun context(activity: MainActivity): Context }
-
使用多个
Module
将抽象方法和@provide
标注的方法分开,如:@Module abstract class MainActivityBindsModule { @Binds abstract fun context(activity: MainActivity): Context } @Module(includes = arrayOf(MainActivityBindsModule ::class)) class MainActivityProvidesModule { @Provides fun provideFragment() = BlankFragment() }
总结
通过前面的例子我们亦可以看出:
- Dagger2-android 使用的是Dagger2中的继承关系;
- 使用它,我们就可以不用写那么多的模版代码,相对于
Dagger2
方便了许多; - Dagger2-Android 比Dagger2更强大,我们使用
@Binds
绑定抽象方法来注入一些提供初始化的类等。