UIAbility 组件之间交互(设备内)
UIAbility 是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的 UIAbility,包括应用内的其他 UIAbility、或者其他应用的 UIAbility(例如启动三方支付 UIAbility)。
本文主要介绍启动应用内的 UIAbility 组件的方式
启动应用内的 UIAbility
启动应用内的 UIAbility 并返回结果
启动 UIAbility 的指定页面
启动应用内的 UIAbility
当一个应用内包含多个 UIAbility 时,存在应用内启动 UIAbility 的场景。例如在支付应用中从入口 UIAbility 启动收付款 UIAbility。
假设应用中有两个 UIAbility:EntryAbility 和 FuncAbility(可以在同一个 Module 中,也可以在不同的 Module 中),需要从 EntryAbility 的页面中启动 FuncAbility。
startAbility 或者 startAbilityForResult(接受返回值)
terminateSelf 关闭自己
killAllProcesses 关闭所有
(1) 点击启动
在 EntryAbility 中,通过调用 startAbility()方法启动 UIAbility,want 为 UIAbility 实例启动的入口参数,其中 bundleName 为待启动应用的 Bundle 名称,abilityName 为待启动的 Ability 名称,moduleName 在待启动的 UIAbility 属于不同的 Module 时添加,parameters 为自定义信息参数。
import { common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
private context = getContext(this) as common.UIAbilityContext;
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() => {
// context为Ability对象的成员,在非Ability对象内部调用需要
// 将Context对象传递过去
let wantInfo: Want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.samples.stagemodelabilitydevelop',
moduleName: 'entry', // moduleName非必选
abilityName: 'FuncAbilityA',
parameters: {
// 自定义信息
info: '来自EntryAbility Page_UIAbilityComponentsInteractive页面'
},
};
// context为调用方UIAbility的UIAbilityContext
this.context.startAbility(wantInfo).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
}).catch((error: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
});
})
}
//...
}
//...
}
//...
}
}
(2) 获取参数
在 FuncAbility 的 onCreate()或者 onNewWant()生命周期回调文件中接收 EntryAbility 传递过来的参数。
import { AbilityConstant, UIAbility, Want } from "@kit.AbilityKit";
export default class FuncAbilityA extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 接收调用方UIAbility传过来的参数
let funcAbilityWant = want;
let info = funcAbilityWant?.parameters?.info;
}
//...
}
INFO
在被拉起的 FuncAbility 中,可以通过获取传递过来的 want 参数的 parameters 来获取拉起方 UIAbility 的 PID、Bundle Name 等信息。
(3) 停止
在 FuncAbility 业务完成之后,如需要停止当前 UIAbility 实例,在 FuncAbility 中通过调用 terminateSelf()方法实现。
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = '[Page_FromStageModel]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_FromStageModel {
build() {
Column() {
//...
Button('FuncAbilityB')
.onClick(() => {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
// context为需要停止的UIAbility实例的AbilityContext
context.terminateSelf((err) => {
if (err.code) {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
return;
}
});
})
}
//...
}
}
INFO
调用 terminateSelf()
方法停止当前 UIAbility 实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍然能查看到该实例对应的任务。如不需要保留该实例的快照,可以在其对应 UIAbility 的 module.json5 配置文件中,将 abilities 标签的 removeMissionAfterTerminate 字段配置为 true。
(4) 关闭所有
如需要关闭应用所有的 UIAbility 实例,可以调用 ApplicationContext 的 killAllProcesses()
方法实现关闭应用所有的进程。
启动应用内的 UIAbility 并获取返回结果
在一个 EntryAbility 启动另外一个 FuncAbility 时,希望在被启动的 FuncAbility 完成相关业务后,能将结果返回给调用方。例如在应用中将入口功能和账号登录功能分别设计为两个独立的 UIAbility,在账号登录 UIAbility 中完成登录操作后,需要将登录的结果返回给入口 UIAbility
(1) 启动
在 EntryAbility 中,调用 startAbilityForResult()
接口启动 FuncAbility
,异步回调中的 data 用于接收 FuncAbility 停止自身后返回给 EntryAbility 的信息。
import { common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { promptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() => {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
const RESULT_CODE: number = 1001;
let want: Want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.samples.stagemodelabilitydevelop',
moduleName: 'entry', // moduleName非必选
abilityName: 'FuncAbilityA',
parameters: {
// 自定义信息
info: '来自EntryAbility UIAbilityComponentsInteractive页面'
}
};
// 开启后的异调函数里面先看看返回值
context.startAbilityForResult(want).then((data) => {
if (data?.resultCode === RESULT_CODE) {
// 解析被调用方UIAbility返回的信息
let info = data.want?.parameters?.info;
hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
if (info !== null) {
promptAction.showToast({
message: JSON.stringify(info)
});
}
}
hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '');
}).catch((err: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);
});
})
}
//...
}
//...
}
//...
}
}
(2) 停止
在 FuncAbility 停止自身时,需要调用 terminateSelfWithResult()
方法,入参 abilityResult 为 FuncAbility 需要返回给 EntryAbility 的信息
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = '[Page_FuncAbilityA]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_FuncAbilityA {
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() => {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
const RESULT_CODE: number = 1001;
let abilityResult: common.AbilityResult = {
resultCode: RESULT_CODE,
want: {
bundleName: 'com.samples.stagemodelabilitydevelop',
moduleName: 'entry', // moduleName非必选
abilityName: 'FuncAbilityB',
parameters: {
info: '来自FuncAbility Index页面'
},
},
};
context.terminateSelfWithResult(abilityResult, (err) => {
if (err.code) {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`);
return;
}
});
})
}
//...
}
//...
}
//...
}
}
启动 UIAbbility 指定页面
概述
一个 UIAbility 可以对应多个页面,在不同的场景下启动该 UIAbility 时需要展示不同的页面,例如从一个 UIAbility 的页面中跳转到另外一个 UIAbility 时,希望启动目标 UIAbility 的指定页面
UIAbility 的启动分为两种情况:UIAbility 冷启动和 UIAbility 热启动
UIAbility 冷启动:指的是 UIAbility 实例处于完全关闭状态下被启动,这需要完整地加载和初始化 UIAbility 实例的代码、资源等。
UIAbility 热启动:指的是 UIAbility 实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该 UIAbility 实例,这种情况下可以快速恢复 UIAbility 实例的状态
启动流程
- (1) 调用方 UIAbility 指定启动页面
调用方 UIAbility 启动另外一个 UIAbility 时,通常需要跳转到指定的页面。例如 FuncAbility 包含两个页面(Index 对应首页,Second 对应功能 A 页面),此时需要在传入的 want 参数中配置指定的页面路径信息,可以通过 want 中的 parameters 参数增加一个自定义参数传递页面跳转信息。
import { common, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_UIAbilityComponentsInteractive {
build() {
Column() {
//...
List({ initialIndex: 0 }) {
ListItem() {
Row() {
//...
}
.onClick(() => {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
let want: Want = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.samples.stagemodelabilityinteraction',
moduleName: 'entry', // moduleName非必选
abilityName: 'FuncAbility',
parameters: { // 自定义参数传递页面信息
router: 'funcA'
}
};
// context为调用方UIAbility的UIAbilityContext
context.startAbility(want).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ability.');
}).catch((err: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
});
})
}
//...
}
//...
}
//...
}
}
- (2) 目标 UIAbility 冷启动
目标 UIAbility 冷启动时,在目标 UIAbility 的 onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标 UIAbility 的 onWindowStageCreate()生命周期回调中,解析 EntryAbility 传递过来的 want 参数,获取到需要加载的页面信息 url,传入 windowStage.loadContent()方法
import { AbilityConstant, Want, UIAbility } from "@kit.AbilityKit";
import { hilog } from "@kit.PerformanceAnalysisKit";
import { window, UIContext } from "@kit.ArkUI";
const DOMAIN_NUMBER: number = 0xff00;
const TAG: string = "[EntryAbility]";
export default class EntryAbility extends UIAbility {
funcAbilityWant: Want | undefined = undefined;
uiContext: UIContext | undefined = undefined;
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
// 接收调用方UIAbility传过来的参数
this.funcAbilityWant = want;
}
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN_NUMBER, TAG, "%{public}s", "Ability onWindowStageCreate");
// Main window is created, set main page for this ability
let url = "pages/Index";
if (
this.funcAbilityWant?.parameters?.router &&
this.funcAbilityWant.parameters.router === "funcA"
) {
url = "pages/Page_ColdStartUp";
}
windowStage.loadContent(url, (err, data) => {
// ...
});
}
}
- (3) 目标 UIAbility 热启动
在应用开发中,会遇到目标 UIAbility 实例之前已经启动过的场景,这时再次启动目标 UIAbility 时,不会重新走初始化逻辑,只会直接触发 onNewWant()生命周期方法。为了实现跳转到指定页面,需要在 onNewWant()中解析参数进行处理。
例如短信应用和联系人应用配合使用的场景
- 用户先打开短信应用,短信应用的 UIAbility 实例启动,显示短信应用的主页。
- 用户将设备回到桌面界面,短信应用进入后台运行状态。
- 用户打开联系人应用,找到联系人张三。
- 用户点击联系人张三的短信按钮,会重新启动短信应用的 UIAbility 实例。
- 由于短信应用的 UIAbility 实例已经启动过了,此时会触发该 UIAbility 的 onNewWant()回调,而不会再走 onCreate()和 onWindowStageCreate()等初始化逻辑。
开发步骤如下:
- 冷启动短信应用的 UIAbility 实例时,在 onWindowStageCreate()生命周期回调中,通过调用 getUIContext()接口获取 UI 上下文实例 UIContext 对象。
import { hilog } from "@kit.PerformanceAnalysisKit";
import { Want, UIAbility } from "@kit.AbilityKit";
import { window, UIContext } from "@kit.ArkUI";
const DOMAIN_NUMBER: number = 0xff00;
const TAG: string = "[EntryAbility]";
export default class EntryAbility extends UIAbility {
funcAbilityWant: Want | undefined = undefined;
uiContext: UIContext | undefined = undefined;
// ...
onWindowStageCreate(windowStage: window.WindowStage): void {
// Main window is created, set main page for this ability
hilog.info(DOMAIN_NUMBER, TAG, "%{public}s", "Ability onWindowStageCreate");
let url = "pages/Index";
if (
this.funcAbilityWant?.parameters?.router &&
this.funcAbilityWant.parameters.router === "funcA"
) {
url = "pages/Page_ColdStartUp";
}
windowStage.loadContent(url, (err, data) => {
if (err.code) {
return;
}
let windowClass: window.Window;
windowStage.getMainWindow((err, data) => {
if (err.code) {
hilog.error(
DOMAIN_NUMBER,
TAG,
`Failed to obtain the main window. Code is ${err.code}, message is ${err.message}`
);
return;
}
windowClass = data;
this.uiContext = windowClass.getUIContext();
});
hilog.info(
DOMAIN_NUMBER,
TAG,
"Succeeded in loading the content. Data: %{public}s",
JSON.stringify(data) ?? ""
);
});
}
}
- 在短信应用 UIAbility 的 onNewWant()回调中解析调用方传递过来的 want 参数,通过调用 UIContext 中的 getRouter()方法获取 Router 对象,并进行指定页面的跳转。此时再次启动该短信应用的 UIAbility 实例时,即可跳转到该短信应用的 UIAbility 实例的指定页面。
import { AbilityConstant, Want, UIAbility } from "@kit.AbilityKit";
import { hilog } from "@kit.PerformanceAnalysisKit";
import type { Router, UIContext } from "@kit.ArkUI";
import type { BusinessError } from "@kit.BasicServicesKit";
const DOMAIN_NUMBER: number = 0xff00;
const TAG: string = "[EntryAbility]";
export default class EntryAbility extends UIAbility {
funcAbilityWant: Want | undefined = undefined;
uiContext: UIContext | undefined = undefined;
// ...
onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
if (want?.parameters?.router && want.parameters.router === "funcA") {
let funcAUrl = "pages/Page_HotStartUp";
if (this.uiContext) {
let router: Router = this.uiContext.getRouter();
router
.pushUrl({
url: funcAUrl,
})
.catch((err: BusinessError) => {
hilog.error(
DOMAIN_NUMBER,
TAG,
`Failed to push url. Code is ${err.code}, message is ${err.message}`
);
});
}
}
}
}
INFO
当被调用方 UIAbility 组件启动模式设置为 multiton 启动模式时,每次启动都会创建一个新的实例,那么 onNewWant()回调就不会被用到