Hilt 依赖注入基础

Hilt 是 Android 官方基于 Dagger 打造的依赖注入(DI)框架,专为 Android 组件生命周期做了深度集成。本章将围绕 Hilt 的核心概念、组件层级、作用域规则以及模块编写方式展开,为后续的懒加载与启动优化打下理论基石。

核心概念

  • 依赖注入(DI):对象不自行创建依赖,而是通过外部传入。Hilt 在编译期生成代码,运行时为 Android 组件自动注入依赖。
  • 组件(Component):Hilt 预定义的容器,每个 Android 组件(Application、Activity、Fragment、View、Service 等)都对应一个 Hilt 组件,负责管理该组件生命周期内的依赖实例。
  • 模块(Module):用 @Module 标注的类,告诉 Hilt 如何提供无法通过 @Inject 构造的类的实例(如第三方库或接口实现)。
  • 作用域(Scope):通过 @Singleton、@ActivityScoped 等注解,控制依赖实例在指定组件生命周期内是单例还是每次注入都新建。
要点:Hilt 将 Dagger 的复杂配置简化为注解和模块声明,但底层仍使用 Dagger 的编译期代码生成,因此运行时开销极低。

Hilt 组件层级与生命周期

Hilt 按照 Android 组件树预定义了一套组件层级图,每个组件对应一个生命周期作用域。下图展示了主要组件及其层级关系:

Hilt 组件层级与作用域关系 ApplicationComponent @Singleton ActivityRetainedComponent @ActivityRetainedScoped ActivityComponent @ActivityScoped FragmentComponent @FragmentScoped ViewComponent @ViewScoped ServiceComponent @ServiceScoped 说明: 每创建一个 Activity/Fragment,Hilt 会创建对应的组件实例(ActivityComponent / FragmentComponent)。 组件销毁时,该组件内作用域绑定的依赖实例也随之释放,避免内存泄漏。

作用域规则

Hilt 提供了 6 个预定义的作用域注解,它们与组件生命周期严格绑定:

作用域注解 绑定组件 生命周期 典型用途
@SingletonApplicationComponent应用进程运行期间全局网络客户端、数据库实例
@ActivityRetainedScopedActivityRetainedComponentActivity 实例保留期间(旋转不重建)Presenter、ViewModel(非 AndroidX 时)
@ActivityScopedActivityComponentActivity onCreate ~ onDestroyActivity 自身依赖如界面资源
@FragmentScopedFragmentComponentFragment onCreate ~ onDestroyFragment 级别的 UI 逻辑
@ViewScopedViewComponentView attach ~ detach自定义 View 内部依赖
@ServiceScopedServiceComponentService onCreate ~ onDestroy后台服务依赖
关键规则:未加作用域注解的依赖,在每次注入时都会创建新实例(非单例)。作用域注解应尽量靠近实际单例需求,避免不必要的长生命周期驻留。

模块与提供方式

当依赖无法通过 @Inject 构造函数标注时(如接口、第三方库类),需要编写模块。

模块声明

@Module
@InstallIn(ApplicationComponent.class)
public class NetworkModule {

    @Provides
    @Singleton
    public OkHttpClient provideOkHttpClient() {
        return new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)
                .build();
    }

    @Provides
    @Singleton
    public Retrofit provideRetrofit(OkHttpClient client) {
        return new Retrofit.Builder()
                .baseUrl("https://api.example.com")
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

绑定接口实现

@Module
@InstallIn(ActivityComponent.class)
public abstract class AuthModule {

    @Binds
    @ActivityScoped
    public abstract AuthService bindAuthService(AuthServiceImpl impl);
}
注意:@Binds 方法必须是抽象方法,且参数类型为接口实现类的具体类型。模块中 @Provides 和 @Binds 可以混合使用,但 @Binds 方法只能放置在抽象模块中。

常见错误与调试

  • MissingBinding:编译期报错,Hilt 无法找到某个依赖的提供者。检查是否漏加 @Inject 构造函数或模块。
  • DuplicateBinding:同一类型有多个提供者。使用 @Qualifier(如 @Named("cache"))区分。
  • ComponentTree 不一致:如在 Application 中注入 @ActivityScoped 的依赖,编译期直接报错。遵循组件层级注入。
  • 调试技巧:启用 Hilt 编译器参数 -Adagger.fastInit=ENABLED 可以加速编译;使用 @HiltViewModel 结合 hiltViewModel() 在 Fragment 中获取 ViewModel 时,确保 ViewModel 所在 Fragment 使用 @AndroidEntryPoint 注解。
// 典型调试日志(编译期)
error: [Dagger/MissingBinding] com.example.data.Repository cannot be provided without an @Provides-annotated method.
// 解决方案:在模块中添加 @Provides Repository provideRepository(...)
性能提示:模块中的 @Provides 方法应尽可能轻量,避免在方法内执行 I/O 或复杂计算。Hilt 会在首次注入时调用一次,单例作用域下仅调用一次,但非单例作用域每次注入都会调用。