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 提供了 6 个预定义的作用域注解,它们与组件生命周期严格绑定:
| 作用域注解 | 绑定组件 | 生命周期 | 典型用途 |
|---|---|---|---|
@Singleton | ApplicationComponent | 应用进程运行期间 | 全局网络客户端、数据库实例 |
@ActivityRetainedScoped | ActivityRetainedComponent | Activity 实例保留期间(旋转不重建) | Presenter、ViewModel(非 AndroidX 时) |
@ActivityScoped | ActivityComponent | Activity onCreate ~ onDestroy | Activity 自身依赖如界面资源 |
@FragmentScoped | FragmentComponent | Fragment onCreate ~ onDestroy | Fragment 级别的 UI 逻辑 |
@ViewScoped | ViewComponent | View attach ~ detach | 自定义 View 内部依赖 |
@ServiceScoped | ServiceComponent | Service 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 会在首次注入时调用一次,单例作用域下仅调用一次,但非单例作用域每次注入都会调用。