Skip to content

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 为自定义信息参数。

js
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 传递过来的参数。

js
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()方法实现。

js
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 的信息。

js
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 的信息

js

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 参数增加一个自定义参数传递页面跳转信息。

js
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()方法

js
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()中解析参数进行处理。

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

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

图片

开发步骤如下:

  • 冷启动短信应用的 UIAbility 实例时,在 onWindowStageCreate()生命周期回调中,通过调用 getUIContext()接口获取 UI 上下文实例 UIContext 对象。
js
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 实例的指定页面。
js
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()回调就不会被用到