鸿蒙开发 UIAbility

654 阅读8分钟

什么是组件

组成项目的基本条件单元。个人理解组件可大可小,电脑的风扇系统可以称之为电脑中的一个组件,风扇中的扇叶即可以说是风扇系统的一个组件。也可以说是电脑的一个组件。才疏学浅... 欢迎评论区狂喷指点。

在鸿蒙系统中大致可以把组件分为:

基础组件:

视图层的基本组成单元,包括用于显示文本信息的Text组件、展示图像的Image组件、用于文本输入的TextInput组件、触发用户操作的Button组件、显示加载进度的LoadingProgress组件等

容器组件:

布局名称布局标签
线性布局Row/Column
层叠布局Stack
弹性布局Flex
相对布局RelativeContainer
栅格布局GridRow/GridCol
媒体查询mediaquery
创建列表List
创建网格Grid/GridItem
创建轮播Swiper

媒体组件:

用于处理媒体内容的组件,如视频播放器、音频播放器等

绘制组件:

提供绘制图形和图像功能的组件

画布组件:

为开发者提供一个可以在其上自由绘制的画布,以实现更复杂的界面效果。

什么是Ability?

Ability是鸿蒙应用中的一种能力

什么是UIAbility

UIAbility组件是一种包含UI的应用组件,主要用于和用户交互。

应用可以包括几个UIAbility

一个应用可以包含一个或多个UIAbility组件。例如,在支付应用中,可以将入口功能和收付款功能分别配置为独立的UIAbility。

  • 如果开发者希望在任务视图中看到一个任务,则建议使用一个UIAbility,多个页面的方式。
  • 如果开发者希望在任务视图中看到多个任务,或者需要同时开启多个窗口,则建议使用多个UIAbility开发不同的模块功能。

UIAblity生命周期

onCreate(want, launchParam) {
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
}

onDestroy() {
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
}

onWindowStageCreate(windowStage: window.WindowStage) {
  // Main window is created, set main page for this ability
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');

  windowStage.loadContent('pages/Index', (err, data) => {
    if (err.code) {
      hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
      return;
    }
    hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
  });
}

onWindowStageDestroy() {
  // Main window is destroyed, release UI related resources
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy');
}

onForeground() {
  // Ability has brought to foreground
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground');
}

onBackground() {
  // Ability has back to background
  hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground');
}

UIAbility组件启动模式

singleton单例模式

每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。

应用的UIAbility实例已创建,该UIAbility配置为单实例模式,再次调用startAbility()方法启动该UIAbility实例。由于启动的还是原来的UIAbility实例,并未重新创建一个新的UIAbility实例,此时只会进入该UIAbility的onNewWant()回调,不会进入其onCreate()onWindowStageCreate()生命周期回调。

multiton多实例模式 multiton启动模式为多实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为multiton(多实例模式)。

specified指定实力模式模式

在创建UIAbility实例之前,开发者可以为该实例指定一个唯一的字符串Key,这样在调用startAbility()方法时,应用就可以根据指定的Key来识别响应请求的UIAbility实例。在EntryAbility中,调用startAbility()方法时,可以在want参数中增加一个自定义参数,例如instanceKey,以此来区分不同的UIAbility实例。

UIAbility组件基本用法

指定UIAbility的启动页面

应用中的UIAbility在启动过程中,需要指定启动页面,否则应用启动后会因为没有默认加载页面而导致白屏。可以在UIAbility的onWindowStageCreate()生命周期回调中,通过WindowStage对象的loadContent()方法设置启动页面。

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Main window is created, set main page for this ability
    windowStage.loadContent('pages/Index', (err, data) => {
      // ...
    });
  }
  // ...
}

获取UIAbility的上下文信息

UIAbility类拥有自身的上下文信息,该信息为UIAbilityContext类的实例,UIAbilityContext类拥有abilityInfo、currentHapModuleInfo等属性。通过UIAbilityContext可以获取UIAbility的相关配置信息,如包代码路径、Bundle名称、Ability名称和应用程序需要的环境状态等属性信息,以及可以获取操作UIAbility实例的方法(如startAbility()、connectServiceExtensionAbility()、terminateSelf()等)。

如果需要在页面中获得当前Ability的Context,可调用getContext接口获取当前页面关联的UIAbilityContext或ExtensionContext。

AppStorage/LocalStorage

ArkUI提供了AppStorage和LocalStorage两种应用级别的状态管理方案,可用于实现应用级别和UIAbility级别的数据同步。使用这些方案可以方便地管理应用状态,提高应用性能和用户体验。其中,AppStorage是一个全局的状态管理器,适用于多个UIAbility共享同一状态数据的情况;而LocalStorage则是一个局部的状态管理器,适用于单个UIAbility内部使用的状态数据。通过这两种方案,开发者可以更加灵活地控制应用状态,提高应用的可维护性和可扩展性。

UIAbility组件间交互(设备内)

启动应用内的UIAbility

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

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

.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.');
  });
})

在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业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。

context.terminateSelf((err) => {
  if (err.code) {
  hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
  return;
  }
  }

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

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

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

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}`);
});

在FuncAbility停止自身时,需要调用terminateSelfWithResult()方法,入参abilityResult为FuncAbility需要返回给EntryAbility的信息。

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;
  }

启动其他应用的UIAbility

启动其他应用的UIAbility,通常用户只需要完成一个通用的操作(例如需要选择一个文档应用来查看某个文档的内容信息),推荐使用隐式Want启动。系统会根据调用方的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启动方式。