Dagger — 是一个用于Java和Kotlin的依赖注入框架,通过在编译时处理注解来生成DI代码。Hilt — 是Android中Dagger之上的一个层,简化了组件和生命周期的配置。根据Google,2025的数据,超过70%的Google Play Top-100 Android应用都在使用Hilt,通过预定义的组件支持Activity、Fragment、ViewModel和Service。这两个框架都在编译时提供依赖图检查,消除了运行时的注入错误。
要点
Dagger — 是一个在编译时生成代码的依赖注入框架(compile-time DI)。由Square开发并后来移交给Google,Dagger使用Java APT注解处理器来分析依赖图并生成工厂类。与运行时DI(Guice、Koin)不同,Dagger不使用反射——所有代码在编译时创建,从而在运行时提供最大性能并在构建阶段检测错误。
Hilt — 是Google的一个库,建立在Dagger之上并针对Android进行了优化。Hilt提供与Android组件生命周期相对应的预定义组件:Application的@SingletonComponent、Activity的@ActivityComponent、Fragment的@FragmentComponent、ViewModel的@ViewModelComponent。这消除了纯Dagger中所需的常规Component和Module配置。Hilt还通过@AndroidEntryPoint自动为每个Android组件生成依赖图。
根据Google I/O 2024,Hilt是用Kotlin编写的Android应用的推荐DI解决方案。Jetpack库(Navigation、Room、WorkManager)通过@HiltViewModel和@HiltWorker与Hilt内置集成。在不使用Android的项目(纯Java/Kotlin库、服务器应用)中,使用没有Hilt层的纯Dagger。
没有DI框架,开发人员通过构造器或工厂手动创建对象,沿着链传递依赖关系。每个新需求——更改链中所有构造器的签名。Dagger自动化此过程:只需声明需要什么类型(@Inject constructor),Dagger将创建依赖图,解决所有嵌套类型。当依赖关系发生变化时,Dagger会自动更新生成的代码——链中不可能出错。
依赖注入(Dependency Injection)——是一种对象从外部接收其依赖关系而不是自己创建它们的模式。DI实现了控制反转(IoC)原则:类不负责创建自己的依赖关系,而是通过构造器、方法或字段声明它们。通过构造器注入(constructor injection)被认为是最可取的方式,因为它保证对象在有效状态下创建。
| 注入类型 | Dagger语法 | 何时使用 |
|---|---|---|
| Constructor injection | @Inject constructor | 主要方式——用于所有自己的类 |
| Field injection | @Inject lateinit var | 仅用于Android组件(Activity、Fragment) |
| Method injection | @Inject fun bind() | 用于后构造初始化 |
DI的主要优势包括可测试性(依赖关系可以通过mock对象替换)、松耦合(类依赖于接口而不是实现)以及通过作用域显式管理对象的生命周期。Dagger自动保证对象在其作用域内创建一次,并在退出作用域时销毁。
Component — Dagger依赖图的核心元素。这是一个用@Component注解的接口,描述了Module和注入目标之间的桥梁。Dagger在编译时生成Component的实现(例如DaggerAppComponent)。Component通过返回所需类型的抽象方法或通过接受对象进行字段注入的inject方法来确定哪些类型可用于注入。
// 模块:提供Dagger无法自行创建的依赖关系
@Module
class NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.build()
}
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(ApiService::class.java)
}
}
// 组件:连接Module和注入目标
@Component(modules = [NetworkModule::class])
interface AppComponent {
fun inject(activity: MainActivity)
fun getApiService(): ApiService
}
@Module — 是一个包含@Provide方法的类,这些方法返回依赖关系的实例。Module用于Dagger无法自动创建的类型:第三方库(OkHttp、Retrofit)、带有构造器参数的对象、具有实现选择接口。@Binds — 当方法返回接口并接受单一实现时,是@Provides的替代方案:Dagger生成直接转换而不调用方法。
@Scope确定对象在依赖图中的生命周期。@Singleton — 对象为整个应用创建一次。@ActivityScoped — 对象在Activity存活期间存活。@FragmentScoped — 在Fragment存活期间存活。没有作用域时,Dagger在每次注入时创建一个新实例。@Reusable — 用于不必是单例但创建成本高的对象的作用域——Dagger可以缓存实例,但不保证。
Hilt 由于预定义的组件和基础图的自动生成,简化了Android的Dagger配置。Application类上的@HiltAndroidApp注解触发Hilt组件的生成。没有这个注解,Hilt无法工作——对于每个使用Hilt的Android应用都是必需的。@HiltAndroidApp创建了父组件SingletonComponent,应用程序的所有其他组件都继承自它。
@HiltAndroidApp
class MyApplication : Application()
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
@Inject lateinit var apiService: ApiService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// apiService已在调用onCreate之前被注入
}
}
@Module
@InstallIn(SingletonComponent::class)
class AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext ctx: Context): AppDatabase {
return Room.databaseBuilder(ctx, AppDatabase::class.java, "app.db").build()
}
}
@AndroidEntryPoint — 用于Activity、Fragment、Service、BroadcastReceiver和View的注解。为每个类型生成一个Hilt组件:Activity上的@AndroidEntryPoint创建继承自SingletonComponent的ActivityComponent。子组件自动接收所有父依赖关系。带@Inject lateinit var的字段注入仅在@AndroidEntryPoint注解的类中可用——在普通类中使用构造器注入。
@InstallIn指示模块安装到哪个Hilt组件中。带有@InstallIn(SingletonComponent::class)的NetworkModule在整个应用中可用。带有@InstallIn(ActivityComponent::class)的Module — 仅在Activity中。这隔离了依赖图:特定于Activity的模块在Fragment和ViewModel中不可见,防止意外使用不允许的依赖关系。@ApplicationContext — Hilt内置的用于获取应用上下文的限定符。
当需要注入同一接口的两个不同实现时,使用限定符。Hilt支持用于文本标识符的@Named和带@Qualifier的自定义注解。例如,@Named("baseUrl")和@Named("imageBaseUrl")用于不同的文本配置。由于编译时检查,自定义限定符比@Named更受欢迎——错误的文本名称直到运行时才会被发现。
@HiltViewModel — 注解,替代手动工厂ViewModelProvider.Factory。带有@HiltViewModel注解和@Inject constructor的类通过Dagger自动接收所有依赖关系。Hilt生成由Jetpack ViewModelProvider使用的ViewModelFactory。没有Hilt,开发人员需要手动编写工厂,从Activity或片段传递每个参数。
@HiltViewModel
class MainViewModel
@Inject constructor(
private val apiService: ApiService,
private val database: AppDatabase
) : ViewModel() {
private val _users = MutableStateFlow<List<User>>(emptyList())
val users: StateFlow<List<User>> = _users.asStateFlow()
fun loadUsers() {
viewModelScope.launch {
_users.value = apiService.getUsers()
}
}
}
// 在Activity中——Hilt自动创建ViewModel
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
ViewModelScoped — Hilt的作用域,用于在ViewModel存活期间存活的依赖关系。如果两个相同类型的ViewModel注入相同的@ViewModelScoped依赖关系,每个都获得自己的实例。这区分了@ViewModelScoped和@ActivityScoped,在后者中一个Activity为所有片段获取一个实例。对于特定于ViewModel的依赖关系(例如SavedStateHandle),使用带@Inject constructor(savedStateHandle: SavedStateHandle)的@HiltViewModel。
Hilt通过Hilt Extensions库支持assisted injection(辅助注入)。当部分依赖仅在运行时才知道(例如来自intent的用户ID),assisted injection允许在注入期间将参数传递给构造器。对于assisted injection,使用@AssistedInject与@Assisted参数组合。Hilt生成AssistedFactory,可以用标准方式注入。
纯Dagger需要手动创建Component、定义作用域以及配置每个Android组件中的注入。开发人员创建AppComponent、ActivityComponent、FragmentComponent,并通过@Subcomponent管理它们的关系。这种方法提供了最大的控制,但需要大量的样板代码。Dagger用于需要自定义DI架构的大型项目或非Android Java/Kotlin项目。
Hilt自动化样板代码:一个@HiltAndroidApp、每个组件一个@AndroidEntryPoint、预定义的作用域。Google推荐Hilt用于所有新的Android项目。从Dagger迁移到Hilt包括用@InstallIn替换Component、用预定义的Hilt组件替换@Subcomponent,以及用手动ViewModelProvider.Factory替换为@HiltViewModel。大多数@Module类通过添加@InstallIn而不更改@Provides方法进行迁移。
| 特性 | Dagger | Hilt |
|---|---|---|
| 配置 | 手动:Component、Subcomponent、Builder | 自动:@HiltAndroidApp、@AndroidEntryPoint |
| Android组件 | 没有预定义的 | 12+内置组件 |
| ViewModel | 手动工厂 | @HiltViewModel + @Inject constructor |
| 多模块 | 通过@Component(dependencies) | 通过@InstallIn + 聚合 |
| 复杂性 | 高——需要经验 | 低——直观 |
| 灵活性 | 最大 | 标准(覆盖95%的场景) |
Hilt的局限性:该库仅支持Android(不适合纯服务器Java项目),强加了一定的组件结构(难以覆盖),并为Jetpack Compose项目添加了对android.hilt:hilt-navigation-compose的依赖。对于Compose应用,Hilt提供@HiltViewModel,可通过hiltViewModel()在Composable中使用——无需手动从Activity提供ViewModel。
常见问题
Dagger — 基本的编译时DI框架,手动配置Component和Module。Hilt — Android的层,自动化组件的创建以及与Activity、Fragment、ViewModel、Service和BroadcastReceiver生命周期的集成。
@HiltAndroidApp 启用了Application的Hilt组件生成。没有这个注解,Hilt无法创建基础组件SingletonComponent,所有ActivityComponent、FragmentComponent和ViewModelComponent都继承自它。该注解对于每个Hilt项目都是必需的。
Hilt Navigation为NavBackStackEntry中的ViewModel提供@HiltViewModel,以及hiltNavGraphViewModels()用于在导航图范围内限定ViewModel。库android.hilt:hilt-navigation-fragment自动为每个NavBackStackEntry创建ViewModel。
使用@ApplicationContext获取应用上下文,或@ActivityContext获取Activity上下文。Hilt在库android.hilt:hilt-android中内置了这些限定符。@ActivityContext仅在安装在ActivityComponent中的模块中可用。
@Binds — 当方法恰好接受一个参数并将其类型作为接口返回时,是@Provides的有效替代方案。@Binds生成直接转换而不调用方法,这减少了生成的代码量并提高了注入时的性能。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。