harmony 鸿蒙启动应用内的UIAbility组件

  • 2022-12-13
  • 浏览 (1787)

启动应用内的UIAbility组件

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

本文主要介绍启动应用内的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, 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 = this.getUIContext().getHostContext() 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;
      }
      //...
    }
    

    说明:

    在被拉起的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 = this.getUIContext().getHostContext() 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;
                }
              });
            })
        }
        //...
      }
    }
    

    说明:

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

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

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

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

  1. 在EntryAbility中,调用startAbilityForResult()接口启动FuncAbility,异步回调中的data用于接收FuncAbility停止自身后返回给EntryAbility的信息。示例中的context的获取方式请参见获取UIAbility的上下文信息

    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 = this.getUIContext().getHostContext() 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) {
                      this.getUIContext().getPromptAction().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 = this.getUIContext().getHostContext() 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;
                  }
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  3. FuncAbility停止自身后,EntryAbility通过startAbilityForResult()方法回调接收被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。

    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 = this.getUIContext().getHostContext() 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) {
                      this.getUIContext().getPromptAction().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可以对应多个页面,在不同的场景下启动该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参数增加一个自定义参数传递页面跳转信息。示例中的context的获取方式请参见获取UIAbility的上下文信息

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 = this.getUIContext().getHostContext() 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}`);
            });
          })
        }
        //...
      }
      //...
    }
    //...
  }
}

目标UIAbility冷启动

目标UIAbility冷启动时,在目标UIAbility的onCreate()生命周期回调中,接收调用方传过来的参数。然后在目标UIAbility的onWindowStageCreate()生命周期回调中,解析调用方传递过来的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) => {
      // ...
    });
  }
}

目标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 '@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) ?? '');
        });
      }
    }
    
  2. 在短信应用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}`);
            });
          }
        }
      }
    }
    

说明:

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

启动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属性。

示例中的context的获取方式请参见获取UIAbility的上下文信息

import { AbilityConstant, common, Want, StartOptions } 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 = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
            let 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的UIAbilityContext
            context.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}`);
            });
          })
        }
        //...
      }
      //...
    }
    //...
  }
}

效果示意如下图所示。

通过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调用示意图 call

  • CallerAbility调用startAbilityByCall()接口获取Caller,并使用Caller对象的call方法向CalleeAbility发送数据。

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

说明: 1. 当前仅支持系统应用使用Call调用。

  1. CalleeAbility的启动模式需要为单实例。

  2. Call调用既支持本地(设备内)Call调用,也支持跨设备Call调用,下面介绍设备内Call调用方法。跨设备Call调用方法请参见跨设备Call调用

接口说明

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

表2 Call功能主要接口

接口名 描述
startAbilityByCall(want: Want): Promise<Caller> 启动指定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<void> 向通用组件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被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。

  1. 配置UIAbility的启动模式。

例如将CalleeAbility配置为单实例模式singleton,配置方式请参见UIAbility组件启动模式

  1. 导入UIAbility模块。
   import { UIAbility } from '@kit.AbilityKit';
  1. 定义约定的序列化数据。 调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。

    import { rpc } from '@kit.IPCKit';
    
    
    class MyParcelable {
      num: number = 0;
      str: string = '';
    
    
      constructor(num: number, string: string) {
        this.num = num;
        this.str = string;
      }
    
    
      mySequenceable(num: number, string: 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;
      }
    }
    
  2. 实现Callee.on监听及Callee.off解除监听。

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

```ts
import { AbilityConstant, UIAbility, Want, Caller } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { rpc } from '@kit.IPCKit';

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: number, string: 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给Caller
  return 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;
      }
      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'caller release succeed');
    } catch (error) {
      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', `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 '@kit.AbilityKit';
    
  2. 获取Caller通信接口。

UIAbilityContext属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取UIAbility实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease监听。应用开发者根据实际需要做相应处理。

```ts
import { common, Want, Caller } 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 {
  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() {
    Column() {
      // ...
      List({ initialIndex: 0 }) {
        // ...
        ListItem() {
          Row() {
            // ...
          }
          .onClick(() => {
            let context = this.getUIContext().getHostContext() as common.UIAbilityContext; // UIAbilityContext
            let 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);
                this.getUIContext().getPromptAction().showToast({
                  message: 'CallerSuccess'
                });
                try {
                  caller.release();
                } catch (releaseErr) {
                  console.log('Caller.release catch error, error.code: ' + JSON.stringify(releaseErr.code) +
                    ' error.message: ' + JSON.stringify(releaseErr.message));
                }
              }
            }).catch((err: BusinessError) => {
              hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability. Code is ${err.code}, message is ${err.message}`);
            });
          })
        }
        // ...
      }
      // ...
    }
    // ...
  }
}
```

相关实例

针对UIAbility组件间交互开发,有以下相关实例可供参考:

你可能感兴趣的鸿蒙文章

harmony 鸿蒙Ability Kit(程序框架服务)

harmony 鸿蒙获取应用异常退出原因

harmony 鸿蒙UIAbility备份恢复

harmony 鸿蒙使用显式Want启动应用组件

harmony 鸿蒙Ability Kit简介

harmony 鸿蒙AbilityStage组件容器

harmony 鸿蒙访问DataAbility

harmony 鸿蒙FA模型访问Stage模型DataShareExtensionAbility

harmony 鸿蒙常见action与entities(不推荐使用)

harmony 鸿蒙API切换概述

0  赞