跳转到正文
YGJoon 技术博客

Android Kotlin Hilt 依赖注入实战

发布于:
·772 字 · 约 2 分钟·作者:
Hilt 在 Android 工程里的完整落地:模块划分、自定义作用域、Compose 注入、动态特性模块,以及可测试性带来的分层收益。
Android Kotlin Hilt 依赖注入实战文章封面
目录

Hilt 结构

项目配置

plugins {
    id("com.android.application")
    kotlin("android")
    kotlin("kapt")
    id("com.google.dagger.hilt.android")
}

dependencies {
    implementation("com.google.dagger:hilt-android:2.51.1")
    kapt("com.google.dagger:hilt-android-compiler:2.51.1")
    implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
}

Application:

@HiltAndroidApp
class App : Application()

模块定义

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()

    @Provides
    fun provideRetrofit(client: OkHttpClient): Retrofit = Retrofit.Builder()
        .baseUrl("https://api.example.com")
        .client(client)
        .addConverterFactory(MoshiConverterFactory.create())
        .build()
}

ViewModel 注入:

@HiltViewModel
class HomeViewModel @Inject constructor(
    private val repository: HomeRepository
) : ViewModel() {
    val uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
}

@AndroidEntryPoint
class HomeActivity : ComponentActivity() {
    private val viewModel: HomeViewModel by viewModels()
}

自定义作用域

@Scope
@Retention(AnnotationRetention.RUNTIME)
annotation class FeatureScope

@Module
@InstallIn(ActivityRetainedComponent::class)
object FeatureModule {
    @Provides
    @FeatureScope
    fun provideFeatureRepository(api: FeatureApi): FeatureRepository {
        return FeatureRepository(api)
    }
}

与 Compose 集成

@Composable
fun HomeRoute(viewModel: HomeViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsState()
    HomeScreen(state)
}

动态特性模块

@EntryPoint
@InstallIn(SingletonComponent::class)
interface FeatureEntryPoint {
    fun homeNavigator(): HomeNavigator
}

fun Context.loadFeature(): HomeNavigator {
    val entryPoint = EntryPointAccessors.fromApplication(this, FeatureEntryPoint::class.java)
    return entryPoint.homeNavigator()
}

测试策略

@HiltAndroidTest
@UninstallModules(NetworkModule::class)
class HomeViewModelTest {
    @get:Rule
    val hiltRule = HiltAndroidRule(this)

    @BindValue
    lateinit var repository: HomeRepository

    @Before
    fun setup() {
        repository = FakeHomeRepository()
        hiltRule.inject()
    }
}

常见问题

问题原因解决方案
找不到入口Application 未标记 @HiltAndroidApp检查清单与注解
多模块依赖冲突不同模块重复定义 @InstallIn使用 @EntryPoint 接入跨模块依赖
热启动慢初始化对象过多分离懒加载,使用 @Provides @Singleton 缓存

实战案例:DI 是”把 new 藏起来”

我是 Flutter 开发者,Dart 侧最常用的 DI 是 get_it + injectable(后者也是代码生成,和 Hilt 一个路数)。我在 GetX 项目里其实一直在”手动 DI”:Get.put(ApiClient()) 满天飞,测试时想换个 mock 的 ApiClient,得满世界改。

Hilt 解决的就是这个问题:@Inject constructor 声明依赖,@Module + @Provides 教 Hilt 怎么造,@AndroidEntryPoint 的 Activity/Fragment 直接拿。Google 的 Now in Android 项目就是全套 Hilt,值得当模板抄。

但 Hilt 有两个实战劝退点,官方文档不会强调:

  1. 编译慢。Hilt 是 KSP/kapt 代码生成,大项目 clean build 能慢 30%+。小项目用 Hilt 属于”大炮打蚊子”,手动传参更实在。
  2. 调试地狱。@Inject 的依赖链断了,报错是 Dagger 生成的几千行代码里的某一行,新人根本看不懂。Dart 的 get_it 报错就一句”not registered”,友好得多。

我的建议:10 万行以下的项目别上 Hilt,get_it/手动构造 够了;真正的大型多模块项目,Hilt 的模块化(@InstallIn(SingletonComponent::class))才值回票价。

参考:Android 官方文档《Dependency injection with Hilt》;Now in Android 开源项目

总结

  1. Hilt 简化了 Dagger 配置,搭配 Kotlin 特性可实现清晰的依赖图。
  2. 注意作用域与模块划分,确保依赖生命周期与业务一致。
  3. 构建完整的测试替换策略,有助于验证依赖注入是否正确配置。

分享这篇文章:


相关文章

上一篇
Android Kotlin Gradle Kotlin DSL 与构建优化
下一篇
Android Kotlin KSP 编译期代码生成