Hilt提供了一种将Dagger依赖注入集成到Android应用的标准方法。Hilt的工作原理是自动生成Dagger设置代码。这省去了使用Dagger的大部分样板代码,只保留了定义如何创建对象以及在哪里注入这些对象的部分。Hilt会生成Dagger组件以及自动注入Android类(例如Activity和Fragment)的代码。Hilt会根据传递类路径生成一组标准的Android Dagger组件。这需要使用Hilt注解标记Dagger模块,以告知Hilt它们应该被放入哪个组件中。在Android框架类中获取对象需要使用另一个Hilt注解,该注解会将Dagger注入代码生成到您将要扩展的基类中。对于Gradle用户,扩展此类是通过底层的字节码转换实现的。

Hilt的目标

  • 简化Android应用的Dagger相关基础架构。
  • 创建一套标准的组件和作用域,以简化设置、提高代码可读性和可理解性,并方便应用间的代码共享。
  • 提供一种简便的方法来为各种构建类型(例如测试、调试或发布)配置不同的绑定。

为什么要使用Hilt

  • 减少样板文字
  • 解耦的构建依赖项
  • 简化配置
  • 改进测试
  • 标准化组件

Gradle配置信息

在集成Hilt时,我们使用KSP工具来编译生成hilt代码。KSP是kotlin符号处理api,可用于开发轻量级编译器插件。KSP提供了一个简化的编译器插件API,它充分利用了Kotlin的强大功能,同时最大限度地降低了学习难度。与KAPT相比,使用KSP的注解处理器运行速度最高可提升2倍。

Hilt Gradle插件会运行字节码转换,以简化API的使用。该插件旨在提升IDE中的开发体验,因为生成的类可能会干扰基类方法的代码补全。文档中的示例均假设使用了该插件。要配置Hilt Gradle插件,首先需要在项目根build.gradle.kts文件中声明依赖项:

plugins {
  id("com.google.dagger.hilt.android") apply false version "2.57.2"
  id("com.google.devtools.ksp") apply false version "2.3.2"
}

在模块的build.gradle.kts文件中配置构建依赖

plugins {
  id("com.google.dagger.hilt.android")
  id("com.google.devtools.ksp")
}

dependencies {
  implementation("com.google.dagger:hilt-android:2.57.2")
  ksp("com.google.dagger:hilt-android-compiler:2.57.2")
}

对Application进行注入

所有应用在使用Hilt时必须包含一个带有@HiltAndroidApp注解的Application类,该注解会启动Hilt组件的代码生成,并为应用生成一个使用这些生成的组件的基类。示例如下

@HiltAndroidApp
class MyApplication : MyBaseApplication() {
  @Inject lateinit var bar: Bar

  override fun onCreate() {
    super.onCreate() // Injection happens in super.onCreate()
    // Use bar
  }
}

对Android组件进行注入

在Application中启用成员注入后,可以使用@AndroidEntryPoint注解在其它Android类中启用成员注入,支持的组件如下

  • Activity
  • Fragment
  • Service
  • BroadcastReceiver
  • View

注意:ViewModel是通过@HiltViewModel来支持

@AndroidEntryPoint
class MyActivity : ComponentActivity() {
  @Inject lateinit var bar: Bar // Bindings in SingletonComponent or ActivityComponent

  override fun onCreate() {
    // Injection happens in super.onCreate().
    super.onCreate()
    // Do something with bar ...
  }
}

注意:Hilt当前仅支持继承ComponentActivity的Activity和继承Androidx库的Fragment

使用Module提供注入对象

Hilt Module是标准的Dagger模块,它有一个额外的@InstallIn注解,用于确定将模块安装到哪个Hilt组件中。在Hilt 组件中安装模块时,需要使用注解为模块添加注解@InstallIn。使用Hilt时,所有Dagger模块都必须添加这些注解

注意:如果一个模块没有注解@InstallIn,则该模块将不会成为组件的一部分,并可能导致编译错误。

指定要将模块安装到哪个Hilt组件中,方法是将相应的组件类型传递给@InstallIn注解。例如要安装一个模块以便应用程序中的任何组件都可以使用它,请使用SingletonComponent:

@Module
@InstallIn(SingletonComponent::class) // Installs BarModule in the generate SingletonComponent.
internal object BarModule {
  @Provides
  fun provideBar(): Bar {
    return Bar()
  }
}

对ViewModel进行注入

Hilt ViewModel是由Hilt通过构造函数注入的Jetpack ViewModel。要启用Hilt对ViewModel的注入,请使用@HiltViewModel注解:

@HiltViewModel
class MainViewModel @Inject constructor(val bar: Bar) : ViewModel()

在Compose中注入ViewModel

配置lifecycle依赖

dependencies {
  implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4")
}

通过调用viewModel()函数,从任何可组合项访问ViewModel。

@HiltViewModel
class MainViewModel @Inject constructor(val bar: Bar) : ViewModel()

// import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun mainPage(viewModel: MainViewModel = viewModel()) {
  // use viewModel here
}

viewModel()会返回一个现有的ViewModel,或创建一个新的ViewModel。默认情况下,返回的ViewModel的作用域限定为封装activity、fragment或导航目的地,并且只要该作用域处于有效状态,就会保留该ViewModel。 例如,如果在某个activity中使用了可组合项,则在该activity完成或进程终止之前,viewModel()会返回同一实例。viewModel()函数自动使用Hilt通过@HiltViewModel注解构造的ViewModel。

Hilt还适配了Navigation Compose库,集成如下:

dependencies {
  implementation("androidx.hilt:hilt-navigation-compose:1.3.0")
}

使用Navigation Compose时,请始终使用hiltViewModel可组合函数获取带有@HiltViewModel注解的ViewModel的实例。该函数可与带有@AndroidEntryPoint注解的fragment或activity搭配使用。 例如,如果 mainPage是导航图中的目的地,请调用hiltViewModel()来获取作用域限定为该目的地的MainViewModel实例,如以下代码段所示:

// import androidx.hilt.navigation.compose.hiltViewModel

@Composable
fun myApp() {
  val navController = rememberNavController()
  val startRoute = "main"
  NavHost(navController, startDestination = startRoute) {
    composable("main") { backStackEntry ->
      // Creates a ViewModel from the current BackStackEntry
      // Available in the androidx.hilt:hilt-navigation-compose artifact
      val viewModel = hiltViewModel<MainViewModel>()
      mainPage(viewModel)
    }
    /* ... */
  }
}

完整示例

@HiltAndroidApp
class MyApplication : MyBaseApplication()

@AndroidEntryPoint
class MyActivity : ComponentActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
      myApp()
    }
  }
}

@Composable
fun myApp() {
  val navController = rememberNavController()
  val startRoute = "main"
  NavHost(navController, startDestination = startRoute) {
    composable("main") { backStackEntry ->
      val viewModel = hiltViewModel<MainViewModel>()
      mainPage(viewModel)
    }
  }
}

@Composable
fun mainPage(viewModel: MainViewModel = viewModel()) {
  println(viewModel.getName())
}

@HiltViewModel
class MainViewModel @Inject constructor(private val bar: Bar) : ViewModel(){
  fun getName(): String {
    return bar.name
  }
}

@Module
@InstallIn(SingletonComponent::class)
internal object BarModule {
  @Provides
  fun provideBar(): Bar {
    return Bar("jinhongke")
  }
}

data class Bar(private val name: String)
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐