您现在的位置是:首页 >学无止境 >Hilt在Android中的使用网站首页学无止境
Hilt在Android中的使用
1、简介:Hilt 是 Android 颇具特色的依赖项注入库,可减少在项目中使用手动依赖项注入时产生的样板代码。
2、使用
1.我们需要为 Application 类添加 @HiltAndroidApp 注解
@HiltAndroidApp 会触发 Hilt 的代码生成操作,生成的代码包括应用的一个基类,该基类可使用依赖项注入。application 容器是应用的父级容器,这意味着其他容器可以访问它提供的依赖项。
@HiltAndroidApp
class LogApplication : Application() {
...
} |
2.为 Android 类添加 @AndroidEntryPoint 注解会创建一个沿袭 Android 类生命周期的依赖项容器
利用 @AndroidEntryPoint,Hilt 可创建附着于 LogsFragment 生命周期的依赖项容器,并能够将实例注入 LogsFragment
@AndroidEntryPoint
class LogsFragment : Fragment() {
...
} |
3.对于要进行注入的字段,我们可以利用 @Inject 注解让 Hilt 注入不同类型的实例
注意:由 Hilt 注入的字段不能是私有字段
@AndroidEntryPoint
class LogsFragment : Fragment() {
@Inject lateinit var logger: LoggerLocalDataSource
...
} |
4.告知 Hilt 如何通过 @Inject 提供依赖项
如要告知 Hilt 如何提供类型的实例,请向要注入的类的构造函数添加 @Inject 注解。
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao) {
...
} |
5.将实例的作用域限定为容器
| Android 类 | 生成的组件 | 作用域 |
|---|---|---|
Application | SingletonComponent | @Singleton |
ViewModel | ViewModelComponent | @ViewModelScoped |
Activity | ActivityComponent | @ActivityScoped |
Fragment | FragmentComponent | @FragmentScoped |
View | ViewComponent | @ViewScoped |
Service | ServiceComponent | @ServiceScoped |
@Singleton
class LoggerLocalDataSource @Inject constructor(private val logDao: LogDao) {
...
} |
6.Hilt 模块
有时,类型不能通过构造函数注入。发生这种情况可能有多种原因。例如,您不能通过构造函数注入接口。此外,您也不能通过构造函数注入不归您所有的类型,如来自外部库的类。在这些情况下,您可以使用 Hilt 模块向 Hilt 提供绑定信息
Hilt 模块是带有@Module 和 @InstallIn 注解的类。@Module 会告知 Hilt 这是一个模块,而 @InstallIn 会通过指定 Hilt 组件告知 Hilt 绑定在哪些容器中可用
6.1创建模块
@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {
} |
6.2使用 @Provides 提供实例
我们可以在 Hilt 模块中为函数添加 @Provides 注解,告知 Hilt 如何提供无法通过构造函数注入的类型。
每当 Hilt 需要提供相应类型的实例时,都会执行带有 @Provides 注解的函数的函数主体。带有 @Provides 注解的函数的返回值类型会告知 Hilt 绑定的类型,即如何提供该类型的实例。函数参数是该类型的依赖项。
在 Kotlin 中,仅包含 @Provides 函数的模块可以是 object 类。通过这种方式,提供程序会得到优化。
@Module
object DatabaseModule {
@Provides
fun provideLogDao(database: AppDatabase): LogDao {
return database.logDao()
}
} |
6.3使用 @Binds 注入接口实例
Hilt 模块不能同时包含非静态和抽象绑定方法,因此您不能将 @Binds 和 @Provides 注解放在同一个类中(A @Module may not contain both non-static and abstract binding methods)
一个抽象函数会返回我们告知 Hilt 的接口(即 AppNavigator),而函数参数是该接口的实现(即 AppNavigatorImpl)
@InstallIn(ActivityComponent::class)
@Module
abstract class NavigationModule {
@Binds
abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator
} |
6.4限定符
如要告知 Hilt 如何提供同一类型的不同实现(多个绑定),您可以使用限定符
6.4.1 定义注解
package com.example.android.hilt.di @Qualifier annotation class InMemoryLogger @Qualifier annotation class DatabaseLogger |
6.4.2对实现添加注解
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
} |
6.4.3通过注解选中实现方式
@AndroidEntryPoint
class LogsFragment : Fragment() {
@InMemoryLogger
@Inject lateinit var logger: LoggerDataSource
...
}
@AndroidEntryPoint
class ButtonsFragment : Fragment() {
@InMemoryLogger
@Inject lateinit var logger: LoggerDataSource
...
} |
3、原理分析
3.1通过注解处理器生成hilt相关文件
3.2通过插件将super类替换
plugin主要代码地址:
/Users/xxx/.gradle/caches/modules-2/files-2.1/com.google.dagger/hilt-android-gradle-plugin/2.28-alpha/eb33a043b2bbdc7cdee3c851d0f8532bfd3645a5/hilt-android-gradle-plugin-2.28-alpha-sources/dagger/hilt/android/plugin/
查看AndroidEntryPointClassTransformer.kt主要代码:
private fun transformClass(clazz: CtClass): Boolean {
if (ANDROID_ENTRY_POINT_ANNOTATIONS.none { clazz.hasAnnotation(it) }) {
// Not a Android entry point annotated class, don't do anything.
return false
}
// TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
val superclassName = clazz.classFile.superclass
//将标有@AndroidEntryPoint和@HiltAndroidApp的父类替换为Hilt_XXX
val entryPointSuperclassName =
clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
logger.info(
"[$taskName] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
"$superclassName."
)
clazz.superclass = classPool.get(entryPointSuperclassName)
transformSuperMethodCalls(clazz, superclassName, entryPointSuperclassName)
return true
} |





U8W/U8W-Mini使用与常见问题解决
QT多线程的5种用法,通过使用线程解决UI主界面的耗时操作代码,防止界面卡死。...
stm32使用HAL库配置串口中断收发数据(保姆级教程)
分享几个国内免费的ChatGPT镜像网址(亲测有效)
Allegro16.6差分等长设置及走线总结