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:作用域链路过深 —— 多层嵌套作用域会导致注入链路过长,影响编译和运行时性能。建议保持平铺,不超过两层。
  • 落地步骤:
    1. 定义自定义作用域注解@DeviceSessionScoped。
    2. 创建@Component子组件接口,继承SingletonComponent。
    3. 在@Module中为所有与设备相关的依赖标注@DeviceSessionScoped。
    4. 在DeviceManager中持有Component引用,并在onDisconnect()处调用component.destroy()。
    5. 编写单元测试验证每个设备的组件实例唯一且生命周期正确。
  • 验收标准:
    • 同时连接两台设备后,各自的状态互不干扰。
    • 任意设备断开后,其对应的所有Hilt依赖对象的@Destroy方法被触发。
    • 无全局静态集合持有设备相关实例。

2.7 作用域层级与边界图

下图展示了在多设备连接场景下,作用域边界如何隔离不同设备的依赖实例:

多设备连接作用域层级与边界图 @Singleton (Application 组件) 全局配置、无状态工厂、日志系统 @DeviceSessionScoped (设备A) 连接生命周期 DeviceManager-A Session-A @DeviceSessionScoped (设备B) 连接生命周期 DeviceManager-B Session-B 作用域边界说明 1. Application 组件提供非设备相关的基础服务,不持有任何设备状态。 2. 每个设备拥有独立的 DeviceSession 作用域,其中包含唯一的 DeviceManager 与 Session 实例。 3. 不同设备之间的作用域相互隔离,无法通过 @Inject 相互引用,保证了数据流与控制流的独立。 4. 设备断开时,Hilt 自动销毁对应的子组件,释放所有绑定在该作用域内的对象内存。
关键洞察:自定义作用域的本质是“生命周期分片”。在多设备场景中,每个设备连接对应一个独立的Hilt子组件,该子组件内部的所有依赖都遵循相同的创建/销毁时序。这保证了设备A的DeviceManager不会意外被设备B的线程访问,也使得当设备A断开时,其关联的所有对象可以整体回收,无需手动逐字段置空。