SDK 运行状态与事件处理契约
本文档定义 SDK 通过 onStateChanged、onError、onEvent 向 App/Demo 暴露的运行信号含义和推荐处理方式。在线、离线共用同一套契约表,通过“适用范围”区分 common、online、offline、offline/listen、offline/oneToOne、model。当前阶段不新增 SDK public API,不引入复杂自动恢复控制器;SDK 负责输出状态、错误、事件,Demo/App 负责消费并展示。
设计原则
- 状态单一来源:通道状态只能由 SDK 内部决定,Demo/App 不自行伪造
running、failed等状态。 - 业务逻辑优先:不改变 ASR、翻译、TTS、PCM 推送、建房、建通道、语言和音色等核心业务路径。
- 回调含义稳定:共享错误使用统一处理语义;平台特有错误码差异会在对照表中明确标注。
- 在线离线统一消费:Demo/App 使用同一套状态、错误、事件处理入口;差异通过
适用范围和reason/code/event判断。 - 主动取消不打扰用户:页面退出、用户主动停止、请求取消不弹错误框。
- 断网只走状态:通道进入
running后的网络/RTC 断开,SDK 只通过onStateChanged(reconnecting)通知,不触发onError。NETWORK_UNAVAILABLE(2001107)在运行期断网场景仅写入状态快照的code字段,不作为onError抛出。上层判断断网必须监听onStateChanged,仅监听onError收不到运行期断网。
onStateChanged 统一对照表
| 适用范围 | state | reason | 说明 | 常见原因/触发来源 | SDK 处理 | Demo/App 推荐处理 |
|---|---|---|---|---|---|---|
| common | idle | none | 通道未启动或已初始化等待启动。 | 页面刚进入、引擎初始化完成、离线 pipeline idle。 | 不推流,不接收业务数据。 | 显示待启动或初始化状态。 |
| common | starting | startRequested | 通道启动流程开始。 | 调用 start 或创建通道后内部启动。 | 建房、初始化实时链路、准备离线 pipeline、订阅消息/TTS。 | 显示“通道连接中/正在加载”,禁止重复创建。 |
| online | starting | rtcConnecting | 在线媒体通道连接中。 | 当前底层 RTC/媒体通道正在入会。 | 等待连接结果。 | 保持 loading,不允许开始采集。 |
| online | starting | rtcConnected | 媒体通道已连接,但完整业务链路未必全部 ready。 | 媒体连接早于消息/TTS 订阅完成。 | 继续等待其他启动依赖。 | 仍显示连接中,不自行置为 ready。 |
| offline/listen | starting | startRequested | 离线收听 pipeline 正在准备。 | 离线 ASR/MT/TTS pipeline preparing。 | 加载模型并准备单路 pipeline。 | 显示“正在加载离线模型/通道连接中”。 |
| offline/oneToOne | starting | startRequested | 离线一对一左右 pipeline 未全部 ready。 | 左右 pipeline 任一处于 preparing。 | 等待左右声道 pipeline 均就绪。 | 显示“正在加载离线模型”,禁止采集。 |
| common | running | started | 通道完整可用。 | 在线链路和业务依赖 ready;离线 pipeline running。 | 允许推流、收消息、接收音频。 | 显示“通道已就绪”,允许用户开始采集。 |
| online | running | rtcConnected | 在线连接恢复或媒体通道连接成功。 | 底层连接回调成功。 | 保持可用。 | 隐藏重连/弱网提示。 |
| common | running | networkRestored | 系统网络恢复。 | 网络从不可用变为可用。 | 保持或恢复可用。 | 隐藏网络提示。 |
| online | degraded | networkUnavailable | 网络能力受损但对话未终止。 | QoS 差、丢包高或网络波动。 | 尽量保持通道,不主动失败。 | 非阻塞弱网提示,不弹错误框。 |
| online | degraded | messageChannelFailure | 消息链路异常但通道暂未终止。 | RTM/stream message 短暂异常。 | 记录诊断,保留通道。 | 弱提示或日志,不立即离开。 |
| online | degraded | rtcTokenRequested | 底层请求 token 续期。 | token 即将过期或底层要求续期。 | 记录状态,后续可扩展续期。 | 不打断用户,仅日志。 |
| online | degraded | rtcTokenWillExpire | token 即将过期。 | 底层 token will expire 回调。 | 记录状态。 | 不打断用户,仅日志。 |
| online | reconnecting | networkUnavailable | 网络不可用,等待恢复。 | 系统网络断开。 | 保留当前状态,等待底层恢复或后续失败。 | 显示“连接恢复中”,禁止重复创建。 |
| online | reconnecting | rtcInterrupted | 媒体链路短暂中断。 | 底层连接 interrupted。 | 保留房间和通道。 | 显示重连中,不弹 fatal。 |
| online | reconnecting | rtcLost | 媒体链路丢失。 | 底层 lost/keepalive timeout。 | 等待底层恢复或升级失败。 | 显示重连中,等待 SDK 最终失败。 |
| online | reconnecting | messageChannelFailure | 消息链路恢复中。 | RTM 断开、订阅失败。 | 保留通道并记录。 | 显示重连中或弱提示。 |
| common | stopping | stopRequested | 通道停止中。 | 用户退出、页面销毁、主动 stop。 | 停止音频、取消任务、释放资源。 | 禁用按钮,显示停止中。 |
| common | stopped | stopped | 通道已停止。 | stop/release 完成。 | 已清理通道状态。 | 显示已停止或离开页面。 |
| online | failed | sessionExpired | 会话或 token 已失效。 | INVALID_TOKEN、token expired。 | 停止当前会话,不复用旧 token。 | 弹窗:重新创建或离开。 |
| common | failed | invalidConfiguration | 配置错误。 | appId、channel、参数、离线声道或音色配置非法;语言代码非法或当前模式不支持时优先使用 2001113 / INVALID_LANGUAGE_CODE。 | 不重试。 | 弹窗:配置错误,离开或提示检查配置。 |
| common | failed | permissionDenied | 权限不足。 | 麦克风或系统权限不足。 | 不继续启动。 | 弹窗:去授权或离开。 |
| online | failed | bannedByServer | 服务端封禁。 | 底层返回 banned。 | 停止并释放。 | 弹窗:对话不可用,离开。 |
| online | failed | serviceRejected | 服务端拒绝、房间关闭或服务端音频用户离线。 | rejected、join failed、close_room、建房返回的 translation_list.subscribe_uid 离线。 | 停止或标记当前对话不可继续,抛出错误和事件。 | 弹窗:重新创建或离开。 |
| offline | failed | serviceRejected | 离线能力或模型资源不满足。 | 账号无离线能力、模型缺失、模型需要更新。 | 不启动离线 pipeline。 | 引导鉴权、下载或更新模型。 |
| online | failed | rtcKeepAliveTimeout | 连接恢复失败。 | 长时间无法恢复或底层保活失败。 | 停止并释放。 | 弹窗:重新创建或离开。 |
| common | failed | engineError | 引擎内部错误。 | 在线底层链路异常;离线 pipeline didFail。 | 根据错误码释放或保持诊断。 | 弹窗:在线重新创建,离线重新初始化,或离开。 |
当前离线实现不产生
rtcConnecting、rtcConnected、rtcInterrupted、rtcLost、rtcKeepAliveTimeout、messageChannelFailure等 RTC/RTM 状态原因;这些原因只适用于在线通道。离线 UI 仍消费同一套state,但原因主要来自 pipeline 和模型资源状态。
断网识别与跨平台一致性
断网在不同阶段走不同回调通道,必须按阶段区分判断,iOS 与 Android 行为完全一致。
| 阶段 | 触发条件 | 回调通道 | 判断依据 | 两端一致性 |
|---|---|---|---|---|
启动期(start 尚未成功) | 建房/建通道/鉴权等网络请求失败或超时 | onError(iOS 同时 completion(.failure),Android 同时 CreateRoomCallback/CreateChannelCallback.onError) | iOS 通常为 2002002 或 HTTP 细分码;Android 的建房、建通道、鉴权超时为 2001107;鉴权失败为 2001102 | 平台错误码差异已显式说明 |
| 运行期·系统断网 | 系统网络从可用变不可用 | onStateChanged | state == reconnecting 且 reason == networkUnavailable 且 code == 2001107 | 一致 |
| 运行期·RTC 链路中断 | 底层 RTC interrupted / lost / keepAliveTimeout | onStateChanged | state == reconnecting 且 reason ∈ {rtcInterrupted, rtcLost, rtcKeepAliveTimeout} | 一致 |
| 网络/链路恢复 | 网络或 RTC 恢复 | onStateChanged | reason == networkRestored,或 state == running 且 reason == rtcConnected | 一致 |
| 长时间无法恢复(升级失败) | 重连超时、底层保活失败 | onStateChanged(failed) + onError 双发 | state == failed 且 reason == rtcKeepAliveTimeout/engineError,并伴随 onError(2001107/...) | 一致 |
判断要点:
- 运行期断网只发
onStateChanged(reconnecting),不发onError。上层不要依赖onError感知运行期断网;networkUnavailable(2001107)此时只在状态快照code字段出现。 - 启动期断网(建房/鉴权失败)才走
onError/失败回调,与运行期断网是两个不同阶段,不要混淆。 - 断网快照
isRecoverable == true,应显示“连接恢复中”并禁止重复创建通道;只有升级为failed时才弹窗让用户重新创建或离开。 - 离线模式不依赖网络:iOS 离线引擎与 Android 离线模式均不响应系统断网(
handleSystemNetworkStatusChange仅对online生效),断网不产生回调,属预期。
App 端断网处理流程
App/Demo 在 iOS、Android 上使用同一套处理流程,唯一入口是 onStateChanged(断网不进 onError)。SDK 负责自动重连,App 只负责展示与用户决策,不要自己 stop/release/重建通道去“手动重连”。
处理步骤:
- 在
onStateChanged回调中读取snapshot.state与snapshot.reason。 state == reconnecting(断网中,reason为networkUnavailable或rtcInterrupted/rtcLost/rtcKeepAliveTimeout):- 显示“网络连接异常,正在恢复中…”的非阻塞提示(横幅/状态栏),不弹错误弹窗。
- 暂停或屏蔽采集相关操作,禁用“开始采集”按钮,禁止重复创建通道。
- 不释放当前会话引用,等待 SDK 后续状态。
state == running(reason == networkRestored或rtcConnected,即恢复):- 清除“正在恢复中”提示,恢复按钮可用,允许继续采集。
state == failed(重连最终失败,isRecoverable == false,常见reason == rtcKeepAliveTimeout/engineError,并伴随onError):- 停止录音/播放,弹窗让用户“重新创建对话/重新初始化”或“离开”。
- 以
onError的code/message做归因与文案,以onStateChanged(failed)同步 UI;两端均为双发,不要重复弹两个窗。
- 主动停止/退出页面:调用
stop后收到stopping/stopped属正常,断网中退出不弹错误框。
统一处理伪代码(iOS/Android 逻辑一致,仅 API 名风格不同):
onStateChanged(snapshot):
switch snapshot.state:
case reconnecting: // 断网中:SDK 正在自动重连
showReconnectingBanner() // 非阻塞提示
disableCaptureButton() // 禁止采集/禁止重复创建
case running: // 恢复
hideReconnectingBanner()
enableCaptureButton()
case failed: // 重连最终失败(不可恢复)
if snapshot.isRecoverable == false:
stopAudio()
showFatalDialog(recreate / leave) // 与 onError 同一事件,弹一次
case degraded: // 弱网但未断:仅弱网提示
showWeakNetworkHint()
default: updateStatusText(snapshot)
弱网(degraded 及 online_network_quality/online_rtc_stats 等丢包事件)不等于断网,仅做弱提示,不停止、不重建,详见后文“Demo 弱网与丢包处理策略”。
onError 统一对照表
所有错误码的说明、处理契约、恢复策略和等级划分请参见 错误码速查。
onEvent 与模型事件统一对照表
| 适用范围 | event/回调 | args/参数 | 说明 | Demo/App 推荐处理 |
|---|---|---|---|---|
| online | online_started | nil | 在线通道已就绪。 | 更新状态,允许采集。 |
| online | online_stopped | nil | 在线通道已停止。 | 清理 UI 状态。 |
| online | online_runtime_state_changed | TmkTranslationChannelStateSnapshot | 在线运行状态快照事件。 | 日志为主,UI 以 onStateChanged 为准。 |
| online | online_audio_metadata | TmkResult<String> | 在线音频 metadata。 | 诊断/trace,不弹窗。 |
| online | online_stream_message_raw | TmkResult<String> | 在线原始消息。 | 诊断,不展示给用户。 |
| online | online_stream_message_parsed | TmkResult<String> | 在线消息解析成功。 | 日志/埋点,不直接改变 UI。 |
| online | online_notification | TmkResult<String> | 在线服务端通知。 | kind=close_room 时弹重建/离开。 |
| common | notification | TmkResult<String> 或字典 | 兼容服务端/引擎通知。 | 按 kind 处理;未知通知只记录日志。 |
| online | online_recognition_failure | TmkResult<String> | 在线识别失败通知。 | 弱提示或日志。 |
| online | online_tts_state | TmkResult<String> | 在线 TTS 状态。 | 日志/播放状态。 |
| online | online_network_quality | TmkResult<String>,extraData.uid/tx_quality/rx_quality | 在线 RTC QoS 网络质量等级。 | 弱网提示或日志,不弹 fatal。 |
| online | online_rtc_stats | TmkResult<String>,extraData.tx_packet_loss_rate/rx_packet_loss_rate | 在线 RTC 通道收发总丢包率统计。 | 诊断和弱网判断;连续高丢包可弱提示,不直接关闭通道。 |
| online | online_remote_audio_stats | TmkResult<String>,extraData.uid/audio_loss_rate | 远端音频丢包统计。 | 诊断和弱网判断,不弹窗。 |
| online | online_local_audio_stats | TmkResult<String>,extraData.audio_loss_rate | 本地音频发送丢包统计。 | 诊断和弱网判断,不弹窗。 |
| online | online_remote_user_offline | TmkResult<String>,extraData.uid/reason_code/is_expected_service_uid | 在线 RTC 远端用户离线。is_expected_service_uid=true 表示建房返回的服务端音频/翻译订阅 uid 离线,对话已不可继续。Android 会同时将该场景升级为 failed/serviceRejected 和 RTC_OPERATION_FAILED(2003004)。 | is_expected_service_uid=true 时立即停止当前会话引用并弹窗:重新创建或离开;否则仅记录日志。 |
| offline | offline_stream_message_parsed | TmkResult<String> | 离线引擎内部消息解析成功,extraData 包含 kind、state、trace_id 等。 | 诊断/日志;UI 以识别、翻译、音频回调为准。 |
| offline | offline_recognition_failure | TmkResult<String> | 当前语音段识别失败。 | 弱提示或日志,不关闭通道。 |
| offline | offline_notification | TmkResult<String> | 离线通知类消息,kind 用于区分具体通知。 | 按 kind 处理;未知通知只记录日志。 |
| offline | offline_tts_state | TmkResult<String> | 离线 TTS 状态变化,例如 state=failed。 | 更新播放/合成状态;失败可弱提示。 |
| offline | offline_audio_metadata | TmkResult<String> | VAD/trace metadata,用于诊断和链路追踪。 | 诊断/埋点,不弹窗。 |
| offline | offline_pipeline_state | TmkResult<String> | pipeline 状态事件,state 可为 idle/preparing/running/stopping/released。 | 日志为主;UI 以 onStateChanged 为准。 |
| offline | offline_asr_partial | TmkResult<String> | ASR 中间结果。 | 可用于诊断;业务文本展示以 onRecognized 为准。 |
| offline | offline_asr_final | TmkResult<String> | ASR 最终结果。 | 可用于诊断;业务文本展示以 onRecognized 为准。 |
| offline | offline_mt_partial | TmkResult<String> | 翻译中间结果。 | 可用于诊断;业务文本展示以 onTranslate 为准。 |
| offline | offline_mt_final | TmkResult<String> | 翻译最终结果。 | 可用于诊断;业务文本展示以 onTranslate 为准。 |
| offline | offline_tts_output | TmkResult<String> | TTS 音频输出事件。 | 诊断/日志;音频播放以 onAudioDataReceive 为准。 |
| Android | tts_metadata_received | String | Android 当前 TTS trace metadata 事件。 | Android Demo 记录耗时日志。 |
| model | onOfflineModelEvent("offline_model_cancelled", nil) | nil | 用户或页面生命周期取消模型下载。 | 按取消处理,按钮恢复为可下载,不弹错误框。 |
| model | onOfflineModelEvent("offline_model_update_required", nil) | nil | 本地模型 manifest 或文件版本低于服务端要求。 | 标记“有更新,重新下载”,禁止直接启动离线通道。 |
| model | onOfflineModelDownloadProgress | fileName/index/total/downloaded/fileTotal | 当前模型包下载进度;downloaded == -1 表示未知进度下载中。 | 更新下载进度和取消按钮。 |
| model | onOfflineModelUnzipProgress | fileName/progress | 当前模型包解压进度,progress 取值 0.0~1.0。 | 更新解压进度。 |
| model | onOfflineModelReady | 无 | 全部必需模型下载并解压完成。 | 重新校验模型完整性,校验通过后创建离线通道。 |
| model | onOfflineModelPackageInfosChanged | [TmkOfflineModelPackageInfo] | 模型包状态集合变化。 | 刷新模型列表 UI。 |
| model | onOfflineModelError | TmkTranslationError | 下载、解压、manifest、校验或离线鉴权失败。 | 按错误码处理;下载错误恢复按钮,必要时提示用户重试。 |
离线模型包状态
离线模型包状态属于资源状态,不等同于通道运行状态。Demo/App 需要先根据模型包状态判断是否允许创建离线通道,再消费统一的 onStateChanged/onError/onEvent。
| package state | 说明 | Demo/App 推荐处理 |
|---|---|---|
ready | 资源包已完整就绪。 | 显示“已就绪”;所有必需包 ready 后可创建离线通道。 |
needsDownload | 资源包未下载。 | 显示“待下载”;禁止启动离线通道。 |
needsUpdate | 资源包需要更新。 | 显示“需更新”;提示重新下载。 |
resumable | 存在断点文件,可继续下载。 | 显示“可续传”;点击下载继续。 |
downloading | 正在下载。 | 显示进度;允许取消。 |
unzipping | 正在解压。 | 显示解压进度;避免重复触发下载。 |
failed | 下载或解压失败。 | 显示失败;允许重试。 |
cancelled | 下载已取消。 | 显示已取消;允许重新下载。 |
Demo 弱网与丢包处理策略
在线网络质量和丢包事件只用于可感知提示和诊断,不直接改变 SDK 通道状态。SDK 内部继续负责重连、恢复、失败升级和错误码抛出;Demo/App 不应因为单次弱网事件主动停止、释放或重新创建通道。
| 事件 | 判断规则 | Demo/App 处理 | 是否弹窗 | 是否重建通道 |
|---|---|---|---|---|
online_network_quality | tx_quality/rx_quality >= 6 连续出现。 | 显示“网络连接异常,正在恢复...”,保留当前通道,等待 SDK 状态或错误回调。 | 否 | 否 |
online_network_quality | tx_quality/rx_quality >= 4 连续出现。 | 显示“当前网络较差,翻译可能延迟”。 | 否 | 否 |
online_network_quality | tx_quality/rx_quality == 3 连续出现。 | 显示“当前网络不稳定,翻译可能延迟”。 | 否 | 否 |
online_rtc_stats | tx_packet_loss_rate/rx_packet_loss_rate >= 25 连续出现。 | 显示“音频网络丢包严重,正在恢复...”,记录日志。 | 否 | 否 |
online_rtc_stats | tx_packet_loss_rate/rx_packet_loss_rate >= 10 连续出现。 | 显示“当前音频网络不稳定,翻译可能延迟”。 | 否 | 否 |
online_remote_audio_stats | audio_loss_rate >= 10 连续出现。 | 显示弱网提示,辅助定位远端音频接收质量。 | 否 | 否 |
online_local_audio_stats | audio_loss_rate >= 10 连续出现。 | 显示弱网提示,辅助定位本地音频发送质量。 | 否 | 否 |
online_remote_user_offline | is_expected_service_uid=true。 | 当前服务端音频/翻译订阅 uid 离线,对话不可继续;Demo 立即停止当前会话引用并弹“重新创建/离开”。SDK 同步上抛 failed/serviceRejected 与 RTC_OPERATION_FAILED,用于 App 统一错误处理和日志归因。 | 是 | 用户确认后重建 |
Demo 需要对网络事件做连续采样防抖,避免单次 Agora QoS 抖动造成 UI 频繁跳变。当前 iOS 与 Android Demo 均采用同类策略:弱网/丢包事件仅更新状态栏文案;真正需要用户决策的情况仍以 onStateChanged(.failed)、onError 或 close_room 通知为准。
Demo 统一处理要求
degraded:仅弱网或能力受损提示,不停止录音/播放。reconnecting:显示连接恢复中,禁止重复创建通道。running:清除弱网/重连提示,允许用户操作。failed + recoverable=false:停止录音/播放,弹窗让用户重新创建/重新初始化或离开。close_room:停止录音/播放,释放当前 Demo 引用,弹窗让用户重新创建或离开。online_remote_user_offline + is_expected_service_uid=true:按当前在线对话不可继续处理,停止录音/播放,释放当前 Demo 引用,弹窗让用户重新创建或离开。- Demo 不自行伪造 SDK 状态,不把
starting改成running。 - 在线失败后的主要动作是“重新创建对话”;离线失败后的主要动作是“重新初始化离线通道/重新检查模型资源”。