HarmonyOS 7 SceneForge 3DGS端侧重建实录 03:Session Lifecycle × Progress:Pause/Resume、前后台运行模式与重建进度【鸿蒙心迹】
前两篇一直在解决“重建开始之前”的问题。
01 先把 HMS_SpatialRecon_DataFrame 的输入规格做稳:1440×1920 统一到 1080×1440,内参同步缩放,48 帧完整推入 Session;02 又在采集链上加了 Frame Gate,把 72 帧原始输入筛成 54 个有效视角,重复、模糊、低基线和位姿跳变都有单独原因。
第三篇终于进入 StartSession 之后。
真正跑端侧 3DGS 时,我发现最难的并不是“进度条怎么写”,而是 Session 在重建阶段会遇到三种现实状态:
应用从前台切到后台;
设备热状态升高,需要主动暂停;
用户回到前台以后继续任务。
Spatial Recon Kit 当前 C API 已经给出了对应能力:HMS_SpatialRecon_SetRunningMode 用来切换前后台运行模式,HMS_SpatialRecon_PauseSession / ResumeSession 用来真正暂停和恢复 Session,HMS_SpatialRecon_GetProgress 同时返回 0~1 的进度与 HMS_SpatialReconStage 阶段。官方说明还明确要求 SetRunningMode 在 StartSession 之后、重建完成之前使用。
所以 SceneForge 03 不把这三个接口混成一个“暂停按钮”,而是分别处理:
运行模式变化、业务暂停、进度阶段。
本轮统一数据:
taskId:
recon_20261003_03
sessionId:
sf_session_20261003_03
scene:
tabletop_tea_set_02
acceptedFrames:
54
startProgress:
7.6%
backgroundAt:
31.4%
runningMode:
FOREGROUND → BACKGROUND → FOREGROUND
thermalGuardLevel:
5
(SceneForge 项目策略)
pauseAt:
46.2%
pauseDuration:
18s
pauseCount:
1
resumeCount:
1
foregroundReturnAt:
68.5%
modeSwitches:
2
getProgressFailures:
0
totalBuildCost:
94.8s
finalProgress:
100%
finalStage:
FINISHED
status:
RECON_FINISHED

一、SetRunningMode 和 PauseSession 不是一回事
这两个接口非常容易被写成一个概念。
应用切到后台时,我最初的处理是:
onBackground
→ PauseSession
功能上没错,但并不符合 SceneForge 的产品需求。
用户只是回了一条消息,不代表一定要把模型重建完全停掉。
HMS_SpatialReconRunningMode 当前区分:
FOREGROUND
系统为重建分配更积极的资源;
BACKGROUND
系统优先前台应用,重建速度相对慢。
所以 SceneForge 的默认策略改成:
普通前后台切换
→ SetRunningMode
真正需要停止计算
→ PauseSession
这样 Session 生命周期会清楚很多。
二、onBackground 只切运行模式,不重新创建 Session
当前任务在:
31.4%
时进入后台。
这段代码解决的问题是:Session 已经进入 BUILDING 后,应用生命周期发生变化,如何让 Native Session 延续同一任务。
void SpatialReconLifecycleController::OnBackground()
{
if (session_ == nullptr ||
stage_ == SPATIAL_RECON_STAGE_FINISHED) {
return;
}
HMS_SpatialReconStatus ret =
HMS_SpatialRecon_SetRunningMode(
session_,
SPATIAL_RECON_RUNNING_BACKGROUND_MODE);
if (ret == SPATIAL_RECON_STATUS_SUCCESS) {
mode_ = SPATIAL_RECON_RUNNING_BACKGROUND_MODE;
modeSwitches_++;
}
}
这里没有:
DestroySession
CreateSession
重新 PushFrame
因为 54 个 Accepted Frame 已经完成输入。
03 只管理同一个正在重建的 Session。
三、返回前台时同样只是切回 FOREGROUND
应用恢复到前台时:
progress=68.5%
项目层调用:
void SpatialReconLifecycleController::OnForeground()
{
if (session_ == nullptr ||
stage_ == SPATIAL_RECON_STAGE_FINISHED) {
return;
}
HMS_SpatialReconStatus ret =
HMS_SpatialRecon_SetRunningMode(
session_,
SPATIAL_RECON_RUNNING_FOREGROUND_MODE);
if (ret == SPATIAL_RECON_STATUS_SUCCESS) {
mode_ = SPATIAL_RECON_RUNNING_FOREGROUND_MODE;
modeSwitches_++;
}
}
本轮:
modeSwitches=2
刚好对应:
Foreground → Background
Background → Foreground
运行模式变化没有生成新 sessionId,也没有改变 taskId。
四、热保护才真正触发 PauseSession
SceneForge 还订阅设备热状态。
本轮项目策略是:
thermalGuardLevel >= 5
→ PauseSession
这里的 5 是当前 Demo 的工程阈值,不是 Spatial Recon Kit 官方固定值。
重建进度到:
46.2%
时触发热保护:
void SpatialReconLifecycleController::OnThermalLevelChanged(
int level)
{
if (level < thermalGuardLevel_) {
return;
}
if (stage_ != SPATIAL_RECON_STAGE_BUILDING) {
return;
}
HMS_SpatialReconStatus ret =
HMS_SpatialRecon_PauseSession(session_);
if (ret == SPATIAL_RECON_STATUS_SUCCESS) {
stage_ = SPATIAL_RECON_STAGE_PAUSED;
pauseCount_++;
pauseStartedAt_ = NowMs();
}
}
这一步和后台模式不同。
进入 PAUSED 后,重建真正停止推进。
五、18 秒以后 Resume,不重走 StartSession
设备冷却以后:
pauseDuration=18s
SceneForge 调用:
ResumeSession
而不是再次 StartSession。
因为当前 Session 已经有完整的内部重建状态。
如果重新 Start,会让业务层无法判断是“恢复”还是“重新开始”。
本轮:
pauseCount=1
resumeCount=1
两边严格成对。
六、GetProgress 必须同时看 progress 和 stage
我一开始只给 UI 一个百分比:
46.2%
然后发现暂停以后,百分比保持不动,用户会误以为:
应用卡死了。
所以 03 的 Snapshot 从:
progress
升级成:
progress + stage + runningMode
官方当前 HMS_SpatialReconStage 至少包含:
INIT
BUILDING
PAUSED
FINISHED
SAVING
UNKNOWN
于是页面可以准确展示:
46.2%
PAUSED
热保护暂停 18s
而不是“46.2% 一直不动”。
七、进度采样器不要在页面里直接调用 Native API
SceneForge 让 Native Manager 自己轮询:
void ProgressSampler::Sample()
{
float progress = 0.0f;
HMS_SpatialReconStage stage =
SPATIAL_RECON_STAGE_UNKNOWN;
HMS_SpatialReconStatus ret =
HMS_SpatialRecon_GetProgress(
session_,
&progress,
&stage);
if (ret != SPATIAL_RECON_STATUS_SUCCESS) {
getProgressFailures_++;
return;
}
snapshot_.progress = progress * 100.0f;
snapshot_.stage = stage;
}
ArkTS 页面只消费 Snapshot。
这样页面切换、重组、重绘都不会直接干预 Session。
本轮:
getProgressFailures=0
八、PAUSED 状态下不要继续假装倒计时
产品里经常会写:
预计剩余 48 秒
但重建一旦进入 PAUSED,剩余时间估算立刻失效。
所以 SceneForge 的 UI 规则:
BUILDING
→ 可以显示趋势估算
PAUSED
→ 只显示暂停原因与暂停时长
SAVING
→ 改成保存阶段进度
03 当前停在重建阶段,不把 04 的保存进度混进来。
九、前后台切换时不要修改 stage
SetRunningMode(BACKGROUND) 成功以后,Session 仍可能处于:
SPATIAL_RECON_STAGE_BUILDING
所以:
BACKGROUND
是运行模式,不是 HMS_SpatialReconStage。
这个区分非常重要。
SceneForge 快照里分别保存:
stage=BUILDING
runningMode=BACKGROUND
而不是发明一个:
stage=BACKGROUND
这样后续 Save 阶段也不会乱。
十、为什么热保护暂停后仍然允许后台模式
如果热状态是在后台发生:
runningMode=BACKGROUND
stage=PAUSED
两者仍然可以同时成立。
Resume 以后会继续沿用当前 runningMode。
也就是说:
Pause / Resume
控制任务是否推进;
RunningMode
控制系统按前台还是后台方式调度。
这两个维度真正拆开以后,生命周期代码反而更简单。
十一、最终 FINISHED 以后不再允许 Pause / Resume / SetRunningMode
本轮总重建耗时:
94.8s
最终:
progress=100%
stage=FINISHED
status=RECON_FINISHED
SceneForge 状态机一旦进入 FINISHED:
Pause
Resume
SetRunningMode
项目层都直接拒绝。
后面唯一允许的主动作变成:
SaveResultToFile
这正好进入 04。
十二、DevEco 图里把“运行模式”和“暂停”分开看
开发图:

HiLog:
taskId=
recon_20261003_03
start session
acceptedFrames=54
progress=
7.6%
stage=BUILDING
app background
mode=BACKGROUND
progress=
31.4%
thermal level=5
pause at=
46.2%
pauseCount=
1
resume after=
18s
foreground
progress=
68.5%
modeSwitches=
2
progress=
100%
stage=
FINISHED
totalBuildCost=
94.8s
status=
RECON_FINISHED
这条日志真正证明的是:
同一个 Native Session 经历了前后台、暂停、恢复,最终仍然完整结束。
十三、手机运行图不再展示采集帧,而是展示 Session 时间线
最终运行图:

顶部是同一个:
tabletop_tea_set_02
中间生命周期:
BUILDING 7.6%
BACKGROUND 31.4%
PAUSED 46.2%
RESUME
FOREGROUND 68.5%
FINISHED 100%
右侧还有:
thermalGuardLevel=5
modeSwitches=2
totalBuildCost=94.8s
最终:
RECON_FINISHED
这张图和 01 / 02 的“输入帧质量”已经是完全不同的工程阶段。
十四、GetProgress 在 04 还会继续用,但口径会变
官方文档当前明确说明:
当调用 HMS_SpatialRecon_SaveResultToFile 以后,GetProgress 返回的进度反映的是文件保存进度,不再是整个重建进度。
所以 03 在 FINISHED 停止构建进度统计。
04 开始保存时,会重新记录:
saveProgress
stage=SAVING
两个阶段不能共用一条“总进度曲线”。
十五、热策略只是项目策略,不能写成系统固定推荐值
SceneForge 当前:
level >= 5
pause
这来自本轮设备和 Demo 的取舍。
真正产品应该结合:
设备型号
壳温
电量
用户场景
重建耗时
系统热事件
重新验证。
文章里只保留“热状态升高时主动 Pause”的工程思路,不把 5 级写成官方阈值。
十六、Session 生命周期必须由一个 Owner 管
如果:
页面 A
可以 Pause
页面 B
可以 Destroy
后台事件
可以 SetRunningMode
热事件
又可以 Resume
这些动作分散在多个对象里,状态会非常难控。
SceneForge 统一由:
SpatialReconLifecycleController
持有 Session。
其他模块只能发送:
Intent
例如:
APP_BACKGROUND
THERMAL_HIGH
THERMAL_RECOVERED
APP_FOREGROUND
Controller 根据当前 stage 决定能不能执行。
十七、03 最后固定八组状态测试
第一组,BUILDING 时切后台,只切 RunningMode。
第二组,后台返回前台,仍然同一个 sessionId。
第三组,热阈值触发 Pause,stage 进入 PAUSED。
第四组,PAUSED 再 Pause,项目层不重复调用。
第五组,Resume 成功,pause / resume 成对。
第六组,FINISHED 后 SetRunningMode 不再执行。
第七组,GetProgress 失败只记录 DFX,不擅自 Destroy Session。
第八组,最终 100% / FINISHED 与回调状态一致。
全部通过以后:
RECON_FINISHED
才有意义。
十八、下一篇终于进入“把模型真正写出来”
03 完成以后,SceneForge 手里已经有:
一个 FINISHED 的 3DGS Session
但用户还没有可分享、可加载的文件。
04 会继续同一个 sf_session_20261003_03,分别保存:
PLY
3DGS 模型
MP4
环绕预览视频
并处理:
ModelWriteInfo
保存进度
并发保存阻断
文件清单
Session Destroy
整个系列会从“重建成功”正式进入“结果落盘”。
十九、业务暂停和热保护暂停必须带上 pauseReason
如果只保存:
stage=PAUSED
UI 根本不知道该显示:
用户暂停
还是设备热保护
还是系统资源策略
SceneForge 因此在项目快照里增加:
pauseReason
当前枚举:
USER
THERMAL_GUARD
SYSTEM_POLICY
UNKNOWN
本轮:
pauseReason=THERMAL_GUARD
这条信息不会传给 Spatial Recon Kit,它只是 SceneForge 自己的 DFX 语义。
真正调用的仍然是同一个:
HMS_SpatialRecon_PauseSession
但产品体验完全不同。
用户主动暂停时,页面应该显示:
已手动暂停
热保护暂停时则显示:
设备温度较高,已暂缓重建
把底层状态和业务原因拆开,后面统计“为什么任务耗时变长”时也更容易。
二十、热事件需要去抖,不能 level 上下一跳就反复 Pause/Resume
设备温度不是一个永远单调的值。
如果:
5 → 4 → 5 → 4
每次变化都立刻调用:
Pause
Resume
Pause
Resume
Session 会被频繁打断。
SceneForge 当前策略是:
触发 Pause 后
至少保持一个冷却窗口
连续满足恢复条件
再 Resume
本轮为了让测试数据可重复,冷却窗口固定:
18s
这 18 秒不是系统要求,只是当前 Demo 的测试策略。
真正产品可以结合:
热等级
温度趋势
电量状态
任务剩余进度
动态调整。
二十一、进度采样不能太频繁
GetProgress 是 Native API。
如果 ArkTS 页面每一帧都请求:
60 次 / 秒
没有必要。
SceneForge 当前 ProgressSampler 使用低频采样,例如:
500ms~1000ms
区间内刷新一次 UI。
真正状态变化,例如:
BUILDING → PAUSED
PAUSED → BUILDING
BUILDING → FINISHED
则立即推送。
这样可以做到:
阶段切换及时
百分比更新平滑
Native 调用不过度
页面也不会因为百分比频繁变化导致大量重组。
二十二、进度回退需要记录异常,不直接修正成“最大值”
重建进度正常情况下应该整体向前。
但如果某次 GetProgress 读到:
48.1%
下一次 47.6%
SceneForge 不会直接写:
progress = max(old, new)
因为那样虽然 UI 看起来更顺,却把异常隐藏了。
项目策略是:
保留原始 progress
记录 PROGRESS_REGRESSION
UI 仍可做视觉平滑
这样 DFX 能看到真实系统返回值。
“显示平滑”和“数据真实”不能用一个变量解决。
二十三、最终回调和 GetProgress FINISHED 要互相验证
StartSession 有完成回调。
GetProgress 又能返回:
stage=FINISHED
progress=100%
SceneForge 不把其中任意一个当成唯一真相。
当前验收要求:
完成回调 SUCCESS
并且
最近一次 stage=FINISHED
并且
progress 接近 100%
三者一致以后:
status=RECON_FINISHED
如果回调成功但 stage 仍然 BUILDING:
CALLBACK_STAGE_MISMATCH
先记录异常,不马上进入保存阶段。
这种“双证据”设计会让 04 的 SaveResultToFile 更安全。
二十四、前台返回时不要无条件 Resume
一种常见写法是:
onForeground
→ ResumeSession
→ SetRunningMode(FOREGROUND)
但如果 Session 从来没有 Pause:
ResumeSession
反而会得到错误。
SceneForge 返回前台时先看:
stage
pauseReason
只有:
stage=PAUSED
并且
pauseReason 属于可自动恢复类型
才 Resume。
普通后台模式返回前台只调用:
SetRunningMode(FOREGROUND)
这也是为什么本轮:
resumeCount=1
而不是每次 foreground 都 +1。
二十五、用户手动 Pause 后,热保护恢复不能替用户 Resume
假设用户自己点:
暂停
与此同时设备温度也升高。
过一会温度恢复,SceneForge 不应该:
自动 Resume
因为用户暂停意图仍然存在。
所以项目快照还要保存:
userPauseLatch
恢复策略优先级:
USER pause
> THERMAL pause
> background mode
只有阻塞原因全部解除,Session 才真正继续。
本轮没有用户手动暂停,因此:
userPauseLatch=false
这条边界留到后续真实产品时直接可用。
二十六、Session 运行期间不能启动第二个重建任务
官方社区实践已经明确提醒:
同一时刻只允许一个重建 Session 运行
SceneForge 项目层也做同样的互斥。
如果当前:
sf_session_20261003_03
stage=BUILDING
用户又创建新任务:
RECON_SESSION_BUSY
直接阻止。
这不是 UI 上把按钮置灰就结束。
Native Manager 自己也要有:
activeSessionId
作为最终防线。
二十七、FINISHED 后不马上 Destroy,是因为 04 还需要 Session
这个生命周期很关键。
重建完成:
FINISHED
不等于:
DestroySession
因为 04 还要调用:
SaveResultToFile
所以 03 的终态是:
RECON_FINISHED
Session 仍然存在
真正 Destroy 会延后到:
PLY / MP4 保存完成
Manifest 写入完成
之后。
这样两篇文章虽然分开,Session 生命周期仍然是同一条工程主线。
二十八、03 最终形成的是一张二维状态表
SceneForge 到这一篇已经不再用一个字符串描述所有状态。
真正决定行为的是:
Stage
×
RunningMode
×
PauseReason
例如:
BUILDING + FOREGROUND + NONE
BUILDING + BACKGROUND + NONE
PAUSED + BACKGROUND + THERMAL_GUARD
FINISHED + FOREGROUND + NONE
每一种组合允许的操作不同。
这比“status=RUNNING / STOPPED”更适合长期维护。
下一篇保存阶段再增加:
operation=SAVING_PLY / SAVING_MP4
仍然不会破坏这套模型。
二十九、生命周期日志要保存“动作前状态”和“动作后状态”
像 Pause / Resume 这种操作,如果日志只写:
pause success
排查价值很有限。
SceneForge 最终日志结构会同时记录:
beforeStage
beforeMode
action
result
afterStage
afterMode
progress
reason
例如本轮热保护事件能完整还原成:
BUILDING / BACKGROUND
→ THERMAL_PAUSE
→ PAUSED / BACKGROUND
→ 46.2%
18 秒后:
PAUSED / BACKGROUND
→ RESUME
→ BUILDING / BACKGROUND
这种日志非常适合后面做自动回归。即使用户反馈“切后台以后任务不动了”,也能直接判断到底是运行模式切换、热策略暂停,还是 Progress API 本身异常。
三十、RECON_FINISHED 只代表“重建阶段收口”,不是整个业务任务结束
SceneForge 的总任务状态继续细分:
RECON_RUNNING
RECON_FINISHED
RESULT_SAVING
RESULT_READY
03 最终的 RECON_FINISHED 只是说明模型计算已经完成。
用户真正可以离开任务页面、分享或浏览模型,还要等 04 的结果保存。
把“算法完成”和“业务完成”分开以后,UI 就不会在 100% 时立刻弹出“全部完成”,却随后又等几秒写文件。
这个细节在长任务体验里很重要,也让后面的 PLY/MP4 保存成为自然延续,而不是突然增加的一段流程。
参考资料
spatial_recon_interface.h
https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-spatial-recon-interface-h- Spatial Recon Kit 空间计算能力
https://developer.huawei.com/consumer/cn/features/spatialization HMS_SpatialRecon_ModelWriteInfo
https://developer.huawei.com/consumer/cn/doc/doccenter-capabilities/api/capi-spatialrecon-hms-spatialrecon-modelwriteinfo
更多推荐




所有评论(0)