行业资讯

HarmonyOS 7 / API 26 3DGS 模型资源落盘实战:版本号、封面图和缓存淘汰怎么设计

发布时间:2026/8/8 14:01:06
HarmonyOS 7 / API 26 3DGS 模型资源落盘实战:版本号、封面图和缓存淘汰怎么设计 HarmonyOS 7 / API 26 3DGS 模型资源落盘实战版本号、封面图和缓存淘汰怎么设计重建完成不等于功能完成3DGS 端侧重建最容易让人误判的一点是页面提示“重建完成”开发者就以为功能闭环了。实际放到应用里真正麻烦的往往在后面模型文件放哪、封面图怎么生成、版本号怎么标、缓存满了删谁、下次打开怎么恢复。如果这些没有设计好用户会遇到很差的体验第一次生成能看退出再回来找不到旧模型被新模型覆盖缓存越来越大弱设备上加载越来越慢上架审核时也解释不清这个能力到底存了什么。这篇只讲资源落盘不讲重建算法也不讲模型首屏黑屏。目标很明确3DGS 生成结果要能保存、能恢复、能淘汰、能解释。先给资源建一张索引表不要只把模型文件丢到沙箱目录里。文件系统只能告诉你“这里有个文件”但它不知道这个文件属于哪个场景、是什么版本、有没有封面、上次什么时候打开、能不能删除。我会给 3DGS 资源建一个索引对象字段作用为什么需要assetId稳定资源 id页面跳转和列表恢复用它sourceId输入素材 id方便追溯这次重建来自哪里modelUri模型文件路径ArkGraphics 3D 加载入口posterUri封面图路径不支持 3D 时可降级展示version资源版本SDK 或格式升级后能重建byteSize文件大小缓存淘汰要用lastOpenAt最近打开时间删除最久不用的资源createdAt创建时间问题追踪和排序案例一重建成功后退出再回来找不到模型这个问题多数不是重建失败而是产物没有进入资源索引。页面当时拿着内存里的 outputUri 能显示退出后这个临时路径失效下次自然打不开。复现步骤启动一次 3DGS 重建完成后直接把 outputUri 传给预览页退出应用或杀进程再次打开历史记录如果没有索引记录页面只能显示空状态。interface SpatialAssetRecord { assetId: string; sourceId: string; modelUri: string; posterUri: string; version: number; byteSize: number; createdAt: number; lastOpenAt: number; } interface ReconOutput { sessionId: string; sourceId: string; modelUri: string; posterUri: string; byteSize: number; } export class SpatialAssetRepository { private records: SpatialAssetRecord[] []; saveFromRecon(output: ReconOutput): SpatialAssetRecord { const now Date.now(); const record: SpatialAssetRecord { assetId: asset- output.sessionId, sourceId: output.sourceId, modelUri: output.modelUri, posterUri: output.posterUri, version: 1, byteSize: output.byteSize, createdAt: now, lastOpenAt: now }; this.records this.records.filter(item item.assetId ! record.assetId); this.records.unshift(record); return { ...record }; } find(assetId: string): SpatialAssetRecord | undefined { return this.records.find(item item.assetId assetId); } }这个 Repository 不负责渲染也不负责重建。它只负责资源记录。好处是页面下次回来时不再依赖上一次内存里的临时变量。案例二缓存越积越多低端设备越来越慢3DGS 模型可能比普通图片更占空间。如果每次生成都保留缓存会越来越大。用户一开始觉得功能好玩后面会觉得应用越来越慢、越来越占空间。复现步骤连续生成 20 个模型每个模型都保存模型文件和封面图不做缓存上限观察应用沙箱体积加入淘汰策略后超过上限自动清理最久未打开的资源。interface CacheCleanResult { removedCount: number; removedBytes: number; keptCount: number; } export class SpatialCacheCleaner { constructor(private maxBytes: number) {} clean(records: SpatialAssetRecord[]): CacheCleanResult { const sorted [...records].sort((a, b) b.lastOpenAt - a.lastOpenAt); let total 0; const kept: SpatialAssetRecord[] []; const removed: SpatialAssetRecord[] []; for (const item of sorted) { if (total item.byteSize this.maxBytes) { kept.push(item); total item.byteSize; } else { removed.push(item); } } return { removedCount: removed.length, removedBytes: removed.reduce((sum, item) sum item.byteSize, 0), keptCount: kept.length }; } }这个策略不复杂但比“全部保留”稳得多。实际项目里可以再加规则用户收藏的模型不删正在上传或正在编辑的模型不删最近 24 小时生成的模型先保留。版本号是给未来升级留后路3DGS 能力还会更新模型格式、滤镜参数、压缩策略都有可能变化。如果资源表里没有版本号后面升级时就不知道旧模型是否需要重新处理。interface AssetMigrationResult { migrated: boolean; reason: string; nextVersion: number; } export class SpatialAssetMigrator { private currentVersion 2; check(record: SpatialAssetRecord): AssetMigrationResult { if (record.version this.currentVersion) { return { migrated: false, reason: 资源版本已是最新, nextVersion: record.version }; } if (!record.modelUri) { return { migrated: false, reason: 缺少模型文件不能迁移, nextVersion: record.version }; } return { migrated: true, reason: 需要按新版本重新生成索引或封面, nextVersion: this.currentVersion }; } }版本号不是为了复杂而是为了以后不被旧资源拖住。尤其是 HarmonyOS 7 / API 26 这种新能力SDK 和设备支持都可能继续变化资源层一定要给升级留空间。资源恢复时的页面逻辑状态页面应该怎么做不能怎么做记录存在文件存在进入 3D 预览重复跑重建记录存在文件丢失展示封面和恢复提示直接白屏版本过旧提示重新处理或降级预览静默失败设备不支持展示封面图和说明强行初始化 3D 场景缓存被清理给出重新生成入口显示坏链接最后总结3DGS 资源落盘不是附属功能而是端侧重建能不能长期使用的关键。重建完成只是第一步后面还要有资源索引、封面图、版本号、缓存淘汰和恢复逻辑。我会把这件事单独抽出来做而不是写在预览页面里。页面只负责展示资源状态Repository 负责保存和读取Cleaner 负责缓存清理Migrator 负责版本升级。这样后面接更多 3DGS 模型、更多设备形态和更复杂的展示方式代码不会越写越乱。