UIAbility 启动模式实战:singleton、multiton、specified 到底怎么选
UIAbility 启动模式实战singleton、multiton、specified 到底怎么选很多 HarmonyOS 应用一开始只有一个首页默认配置也能跑起来。问题通常出现在第二阶段通知点击要打开详情页外部应用要拉起某个业务页面用户连续打开多个编辑任务或者同一个文档被重复点开。这时如果还把所有入口都当成普通页面跳转处理页面栈很快会变乱。UIAbility 的启动模式解决的就是“实例怎么管理”的问题。它不是一个背概念的配置项而是决定系统再次启动同一个 Ability 时是复用旧实例、创建新实例还是按业务 key 找到指定实例。本文用一个文档编辑类应用做例子讲清楚三件事首页入口为什么适合singleton。多任务场景为什么可能需要multiton。文档编辑为什么更适合specified。onNewWant应该怎么处理新参数。如何用日志验证启动模式真的生效。1. 先用业务场景选择启动模式启动模式不要凭感觉选。更稳的方式是先问业务问题同一个入口被再次拉起时用户希望看到原来的实例还是希望新开一个实例比如首页通常只有一个重复点击桌面图标应该回到已有首页这类入口适合singleton。文档编辑则不同A 文档和 B 文档应该互不影响但重复打开 A 文档又应该回到已有 A 文档这类场景更适合specified。如果每次打开都需要完全独立比如临时预览任务就可以考虑multiton。场景推荐模式原因首页、主入口singleton保持主入口唯一重复启动走onNewWant临时详情、一次性任务multiton每次启动可以创建独立实例文档、会话、编辑器specified按业务 key 复用指定实例exporttypeLaunchScenehome|preview|document;exporttypeLaunchTypesingleton|multiton|specified;exportfunctionchooseLaunchType(scene:LaunchScene):LaunchType{if(scenehome){returnsingleton;}if(scenedocument){returnspecified;}returnmultiton;}代码解释这段代码不是系统 API而是把团队的启动模式选择规则写清楚。项目评审时可以先看这个规则再决定module.json5怎么配置。避免每个开发者凭经验选择启动模式后期维护成本会低很多。2. 在 module.json5 中配置 launchType启动模式最终要落到module.json5。如果这里只配置错了ArkTS 代码写得再完整系统实例选择也不会按预期执行。下面示例把首页配置为singleton把文档编辑 Ability 配置为specified{ module: { abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, launchType: singleton, exported: true }, { name: DocumentAbility, srcEntry: ./ets/documentability/DocumentAbility.ets, launchType: specified, exported: false } ] } }代码解释EntryAbility作为主入口重复启动时复用已有实例。DocumentAbility用specified为后续按文档 id 复用实例做准备。修改module.json5后要重新构建安装不能只看页面热更新效果。3. singleton 的重点是 onNewWantsingleton不是“只启动一次就完事”。它真正需要处理的是旧实例还在时新 Want 进来怎么办如果没有处理onNewWant用户从通知点击进入详情页时系统可能已经把参数交给了旧实例但你的页面没有任何变化于是看起来像“点击无效”。import{UIAbility,Want}fromkit.AbilityKit;import{hilog}fromkit.PerformanceAnalysisKit;constDOMAIN0x0000;exportdefaultclassEntryAbilityextendsUIAbility{onNewWant(want:Want):void{hilog.info(DOMAIN,EntryAbility,onNewWant received);EntryRouteDispatcher.dispatch(want);}}classEntryRouteDispatcher{staticdispatch(want:Want):void{constpagewant.parameters?.pageasstring??Index;constidwant.parameters?.idasstring??;hilog.info(DOMAIN,EntryRoute,page%{public}s id%{public}s,page,id);}}代码解释onNewWant专门处理复用实例收到的新参数。不建议在onNewWant里直接写复杂页面逻辑最好交给路由分发类。日志里打印page和id后续排查通知、外链、跨应用启动会方便很多。4. multiton 适合独立任务不适合普通详情页滥用multiton可以让同一个 Ability 创建多个实例但这不代表它适合所有详情页。实例多了以后页面栈、内存占用、返回路径和状态同步都会变复杂。适合使用multiton的场景通常有两个特征每个任务之间确实独立。用户需要同时保留多个任务上下文。exportinterfaceAbilityTaskSnapshot{instanceId:string;title:string;pageStack:string[];updatedAt:number;}exportclassAbilityTaskStore{privatestaticsnapshots:AbilityTaskSnapshot[][];staticadd(snapshot:AbilityTaskSnapshot):void{AbilityTaskStore.snapshots.push(snapshot);}staticlist():AbilityTaskSnapshot[]{returnAbilityTaskStore.snapshots;}}代码解释多实例场景要能记录每个实例的业务快照。pageStack用来辅助排查返回路径是否符合预期。如果你不需要记录这些状态大概率也不需要multiton。5. specified 的核心是实例 keyspecified的价值在于“按业务 key 复用实例”。以文档编辑为例同一个用户打开同一个文档应当回到已有编辑实例不同文档则应该互相隔离。实例 key 不要随手拼。建议明确包含哪些字段例如用户 id、文档 id、租户 id。字段太少会误复用字段太多又会导致本该复用的实例无法命中。import{Want}fromkit.AbilityKit;exportclassDocumentInstanceKeyFactory{staticcreate(want:Want):string{constuserIdwant.parameters?.userIdasstring??guest;constdocumentIdwant.parameters?.documentIdasstring??;if(!documentId){return${userId}:empty;}return${userId}:${documentId};}}代码解释userId documentId可以避免不同用户之间错误复用实例。documentId缺失时要有兜底避免生成不可预测的 key。真实项目中可以继续加入租户、空间、版本等字段。6. 把 Want 转成业务路由对象Ability 收到的是 Want但页面不应该直接依赖 Want。页面更适合接收业务路由对象比如目标页面、业务 id、来源类型。这样做的好处是启动来源可以变化但页面接收的数据结构稳定。import{Want}fromkit.AbilityKit;exportinterfaceRouteRequest{targetPage:string;bizId:string;source:string;}exportclassAbilityRouteParser{staticparse(want:Want):RouteRequest{return{targetPage:want.parameters?.pageasstring??Index,bizId:want.parameters?.idasstring??,source:want.parameters?.sourceasstring??unknown};}}代码解释RouteRequest是页面和 Ability 之间的稳定协议。source用于区分桌面、通知、外部应用或内部跳转。默认值能避免参数缺失时页面直接异常。7. 运行验证要覆盖三条路径启动模式是否正确不能只启动一次就下结论。至少要验证三条路径验证路径预期结果重点日志首次打开应用走onCreate和窗口加载onCreate、loadContent已有实例再次拉起走onNewWantonNewWant、路由参数不同文档 id 拉起specified key 不同instanceKeyimport{hilog}fromkit.PerformanceAnalysisKit;constDOMAIN0x0000;exportclassLaunchModeVerifier{staticmark(stage:string,source:string,bizId:string):void{hilog.info(DOMAIN,LaunchModeVerifier,stage%{public}s source%{public}s bizId%{public}s,stage,source,bizId);}}代码解释stage记录当前处于onCreate、onNewWant还是页面路由。source记录启动来源方便区分桌面、通知和外部调用。bizId用来验证 specified key 是否符合预期。8. 常见错误和修复建议问题典型表现修复建议忘记配置launchType行为和预期不一致回到module.json5检查 Ability 声明singleton 没处理onNewWant重复点击没有页面变化在onNewWant中解析新 Wantspecified key 不稳定该复用时没复用明确 key 字段避免随机值页面直接读 Want页面和系统入口耦合先转换成RouteRequestexportconstLaunchModeChecklist:string[][module.json5 是否配置 launchType,singleton 是否处理 onNewWant,specified 是否有稳定 instance key,Want 是否转换成业务 RouteRequest,日志是否覆盖 onCreate、onNewWant 和路由分发];代码解释这份清单适合放到团队 code review 模板里。每新增一个 Ability 或外部入口都应该按清单检查。如果列表里有任何一项不满足先修启动链路再继续写页面。9. 总结UIAbility 启动模式真正解决的是实例管理问题。singleton关注复用旧实例后的新参数处理multiton关注多个独立任务的状态隔离specified关注按业务 key 精准复用实例。写项目时建议按这个顺序落地先判断业务是否需要复用实例。再配置module.json5的launchType。然后处理onCreate和onNewWant。最后把 Want 转成业务路由对象并用日志验证完整链路。参考资料华为开发者文档https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/abilitystage华为开发者文档https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/stage-model-development-overview华为开发者文档https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/want-overview