4. 跨设备共享作用域设计:CrossDeviceScope 与统一标识
在多设备连接场景中,依赖注入的生命周期管理必须跨越单设备的边界。传统 @Singleton 或 @ActivityScoped 作用域无法处理设备间的实例共享与隔离。本章定义 CrossDeviceScope 作为跨设备共享作用域,并引入 统一标识 作为设备间依赖定位的唯一依据。
4.1 作用域边界定义
CrossDeviceScope 是一个逻辑作用域,其中的依赖实例生命周期与一组已配对的物理设备会话绑定。作用域边界由以下三个要素共同划定:
- 设备集合:当前会话中参与的所有设备唯一标识(DeviceId)组成的集合。
- 统一标识:由跨设备会话名称或哈希值生成的不可变标识(CrossDeviceId),用于在 DI 容器中定位作用域实例。
- 会话活跃性:当集合中任一设备主动离开或心跳超时,作用域标记为可回收。
关键决策:CrossDeviceScope 不依赖单一设备进程存活,而是基于外部协调服务(如协程 SupervisorJob 或持久化存储中的会话记录)维持边界。
4.2 模块分层
为实现跨设备作用域的可管理性,按如下层级组织模块:
| 层级 | 模块名称 | 职责 |
|---|---|---|
| L1 | 设备抽象层 | 提供设备发现、连接状态监听、DeviceId 分配 |
| L2 | 会话管理模块 | 维护设备集合,计算/更新 CrossDeviceId |
| L3 | 统一标识服务 | 生成、校验、缓存 CrossDeviceId |
| L4 | 信任链路模块 | 签名认证,防止标识伪造 |
| L5 | 作用域容器 | 持有 CrossDeviceScope 实例,执行注入与销毁 |
4.3 组件职责
- CrossDeviceScopeManager:单例组件,管理所有活跃的跨设备作用域集合。提供
getScope(crossDeviceId)与destroyScope(crossDeviceId)接口。 - 统一标识服务:根据设备列表与自定义命名策略生成标识。标识接口定义如下:
// 统一标识服务接口定义(非代码,仅示意)
interface CrossDeviceIdGenerator {
suspend fun generateId(deviceIds: Set, sessionName: String): String
suspend fun validateId(candidateId: String, proof: String): Boolean
}
- 设备注册表:记录每个 CrossDeviceScope 下的设备状态,支持增删查。
- 会话令牌持有者:持有签名后的统一标识,用于跨进程/跨设备传递认证。
4.4 数据流与控制流
- 设备 A 上线 → 设备抽象层生成
DeviceId_A。 - 会话管理模块收到配对成功事件 → 收集全部 DeviceId 集合 → 调用统一标识服务生成
CrossDeviceId_X。 - 信任链路模块对
CrossDeviceId_X签名,分发到各设备。 - 各设备通过
CrossDeviceScopeManager.getScope(CrossDeviceId_X)获取或创建作用域。 - 作用域内注入的依赖(如共享数据通道、同步锁)在所有设备中保持单例。
- 任一设备离线 → 会话管理模块通知作用域容器 → 标记为“待回收”→ 待所有依赖释放后销毁。
数据流特点:控制流采用事件驱动,数据流在作用域内存中共享;统一标识作为路由键,确保不同会话的依赖完全隔离。
4.5 信任链路
统一标识必须防篡改、防重放。信任链路采用双层签名:
- 设备级签名:每个 DeviceId 由设备私钥签名,校验方用设备公钥验证。
- 会话级签名:CrossDeviceId 由会话发起设备签名,其他设备在加入时验证签名链。
| 层次 | 签名方式 | 验证方 |
|---|---|---|
| 设备身份 | ECDSA 签名 | 协调服务 / 其他设备 |
| 统一标识 | HMAC + 会话密钥 | 每台设备本地校验 |
风险点:若设备离线后仍持有旧签名,可能尝试重放已被销毁的作用域标识。应在信任链路中加入时间戳与作用域序号(Epoch)。
4.6 选型权衡
| 决策 | 方案 A | 方案 B | 推荐场景 |
|---|---|---|---|
| 标识生成 | UUID v4 | 命名空间哈希(如 SHA‑256(设备列表+时间)) | 需要确定性标识时选 B |
| 作用域存储 | 进程内 Map | 持久化 SQLite | 设备频繁重连时选 B |
| 跨设备同步 | 广播 + 本地复制 | 集中协调服务 | 设备数量 ≤ 5 选 A |
4.7 风险点
- 标识冲突:不同会话巧合产生相同 CrossDeviceId → 解决:在标识前附加会话发起设备公钥指纹。
- 作用域泄漏:设备离线后作用域未被及时销毁 → 设置心跳超时强制回收。
- 状态不一致:设备 A 已离开,设备 B 仍在向作用域写入 → 引入写前校验(Write-Ahead Check)机制。
4.8 落地步骤
- 定义统一标识格式(前缀 + 设备列表哈希 + 会话序号)。
- 实现
CrossDeviceIdGenerator接口并注入为@Singleton。 - 创建
@CrossDeviceScope注解,标注其在 Hilt 模块中的定义。 - 在
Application级别初始化CrossDeviceScopeManager。 - 在设备配对成功后调用
manager.getOrCreate(crossDeviceId)。 - 集成信任链路模块,单元测试签名与验证流程。
4.9 验收标准
| 编号 | 验收要求 | 验证方式 |
|---|---|---|
| AC‑01 | 同一设备集合在不同时间生成的 CrossDeviceId 相同 | 自动化重复测试 |
| AC‑02 | 设备 A 注入的依赖实例与设备 B 通过同一 ID 获取的实例是同一个 Java 对象 | 对象引用比较(需可序列化验证) |
| AC‑03 | 设备离线后 30 s 内作用域被销毁,再次上线需创建新作用域 | 日志 + 依赖计数检查 |
| AC‑04 | 伪造的 CrossDeviceId 被信任链路拒绝,注入失败 | 注入异常验证 |
实践建议:在验收阶段使用
@Inject 注入一个 Counter 对象,在设备 A 中递增,检查设备 B 中读取的值是否同步变化,以此验证跨设备作用域单例。