直接回答
因旋转、语言变化或窗口缩放而发生的 Activity 重建,会创建一个新的屏幕实例;这不代表用户希望模型请求从头再跑。Android 官方建议将业务状态放在合适的状态持有者中,例如 ViewModel,并单独恢复少量界面状态。对本地 AI 来说,关键是明确所有权:屏幕负责显示任务,更长生命周期的应用或功能协调器负责运行任务及原生资源。如果引擎或协程由屏幕本身拥有,界面销毁时可能关闭用户仍期待完成的操作;如果新屏幕因首次组合而盲目再次提交,模型又会收到重复请求。 测试前先让任务身份明确。每次用户操作生成稳定的 operation ID,并维护有边界的状态,例如排队、运行、部分完成、成功、失败或已取消。状态记录保存稳定结果引用和进度元数据,不应保存原生指针、相机对象、Activity、大型媒体缓冲区,也不能假设进程会一直存在。屏幕重建后,新界面应观察现有任务并绘制当前状态,不能仅因第一次执行 Compose 组合就再次调用推理。ViewModel 可以跨配置变化保留,但不保证进程存活;持久化恢复必须另测。 在受控条件下验证边界。启动文本摘要或图像理解任务,分别在任务排队、流式输出中、已有部分结果、任务已完成但尚未导航时触发界面重建。可以旋转设备或调整多窗口大小;只有应用确实处理系统语言变化时,才加入语言切换场景。按 operation ID 统计引擎提交次数,观察取消与关闭事件,并确认新界面订阅同一个结果流或持久化记录。不要用配置变化测试代替进程死亡测试:前者通常是在同进程创建新的 Activity,后者要求根据持久数据重建原生状态。 如果任务不应穿越屏幕切换,产品行为也要明确。若取消是有意动作,界面应显示已取消,并让用户主动决定是否重新提交;如果任务由应用或功能协调器所有,屏幕退出只应移除观察者,不能顺手关闭 runtime。若进程确实死亡,不应声称任务仍在继续:恢复最后一个已提交状态,把不确定输出标为未完成,并提供明确重试。不能把原生句柄存入 SavedStateHandle 或 Bundle,因为它们用于可序列化的界面数据,不是引擎所有权。 验证证据要能发现重复任务和隐性资源泄漏。记录 operation ID、生命周期事件、提交次数、终态、持久结果版本及 native owner 是否关闭;普通诊断日志不要包含用户提示词和媒体字节。项目事实卡只支持显式生命周期状态和测试方法,不能证明 Cove 每一个页面都已安全处理重建。结论应限定到实际测试的构建、页面、任务类别、系统配置和恢复路径,不要推广成所有设备兼容或进程死亡也可自动续跑。 界面重新挂接任务时,还要考虑竞态。旧屏幕可能在新屏幕订阅之后才收到取消或结果回调。任务状态应以单一所有者的终态为准,重复回调不能让已完成任务倒退成运行中,也不能把部分输出追加两遍。测试可以用一个可控 fake runtime 按指定顺序延迟和释放事件,而不是依赖真实推理恰好出现某个时序。fake 用来验证应用的状态协调,不构成模型性能证据。 多窗口缩放也值得单独覆盖。窗口大小改变可能连续触发多轮布局和 Activity 生命周期事件。确保每次重建只是重新订阅同一 operation ID,界面只改变呈现而不改变推理输入。若用户主动编辑原文本,应定义这是否创建新的操作版本;不能悄悄让旧任务的结果覆盖新编辑。记录输入版本号和结果归属,有助于避免迟到响应污染当前屏幕。 验收时分别报告:配置变化是否只重建界面、任务是否继续由同一进程所有者维护、用户取消是否生效、进程被杀后是否显示中断。不要将这四种结果压成一个“恢复成功”指标。对无法安全继续的任务,清楚说明哪些内容已保存以及用户能做什么,比假装自动恢复更可靠。
本地摘要流式生成时旋转屏幕
用户提交一段长笔记进行本地摘要,看到部分输出时旋转手机。Activity 被重建,而请求协调器和模型 runtime 可能仍在同一进程运行。
决策
保留一个 operation ID 和唯一任务所有者;新界面观察既有进度,不因组合重启而再次推理。
预期结果
用户只看到一份连续结果,部分输出不会重复,屏幕生命周期也不会提前关闭共享 runtime。
需要收集的证据
只有将以下证据与同一模型文件和测试运行关联起来,结论才可复核:
- COVE-LIFECYCLE-01:事实卡支持围绕模型显式生命周期状态开展测试,但不证明每个页面已具备重建安全性。
- ANDROID-BG-01:后台与任务状态需结合运行阶段验证;它不意味着系统会保证任意推理任务存活。
只有记录了模型身份、构建配置、实际后端、任务样例和失败情况,运行时比较才可复核。
实施流程
- 分离界面状态与操作所有权. 分别绘制瞬时 UI、持久任务状态和 runtime 资源边界。屏幕可以持有滚动位置和选项卡;更长生命周期的协调器持有任务身份、取消策略和引擎生命周期。
- 为用户操作定义稳定身份. 在点击提交边界生成 operation ID。保存明确状态和结果引用,让新屏幕重新订阅原任务,而不是把重新组合识别成新的推理提交。
- 在关键状态触发界面重建. 分别在排队、运行、流式部分输出和完成后触发旋转或窗口缩放。若产品支持配置变化,再测试语言;按 operation ID 统计模型提交数。
- 验证观察者与所有者行为. 确认旧界面停止收集并只释放界面资源,新界面观察同一任务;Activity 销毁不能关闭由界面之外持有的 runtime。
- 把进程死亡另列测试. 另做杀进程测试并从持久状态重建。将未提交输出标为不完整,不声称内存 ViewModel 或 native session 跨进程死亡存活。
让每个阶段的状态都可观察。失败信息应指出阶段、模型、运行时和恢复动作,同时避免记录用户私密内容。
验收标准
- 同一次用户操作只有一个 operation ID,配置变化后不会重复提交推理。
- 新界面正确显示排队、运行、部分结果、终态或取消状态,不再引用旧 Activity。
- 界面销毁只移除界面观察者;共享 runtime 的关闭策略另行断言。
- 配置变化与进程死亡分别记录,不混用恢复预期。
决策对照表
| 维度 | 选择或证据 | 解读方式 |
|---|---|---|
| 同进程配置变化重建 | 重新连接既有任务所有者 | Activity 实例变化;native 状态可能仍在,但不应重复创建或由屏幕所有。 |
| 进程死亡后重新启动 | 从持久状态重建,或标记任务中断 | 内存任务与 native handle 已消失,需要单独定义恢复流程。 |
| 用户主动取消 | 只取消一次并显示终态 | 这是产品明确的状态转换,不应由偶发屏幕销毁偷偷触发。 |
需要避免的失败模式
- 把 Activity 销毁等同于用户取消任务,导致屏幕变化关闭了仍被期待的推理。
- 将界面重新组合误当成新提交,或把进程死亡恢复能力错误地归因于 ViewModel。
还要避免静默回退、版本未固定、缺少取消处理,以及把不同配置的结果混在一起下结论。失败运行也应与成功记录一并保存。
最小复现记录
| 层级 | 记录内容 |
|---|---|
| 设备 | 厂商、型号、芯片、内存档位、操作系统版本 |
| 软件 | 应用版本和 Git 提交号 |
| 模型 | 系列、变体、版本、格式、文件长度、摘要、量化方式 |
| 运行时 | 名称、版本、请求后端及实际后端证据 |
| 工作负载 | 样例版本、输入摘要、提示词摘要、输出策略 |
| 结果 | 完成、失败、取消、回退及保护隐私的诊断信息 |
发布检查表
- ☐ 主要搜索问题已回答,且没有未经支持的数字。
- ☐ 所有模型文件和运行时版本均已固定。
- ☐ 代表性任务和失败处理策略均有说明。
- ☐ 生命周期、取消、清理和回退均已测试。
- ☐ 用户内容与网络边界均有说明。
- ☐ 结论只适用于被记录的配置。
- ☐ 新增测试结果时链接到原始方法或证据。
来源与相关证据
中文部署与排错专题见。奇连 AI 端侧专题。
常见问题
旋转屏幕时应重新运行推理吗?
通常不应。新屏幕应订阅原 operation ID,并验证该 ID 只对应一次提交。只有产品明确把旋转定义为取消且用户主动提交新请求时,才启动新的推理操作。
ViewModel 能保证每种重启后任务都继续吗?
不能。ViewModel 有助于跨配置变化保留业务状态,但进程死亡会移除内存 native 状态。持久恢复需要独立设计、持久记录和专门测试;未完成输出应如实标记。
可以把 native 引擎句柄存到实例状态里吗?
不可以。保存少量可序列化 UI 数据或稳定 ID;native 句柄必须由存活对象拥有,进程丢失后应重新构建,而不是写进 Bundle。可通过任务 ID 重新关联结果。