写在前面
如果你写过 ArkUI 之外任何需要跨能力协作的鸿蒙应用,大概率遇到过这个场景:
用户在「主页能力」点「设置」按钮,你用
context.startAbility(want)跳到「设置能力」。跳过去就完,用户在设置里改了啥你拿不到。 用户切回主页,你问他「你在设置里改了啥?」------你不知道。因为startAbility不等回参,跳过去就出本能力生命周期。 你查文档发现「要拿回参得用startAbilityForResult」------它跳能力后等用户操作完,自动拉回本能力并给你一个AbilityResult(含 resultCode + want.parameters)。你点进去发现Want路由载体、resultCode区分、onAbilityResult监听、terminateSelf收尾------比前端window.open+postMessage复杂十倍,一脸懵。
这是「单向跳」和「跳拿回参」的分水岭。鸿蒙给的能力路由答案是 UIAbilityContext.startAbilityForResult ------Want 路由定位目标能力、resultCode 表示返回结果、want.parameters 携回写参、terminateSelf 销毁本能力。
本文就用一个真机可跑 的「跳系统设置能力 + 拿 resultCode 回参」demo,把能力调用从「听名字一脸懵」讲到「下个项目直接抄」。代码托管在 AtomGit,文末有链接,真机实拍截图作证。这是韶非 UI 系列第三篇,接续上两篇 HTTP 网络栈 + 文件 IO。
适合人群:写过鸿蒙应用、被「跳能力拿不到回参」折磨过的同学。 不适合人群:还在学
@State的同学------出门左转看我的入门篇。
一、先讲清楚:能力调用到底是啥
一句话:能力调用是鸿蒙的能力路由机制,管「跳能力、拿回参、收本能力」全流程。
你之前写前端 window.open(url) 是浏览器宿主 API------鸿蒙不是浏览器环境,没有这种。能力调用是鸿蒙专门给跨能力协作的原生机制,能力对标 window.open + postMessage 但更精细可控。
核心 API 一览:
| API | 作用 | 一句话理解 |
|---|---|---|
context.startAbility(want) |
单向跳能力 | 「跳过去不等回参」 |
context.startAbilityForResult(want) |
跳拿回参 | 「跳过去等用户操作完拉回本能力」 |
context.terminateSelf() |
销毁本能力 | 「主动结束生命周期」 |
Want |
路由载体 | 「bundleName + abilityName 定位目标能力」 |
AbilityResult |
回参结果 | 「resultCode(0=正常/-1=取消) + want.parameters」 |
记住这五个,往下看。
二、动手:一个跳系统设置能力拿回参的 demo
2.1 import + 拿 UIAbilityContext
typescript
import common from '@ohos.app.ability.common'
import Want from '@ohos.app.ability.Want'
@Entry
@Component
struct Index {
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext
@State resultLog: string = '尚未发起能力调用'
@State resultCode: number = -1
@State callCount: number = 0
// ...
}
三个细节:
import common from '@ohos.app.ability.common'------common.UIAbilityContext是能力调用的入口getContext(this) as common.UIAbilityContext------拿 UIAbility 上下文,用于调startAbility/startAbilityForResult/terminateSelfWant是能力路由载体,要单独 import 装载 bundleName/abilityName/parameters
2.2 startAbilityForResult:跳能力拿回参
typescript
async callAbilityForResult(): Promise<void> {
this.callCount++
this.resultLog = `第 ${this.callCount} 次发起能力调用中...`
try {
// Want 是能力路由载体:bundleName + abilityName 定位目标能力
// 系统设置能力 bundle=com.ohos.settings, ability=SettingsAbility
const want: Want = {
bundleName: 'com.ohos.settings',
abilityName: 'com.ohos.settings.MainAbility',
parameters: { 'caller': 'ArkTS-demo', 'ts': `${Date.now()}` }
} as Want
// startAbilityForResult 跳过去,用户在目标能力里交互后返回本能力
// 回调 AbilityResult:{ resultCode: number, want?: Want }
const result = await this.context.startAbilityForResult(want)
this.resultCode = result.resultCode
const paramEcho = result.want?.parameters?.['echo'] as string || '(目标能力没回写 echo)'
this.resultLog = `第 ${this.callCount} 次:resultCode = ${result.resultCode}(0=正常返回,-1=取消)\n回写参:${paramEcho}`
} catch (e) {
this.resultLog = `第 ${this.callCount} 次失败:${e.message}`
this.resultCode = -1
}
}
startAbilityForResult 三个关键点:
① Want 路由载体:bundleName + abilityName 定位目标
typescript
const want: Want = {
bundleName: 'com.ohos.settings', // 目标能力 bundle 包名
abilityName: 'com.ohos.settings.MainAbility', // 目标能力 ability 名
parameters: { 'caller': 'ArkTS-demo', 'ts': `${Date.now()}` } // 携参给目标能力
} as Want
Want 是鸿蒙能力路由的核心数据结构,对标前端的 url + query。三个字段:
| 字段 | 作用 |
|---|---|
bundleName |
目标能力 bundle 包名(应用唯一标识) |
abilityName |
目标能力 ability 名(应用内能力唯一标识) |
parameters |
携参给目标能力(键值对) |
ArkTS 强约束:
want不能是裸对象字面量,必须as Want显式断言。
② AbilityResult 回参:resultCode + want.parameters
typescript
const result = await this.context.startAbilityForResult(want)
result.resultCode // 0=正常返回, -1=取消
result.want?.parameters?.['echo'] // 目标能力回写的参数
resultCode 是返回码,类似前端的 window.returnValue:
| resultCode | 含义 |
|---|---|
| 0 | 正常返回(用户操作完) |
| -1 | 取消(用户没操作就退) |
| 其他 | 业务自定义(目标能力写) |
result.want?.parameters 是目标能力回写的参数,类似前端的 event.data。
③ 异步等回:await 等用户操作完拉回
typescript
const result = await this.context.startAbilityForResult(want)
// ← 这一行会等用户在目标能力里交互完拉回本能力后才继续
startAbilityForResult 返回 Promise<AbilityResult>,await 它就会等用户操作完。期间本能力挂后台,用户拉回时自动恢复。
2.3 startAbility:单向跳能力
typescript
async callAbilityNoResult(): Promise<void> {
this.callCount++
this.resultLog = `第 ${this.callCount} 次发起(不等回参)中...`
try {
const want: Want = {
bundleName: 'com.ohos.settings',
abilityName: 'com.ohos.settings.MainAbility'
} as Want
// startAbility 跳过去不等回参,无 AbilityResult 返回
await this.context.startAbility(want)
this.resultLog = `第 ${this.callCount} 次:已发起(不等回参,日志不显示 resultCode)`
} catch (e) {
this.resultLog = `第 ${this.callCount} 次失败:${e.message}`
}
}
startAbility 比 startAbilityForResult 简单一截------跳过去不等回参,无 AbilityResult 返回。适合「跳设置/跳关于/跳协议」这种单向跳场景。
2.4 terminateSelf:销毁本能力
typescript
async killSelf(): Promise<void> {
this.resultLog = 'terminateSelf 已调,能力即将销毁'
try {
await this.context.terminateSelf()
} catch (e) {
this.resultLog = `销毁失败:${e.message}`
}
}
terminateSelf 主动销毁本能力,结束生命周期。适合「登录页跳主页后销毁登录页」这种防返回场景。
三、真机实拍:跳系统设置能力真发出去并真拿回参
我把这个 demo 装到真机上跑(鸿蒙 6.1.1.125, API 24),调系统设置能力(com.ohos.settings),下面两张都是真机实拍,没有任何 P 图。
初始态:Want 路由载体展示 + 调用结果「尚未发起能力调用」+ ForResult/不调回参/terminateSelf 三按钮:

点 ForResult 按钮跳能力后返回态:调用结果「第 1 次:resultCode = 0(0=正常返回)+ 回写参:(目标能力没回写 echo)」:

重点看第二张:调用结果显示「第 1 次:resultCode = 0(0=正常返回)」------ForResult 真跳过去又正常返回了 。回写参「(目标能力没回写 echo)」是因为系统设置能力没 echo 我传的参数------如果调自己的能力,在目标能力
onAbilityResult回调里写回就行。这是startAbilityForResult双向通讯的证明。
四、startAbilityForResult vs 前端 window.open + postMessage:啥差异
新手最容易纠结的问题:既然前端 window.open 那么简洁,鸿蒙为啥要造能力调用?
| 维度 | 前端 window.open + postMessage |
startAbilityForResult |
|---|---|---|
| 运行环境 | 浏览器宿主 | 鸿蒙原生运行环境 |
| 路由载体 | url + query | Want(bundleName + abilityName + parameters) |
| 等回参 | window.open + postMessage 双步 |
await 一行搞定 |
| 回参类型 | event.data 任意 |
AbilityResult(resultCode + want.parameters) |
| 销毁来源 | window.close |
terminateSelf |
| 安全模型 | 同源策略 | 鸿蒙权限 + bundle 签名 |
一句话决策:鸿蒙应用跨能力协作必须用能力调用,不能用 window.open(不存在)。鸿蒙不是浏览器,这套原生机制更安全可控。
五、常见坑(都是血泪)
| 坑 | 症状 | 解法 |
|---|---|---|
用 window.open/window.postMessage |
编译报错「找不到 window」 | 鸿蒙用能力调用,没浏览器宿主 API |
startAbility 期望拿回参 |
拿不到 resultCode | 拿回参用 startAbilityForResult,不是 startAbility |
裸对象字面量传 want |
编译报错 arkts-no-untyped-obj-literals |
显式 as Want 断言 |
bundleName/abilityName 写错 |
跳能力报「找不到能力」 | 真机装目标能力 + 名字完全一致 |
resultCode 当业务码混 |
业务判断错 | resultCode 是返回码(0/-1),业务码在 want.parameters |
忘 terminateSelf 防返回 |
登录页能返回主页 | 登录成功后调 terminateSelf 销毁登录能力 |
await 期间改本能力状态 |
拉回时状态错乱 | await 后才改状态,期间不要动 |
六、Want 路由载体的安全模型
鸿蒙能力调用受安全约束------不是任意能力都能调,要满足以下之一:
| 场景 | 能调吗 | 何时用 |
|---|---|---|
| 调系统能力(com.ohos.settings 等) | 能(系统能力公开) | 跳设置/跳关于/跳协议 |
| 调自有能力(同 bundle) | 能 | 跳自家能力拿回参 |
| 调其他应用能力 | 需对方 ability export | 应用间协作(少数场景) |
| 调未导出能力 | 不能 | 鸿蒙安全模型 |
这是鸿蒙安全模型的硬约束------比浏览器 window.open 严,但比 iOS URL Scheme 松(鸿蒙能力路由可控粒度更细)。
七、完整代码仓库
本文所有代码都已托管到 AtomGit,欢迎 clone、提 issue、点 star:
🔗 仓库地址 :atomgit.com/JaneConan/a...
仓库包含:
- 完整的「跳系统设置能力拿回参」demo 工程
Index.ets主页面(startAbilityForResult+startAbility+terminateSelf三姿势)Want路由载体示范 +AbilityResult回参处理- 可直接用 DevEco Studio 打开运行(真机装系统设置能力必能跑)
八、下一步该学什么?
跑通这个 demo 之后,你的鸿蒙能力路由就入门了。这是韶非 UI 系列第三篇,后续按这个顺序往下:
- 后台任务
backgroundTaskManager(下一篇):延迟挂起 + 持续后台跑,告别前台才活 - 数据持久化
@ohos.data.relationalStore:鸿蒙 SQLite 封装,结构化数据存取 - WebSocket
@ohos.net.webSocket:长连接、推送、实时通讯,聊天应用必学 - 媒体访问
@ohos.file.photoAccessHelper:访问相册、扫描媒体文件,应用调系统相册必学 - 推送通知
@ohos.notificationManager:通知栏展示、点击拉起,离线触达必学
写在最后
startAbilityForResult 的本质,是**「能力路由的双向通讯」**------不是浏览器 window.open,是鸿蒙专门给跨能力协作的原生机制,能力对标 window.open + postMessage 但更安全可控。代价是 Want 路由载体多一步、resultCode 区分多一步。
一旦你开始用能力路由思维写跨能力协作,你会发现大部分「跳能力拿回参」「跳能力拿用户操作」的需求,都是 startAbilityForResult + Want 的自然结果。代码量比 window.open + postMessage 多两行,安全可控性高九成。
代码已经给你了,仓库链接在上面。现在关掉这篇文章,打开 DevEco Studio,把 demo 跑起来,亲手点 ForResult 跳能力再返回感受下双向通讯。
跑通了,回来评论区打个「1」,我看看有多少人真的动手了。🚀
作者:JaneConan 仓库:atomgit.com/JaneConan/a... 协议:Apache-2.0,随便用,别告我