HarmonyOS 应用开发之UIAbility组件间交互(设备内)

news/2024/4/27 18:38:32/文章来源:https://blog.csdn.net/maniuT/article/details/137124153

UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的UIAbility,该UIAbility可以是应用内的其他UIAbility,也可以是其他应用的UIAbility(例如启动三方支付UIAbility)。

本文将从如下场景分别介绍设备内UIAbility间的交互方式。对于跨设备的应用组件交互,请参见 应用组件跨设备交互(流转)。

  • 启动应用内的UIAbility
  • 启动应用内的UIAbility并获取返回结果
  • 启动其他应用的UIAbility
  • 启动其他应用的UIAbility并获取返回结果
  • 启动UIAbility指定窗口模式(仅对系统应用开放)
  • 启动UIAbility的指定页面
  • 通过Call调用实现UIAbility交互(仅对系统应用开放)

启动应用内的UIAbility

当一个应用内包含多个UIAbility时,存在应用内启动UIAbility的场景。例如在支付应用中从入口UIAbility启动收付款UIAbility。

假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在同一个Module中,也可以在不同的Module中),需要从EntryAbility的页面中启动FuncAbility。

  1. 在EntryAbility中,通过调用 startAbility()方法启动UIAbility,want 为UIAbility实例启动的入口参数,其中bundleName为待启动应用的Bundle名称,abilityName为待启动的Ability名称,moduleName在待启动的UIAbility属于不同的Module时添加,parameters为自定义信息参数。示例中的context的获取方式请参见 获取UIAbility的上下文信息。
import common from '@ohos.app.ability.common';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
import hilog from '@ohos.hilog';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {private context = getContext(this) as common.UIAbilityContext;build() {...Button().onClick(() => {// context为Ability对象的成员,在非Ability对象内部调用需要// 将Context对象传递过去let wantInfo: Want = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.samples.myapplication',moduleName: 'entry', // moduleName非必选abilityName: 'FuncAbilityA',parameters: { // 自定义信息info: '来自EntryAbility Page_UIAbilityComponentsInteractive页面'},}// context为调用方UIAbility的UIAbilityContextthis.context.startAbility(wantInfo).then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');}).catch((error: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');});})}
}
  1. 在FuncAbility的 onCreate() 生命周期回调文件中接收EntryAbility传递过来的参数。
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Want from '@ohos.app.ability.Want';export default class FuncAbility extends UIAbility {onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {// 接收调用方UIAbility传过来的参数let funcAbilityWant = want;let info = funcAbilityWant?.parameters?.info;// ...}
}

说明:
被拉起的FuncAbility中,可以通过获取传递过来的want参数的parameters来获取拉起方UIAbility的PID、Bundle Name等信息。

  1. 在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用 terminateSelf() 方法实现。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {...Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext// context为需要停止的UIAbility实例的AbilityContextcontext.terminateSelf((err) => {if (err.code) {hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate Self. Code is ${err.code}, message is ${err.message}`);return;}});})}
}

说明:
调用 terminateSelf() 方法停止当前UIAbility实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍然能查看到该实例对应的任务。如不需要保留该实例的快照,可以在其对应UIAbility的 module.json5配置文件中,将 abilities标签的removeMissionAfterTerminate字段配置为true。

  1. 如需要关闭应用所有的UIAbility实例,可以调用 ApplicationContext 的 killAllProcesses() 方法实现关闭应用所有的进程。

启动应用内的UIAbility并获取返回结果

在一个EntryAbility启动另外一个FuncAbility时,希望在被启动的FuncAbility完成相关业务后,能将结果返回给调用方。例如在应用中将入口功能和帐号登录功能分别设计为两个独立的UIAbility,在帐号登录UIAbility中完成登录操作后,需要将登录的结果返回给入口UIAbility。

  1. 在EntryAbility中,调用 startAbilityForResult() 接口启动FuncAbility,异步回调中的data用于接收FuncAbility停止自身后返回给EntryAbility的信息。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.samples.stagemodelabilitydevelop',moduleName: 'entry', // moduleName非必选abilityName: 'FuncAbilityA',parameters: { // 自定义信息info: '来自EntryAbility UIAbilityComponentsInteractive页面'}};context.startAbilityForResult(want).then((data) => {// ...}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);});})}
}
  1. 在FuncAbility停止自身时,需要调用terminateSelfWithResult() 方法,入参abilityResult为FuncAbility需要返回给EntryAbility的信息。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';const TAG: string = '[Page_FuncAbilityA]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_FuncAbilityA {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextconst 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;}});})}
}
  1. FuncAbility停止自身后,EntryAbility通过 startAbilityForResult() 方法回调接收被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
import promptAction from '@ohos.promptAction';const TAG: string = '[EntryAbility]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextconst 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}`);});})}
}

启动其他应用的UIAbility

启动其他应用的UIAbility,通常用户只需要完成一个通用的操作(例如需要选择一个文档应用来查看某个文档的内容信息)。系统会根据调用方的want参数来识别和启动匹配到的应用UIAbility。

启动UIAbility有 显式Want启动和隐式Want启动 两种方式。

  • 显式Want启动:启动一个确定应用的UIAbility,在want参数中需要设置该应用bundleName和abilityName,当需要拉起某个明确的UIAbility时,通常使用显式Want启动方式。

  • 隐式Want启动:根据匹配条件由用户选择启动哪一个UIAbility,即不明确指出要启动哪一个UIAbility(abilityName参数未设置),在调用 startAbility() 方法时,其入参want中指定了一系列的entities字段(表示目标UIAbility额外的类别信息,如浏览器、视频播放器)和actions字段(表示要执行的通用操作,如查看、分享、应用详情等)等参数信息,然后由系统去分析want,并帮助找到合适的UIAbility来启动。当需要拉起其他应用的UIAbility时,开发者通常不知道用户设备中应用的安装情况,也无法确定目标应用的bundleName和abilityName,通常使用隐式Want启动方式。

本文主要讲解如何通过隐式Want启动其他应用的UIAbility。

  1. 将多个待匹配的文档应用安装到设备,在其对应UIAbility的 module.json5配置文件 中,配置skills标签的entities字段和actions字段。
 {"module": {"abilities": [{..."skills": [{"entities": [..."entity.system.default"],"actions": [..."ohos.want.action.viewData"]}]}]}}
  1. 在调用方want参数中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。
import common from '@ohos.app.ability.common';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
import hilog from '@ohos.hilog';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '', // deviceId为空表示本设备// uncomment line below if wish to implicitly query only in the specific bundle.// bundleName: 'com.samples.stagemodelabilityinteraction',action: 'ohos.want.action.viewData',// entities can be omitted.entities: ['entity.system.default']};// context为调用方UIAbility的UIAbilityContextcontext.startAbility(want).then(() => {hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting FuncAbility.');}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to start FuncAbility. Code is ${err.code}, message is ${err.message}`);});})}
}

效果示意如下图所示,点击“打开PDF文档”时,会弹出选择框供用户选择。

  1. 在文档应用使用完成之后,如需要停止当前UIAbility实例,通过调用 terminateSelf() 方法实现。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';const TAG: string = '[Page_FromStageModel]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_FromStageModel {@State message: string = 'Hello World'build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext// context为需要停止的UIAbility实例的AbilityContextcontext.terminateSelf((err) => {if (err.code) {hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);return;}});})}
}

启动其他应用的UIAbility并获取返回结果

当使用隐式Want启动其他应用的UIAbility并希望获取返回结果时,调用方需要使用 startAbilityForResult() 方法启动目标UIAbility。例如主应用中需要启动三方支付并获取支付结果。

  1. 在支付应用对应UIAbility的 module.json5配置文件 中,配置skills的entities字段和actions字段。
{"module": {"abilities": [{..."skills": [{"entities": [..."entity.system.default"],"actions": [..."ohos.want.action.editData"]}]}]}
}
  1. 调用方使用 startAbilityForResult() 方法启动支付应用的UIAbility,在调用方want参数中的entities和action需要被包含在待匹配UIAbility的skills标签配置的entities和actions中。异步回调中的data用于后续接收支付UIAbility停止自身后返回给调用方的信息。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.samples.stagemodelabilitydevelop',moduleName: 'entry', // moduleName非必选abilityName: 'FuncAbilityA',parameters: { // 自定义信息info: '来自EntryAbility UIAbilityComponentsInteractive页面'}};context.startAbilityForResult(want).then((data) => {// ...}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);});})}
}
  1. 在支付UIAbility完成支付之后,需要调用 terminateSelfWithResult() 方法实现停止自身,并将abilityResult参数信息返回给调用方。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';const TAG: string = '[Page_FuncAbilityA]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_FuncAbilityA {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextconst 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;}});})}
}
  1. 在调用方 startAbilityForResult() 方法回调中接收支付应用返回的信息,RESULT_CODE需要与前面 terminateSelfWithResult() 返回的数值保持一致。
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';
import promptAction from '@ohos.promptAction';const TAG: string = '[EntryAbility]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextconst 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}`);});})}
}

启动UIAbility指定窗口模式(仅对系统应用开放)

当用户打开应用时,应用程序会以不同的窗口模式进行展示,即启动UIAbility的窗口模式。应用程序可以启动为全屏模式,悬浮窗模式或分屏模式。

全屏模式是指应用程序启动后,占据整个屏幕,用户无法同时查看其他窗口或应用程序。全屏模式通常适用于那些要求用户专注于特定任务或界面的应用程序。

悬浮窗模式是指应用程序启动后,以浮动窗口的形式显示在屏幕上,用户可以轻松切换到其他窗口或应用程序。悬浮窗通常适用于需要用户同时处理多个任务的应用程序。

分屏模式允许用户在同一屏幕上同时运行两个应用程序,其中一个应用程序占据屏幕左侧/上侧的一部分,另一个应用程序占据右侧/下侧的一部分。分屏模式主要用于提高用户的多任务处理效率。

使用 startAbility() 方法启动UIAbility时,可以通过在入参中增加 StartOptions 参数的windowMode属性来配置启动UIAbility的窗口模式。

说明:

  1. 如果在使用 startAbility() 方法启动UIAbility时,入参中未指定 StartOptions 参数的windowMode属性,那么UIAbility将以系统默认的窗口展示形态启动。
  2. 为了确保启动的UIAbility展示形态能够被支持,需要在该UIAbility对应的 module.json5配置文件中 abilities标签的supportWindowMode字段确认启动的展示形态被支持。

以下是具体的操作步骤,以悬浮窗模式为例,假设需要从EntryAbility的页面中启动FuncAbility:

  1. 在调用 startAbility() 方法时,增加 StartOptions 参数。
  2. 在 StartOptions 参数中设置windowMode字段为WINDOW_MODE_FLOATING,表示启动的UIAbility将以悬浮窗的形式展示。
  3. windowMode属性仅适用于系统应用,三方应用可以使用displayId属性。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import StartOptions from '@ohos.app.ability.StartOptions';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.samples.stagemodelabilitydevelop',moduleName: 'entry', // moduleName非必选abilityName: 'FuncAbilityB',parameters: { // 自定义信息info: '来自EntryAbility Index页面'}};let options: StartOptions = {windowMode: AbilityConstant.WindowMode.WINDOW_MODE_FLOATING};// context为调用方UIAbility的UIAbilityContextcontext.startAbility(want, options).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}`);});})}
}

效果示意如下图所示。

启动UIAbility的指定页面

概述

一个UIAbility可以对应多个页面,在不同的场景下启动该UIAbility时需要展示不同的页面,例如从一个UIAbility的页面中跳转到另外一个UIAbility时,希望启动目标UIAbility的指定页面。

UIAbility的启动分为两种情况:UIAbility冷启动和UIAbility热启动。

  • UIAbility冷启动:指的是UIAbility实例处于完全关闭状态下被启动,这需要完整地加载和初始化UIAbility实例的代码、资源等。
  • UIAbility热启动:指的是UIAbility实例已经启动并在前台运行过,由于某些原因切换到后台,再次启动该UIAbility实例,这种情况下可以快速恢复UIAbility实例的状态。

本文主要讲解 目标UIAbility冷启动 两种启动指定页面的场景,以及在讲解启动指定页面之前会讲解到在调用方如何指定启动页面。

调用方UIAbility指定启动页面

调用方UIAbility启动另外一个UIAbility时,通常需要跳转到指定的页面。例如FuncAbility包含两个页面(Index对应首页,Second对应功能A页面),此时需要在传入的want参数中配置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。

import common from '@ohos.app.ability.common';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';const TAG: string = '[Page_UIAbilityComponentsInteractive]';
const DOMAIN_NUMBER: number = 0xFF00;@Entry
@Component
struct Page_UIAbilityComponentsInteractive {build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {deviceId: '', // deviceId为空表示本设备bundleName: 'com.samples.stagemodelabilityinteraction',moduleName: 'entry', // moduleName非必选abilityName: 'FuncAbility',parameters: { // 自定义参数传递页面信息router: 'FuncA'}};// context为调用方UIAbility的UIAbilityContextcontext.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}`);});})}
}

目标UIAbility冷启动

目标UIAbility冷启动时,在目标UIAbility的onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中,解析EntryAbility传递过来的want参数,获取到需要加载的页面信息url,传入windowStage.loadContent()方法。

import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';export default class FuncAbility extends UIAbility {funcAbilityWant: Want | undefined = undefined;onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {// 接收调用方UIAbility传过来的参数this.funcAbilityWant = want;}onWindowStageCreate(windowStage: window.WindowStage) {// Main window is created, set main page for this abilitylet url = 'pages/Index';if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {url = 'pages/Page_ColdStartUp';}windowStage.loadContent(url, (err, data) => {// ...});}
}

目标UIAbility热启动

在应用开发中,会遇到目标UIAbility实例之前已经启动过的场景,这时再次启动目标UIAbility时,不会重新走初始化逻辑,只会直接触发onNewWant()生命周期方法。为了实现跳转到指定页面,需要在onNewWant()中解析参数进行处理。

例如短信应用和联系人应用配合使用的场景。

  1. 用户先打开短信应用,短信应用的UIAbility实例启动,显示短信应用的主页。
  2. 用户将设备回到桌面界面,短信应用进入后台运行状态。
  3. 用户打开联系人应用,找到联系人张三。
  4. 用户点击联系人张三的短信按钮,会重新启动短信应用的UIAbility实例。
  5. 由于短信应用的UIAbility实例已经启动过了,此时会触发该UIAbility的onNewWant()回调,而不会再走onCreate()onWindowStageCreate()等初始化逻辑。

图1 目标UIAbility热启动

开发步骤如下所示。

  1. 冷启动短信应用的UIAbility实例时,在onWindowStageCreate()生命周期回调中,通过调用 getUIContext() 接口获取UI上下文实例 UIContext 对象。
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';import { UIContext } from '@ohos.arkui.UIContext';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 abilitylet 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();})});}
}
  1. 在短信应用UIAbility的onNewWant()回调中解析调用方传递过来的want参数,通过调用UIContext中的getRouter() 方法获取 Router 对象,并进行指定页面的跳转。此时再次启动该短信应用的UIAbility实例时,即可跳转到该短信应用的UIAbility实例的指定页面。
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import hilog from '@ohos.hilog';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import { BusinessError } from '@ohos.base';import { Router, UIContext } from '@ohos.arkui.UIContext';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 === 'funcB') {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}`);});}}}// ...
}

说明:
当被调用方 UIAbility组件启动模式 设置为multiton启动模式时,每次启动都会创建一个新的实例,那么 onNewWant() 回调就不会被用到。

通过Call调用实现UIAbility交互(仅对系统应用开放)

Call调用是UIAbility能力的扩展,它为UIAbility提供一种能够被外部调用并与外部进行通信的能力。Call调用支持前台与后台两种启动方式,使UIAbility既能被拉起到前台展示UI,也可以在后台被创建并运行。Call调用在调用方与被调用方间建立了IPC通信,因此应用开发者可通过Call调用实现不同UIAbility之间的数据共享。

Call调用的核心接口是startAbilityByCall()方法,与startAbility()接口的不同之处在于:

  • startAbilityByCall支持前台与后台两种启动方式,而startAbility()仅支持前台启动。
  • 调用方可使用startAbilityByCall()所返回的Caller对象与被调用方进行通信,而startAbility()不具备通信能力。

Call调用的使用场景主要包括:

  • 需要与被启动的UIAbility进行通信。
  • 希望被启动的UIAbility在后台运行。

表1 Call调用相关名词解释

名词描述
CallerAbility进行Call调用的UIAbility(调用方)。
CalleeAbility被Call调用的UIAbility(被调用方)。
Caller实际对象,由startAbilityByCall接口返回,CallerAbility可使用Caller与CalleeAbility进行通信。
Callee实际对象,被CalleeAbility持有,可与Caller进行通信。

Call调用示意图如下所示。

图1 Call调用示意图

  • CallerAbility调用startAbilityByCall接口获取Caller,并使用Caller对象的call方法向CalleeAbility发送数据。
  • CalleeAbility持有一个Callee对象,通过Callee的on方法注册回调函数,当接收到Caller发送的数据时将会调用对应的回调函数。

说明:

  1. 当前仅支持系统应用使用Call调用。
  2. CalleeAbility的启动模式需要为单实例。
  3. Call调用既支持本地(设备内)Call调用,也支持跨设备Call调用,下面介绍设备内Call调用方法。

接口说明

Call功能主要接口如下表所示。具体的API详见 接口文档。

表2 Call功能主要接口

接口名描述
startAbilityByCall(want: Want): Promise启动指定UIAbility并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见 接口文档。AbilityContext与ServiceExtensionContext均支持该接口。
on(method: string, callback: CalleeCallBack): void通用组件Callee注册method对应的callback方法。
off(method: string): void通用组件Callee解注册method的callback方法。
call(method: string, data: rpc.Parcelable): Promise向通用组件Callee发送约定序列化数据。
callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence>向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。
release(): void释放通用组件的Caller通信接口。
on(type: “release”, callback: OnReleaseCallback): void注册通用组件通信断开监听通知。

设备内通过Call调用实现UIAbility交互,涉及如下两部分开发:

  • 创建Callee被调用端
  • 访问Callee被调用端

开发步骤(创建Callee被调用端)

在Callee被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。

  1. 配置UIAbility的启动模式。

例如将CalleeAbility配置为单实例模式singleton

  1. 导入UIAbility模块。
import UIAbility from '@ohos.app.ability.UIAbility';
  1. 定义约定的序列化数据。 调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
import type rpc from '@ohos.rpc';class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;};mySequenceable(num, string): void {this.num = num;this.str = string;};marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};
  1. 实现Callee.on监听及Callee.off解除监听。

被调用端Callee的监听函数注册时机,取决于应用开发者。注册监听之前的数据不会被处理,取消监听之后的数据不会被处理。如下示例在UIAbility的onCreate注册’MSG_SEND_METHOD’监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回,应用开发者根据实际需要做相应处理。具体示例代码如下:

import type AbilityConstant from '@ohos.app.ability.AbilityConstant';
import UIAbility from '@ohos.app.ability.UIAbility';
import type Want from '@ohos.app.ability.Want';
import hilog from '@ohos.hilog';
import Logger from '../utils/Logger';
import type rpc from '@ohos.rpc';
import type { Caller } from '@ohos.app.ability.UIAbility';const MSG_SEND_METHOD: string = 'CallSendMsg';
const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[CalleeAbility]';class MyParcelable {num: number = 0;str: string = '';constructor(num: number, string: string) {this.num = num;this.str = string;}mySequenceable(num, string): void {this.num = num;this.str = string;}marshalling(messageSequence: rpc.MessageSequence): boolean {messageSequence.writeInt(this.num);messageSequence.writeString(this.str);return true;};unmarshalling(messageSequence: rpc.MessageSequence): boolean {this.num = messageSequence.readInt();this.str = messageSequence.readString();return true;};
};function sendMsgCallback(data: rpc.MessageSequence): rpc.Parcelable {hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'CalleeSortFunc called');// 获取Caller发送的序列化数据let receivedData: MyParcelable = new MyParcelable(0, '');data.readParcelable(receivedData);hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `receiveData[${receivedData.num}, ${receivedData.str}]`);let num: number = receivedData.num;// 作相应处理// 返回序列化数据result给Callerreturn new MyParcelable(num + 1, `send ${receivedData.str} succeed`) as rpc.Parcelable;
}export default class CalleeAbility extends UIAbility {caller: Caller | undefined;onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {try {this.callee.on(MSG_SEND_METHOD, sendMsgCallback);} catch (error) {hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`);};}releaseCall(): void {try {if (this.caller) {this.caller.release();this.caller = undefined;}Logger.info('caller release succeed');} catch (error) {Logger.info(`caller release failed with ${error}`);};}onDestroy(): void {try {this.callee.off(MSG_SEND_METHOD);hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Callee OnDestroy');this.releaseCall();} catch (error) {hilog.error(DOMAIN_NUMBER, TAG, '%{public}s', `Failed to register. Error is ${error}`);};}
}

开发步骤(访问Callee被调用端)

  1. 导入UIAbility模块。
import UIAbility from '@ohos.app.ability.UIAbility';
  1. 获取Caller通信接口。 UIAbilityContext属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取UIAbility实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease监听。应用开发者根据实际需要做相应处理。
import { Caller } from '@ohos.app.ability.UIAbility';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
import promptAction from '@ohos.promptAction';
import hilog from '@ohos.hilog';
import Want from '@ohos.app.ability.Want';const DOMAIN_NUMBER: number = 0xFF00;
const TAG: string = '[Page_UIAbilityComponentsInteractive]';@Entry
@Component
struct Page_UIAbilityComponentsInteractive {caller: Caller | undefined = undefined;// 注册caller的release监听private regOnRelease(caller: Caller): void {hilog.info(DOMAIN_NUMBER, TAG, `caller is ${caller}`);try {caller.on('release', (msg: string) => {hilog.info(DOMAIN_NUMBER, TAG, `caller onRelease is called ${msg}`);})hilog.info(DOMAIN_NUMBER, TAG, 'succeeded in registering on release.');} catch (err) {let code = (err as BusinessError).code;let message = (err as BusinessError).message;hilog.error(DOMAIN_NUMBER, TAG, `Failed to caller register on release. Code is ${code}, message is ${message}`);};}build() {Button().onClick(() => {let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContextlet want: Want = {bundleName: 'com.samples.stagemodelabilityinteraction',abilityName: 'CalleeAbility',parameters: { // 自定义信息info: 'CallSendMsg'}};context.startAbilityByCall(want).then((caller: Caller) => {hilog.info(DOMAIN_NUMBER, TAG, `Succeeded in starting ability.Code is ${caller}`);if (caller === undefined) {hilog.info(DOMAIN_NUMBER, TAG, 'get caller failed');return;}else {hilog.info(DOMAIN_NUMBER, TAG, 'get caller success');this.regOnRelease(caller);promptAction.showToast({message: $r('app.string.CallerSuccess')});}}).catch((err: BusinessError) => {hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);});})}
}

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_1028211.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Etcd 基本入门

1&#xff1a;什么是 Etcd ? Etcd 是 CoreOS 团队于2013年6月发起的开源项目&#xff0c;它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法&#xff0c;Etcd基于 Go 语言实现。 名字由来&#xff0c;它源于两个方面&#xff0c;…

面试笔记——MyBatis(执行流程、延迟加载和缓存)

MyBatis 是一个持久层框架&#xff0c;用于简化 Java 应用程序与数据库之间的交互过程。具体而言&#xff0c;它提供了一种将数据库操作映射到 Java 方法的方式&#xff0c;通过 XML 文件或注解配置 SQL 语句与 Java 方法的映射关系。使用 MyBatis&#xff0c;开发人员可以通过…

YOLOV8逐步分解(2)_DetectionTrainer类初始化过程

接上篇文章yolov8逐步分解(1)--默认参数&超参配置文件加载继续讲解。 1. 默认配置文件加载完成后&#xff0c;创建对象trainer时&#xff0c;需要从默认配置中获取类DetectionTrainer初始化所需的参数args&#xff0c;如下所示 def train(cfgDEFAULT_CFG, use_pythonFalse…

Docker-Container

Docker ①什么是容器②为什么需要容器③容器的生命周期容器 OOM容器异常退出容器暂停 ④容器命令清单总览docker createdocker rundocker psdocker logsdocker attachdocker execdocker startdocker stopdocker restartdocker killdocker topdocker statsdocker container insp…

Elasticsearch从入门到精通-07ES底层原理学习

Elasticsearch从入门到精通-07ES底层原理和高级功能 &#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是程序员行走的鱼 &#x1f4d6; 本篇主要介绍和大家一块学习一下ES底层原理包括集群原理、路由原理、分配控制、分配原理、文档分析原理、文档并发安全原理以及一些高…

第十四届蓝桥杯JavaA组省赛真题 - 特殊日期

解题思路&#xff1a; 暴力秒了 public class Main {public static void main(String[] args) {int cnt 0;for (int i 1900; i < 9999; i) {for (int j 1; j < 12; j) {for (int k 1; k < days(i, j); k) {if (sum(i) sum(j) sum(k)) cnt;}}}System.out.print…

算法笔记~—位运算

目录 常见位运算&#xff1a; 1、基础位运算 2、对于一个数n。确定、修改这个数n二进制x位。 3、提取&#xff08;确定&#xff09;一个数n最右侧的1&#xff08;bit&#xff09;与干掉最右侧的1&#xff08;bit&#xff09; 4、异或运算律 5、位运算的优先级&#xff1a…

Qt扫盲-QAssisant 集成其他qch帮助文档

QAssisant 集成其他qch帮助文档 一、概述二、Cmake qch例子1. 下载 Cmake.qch2. 添加qch1. 直接放置于Qt 帮助的目录下2. 在 QAssisant中添加 一、概述 QAssisant是一个很好的帮助文档&#xff0c;他提供了供我们在外部添加新的 qch帮助文档的功能接口&#xff0c;一般有两中添…

百度智能云千帆,产业创新新引擎

本文整理自 3 月 21 日百度副总裁谢广军的主题演讲《百度智能云千帆&#xff0c;产业创新新引擎》。 各位领导、来宾、媒体朋友们&#xff0c;大家上午好。很高兴今天在石景山首钢园&#xff0c;和大家一起沟通和探讨大模型的发展趋势&#xff0c;以及百度最近一段时间的思考和…

为什么Python不适合写游戏?

知乎上有热门个问题&#xff1a;Python 能写游戏吗&#xff1f;有没有什么开源项目&#xff1f; Python可以开发游戏&#xff0c;但不是好的选择 Python作为脚本语言&#xff0c;一般很少用来开发游戏&#xff0c;但也有不少大型游戏有Python的身影&#xff0c;比如&#xff1…

sheng的学习笔记-AI-人脸识别

目录:sheng的学习笔记-AI目录-CSDN博客 需要学习卷机神经网络等知识&#xff0c;见ai目录 目录 基础知识&#xff1a; 人脸验证&#xff08;face verification&#xff09; 人脸识别&#xff08;face recognition&#xff09; One-Shot学习&#xff08;One-shot learning&…

PTA-练习8

目录 实验5-3 使用函数求Fibonacci数 实验5-4 输出每个月的天数 实验5-9 使用函数求余弦函数的近似值 实验5-11 空心的数字金字塔 实验6-6 使用函数验证哥德巴赫猜想 实验6-7 使用函数输出一个整数的逆序数 实验6-8 使用函数输出指定范围内的完数 实验8-1-7 数组循环右…

Transformer的前世今生 day11(Transformer的流程)

Transformer的流程 在机器翻译任务中&#xff0c;翻译第一个词&#xff0c;Transformer的流程为&#xff1a; 先将要翻译的句子&#xff0c;一个词一个词的转换为词向量送入编码器层&#xff0c;得到优化过的词向量以及K、V&#xff0c;将K、V送入解码器层&#xff0c;并跟解码…

halcon例程学习——ball.hdev

dev_update_window (off) dev_close_window () dev_open_window (0, 0, 728, 512, black, WindowID) read_image (Bond, die/die_03) dev_display (Bond) set_display_font (WindowID, 14, mono, true, false) *自带的 提示继续 disp_continue_message (WindowID, black, true)…

android studio忽略文件

右键文件&#xff0c;然后忽略&#xff0c;就不会出现在commit里面了 然后提交忽略文件即可

Vue3 + Vite + TS + Element-Plus + Pinia项目(5)对axios进行封装

1、在src文件夹下新建config文件夹后&#xff0c;新建baseURL.ts文件&#xff0c;用来配置http主链接 2、在src文件夹下新建http文件夹后&#xff0c;新建request.ts文件&#xff0c;内容如下 import axios from "axios" import { ElMessage } from element-plus im…

【C++的奇迹之旅】C++关键字命名空间使用的三种方式C++输入输出命名空间std的使用惯例

文章目录 &#x1f4dd;前言&#x1f320; C关键字(C98)&#x1f309; 命名空间&#x1f320;命名空间定义&#x1f309;命名空间使用 &#x1f320;命名空间的使用有三种方式&#xff1a;&#x1f309;加命名空间名称及作用域限定符&#x1f320;使用using将命名空间中某个成员…

【JVM】Java类加载器 和 双亲委派机制

1、java类加载器的分类 JDK8及之前 启动类加载器&#xff0c;BootStrap Class Loader,加载核心类,加载jre/lib目录下的类&#xff0c;C实现的拓展类加载器&#xff0c; Extension Class Loader&#xff0c;加载java拓展类库&#xff0c;jre/lib/ext目录下&#xff0c;比如javax…

蓝桥杯 java 凑算式 16年省赛Java组真题

题目 思路&#xff1a; 求有多少种解法 比如:68/3952/714就是一种解法&#xff0c;53/1972/486 是另一种解法 8/3952/714是可以除尽的 但是后面一个不行 所以我们也要通分 代码&#xff1a; public class 凑算式 {static int[] a {1, 2, 3, 4, 5, 6, 7, 8, 9};static int c…

SpringBoot Redis的使用

官方文档&#xff1a; 官方文档&#xff1a;Spring Data Redis :: Spring Data Redis 和jedis一样&#xff0c;SpringBoot Redis 也可以让我在Java代码中使用redis&#xff0c;同样也是通过引入maven依赖的形式。 加速访问github: 使用steam可以免费加速访问github Spring…