Dagger / Hilt:是什么,DI及应用

作者: IT Sectr 发布日期: 2026-05-03 阅读时间: 9 分钟

Dagger — 是一个用于Java和Kotlin的依赖注入框架,通过在编译时处理注解来生成DI代码。Hilt — 是Android中Dagger之上的一个层,简化了组件和生命周期的配置。根据Google,2025的数据,超过70%的Google Play Top-100 Android应用都在使用Hilt,通过预定义的组件支持Activity、Fragment、ViewModel和Service。这两个框架都在编译时提供依赖图检查,消除了运行时的注入错误。

要点

  • Dagger — 编译时DI框架,通过@Module、@Provides、@Component注解生成代码
  • Hilt — Android的层,通过@HiltAndroidApp、@AndroidEntryPoint、@HiltViewModel简化Dagger
  • Component — 依赖图,通过代理方法将Module与Inject目标连接起来
  • Scope — @Singleton、@ViewModelScoped、@ActivityScoped管理注入对象的生命周期
  • Hilt 通过@InstallIn支持多模块项目,用于隔离的依赖图

什么是Dagger / Hilt?

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的优势

DI的主要优势包括可测试性(依赖关系可以通过mock对象替换)、松耦合(类依赖于接口而不是实现)以及通过作用域显式管理对象的生命周期。Dagger自动保证对象在其作用域内创建一次,并在退出作用域时销毁。

Dagger架构:Component、Module、Provides

Component — Dagger依赖图的核心元素。这是一个用@Component注解的接口,描述了Module和注入目标之间的桥梁。Dagger在编译时生成Component的实现(例如DaggerAppComponent)。Component通过返回所需类型的抽象方法或通过接受对象进行字段注入的inject方法来确定哪些类型可用于注入。

kotlin
// 模块:提供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可以缓存实例,但不保证。

Android的Hilt:@HiltAndroidApp和@AndroidEntryPoint

Hilt 由于预定义的组件和基础图的自动生成,简化了Android的Dagger配置。Application类上的@HiltAndroidApp注解触发Hilt组件的生成。没有这个注解,Hilt无法工作——对于每个使用Hilt的Android应用都是必需的。@HiltAndroidApp创建了父组件SingletonComponent,应用程序的所有其他组件都继承自它。

kotlin
@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内置的用于获取应用上下文的限定符。

限定符:@Named和自定义限定符

当需要注入同一接口的两个不同实现时,使用限定符。Hilt支持用于文本标识符的@Named和带@Qualifier的自定义注解。例如,@Named("baseUrl")和@Named("imageBaseUrl")用于不同的文本配置。由于编译时检查,自定义限定符比@Named更受欢迎——错误的文本名称直到运行时才会被发现。

Hilt ViewModel:@HiltViewModel和@Inject constructor

@HiltViewModel — 注解,替代手动工厂ViewModelProvider.Factory。带有@HiltViewModel注解和@Inject constructor的类通过Dagger自动接收所有依赖关系。Hilt生成由Jetpack ViewModelProvider使用的ViewModelFactory。没有Hilt,开发人员需要手动编写工厂,从Activity或片段传递每个参数。

kotlin
@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与Hilt:比较和迁移

纯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方法进行迁移。

特性DaggerHilt
配置手动: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和Hilt有什么区别?

Dagger — 基本的编译时DI框架,手动配置Component和Module。Hilt — Android的层,自动化组件的创建以及与Activity、Fragment、ViewModel、Service和BroadcastReceiver生命周期的集成。

@HiltAndroidApp有什么用?

@HiltAndroidApp 启用了Application的Hilt组件生成。没有这个注解,Hilt无法创建基础组件SingletonComponent,所有ActivityComponent、FragmentComponent和ViewModelComponent都继承自它。该注解对于每个Hilt项目都是必需的。

Hilt如何与Jetpack Navigation一起工作?

Hilt Navigation为NavBackStackEntry中的ViewModel提供@HiltViewModel,以及hiltNavGraphViewModels()用于在导航图范围内限定ViewModel。库android.hilt:hilt-navigation-fragment自动为每个NavBackStackEntry创建ViewModel。

如何在Hilt中注入Context?

使用@ApplicationContext获取应用上下文,或@ActivityContext获取Activity上下文。Hilt在库android.hilt:hilt-android中内置了这些限定符。@ActivityContext仅在安装在ActivityComponent中的模块中可用。

什么是@Binds以及何时使用?

@Binds — 当方法恰好接受一个参数并将其类型作为接口返回时,是@Provides的有效替代方案。@Binds生成直接转换而不调用方法,这减少了生成的代码量并提高了注入时的性能。

总结

  • Dagger — 编译时DI框架,使用@Module、@Provides、@Component注解并通过APT生成代码
  • Hilt — Dagger之上的Android层,带有@HiltAndroidApp、@AndroidEntryPoint、@InstallIn和预定义组件
  • Component管理依赖图,Module提供第三方类,Provides提供对象工厂
  • Scope(@Singleton、@ViewModelScoped、@ActivityScoped)确定Dagger图中对象的生命周期
  • @HiltViewModel自动化ViewModel的创建,消除了手动ViewModelProvider.Factory工厂
  • @InstallIn按组件隔离模块,防止依赖关系在应用程序各层之间泄漏
  • Hilt被Google推荐用于所有新的Android项目,Dagger — 用于非Android和自定义DI架构

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读