dagger拥有两种方式注入对象,一种是利用@Inject注解构造函数
在moudle的gradle中做如下配置,我在2.30.1版本中尝试使用构造函数注入,发现怎么编译都会报错,结果最后使用最新版本就可以了:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
dependencies {
...
implementation "com.google.dagger:dagger:2.40.4"
kapt "com.google.dagger:dagger-compiler:2.40.4"
}
这边定义两个类,分别代表本地和远程的数据源,并对构造函数使用@Inject注解
/**
* 模拟本地数据源
*/
class LocalDataSource @Inject constructor()
/**
* 模拟远程数据源
*/
class RemoteDataSource @Inject constructor()
定义包装类DataSource,包含上面两个类,同样对构造函数使用@Inject注解
/**
* 数据源包装类
* Created by aruba on 2021/12/4.
*/
data class DataSource @Inject constructor(
var remoteDataSource: RemoteDataSource,
var localDataSource: LocalDataSource
)
对于需要注入的对象,dagger并不是直接注入,而是需要一个中间件去注入他们,使用代理模式的思想,这样的好处是方便管理和控制
/**
* 注入中间件
* Created by aruba on 2021/12/4.
*/
@Component
interface ApplicationComponent {
/**
* 表示注入到 MainActivity中
*/
fun inject(activity: MainActivity)
}
build下项目后,dagger会对应生成一个DaggerApplicationComponent类,通过上篇文章,我们知道用的是APT技术
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource: DataSource
...
调用生成类DaggerApplicationComponent的注入方法
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource: DataSource
override fun onCreate(savedInstanceState: Bundle?) {
DaggerApplicationComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("aruba_log", dataSource.toString())
}
}
运行后日志: 2021-12-04 /com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@1ae5a6b, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@8b49c8)
可以看到dagger帮我们自动生成注入对象了,并且我们在使用的地方不需要关注它是如何生成的
第二种方式就是模块注入,构造函数注入是以类为对象,模块注入则是以方法为对象 接下来尝试使用网络请求,以获取百度的首页HTML
dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"
}
别忘了在Manifest.xml中添加权限
interface BaiduApiService {
@GET("/index.html")
fun index(): Call<String>
}
/**
* 表示一个网络模块
* Created by aruba on 2021/12/4.
*/
@Module
class NetworkModule {
}
@Module
class NetworkModule {
@Provides
fun getBaiduApiService(): BaiduApiService {
return Retrofit.Builder()
.baseUrl("https://www.baidu.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build().create(BaiduApiService::class.java)
}
}
/**
* 注入中间件
* Created by aruba on 2021/12/4.
*/
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
/**
* 表示注入到 MainActivity中
*/
fun inject(activity: MainActivity)
}
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource: DataSource
/**
* BaiduApiService注入
*/
@Inject
lateinit var baiduApiService: BaiduApiService
override fun onCreate(savedInstanceState: Bundle?) {
DaggerApplicationComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("aruba_log", dataSource.toString())
getIndex()
}
/**
* 获取百度首页
*/
private fun getIndex() {
baiduApiService.index().enqueue(object : Callback<String> {
override fun onResponse(call: Call<String>, response: Response<String>) {
findViewById<TextView>(R.id.tv_hello).text = response.body()
}
override fun onFailure(call: Call<String>, t: Throwable) {
}
})
}
}
效果:
模块注入同样也实现了自动注入对象,并且这种方式可读性和可维护性更高
通过上面两种方式,我们知道了如何注入对象,但是我们并不知道注入的对象的生命周期,有时我们希望获取的对象是一个单例,这种情况仅仅使用注入是无法实现的
下面例子,通过注入两个相同类型对象,查看它们是否是同一份实例 在MainActivity中同时注入两个DataSource对象,并通过打印日志,观测结果
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource1: DataSource
@Inject
lateinit var dataSource2: DataSource
override fun onCreate(savedInstanceState: Bundle?) {
DaggerApplicationComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("aruba_log", dataSource1.toString())
Log.i("aruba_log", dataSource2.toString())
//判断是否是一个对象
Log.i("aruba_log", "${dataSource2 === dataSource1}")
}
}
日志打印: 2021-12-04/com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@137fdbe, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@31b1e1f) 2021-12-04/com.aruba.daggerapplication I/aruba_log: DataSource(remoteDataSource=com.aruba.daggerapplication.di.datasource.RemoteDataSource@b3756c, localDataSource=com.aruba.daggerapplication.di.datasource.LocalDataSource@7b81735) 2021-12-04/com.aruba.daggerapplication I/aruba_log: false
结果显示这两个对象不是同一个实例
在使用构造注入或Module注入时,一旦使用了作用域注解,其Component也要使用相同的作用域注解,否则编译会报错。同一个Component实例在注入对象时,一旦发现注入方式使用了作用域,那么它们注入的对象将会是同一份实例
@Singleton注解为dagger默认提供的一个作用域注解。定义一个构造函数注入方式,并使用该注解
/**
* 表示该注入的对象的作用域为Singleton
* Created by aruba on 2021/12/4.
*/
@Singleton
class SingletonTest @Inject constructor()
在Component中,使用相同作用域,并且我重新定义了一个ScopeActivity来测试结果
@Singleton
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
/**
* 表示注入到 MainActivity中
*/
fun inject(activity: MainActivity)
/**
* 表示注入到 ScopeActivity中
*/
fun inject(activity: ScopeActivity)
}
ScopeActivity中需要注入两个SingletonTest 对象,并打印是否为同一个实例
class ScopeActivity : AppCompatActivity() {
@Inject
lateinit var singleton1: SingletonTest
@Inject
lateinit var singleton2: SingletonTest
override fun onCreate(savedInstanceState: Bundle?) {
DaggerApplicationComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_scope)
Log.i("aruba_log", "singleton1 hashcode: ${singleton1.hashCode()}")
Log.i("aruba_log", "singleton2 hashcode: ${singleton2.hashCode()}")
}
}
日志结果: 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 246939604 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 246939604
结果显示,这两个对象是同一份实例
@Scope
@Retention(AnnotationRetention.SOURCE)
annotation class MyScope
将上面的作用域注解替换成MyScope
@MyScope
class SingletonTest @Inject constructor()
@MyScope
@Component(modules = [NetworkModule::class])
interface ApplicationComponent {
/**
* 表示注入到 MainActivity中
*/
fun inject(activity: MainActivity)
/**
* 表示注入到 ScopeActivity中
*/
fun inject(activity: ScopeActivity)
}
日志结果: 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 246939604 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 246939604
和使用@Singleton是相同的效果
模块注入方式,使用作用域注解在方法上:
@Module
class NetworkModule {
@MyScope
@Provides
fun getBaiduApiService(): BaiduApiService {
return Retrofit.Builder()
.baseUrl("https://www.baidu.com")
.addConverterFactory(ScalarsConverterFactory.create())
.build().create(BaiduApiService::class.java)
}
}
即使用了同一个作用域,不同的Component实例进行注入,最后生成的对象还是不同的实例,即作用域管理的生命周期是跟随Component的。但一般情况下,我们一个APP只需要一份Component实例,而一个App中,往往有着不同的作用域
在MainActivity中,也定义注入一个SingleTest对象,注意每调用一次DaggerApplicationComponent.create(),会创新一个新的DaggerApplicationComponent对象
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource1: DataSource
@Inject
lateinit var dataSource2: DataSource
/**
* BaiduApiService注入
*/
@Inject
lateinit var baiduApiService: BaiduApiService
@Inject
lateinit var singleton: SingletonTest
override fun onCreate(savedInstanceState: Bundle?) {
// 每次create,创建一个新的DaggerApplicationComponent
DaggerApplicationComponent.create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("aruba_log", "MainActivity singleton hashcode: ${singleton.hashCode()}")
findViewById<TextView>(R.id.tv_hello).setOnClickListener {
startActivity(Intent(this@MainActivity, ScopeActivity::class.java))
}
}
}
跳转到ScopeActivity后,我查看下打印结果: 2021-12-04/com.aruba.daggerapplication I/aruba_log: MainActivity singleton hashcode: 20446654 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton1 hashcode: 127836367 2021-12-04/com.aruba.daggerapplication I/aruba_log: singleton2 hashcode: 127836367
MainActivity和ScopeActivity中都调用了Component的create方法,所以两份Component实例注入的对象是不同的实例
如果想要一个Component下使用不同的作用域,Component是不支持的,但Subcomponent可以使用,Subcomponent又可以被添加到Component中
@Scope
@Retention(AnnotationRetention.SOURCE)
annotation class SubScope
@SubScope
class SubObject @Inject constructor()
@SubScope
@Subcomponent
interface SubComponent {
}
同时在子组件中提供注入方法,这边我新建了一个SubActivity
@SubScope
@Subcomponent
interface SubComponent {
@Subcomponent.Factory
interface Factory {
fun create(): SubComponent
}
fun inject(activity: SubActivity)
}
在Moudle注解中,指定使用哪个子组件
@Module(subcomponents = [SubComponent::class])
class SubcomponentModule
@MyScope
@Component(modules = [NetworkModule::class, SubcomponentModule::class])
interface ApplicationComponent {
/**
* 表示注入到 MainActivity中
*/
fun inject(activity: MainActivity)
/**
* 表示注入到 ScopeActivity中
*/
fun inject(activity: ScopeActivity)
/**
* 提供获取创建子组件的Factory
*/
fun subComponent(): SubComponent.Factory
}
object Component {
val daggerApplicationComponent by lazy { DaggerApplicationComponent.create() }
}
MainActivity中注入打印,并跳转到SubActivity
class MainActivity : AppCompatActivity() {
/**
* 表示该对象需要自动注入
*/
@Inject
lateinit var dataSource1: DataSource
@Inject
lateinit var dataSource2: DataSource
/**
* BaiduApiService注入
*/
@Inject
lateinit var baiduApiService: BaiduApiService
@Inject
lateinit var singleton: SingletonTest
override fun onCreate(savedInstanceState: Bundle?) {
// DaggerApplicationComponent.create().inject(this)
Component.daggerApplicationComponent.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i("aruba_log", "MainActivity singleton hashcode: ${singleton.hashCode()}")
findViewById<TextView>(R.id.tv_hello).setOnClickListener {
startActivity(Intent(this@MainActivity, SubActivity::class.java))
}
}
}
SubActivity中创建子组件,并注入
class SubActivity : AppCompatActivity() {
@Inject
lateinit var singleton: SingletonTest
@Inject
lateinit var subObject1: SubObject
@Inject
lateinit var subObject2: SubObject
override fun onCreate(savedInstanceState: Bundle?) {
Component.daggerApplicationComponent.subComponent().create().inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_sub)
Log.i("aruba_log", "SubActivity singleton hashcode: ${singleton.hashCode()}")
Log.i("aruba_log", "SubActivity subObject1 hashcode: ${subObject1.hashCode()}")
Log.i("aruba_log", "SubActivity subObject2 hashcode: ${subObject2.hashCode()}")
}
}
日志结果: 2021-12-04/com.aruba.daggerapplication I/aruba_log: MainActivity singleton hashcode: 20446654 2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity singleton hashcode: 127836367 2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity subObject1 hashcode: 44284508 2021-12-04/com.aruba.daggerapplication I/aruba_log: SubActivity subObject2 hashcode: 27693413
前两条再次证实了不同Component实例,作用域不起作用,后面两条说明我们成功的注入了子组件生成的对象
最后,附上一张dagger的结构图: