Hilt
优点
- 重用代码
- 易于重构
- 易于测试
缺点
-
错误信息比较难理解
-
上手难度非常高
构建并验证依赖关系图,确保没有未满足的依赖关系且没有依赖循环。
集成配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
ext.hilt_version = "2.38.1"
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// 所有模块都要添加下面的 plugin 和 引用和 compiler
plugins {
id 'dagger.hilt.android.plugin'
}
implementation "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
// When using Kotlin.
kapt 'androidx.hilt:hilt-compiler:1.0.0'
Activity 需要向ViewModel传参Id
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class TwoViewModel
@AssistedInject constructor(@Assisted val id: String, okHttpClient: OkHttpClient) :
BaseViewModel(AppHolder.app) {
@dagger.assisted.AssistedFactory
interface AssistedFactory {
fun create(pokemonName: String): TwoViewModel
}
companion object {
fun provideFactory(
assistedFactory: AssistedFactory,
id: String
): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return assistedFactory.create(id) as T// 这里 create 每次都是创建不同的对象
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
@AndroidEntryPoint
class TwoActivity : BaseMvvmActivity<ActivityTwoBinding, TwoViewModel>() {
val id = "123"
@Inject
lateinit var factory: TwoViewModel.AssistedFactory
override fun getViewModelFactory(): ViewModelProvider.Factory? {
return TwoViewModel.provideFactory(factory, id)
}
}
这种方式实质不是单独为 ViewModel 所提供的,为的是一些构造数据里面有String 等非特定类型参数的时候用 Assisted 来解决,这种方式由于有变动参数,所以无法设置 Scope ,AssistedFactory 可以设定 Scope 。
Inject 接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface AnalyticsService {
fun analyticsMethods()
}
class AnalyticsServiceImpl @Inject constructor():AnalyticsService {
override fun analyticsMethods() {
Log.e("analyticsMethods","analyticsMethods")
}
}
@Module
@InstallIn(ActivityComponent::class)
abstract class AnalyticsModule {
//@Binds
//abstract fun bindAnalyticsService(analyticsServiceImpl: AnalyticsServiceImpl): AnalyticsService
@Binds
abstract fun bindAnalyticsService2(analyticsServiceImpl: AnalyticsServiceImpl2): AnalyticsService
}
注解
component
组件 | 作用域 | 父组件 |
---|---|---|
ActivityComponent | ActivityScoped | ActivityRetainedComponent |
ActivityRetainedComponent | ActivityRetainedScoped | SingletonComponent |
FragmentComponent | FragmentScoped | ActivityComponent |
ServiceComponent | ServiceScoped | SingletonComponent |
ViewComponent | ViewScoped | ActivityComponent |
ViewModelComponent | ViewModelScoped | ActivityRetainedComponent |
ViewWithFragmentComponent | ViewScoped | FragmentComponent |
SingletonComponent | Singleton | 无 |
在 Hilt 中 scoped 和 component 是一一绑定的,例如:如果 module 装载到 SingletonComponent 上,那么其内部就只能使用 Singleton 作用域
scopes
在不使用作用域的情况下,每个对象的注入都是通过调用 Providers 方法或者 Inject 构造方法获取到的
在使用作用域的情况下,对象的生命周期和所在的 Component 生命周期相同;
作用域首先要指明在哪个 Component 上,然后才能在其 module 上使用。
- ActivityRetainedScoped
- ActivityScoped
- FragmentScoped
- ServiceScoped
- ViewModelScoped
- ViewScoped
- Singleton
作用域的实现原理
Component 会有一个 xxxProvider 提供作用域注解的对象,这个 xxxProvider 是在 Component 是在构造方法中进行初始化的,其类型为 DoubleCheck ,DoubleCheck作用是,同一个 DoubleCheck 获取的其包裹对象 T 是唯一的。因此同一个 Component 获取作用域注解的对象,是同一个。
Module InstallIn Provides
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@InstallIn(ApplicationComponent::class)
@Module
object DatabaseModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
return Room.databaseBuilder(
appContext,
AppDatabase::class.java,
"logging.db"
).build()
}
@Provides
fun provideLogDao(database: AppDatabase): LogDao {
return database.logDao()
}
}
Binds
设置接口和实现的映射关系
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
@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {
@Binds
abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator
}
class AppNavigatorImpl @Inject constructor(
private val activity: FragmentActivity
) : AppNavigator {
...
}
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var navigator: AppNavigator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
navigator.navigateTo(Screens.BUTTONS)
}
}
...
}
Qualifier 限定符 和 Named
如果一个需要注入的对象,但是这个注入对象有两个创建方式都可以使用,为了明确指定用哪种创建方式,可以使用 Qualifier 声明两个注解分别添加到创建方式上,用来区分两个创建方式
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
package com.example.android.hilt.di
@Qualifier
annotation class InMemoryLogger
@Qualifier
annotation class DatabaseLogger
@InstallIn(ApplicationComponent::class)
@Module
abstract class LoggingDatabaseModule {
@DatabaseLogger
@Singleton
@Binds
abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
}
@InstallIn(ActivityComponent::class)
@Module
abstract class LoggingInMemoryModule {
@InMemoryLogger
@ActivityScoped
@Binds
abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource
}
ActivityContext 和 ApplicationContext
预定义的限定符 (Qualifier) 其具体作用可参考 Qualifier
1
2
3
4
@Qualifier
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
public @interface ActivityContext {}
EntryPoint
有时候对象的使用并不在已经定义好的切入点中,
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
class LogsContentProvider: ContentProvider() {
@InstallIn(SingletonComponent::class)
@EntryPoint
interface LogsContentProviderEntryPoint {
fun logDao(): LogDao
}
...
}
class LogsContentProvider: ContentProvider() {
...
private fun getLogDao(appContext: Context): LogDao {
val hiltEntryPoint = EntryPointAccessors.fromApplication(
appContext,
LogsContentProviderEntryPoint::class.java
)
return hiltEntryPoint.logDao()
}
}
其他
- HiltViewModel
- AndroidEntryPoint
- HiltAndroidApp
错误
1
2
3
Expected @AndroidEntryPoint to have a value. Did you forget to apply the Gradle Plugin? (dagger.hilt.android.plugin)
See https://dagger.dev/hilt/gradle-setup.html
[Hilt] Processing did not complete. See error above for details.
没有添加
1
2
3
4
5
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
// 所有模块都要添加下面的 plugin 和 引用和 compiler
plugins {
id 'dagger.hilt.android.plugin'
}
将 arguments = 改为 arguments +=
1
2
3
4
5
6
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation":
"$projectDir/schemas".toString()]
}
}
hilt 2.38.1 版本 和 kotlin 1.5.20 版本不兼容。将 kotlin 升级为 1.5.21 就可以解决