# 浏览频道与成员 > 用户流程:分配到默认频道 → 浏览频道列表 → 浏览频道中的人员分布 → poke 服务器内成员 > 对应程序流程:TsClient.getChannelList() 建立频道基线 → TsClient.getClientList() 建立成员基线 → 持续接收 onClient* 事件增量更新 --- ## 一、状态树 ### 同步状态 ```mermaid flowchart TD SYNC[同步状态
建立服务器数据基线] SYNC --> S0[未同步 Unsynced
已连接但尚无完整数据] SYNC --> S1[同步中 Syncing
调用 getChannelList 和 getClientList] SYNC --> S2[已同步 Synchronized
列表基线可供 UI 使用] SYNC --> S3[同步失败 SyncFailed
列表请求失败或上下文失效] ``` ### 成员状态 ```mermaid flowchart TD MEMBER[成员状态
维护在线用户及频道位置] MEMBER --> M0[空基线 Empty
尚未执行 ListClients] MEMBER --> M1[已有基线 Loaded
getClientList 已建立成员表] MEMBER --> M2[增量更新 Updating
OnClientEnter/Leave/Moved 正在归并] MEMBER --> M3[不一致 Inconsistent
事件引用未知成员或频道] ``` ### 成员实体状态树 ```mermaid flowchart TD ENTITY[成员实体状态树
描述 ListClients 基线与实时事件如何协同] ENTITY --> BASE[基线分支
由客户端请求建立完整成员集合] ENTITY --> DELTA[增量分支
由服务端推送维护实时变化] ENTITY --> SELF[当前用户分支
通过 ClientID 识别自己的事件] ENTITY --> REPAIR[修复分支
处理事件缺失或引用不一致] BASE --> B1[getClientList 请求
获取服务器当前全部在线客户端] B1 --> B2[按 clientID 建表
使用客户端 ID 去重保存] B2 --> B3[按 channelID 建索引
派生每个频道的成员列表] DELTA --> D1[onClientEnter
新增或覆盖进入视野的成员] DELTA --> D2[onClientMoved
覆盖成员的 channelID] DELTA --> D3[onClientLeave
按 clientID 幂等删除成员] SELF --> S1[getClientId 本地调用
读取服务器分配的自身 ID] S1 --> S2[事件 clientID 比对
判断移动或离开事件是否属于自己] S2 --> S3[更新自身频道事实
自己的移动决定实际所在频道] REPAIR --> R1[检测未知 clientID
移动或离开事件找不到实体] R1 --> R2[重新调用 getClientList
用完整列表修复成员基线] REPAIR --> R3[检测未知 channelID
成员引用本地不存在的频道] R3 --> R4[重新调用 getChannelList
用完整列表修复频道基线] ``` --- ## 二、时序:成员进入、移动与离开 ```mermaid sequenceDiagram participant TS as TeamSpeak 服务器
产生在线成员变化 participant SDK as Go SDK (gomobile)
解析 notify* 通知 participant BRIDGE as TSBridge
JNI 回调转 EventBus participant EB as EventBus
事件合并与分发 participant REDUCER as ViewModel
debounce 后刷新成员 participant STORE as 成员实体仓库
以 clientID 保存唯一成员 participant UI as 频道树 UI
派生人数和成员预览 TS-->>SDK: notifycliententerview SDK->>BRIDGE: OnClientEnter(Client) BRIDGE->>EB: emit(ClientEnter) TS-->>SDK: notifyclientmoved SDK->>BRIDGE: OnClientMoved(id, channelID) BRIDGE->>EB: emit(ClientMoved) TS-->>SDK: notifyclientleftview SDK->>BRIDGE: OnClientLeave(id, reason) BRIDGE->>EB: emit(ClientLeave) Note over EB: debounce 300ms 合并 EB->>REDUCER: collect → refreshClientList() REDUCER->>STORE: TSBridge.getClientsJSON() → 全量刷新 STORE-->>UI: 实体引用变化 ``` --- ## 三、事件依赖 ### 成员相关前置依赖矩阵 | 操作或事件 | 必须依赖 | 建议依赖 | 依赖失败时的处理 | | --- | --- | --- | --- | | `getClientsJSON` 增量归并 | 已有频道基线更易校验 | 与 `getChannelsJSON` 同一同步批次 | 未知频道触发频道补偿读取 | | `ClientEnter` 事件 | 已注册 EventBus 事件收集 | 已有成员基线 | debounce 后全量刷新 | | `ClientMoved` 事件 | 已注册 EventBus 事件收集 | 已有成员基线和自身 ID | debounce 后全量刷新 | | `ClientLeave` 事件 | 已注册 EventBus 事件收集 | 已有成员基线 | debounce 后全量刷新 | ### 服务端通知映射 | 服务端通知 | TSBridge 回调 → EventBus 事件 | 上层状态依赖 | 中文说明 | | --- | --- | --- | --- | | `notifycliententerview` | `OnClientEnter` → `ClientEnter` | 成员实体表、频道成员 Selector | 新成员进入视野后 debounce 合并刷新 | | `notifyclientleftview` | `OnClientLeave` → `ClientLeave` | 成员实体表、离开原因 | debounce 合并刷新 | | `notifyclientmoved` | `OnClientMoved` → `ClientMoved` | 成员位置、当前用户频道 | debounce 合并刷新 | | `notifyclientpoke` | `OnPoked` → `Poked` | Poke 通知或成员卡 | 立即分发,展示发送者和 Poke 内容 | ### 缺失事件能力带来的依赖限制 根据 SDK 文档,当前公开事件不包含频道创建、频道更新和频道删除。因此: 1. `TsClient.getChannelList()` 是频道目录的主要权威来源。 2. 不能假设频道列表会依靠 `onClient*` 事件永久保持最新。 3. 发现成员引用未知频道时,应重新调用 `TsClient.getChannelList()`。 4. 如果产品需要实时频道管理,应扩展 SDK 对相应 `notify*` 通知的支持。 ### 权威性划分 | 数据 | 推荐权威来源 | 原因 | | --- | --- | --- | | 成员实时变化 | EventBus `ClientEnter/Leave/Moved` 事件 | 服务端主动推送实时事实,debounce 后全量刷新 |