HarmonyOS 7 Want 到底是什么?用一个最小 A/B Ability 传参实验看懂信息载体【鸿蒙心迹】

你是不是也在想——“鸿蒙这么火,我能不能学会?”
答案是:当然可以!
这个专栏专为零基础小白设计,不需要编程基础,也不需要懂原理、背术语。我们会用最通俗易懂的语言、最贴近生活的案例,手把手带你从安装开发工具开始,一步步学会开发自己的鸿蒙应用。
不管你是学生、上班族、打算转行,还是单纯对技术感兴趣,只要你愿意花一点时间,就能在这里搞懂鸿蒙开发,并做出属于自己的App!
📌 关注本专栏《零基础学鸿蒙开发》,一起变强!
每一节内容我都会持续更新,配图+代码+解释全都有,欢迎点个关注,不走丢,我是小白酷爱学习,我们一起上路 🚀
全文目录:
前言
刚接触 HarmonyOS 的 Ability 跳转时,Want 很容易被理解成“页面跳转参数”。这个理解不能说完全错,但范围太窄。
华为官方对 Want 的定义更准确:**Want 是对象间信息传递的载体,可以用于应用组件间的信息传递。**在启动 UIAbility 这个场景里,它既可以描述“我要去哪里”,也可以携带“我要带什么数据过去”。官方文档给出的典型字段正是 bundleName、abilityName 和 parameters。
这次不把问题铺开,只做一个最小实验:
A Ability → 构造 Want → 启动 B Ability → B 从 Want 中取出 parameters。
把这条链路跑通,Want 的角色基本就清楚了。
HarmonyOS 7 已对应 API 26,官方升级适配文档明确给出了这一版本关系;本文使用的 UIAbilityContext.startAbility() 等基础能力并不是 API 26 才新增的接口,当前 UIAbilityContext 文档说明该模块首批接口从 API version 9 开始支持,因此 HarmonyOS 7 / API 26 可以继续使用。
一、先别把 Want 理解成“跳转函数”
先看这次实验里最重要的一段数据:
let want: Want = {
bundleName: 'com.example.wantdemo',
abilityName: 'BAbility',
parameters: {
from: 'AAbility',
message: 'Hello Want'
}
};
如果暂时不考虑调用 startAbility(),单独看这个对象,其实已经能看出 Want 的职责。
| 字段 | 这次实验中的作用 |
|---|---|
bundleName | 指明目标 Ability 所属应用的 Bundle 名称 |
abilityName | 指明目标 Ability |
parameters | 携带开发者需要传递的键值对数据 |
官方 Want API 文档说明,当 bundleName 和 abilityName 同时指定时,Want 可以直接匹配指定的 Ability;parameters 则用于保存开发者传入的键值对。
所以更合适的理解是:
Want 本身负责描述目标和携带信息;真正执行 UIAbility 启动动作的是对应 Context 提供的启动接口。
这两个概念不要揉成一个。
二、先把这次实验的官方规则确认清楚
本文按 Stage 模型 + ArkTS + Ability Kit 来组织最小示例。
当前官方 UIAbilityContext 文档说明,每个 UIAbility 实例化时,系统都会创建对应的 UIAbilityContext;开发者可以利用它拉起其他 UIAbility。该模块首批接口从 API version 9 开始支持,并且仅可用于 Stage 模型。startAbility(want) 接收的核心参数就是 Want。
本文实验边界如下:
- 开发背景:HarmonyOS 7 / API 26;
- 应用模型:Stage 模型;
- Kit:Ability Kit;
- 组件:两个 UIAbility,分别记为 A Ability 和 B Ability;
- 场景:同一应用内,从前台 A Ability 显式启动 B Ability;
- 数据:只传两个字符串参数;
- 权限:这个最小场景不额外增加
requestPermissions; - 不涉及跨设备、后台拉起以及系统应用专属能力。
官方当前文档中,UIAbilityContext.startAbility() 的系统能力为 SystemCapability.Ability.AbilityRuntime.Core,并明确要求在主线程调用。文档同时列出了目标 Ability 不存在、组件类型错误、不可见组件、Context 不存在等错误情况。
这里没有必要为了“像完整项目”而塞进额外权限和配置。最小实验只验证 Want 本身。
三、搭一个最小的 A / B Ability
假设应用的 Bundle 名称为:
com.example.wantdemo
Module 中准备两个 UIAbility:
entry
├── src/main/ets/
│ ├── aability/
│ │ └── AAbility.ets
│ ├── bability/
│ │ └── BAbility.ets
│ └── pages/
│ ├── Index.ets
│ └── BPage.ets
└── src/main/module.json5
module.json5 的 abilities 中需要存在对应的 Ability 配置。官方文档中的 UIAbility 配置示例使用 name 指定 Ability 名称,使用 srcEntry 指向实现文件。
下面只保留与实验直接相关的结构:
{
"module": {
// 其他已有配置保持不变
"abilities": [
{
"name": "AAbility",
"srcEntry": "./ets/aability/AAbility.ets",
"exported": true
},
{
"name": "BAbility",
"srcEntry": "./ets/bability/BAbility.ets",
"exported": false
}
]
}
}
这里真正需要记住的是:Want 中的 abilityName 要和目标 UIAbility 的名称对应。
如果项目本身已经有入口 EntryAbility,完全没必要为了文章把它改名成 AAbility。实际迁移时可以把现有入口 Ability 当作 A,只额外增加 B Ability。
四、A 页面:构造一个最小显式 Want
A 页只放一个按钮。
点击按钮以后获取 UIAbilityContext,创建 Want,然后启动 B Ability:
import { common, Want } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct Index {
build() {
Column({ space: 20 }) {
Text('A Ability')
.fontSize(28)
Button('携带参数打开 B Ability')
.onClick(() => {
const context =
this.getUIContext().getHostContext() as common.UIAbilityContext;
const want: Want = {
bundleName: 'com.example.wantdemo',
abilityName: 'BAbility',
parameters: {
from: 'AAbility',
message: 'Hello Want'
}
};
try {
context.startAbility(want)
.catch((err: BusinessError) => {
console.error(
`startAbility failed, code=${err.code}, message=${err.message}`
);
});
} catch (err) {
const error = err as BusinessError;
console.error(
`startAbility failed, code=${error.code}, message=${error.message}`
);
}
})
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
当前官方 UIAbilityContext API 示例已经采用:
import { UIAbility, Want } from '@kit.AbilityKit';
并通过 UIAbilityContext.startAbility(want) 启动目标 UIAbility;Promise 形式的接口返回 Promise<void>。
这段代码真正需要关注的不是按钮,而是下面三个字段:
const want: Want = {
bundleName: 'com.example.wantdemo',
abilityName: 'BAbility',
parameters: {
from: 'AAbility',
message: 'Hello Want'
}
};
其中前两个字段回答“去哪里”,parameters 回答“带什么”。
这就是本文最核心的实验。
五、B Ability:参数不是去页面路由里找,而是从 Want 里拿
接下来实现 B Ability。
官方资料中已经给出了直接在 UIAbility 的 onCreate(want: Want) 中读取 want.parameters 的用法;对于已经存在的 UIAbility 实例再次收到新的 Want,官方示例还会在 onNewWant(want: Want) 中解析参数。
因此 B Ability 可以写成:
import {
AbilityConstant,
UIAbility,
Want
} from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';
export default class BAbility extends UIAbility {
private parseWant(want: Want): void {
const from = want.parameters?.['from'] as string;
const message = want.parameters?.['message'] as string;
console.info(`[BAbility] from=${from}`);
console.info(`[BAbility] message=${message}`);
}
onCreate(
want: Want,
launchParam: AbilityConstant.LaunchParam
): void {
this.parseWant(want);
}
onNewWant(
want: Want,
launchParam: AbilityConstant.LaunchParam
): void {
this.parseWant(want);
}
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.loadContent('pages/BPage');
}
}
B 页面本身甚至不需要承担 Want 解析职责。为了让实验尽可能干净,可以只显示一个静态页面:
@Entry
@Component
struct BPage {
build() {
Column({ space: 16 }) {
Text('B Ability')
.fontSize(28)
Text('Want 参数已由 BAbility 接收,可查看日志')
.fontSize(16)
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
按照这个结构,实验观察点非常明确:
A 页面点击按钮
↓
构造 Want
↓
bundleName + abilityName 指向 BAbility
↓
parameters 携带 from / message
↓
BAbility 收到 Want
↓
读取 want.parameters
六、为什么 B 里同时写 onCreate 和 onNewWant
这个地方比较容易理解错。
如果只为了第一次启动 B Ability,下面这段已经足够说明 Want 如何进入目标组件:
onCreate(want: Want): void {
const message = want.parameters?.['message'] as string;
}
但实际开发不能形成“Want 永远只在 onCreate 里拿”的印象。
官方最新业务示例明确展示了两种接收位置:UIAbility 实例创建时在 onCreate 中解析 Want;已经启动的 UIAbility 实例再次被拉起时,可以在 onNewWant 中接收并解析新的 Want。
因此更稳妥的写法是把解析逻辑抽成:
private parseWant(want: Want): void
然后两个入口复用。
这样关注点仍然是 Want,没有把文章带到 UIAbility 启动模式的细节里。
七、bundleName、abilityName、parameters 分别不要搞混
1. bundleName 不是随便起的业务字符串
bundleName 用来标识目标 Ability 所属应用。
如果示例代码里写的是:
bundleName: 'com.example.wantdemo'
那么迁移到自己的工程时就必须替换成真实应用对应的 Bundle 名称。
把网上 Demo 的 com.example.xxx 原样复制过去,是最直接的一类目标匹配错误。
2. abilityName 指的是目标 Ability
本文目标组件叫:
BAbility
所以 Want 中写:
abilityName: 'BAbility'
官方 Want 文档说明,同时给出 bundleName 和 abilityName 时,可以直接匹配指定 Ability。
因此排查 16000001 一类“指定 Ability 不存在”的问题时,除了看调用代码,也要回头核对 module.json5 中 Ability 的声明。startAbility() 当前 API 文档明确列出了 16000001、16000002、16000004 等启动错误。
3. parameters 才是我们自己的业务数据
本文放进去的是:
parameters: {
from: 'AAbility',
message: 'Hello Want'
}
B 端按照同一个 key 获取:
const from = want.parameters?.['from'] as string;
const message = want.parameters?.['message'] as string;
这里建议业务项目自己统一管理 key,不要在多个 Ability 中到处手写不同字符串。
比如至少可以集中定义:
export class WantKeys {
static readonly FROM: string = 'from';
static readonly MESSAGE: string = 'message';
}
这样可以减少发送端写 message、接收端却误写成 msg 之类的问题。
八、几个容易出现的理解偏差
Want 不等于页面路由参数
这次确实拿 Want 做了“A → B 页面”的参数传递,但 Want 面向的是对象和应用组件之间的信息传递,不应该简单等同于 ArkUI 页面内部的普通路由参数。官方对 Want 的定义本身就比“页面参数”更宽。
startAbility 成功,不代表你应该从它那里等一个业务返回值
本文只讨论单向过程:
A → Want → B
A 把目标和参数放入 Want,B 接收。
把这个单向模型先理解清楚,比一开始同时塞入更多通信概念更容易看明白 Want 本身。
parameters 的 key 是双方约定
系统负责把 Want 作为载体送到目标组件,但 from、message 这些业务 key 是开发者自己定义的。
因此:
parameters: {
message: 'Hello Want'
}
和:
want.parameters?.['message']
实际上构成了一份最小的数据协议。
九、显式 Want 和隐式 Want,是两条不同路线
到这里再看“显式”和“隐式”,就比较容易理解了。
本文使用的是典型的显式 Want:
const want: Want = {
bundleName: 'com.example.wantdemo',
abilityName: 'BAbility'
};
调用方已经知道目标是谁,直接指定应用和 Ability。
而隐式 Want 的思路不是“少写一个 abilityName”这么简单。官方资料将显式 Want 与隐式 Want 单独作为匹配规则进行说明;隐式场景可以涉及 action、entities、uri、type 等信息,并与目标组件声明的能力进行匹配。
所以可以把两条路线简单记成:
显式 Want:
我知道要找谁
→ bundleName + abilityName
→ 直接指向目标组件
隐式 Want:
我描述自己想做什么
→ action / entities / uri / type 等匹配信息
→ 系统按照规则寻找合适目标
这里还有一个当前版本需要特别留意的变化:华为官方最新文档已经将“常见 action 与 entities”章节标记为不推荐使用,并说明由于 action/entity 被泛化使用带来的匹配问题,后续会逐步废弃非必要 action/entity,建议按官方当前推荐方式选择对应能力。
因此本文只把显式和隐式的概念边界说明白,不拿传统 action/entity 示例继续扩展一个新的 Demo。
十、这个最小实验出问题时,按什么顺序查
如果 A 点击以后没有按预期进入 B,可以先别动页面代码,按下面顺序排查。
第一步,看目标信息。
确认:
bundleName
abilityName
是否确实指向当前目标。
第二步,看 module.json5。
确认 BAbility 已经存在于 abilities 配置中,name 与 Want 中使用的名称一致,srcEntry 指向实际文件。官方配置示例明确采用 name 和 srcEntry 描述 UIAbility。
第三步,看 startAbility 错误。
不要只写:
context.startAbility(want);
然后发现“没反应”。
保留错误处理,重点看错误码。官方文档当前列出的错误包括目标 Ability 不存在、Ability 类型错误、不可启动不可见组件、Context 不存在、无匹配 Ability 等。
第四步,看 B 的接收位置。
第一次创建实例时关注:
onCreate(want)
已有实例再次收到 Want 的场景还要关注:
onNewWant(want)
官方现有示例就是同时在这两个回调中解析 Want。
第五步,再检查 parameters。
确认发送端:
message
和接收端:
want.parameters?.['message']
使用的是同一个 key。
这套排查顺序的好处是,能够把“目标没找到”和“目标找到了但业务参数没取对”分开处理。
开发经验总结
Want 本身并不复杂,真正容易混淆的是它和“启动动作”“页面路由”“业务参数”之间的关系。
通过这个 A / B Ability 最小实验,可以留下四个比较稳定的认识:
第一,Want 是信息载体。 官方定义不是“跳转 API”,而是对象和应用组件之间的信息传递载体。
第二,显式 Want 同时解决两个问题:目标是谁、带什么过去。 bundleName 和 abilityName 描述目标,parameters 承载业务数据。
第三,B 获取参数的入口是收到的 Want。 UIAbility 创建时可以从 onCreate(want) 获取;已有实例再次收到新的 Want 时还要关注 onNewWant(want)。
第四,显式 Want 和隐式 Want 不要混成一种写法。 前者明确指定目标,后者走匹配机制;而且官方对传统 action/entities 的使用已经给出了新的“不推荐使用”提示。
❤️ 如果本文帮到了你…
- 请点个赞,让我知道你还在坚持阅读技术长文!
- 请收藏本文,因为你以后一定还会用上!
- 如果你在学习过程中遇到bug,请留言,我帮你踩坑!
更多推荐




所有评论(0)