2. Hilt作用域核心概念:@Scope、@Singleton、自定义作用域注解
作用域是Hilt管理对象生命周期的核心机制。通过将依赖与特定组件的生命周期绑定,作用域决定了何时创建、共享与销毁实例。理解@Scope、@Singleton以及自定义作用域注解的原理与边界,是实现多设备连接场景下精确控制内存与状态的前提。
2.1 作用域核心语义对比
| 注解 / 概念 | 作用域边界 | 生命周期 | 默认关联组件 | 多设备场景适用性 |
|---|---|---|---|---|
@Singleton |
全局 Application | 应用进程级别 | SingletonComponent |
低:所有设备共享同一实例,数据污染风险高 |
@ActivityScoped |
单个 Activity | Activity 的 onCreate/onDestroy | ActivityComponent |
中:需配合多Activity管理,易产生跨页面冗余 |
@FragmentScoped |
单个 Fragment | Fragment 的 onAttach/onDetach | FragmentComponent |
低:碎片化严重,难以维护连接池状态 |
| 自定义作用域 | 业务定义(如一次设备连接生命周期) | 开发者控制 | @Component 自定义接口 |
高:可精确匹配设备连接、断开、重连周期 |
2.2 作用域边界定义思路
对于多设备连接管理,作用域边界应严格遵循“一次连接会话”的生命周期。考虑典型场景:用户同时连接一台蓝牙设备与一台Wi-Fi设备,每台设备的连接、数据流、错误处理是独立的。
- 连接生命周期作用域:将“设备连接”抽象为一个独立的作用域,从connect()到disconnect()自动创建/销毁。
- 设备实例隔离:确保同一连接作用域内的管理器、会话、监听器唯一,不同设备之间完全隔离。
- 跨作用域数据流:仅暴露不可变接口或事件总线,避免外部直接操作内部状态。
2.3 模块分层与组件职责
利用Hilt的模块分层机制,将全局基础设施与设备特定逻辑分离:
- 顶层模块 (Singleton):提供无状态的工厂、全局配置、日志系统。所有设备无关的基础服务。
- 连接模块 (自定义作用域):提供设备句柄、编解码器、弱状态缓存。依赖注入时精确绑定到连接作用域。
- UI 模块 (Activity / Fragment):仅注入设备事件观察者,不关心实现细节。
2.4 数据流与控制流
自定义作用域下的数据流控制遵循“倒置依赖”原则:
- 控制流:由
DeviceManager(作用域内单例)启动连接、重连、断开流程,通过@Binds注入具体协议实现。 - 数据流:设备产生的原始数据通过
@Provides提供的Flow或LiveData流向上冒泡,UI层订阅,但绝不反向持有作用域实例。
2.5 信任链路与选型权衡
作用域信任链路的建立依赖Hilt的组件层级与注解组合。权衡点包括:
| 选型 | 优势 | 风险 |
|---|---|---|
| 使用@Singleton | 代码简单,无需管理子组件 | 全局状态冲突;无法支持设备独立生命周期 |
| 使用自定义作用域 | 精确控制内存,适用于多设备并行 | 需要显式创建/销毁子组件;调试复杂度增加 |
建议:对于只有1~2设备且连接时长极短的场景,使用
@ActivityScoped配合@Provides手动管理已足够;对于3个以上并发连接或需要长连接保活,必须使用自定义作用域。
2.6 风险点与落地步骤
- 风险1:作用域泄露 —— 当子组件未正确销毁时,绑定其中的依赖成为内存泄漏。必须确保
Component的destroy()在设备断开时被显式调用。 - 风险2:作用域链路过深 —— 多层嵌套作用域会导致注入链路过长,影响编译和运行时性能。建议保持平铺,不超过两层。
- 落地步骤:
- 定义自定义作用域注解
@DeviceSessionScoped。 - 创建
@Component子组件接口,继承SingletonComponent。 - 在
@Module中为所有与设备相关的依赖标注@DeviceSessionScoped。 - 在
DeviceManager中持有Component引用,并在onDisconnect()处调用component.destroy()。 - 编写单元测试验证每个设备的组件实例唯一且生命周期正确。
- 定义自定义作用域注解
- 验收标准:
- 同时连接两台设备后,各自的状态互不干扰。
- 任意设备断开后,其对应的所有Hilt依赖对象的
@Destroy方法被触发。 - 无全局静态集合持有设备相关实例。
2.7 作用域层级与边界图
下图展示了在多设备连接场景下,作用域边界如何隔离不同设备的依赖实例:
关键洞察:自定义作用域的本质是“生命周期分片”。在多设备场景中,每个设备连接对应一个独立的Hilt子组件,该子组件内部的所有依赖都遵循相同的创建/销毁时序。这保证了设备A的
DeviceManager不会意外被设备B的线程访问,也使得当设备A断开时,其关联的所有对象可以整体回收,无需手动逐字段置空。