Skip to content

录音转写:录制进行中 + 断点上传 + 会话同步 + 推送回流 ​

一句话定位 ​

这是一个把 移动端录音、后台上传、服务端转写、结果同步、推送唤醒回流 串成完整链路的业务专题页,用来回答:一个真实可上线的“会议录音转文字”功能,后台任务到底该分层落在哪。

代码索引 ​

主题Lab 说明源码
Flutter 会话状态机recording-sync-state-machinerecording_sync_state_machine_demo.dart
Android 录音交接上传recording-upload-handoffRecordingUploadHandoffActivity.kt
推送丢失后的补拉恢复push-miss-sync-recoverypush_miss_sync_recovery.mjs

为什么需要 ​

录音转写类业务经常把多个后台问题误当成同一件事:

  • 为什么“录音在后台继续”不等于“上传、转写、同步也都能一直在客户端稳定跑”?
    • 一句话答:因为录音、上传、转写、回流的系统语义不同,只有录音更接近持续可感知任务,后几段必须拆给系统调度或服务端状态机。
  • 为什么 Flutter 插件把录音、上传、推送都包起来后,仍不等于跨端能力已经拉平?
    • 一句话答:Flutter 能统一业务状态与调用入口,但不能凭空拿到 Android / iOS 的后台执行授权。
  • 为什么 silent push / 厂商推送 / 前台服务拼在一起,依然凑不出“准实时、永不丢”的保活模型?
    • 一句话答:因为这些能力都不是最终真相源,推送可能丢,前台服务只限 Android 持续任务,iOS 还会进一步收紧机会窗。
  • 为什么只靠客户端多写几层重试,不足以顶掉服务端状态机与幂等设计?
    • 一句话答:因为多端登录、重装恢复、推送丢失与任务幂等都需要服务端持有统一状态与任务 ID 才能收敛。

真实线上事故通常是:

  • 录音能继续,上传却在退后台后被中断
  • Android 方案看起来成立,iOS 上却经常只能机会性补传
  • 转写结果已经生成,客户端却因为没有稳定唤醒窗口而迟迟不同步
  • 同一条音频被重复上传、重复转写、重复入库,最后用户看到多份会议纪要

所以这个场景很适合用来强化 03-background-tasks 的业务落位层:

  • 录音 不等于 上传
  • 上传 不等于 转写完成通知
  • 推送唤醒 不等于 可靠后台同步
  • 客户端后台能力 不等于 业务结果可达成

代码索引 ​

当前专题页以业务落位决策为主,暂未绑定可运行实验;相关机制文档见 §底层机制 内的模块链接。

底层机制 / 决策 ​

1. 先拆成四段,不要试图用一个能力兜全链路 ​

对应 Lab:recording-sync-state-machine · recording_sync_state_machine_demo.dart

1.1 四段责任落位总图 ​

下图刻意不用 Mermaid,而用文档内嵌 SVG:因为它同时要表达 四个业务阶段、Android / Flutter / iOS / 服务端四条责任泳道、以及阶段间交接关系。这类总装配图如果硬塞进 Mermaid,节点会很多、换行会挤、责任边界也不够直观;用 SVG 更容易长期维护语义与排版。

录音、上传、转写、回流四段责任落位图展示会议录音转写链路中 Android、Flutter、iOS、服务端分别在录制、上传、转写、回流四个阶段承担什么责任,以及哪些阶段应从客户端移交到服务端真相源。1 录制阶段持续采集、可感知、别丢音频2 上传阶段断点续传、联网约束、失败恢复3 转写阶段异步识别、摘要、幂等状态机4 回流阶段推送提醒、补拉、会话刷新Android / 宿主Flutter / 页面与状态iOS / 能力边界服务端 / 真相源Foreground Service或前台可见录音会话负责采集、落盘、避免被杀WorkManager默认承担补传大文件急传时才升级前台任务不该长期压客户端最多提交任务 ID、展示状态推送 + 前台补拉结果到达后尽快刷新但不承诺后台必秒达录音按钮 / 权限提示波形、时长、会话状态发起上传展示进度、失败重试入口只展示 job 状态transcribing / done / failed会话页回流前台恢复后主动补拉详情合法后台音频能力能录音 ≠ 后续链路都稳机会性上传系统允许时补传,不承诺准点不要指望本机常驻转写必须服务端兜底silent push / BGTask只算机会窗,不算必达通道uploadSessionIdfileId / 幂等键uploaded / retrying失败原因、断点恢复transcribing / partial_readydone / failed 状态机推送、列表提醒、多端补拉任何设备都能恢复结果客户端上传后移交服务端真相源长耗时处理必须服务端化关键收口:客户端负责“采集、发起、展示、补拉”,服务端负责“任务状态机、幂等、最终可达”。

这张图解决的不是“阶段名词记忆”,而是一个更容易混淆的问题:到底是哪一端在每一段真正负责执行,哪一段开始必须把真相源移交给服务端。

录音转写至少要拆成四段:

  1. 录制阶段:麦克风采集、文件落盘、时长/波形/UI 状态展示
  2. 上传阶段:音频文件切片或整文件上传、失败重试、断点续传、网络约束
  3. 转写阶段:服务端异步转码、识别、摘要、结构化入库
  4. 回流阶段:客户端获知结果、刷新会话页、拉取纪要与状态
业务阶段核心诉求Android 更像iOS 更像Flutter 更适合扮演什么
录制阶段持续进行、用户可感知Foreground Service / 前台可见录音会话合法后台音频模式内的录音会话控制层、页面状态、权限交互
上传阶段可靠完成、网络约束、断点续传WorkManager 或前台明确进行中的上传任务系统允许的后台上传模型 / 机会性补传发起、展示进度、恢复后查询状态
转写阶段长耗时、可重试、幂等、可扩展不该长期压在客户端不该长期压在客户端只展示服务端任务状态
回流阶段尽快看到结果,但不承诺秒达推送 + 前台拉取 + 机会补拉推送 / 前台进入 / 系统机会窗补拉会话刷新、结果落地展示

核心决策是:

  • 录制要看用户感知与系统白名单
  • 上传要看可靠完成与网络约束
  • 转写必须服务端化
  • 结果回流不要绑定到“客户端一定能被叫醒”这个幻想上

2. Android:录制与上传经常要分两个模型 ​

对应 Lab:recording-upload-handoff · RecordingUploadHandoffActivity.kt

录音进行中时,Android 更接近前台服务语义;但上传阶段不一定继续使用前台服务。

如果满足这些条件:

  • 只是“录音结束后补上传”
  • 用户不要求必须盯着实时进度
  • 允许联网后再传、失败后稍后重试

那么它更像 WorkManager:

  • 带网络约束
  • 可恢复
  • 不要求持续前台常驻

只有在下面情况,上传才更像“进行中的前台任务”:

  • 用户主动点击“立即上传,别退出”
  • 文件很大,且用户明确期待看到持续进度
  • 上传过程中如果中断,业务损失高,且产品接受通知常驻

这也是为什么要把“录制完成 → uploadSessionId 建立 → 本地文件延后清理”单独做成 handoff 样板,而不是把上传继续塞在录音页里。

3. iOS:录音能成立,不代表录音后的全链路后台都成立 ​

iOS 的真实困难不是“录音 API 没有”,而是:

  • 后台执行窗口由系统严格控制
  • 后台上传/刷新不是任意时刻都能稳定继续
  • silent push、BGTask、后台刷新都更像机会窗

因此 iOS 上要反向约束产品承诺:

  • 允许录音完成后 不一定立刻上传完
  • 允许转写结果 不一定秒级回流到本机
  • 把“最终会同步成功”建立在 服务端任务状态机 + 客户端机会性补拉 上

4. Flutter:适合表达业务意图,不适合承担后台真执行 ​

对应 Lab:recording-sync-state-machine · recording_sync_state_machine_demo.dart

在这个案例里,Flutter 更适合负责:

  • 会话页状态机:recording / waitingUpload / uploading / transcribing / synced / failed
  • 录音按钮、权限说明、上传进度、转写结果 UI
  • 调宿主录音能力、上传任务注册、查询当前任务状态
  • App 回前台后主动补拉会话详情

Flutter 不适合负责:

  • 把 Dart isolate 当录音保活器
  • 指望页面销毁后 Dart 逻辑继续稳定顶住长链路上传
  • 把推送消息到达后的后台执行能力想成平台无差别

结论是:Flutter 在这个场景里最值钱的是统一业务状态,不是统一后台能力。

5. 服务端必须成为“真相源”,而不是客户端的被动附庸 ​

对应 Lab:push-miss-sync-recovery · push_miss_sync_recovery.mjs

录音转写是典型的异步链路,服务端至少应承担:

  • 上传会话 uploadSessionId
  • 音频文件 fileId
  • 转写任务 transcriptionJobId
  • 幂等键:避免重复提交生成多份任务
  • 状态机:pending_upload / uploaded / transcribing / partial_ready / done / failed
  • 失败原因与可恢复标记

如果这些都没有,客户端就会出现:

  • 重试一次就重复建任务
  • 多端登录时状态互相覆盖
  • 推送丢失后无法靠主动拉取恢复
  • 用户删了页面但服务端仍在继续转写,状态无法收敛

常见场景 ​

场景 1:采访/会议录音,录制中允许切后台,录完后联网再传 ​

特征:

  • 用户更关心“录音别丢”而不是“录完秒传”
  • 文件可能较大
  • 现场可能无网或弱网
  • 可以接受稍后转写完成

建议落位:

  • Android:录制阶段走 Foreground Service;录制结束后上传交 WorkManager
  • iOS:录音按合法音频能力设计;上传按机会性 / 系统允许模型处理
  • Flutter:展示录制状态、上传中 / 待上传、转写排队中
  • 服务端:兜底任务状态,不依赖客户端常驻

场景 2:用户点击“结束会议”后必须尽快把纪要发给群成员 ​

特征:

  • 时效要求比普通录音高
  • 结果要发给其他人,不只是本机查看
  • 即使当前设备掉线,也希望结果最终能产出

建议落位:

  • 录制结束后立即触发上传
  • 若客户端没传完,允许用户重新打开 App 后继续补传
  • 转写与纪要生成全部服务端化
  • 群成员收到结果依赖服务端消息/推送,不依赖原录制设备在线

这里最重要的不是“手机能不能一直后台跑”,而是:原始音频一旦安全入服务端,后续链路就不应再绑定单机存活。

场景 3:即时语音笔记,用户期待“录完马上看到文字” ​

特征:

  • 对主观体验要求高
  • 但平台后台能力仍然有限

建议落位:

  • 前台内优先给快速反馈:本地先显示“处理中”
  • 如果用户没离开页面,可走前台直传 + 轮询 / 流式回流
  • 一旦退后台,立刻降级为“稍后同步完成后提醒我”模型

这类场景适合做 前台快路径 + 后台保守路径 双轨设计,而不是一条路径赌到底。

场景 4:跨设备查看录音纪要 ​

特征:

  • 用户可能在 A 设备录音,在 B 设备看结果
  • 会话一致性比本机保活更重要

建议落位:

  • 服务端维护统一任务状态
  • 任意设备进入会话页时都可主动拉取
  • 推送只是“加速看到结果”,不是唯一通知通道

这会自然把设计重心从“本机后台保活”转向“服务端真相源 + 多端同步模型”。

常见坑 ​

坑线上现象为什么会发生修法
把录音、上传、转写都塞进一个前台服务 / 插件Android 看似能跑,iOS 完全不稳不同阶段系统语义不同拆阶段:录制、上传、转写、回流分别建模
把 isolate 当后台上传方案页面退后台或进程被挂起后上传丢失isolate 不是系统后台授权上传交宿主能力或系统允许模型
依赖 silent push 做唯一回流用户经常收不到已完成纪要push 只是机会型触发加前台进入补拉、列表页轮询、手动刷新
客户端本地状态当真相源重装 / 换机 / 多端登录后状态错乱状态没服务端化服务端维护任务状态机与幂等键
录音结束立刻删本地文件上传失败后无法恢复文件生命周期设计错误直到服务端确认 uploaded 再清理本地
同一音频重复点提交生成多份转写任务没有幂等键用会话 ID + 文件指纹做幂等控制
把“结果尽快到达”承诺成“后台一定秒达”iOS 经常被投诉不稳定超出系统边界改成“尽快回流 + 下次进入必补齐”

与相近概念对比 ​

概念它解决什么在本案例里不该被误用成什么
Foreground ServiceAndroid 上持续用户可感知任务全链路万能保活器
WorkManager延迟、约束、可恢复的后台工作录音进行中的持续执行器
isolate / computeDart 计算隔离跨平台后台执行授权
silent push结果回流的加速触发器稳定后台时钟或必达唤醒器
后台音频模式合法录音 / 播放场景白名单任意业务后台常驻许可证
服务端转写任务真正长期、可重试、可扩展处理链路可有可无的“后补优化”

对应实验 ​

Lab说明源码
recording-sync-state-machineFlutter 会话状态机:上传、转写、推送丢失补拉recording_sync_state_machine_demo.dart
recording-upload-handoffAndroid 录音结束后的 uploadSessionId 建立与本地文件生命周期RecordingUploadHandoffActivity.kt
push-miss-sync-recovery推送丢失后靠前台补拉与多端进入恢复结果push_miss_sync_recovery.mjs

复习检查题 ​

  1. 为什么“会议录音转写”是一个适合讲后台任务分层的高价值业务案例?

    答:因为它把录制、上传、服务端异步处理、结果回流四种完全不同的系统语义串在了一起,能逼着我们区分哪些该落客户端持续任务,哪些该落系统调度,哪些必须服务端化,哪些只能机会性同步。

  2. 为什么录音结束后的上传,不应该天然继续沿用录音阶段的前台服务模型?

    答:因为录制阶段强调持续进行与用户可感知,而上传阶段更强调可靠完成、联网约束、断点续传与失败恢复。两者系统语义不同,很多情况下上传更适合交给可恢复的后台调度,而不是继续占用持续前台执行。

  3. 为什么推送回流在这个案例里只能算“加速器”,不能算“真相源”?

    答:因为推送到达与否、何时到达、到达后是否获得足够执行窗口,都受平台与设备状态影响。真正的结果状态必须由服务端保存,客户端只能靠推送加速感知,再用前台进入或机会性补拉收敛状态。

  4. 为什么跨端实现要默认以 iOS 的保守边界来约束产品承诺?

    答:因为 Android 上看似可以通过前台服务、调度器和通知把链路做得更激进,但 iOS 对后台执行窗口、刷新时机和静默唤醒都更严格。若产品承诺建立在 Android 最强模型上,跨端上线后就会在 iOS 上持续暴露稳定性问题。

速记 ​

  • 录音转写不是一个后台任务,而是四段链路
  • 录制看持续感知,上传看可靠完成,转写看服务端,回流看机会补齐
  • Flutter 统一业务状态,不统一系统授权
  • 推送是加速器,不是保活器
  • 真正要稳定,必须让服务端成为任务真相源

站点构建时间:2026/8/24 23:43:17