
Stage 组件启动规则桌面、应用内、跨应用启动怎么设计Stage 应用不是只有桌面图标一个入口。用户可能从应用内按钮进入页面也可能从通知、卡片、分享、其他应用的 Want 拉起某个能力。启动规则如果没有统一设计后面最容易出现权限混乱、参数缺失和入口行为不一致。本文把启动来源分成桌面启动、应用内启动、跨应用启动三类分别说明配置、Want 参数、权限边界和异常兜底。1. 先定义启动来源启动来源决定参数可信度。应用内启动的参数通常可信跨应用启动的参数必须校验桌面启动则一般没有业务参数。exportenumStartSource{Launcherlauncher,InAppinApp,Notificationnotification,Externalexternal}exportinterfaceStartRequest{source:StartSource;abilityName:string;payload:Recordstring,string;}代码解释source是后续权限判断和日志排查的基础。abilityName表示目标组件不直接把页面路径暴露给外部。payload统一为字符串字典便于 Want 参数传递和校验。2. 桌面启动保持最小参数桌面启动的目标应该稳定、轻量、可恢复。它通常只负责打开主 Ability不应该携带复杂业务状态。{ module: { abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, exported: true, launchType: singleton, skills: [ { entities: [entity.system.home], actions: [action.system.home] } ] } ] } }代码解释桌面入口使用singleton重复点击图标回到已有主入口。exported按实际入口需求配置不能为了方便全部开放。桌面 skill 只表达主页入口不承载业务跳转。3. 应用内启动使用统一封装应用内启动 Ability 时不建议页面直接拼 Want。统一封装可以保证参数、日志和错误处理一致。import{common,Want}fromkit.AbilityKit;exportclassAbilityStarter{constructor(privatereadonlycontext:common.UIAbilityContext){}asyncstartAbility(request:StartRequest):Promisevoid{constwant:Want{bundleName:this.context.abilityInfo.bundleName,abilityName:request.abilityName,parameters:{source:request.source,...request.payload}};awaitthis.context.startAbility(want);}}代码解释应用内启动自动使用当前 bundleName减少硬编码。source每次都带上目标 Ability 可以判断来源。启动失败时可以在这里统一捕获并提示。4. 跨应用启动必须校验参数跨应用 Want 的参数不能直接信任。即使目标 Ability 被允许外部拉起也要检查必填字段、长度、枚举值和业务权限。exportinterfaceExternalPayload{scene:string;bizId:string;}exportfunctionparseExternalPayload(payload:Recordstring,Object):ExternalPayload|undefined{constscenepayload[scene];constbizIdpayload[bizId];if(typeofscene!string||typeofbizId!string){returnundefined;}if(![share,detail,payment].includes(scene)){returnundefined;}if(bizId.length64){returnundefined;}return{scene,bizId};}代码解释外部参数先做类型判断不能直接String()后使用。scene只允许白名单值。bizId限制长度避免异常输入进入业务层。5. exported 要按能力边界配置不是所有 Ability 都应该允许外部启动。主入口、分享接收、支付回调可能需要开放内部编辑页、调试页、管理页不应该开放。{ name: InternalEditorAbility, srcEntry: ./ets/editorability/InternalEditorAbility.ets, exported: false, launchType: specified }代码解释内部编辑页不对外开放降低误启动和安全风险。即使外部知道 abilityNameexported: false也不应允许跨应用启动。需要跨应用能力时应单独设计一个边界清晰的入口 Ability。6. 目标 Ability 要识别来源目标 Ability 不能假设所有启动都来自应用内。它应该解析 Want判断来源再决定是否继续。exportclassStartGuard{staticallow(request:StartRequest):boolean{if(request.sourceStartSource.External){returnBoolean(request.payload[bizId]);}returntrue;}}exportdefaultclassDetailAbilityextendsUIAbility{onCreate(want:Want):void{constrequestStartRequestParser.fromWant(want);if(!StartGuard.allow(request)){this.context.terminateSelf();return;}DetailEntryCache.save(request);}}代码解释外部启动缺少关键参数时直接终止当前 Ability。StartRequestParser负责统一解析不在 Ability 里散写参数读取。通过守卫后再进入页面或业务缓存。7. 启动失败要给调用方和用户反馈应用内启动失败可以提示用户跨应用启动失败则要记录日志必要时展示安全提示页。exportclassStartErrorHandler{statichandle(error:Error,request:StartRequest):void{hilog.error(0x20260702,AbilityStart,start failed ability%{public}s source%{public}s message%{public}s,request.abilityName,request.source,error.message);ToastService.show(暂时无法打开请稍后重试);}}代码解释日志包含目标 Ability 和来源方便定位是哪类入口失败。用户提示保持克制不暴露内部错误细节。如果错误来自权限或安全校验应使用专门的安全提示。8. 跨应用启动要有最小公开协议公开给其他应用的启动协议越简单越好。推荐只公开scene、bizId、sourceApp这类最小字段不暴露内部页面结构。exportinterfacePublicStartProtocol{scene:detail|share;bizId:string;sourceApp:string;}exportfunctiontoStartRequest(protocol:PublicStartProtocol):StartRequest{return{source:StartSource.External,abilityName:PublicEntryAbility,payload:{scene:protocol.scene,bizId:protocol.bizId,sourceApp:protocol.sourceApp}};}代码解释公开协议和内部 Ability 解耦内部重构不会破坏外部调用。sourceApp用于日志和风控不用于直接信任。所有外部入口先进入PublicEntryAbility再由内部路由分发。9. 验收清单场景验收点桌面启动进入首页重复点击不创建混乱实例应用内启动参数完整失败有提示通知启动能识别来源并跳转目标业务跨应用启动参数校验、非法输入拦截内部 Abilityexported不误开10. 小结Stage 组件启动规则要围绕“来源、边界、参数、反馈”四件事设计。桌面启动保持稳定应用内启动统一封装跨应用启动严格校验公开协议保持最小。这样后续接通知、卡片、分享和外部调用时入口不会越接越乱。