鸿蒙UIAbility组件进阶

打印 上一主题 下一主题

主题 1023|帖子 1023|积分 3069

AbilityStage

基本概念

在了解AbilityStage之前,我们需要先了解和回顾几个概念,UIAbility、ExtensionAbility、HAP。


  • UIAbility:前边的文章中我们讲过,UIAbility是包罗UI的应用组件,主要用于和用户交互。
  • ExtensionAbility:是一种面向特定场景的应用组件,如输入法、桌面卡片、闲时任务、数据备份等,每一个具体场景对应一种ExtensionAbilityType,只能只用系统已经定义的场景。所有的ExtensionAbility组件均不能被应用直接启动,而是由相应的系统管理服务拉起,以确保其生命周期受系统管控。
  • HAP:应用安装的基本单位,一个应用会包罗一个或者多个.hap文件,.hap是由项目工程中的module编译而成的。
回顾了几个概念之后,我们来说本日的AbilityStage。
AbilityState是Module级别的容器组件,与HAP是一一对应的关系。
当HAP中的代码初次被加载到应用进程中时,也就是Module初始化的时候,系统首先会创建一个AbilityState实例,AbilityStage可以管理Module中的Ability组件和ExtensionAbility组件。
关系图如下:

一个应用,也就是application,可以包罗一个或者多个HAP,每个HAP对应一个AbilityStage,每个AlibityStage可以管理一个或者多个UIAbility,每个UIAbility中包罗一个WindowStage,用于管理Window,每个Window中,可以管理一个或者多个UI页面。
当我们需要打开某一个UIAbility时:

  • 如果该UIAbility所在的HAP还没有加载到进程中,那么系统会将HAP加载到进程中,创建一个AbilityStage实例。创建成功后,实行AbilityStage的onCreate生命周期函数,可以在该方法中进行一些module级别的资源初始化。
  • 创建UIAbility实例,创建成功后,回调UIAbility的onCreate生命周期函数,可以在该回调函数中初始化一些业务逻辑。
  • 系统为该UIAbility实例创建一个WindowStage实例,创建完成后,会回调UIAbility的onWindowStageCreate生命周期回调函数,可以在该回调函数中获取主窗口信息,设置UI界面以及监听WindowStage的生命周期变化。
如下图所示:

在鸿蒙中,应用组件和UI之间的生命周期是松耦合的,在这种筹划模式下,有几个好处


  • 业务逻辑与UI分离,我们可以在UIAbility中处理一些与UI无关的业务逻辑,如打开蓝牙,连接数据库等。而在WindowStage持有的窗口上去处理界面相关的业务逻辑。
  • 便于系统对应用组件进行裁剪。比如无屏设备,系统在创建时不会创建窗口模块,可以减少运行内存占用。
  • 在多设备上,应用组件可以利用同一套生命周期。
    单窗口模式:

    这个流程会触发App1中WindowStage的hidden和inActive生命周期,触发UIAbility中的background生命周期;同时触发App2中WindowStage的shown和active生命周期,触发UIAbility中的foreground生命周期。
    多窗口模式:

    这个流程只会触发App1中WindowStage的inActive生命周期函数,同时触发App2中WindowStage的active生命周期函数。而不会触发两个app中各自UIAbility中的生命周期函数。
生命周期

AbilityStage共有四个生命周期函数


  • onCreate:AbilityStage实例创建完成后回调的生命周期函数,可以在这里进行Module的初始化利用。
  • onAcceptWant:UIAbility指定实例模式启动时,会触发该回调,在该回调中,我们需要返回一个唯一的key值,系统用于判断指定的实例是否已经存在。
  • onConfigurationUpdated:系统全局设置发生改变时触发的回调函数,如系统语言,深淡色等。可以在该函数中吸取到系统最新的设置信息。
  • onMemoryLevel:系统调整内存时触发的函数。
利用方式


  • 在工程Module对应的ets目次下,右键选择“New > Directory”,新建一个目次并定名为myabilitystage。
  • 在myabilitystage目次,右键选择“New > ArkTS File”,新建一个文件并定名为MyAbilityStage.ts。
  • 打开MyAbilityStage.ts文件,导入AbilityStage的依靠包,自定义类继续AbilityStage并加上需要的生命周期回调,示例中增加了一个onCreate()生命周期回调。
    1. import { AbilityStage, Want } from '@kit.AbilityKit';
    2. export default class MyAbilityStage extends AbilityStage {
    3.   onCreate(): void {
    4.     // 应用的HAP在首次加载的时,为该Module初始化操作
    5.   }
    6.   onAcceptWant(want: Want): string {
    7.     // 仅specified模式下触发
    8.     return 'MyAbilityStage';
    9.   }
    10. }
    复制代码
  • 在module.json5设置文件中,通过设置 srcEntry 参数来指定模块对应的代码路径,以作为HAP加载的入口。
    1. {
    2.   "module": {
    3.     "name": "entry",
    4.     "type": "entry",
    5.     "srcEntry": "./ets/myabilitystage/MyAbilityStage.ts",
    6.     // ...
    7.   }
    8. }
    复制代码
UIAbility组件间交互

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

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

  • 在EntryAbility中,通过调用startAbility()方法启动UIAbility,want为UIAbility实例启动的入口参数,此中bundleName为待启动应用的Bundle名称,abilityName为待启动的Ability名称,moduleName在待启动的UIAbility属于差别的Module时添加,parameters为自定义信息参数。
    1. import { common, Want } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { BusinessError } from '@kit.BasicServicesKit';
    4. const TAG: string = '[Page_UIAbilityComponentsInteractive]';
    5. const DOMAIN_NUMBER: number = 0xFF00;
    6. @Entry
    7. @Component
    8. struct Page_UIAbilityComponentsInteractive {
    9.   private context = getContext(this) as common.UIAbilityContext;
    10.   build() {
    11.     Column() {
    12.       //...
    13.       List({ initialIndex: 0 }) {
    14.         ListItem() {
    15.           Row() {
    16.             //...
    17.           }
    18.           .onClick(() => {
    19.             // context为Ability对象的成员,在非Ability对象内部调用需要
    20.             // 将Context对象传递过去
    21.             let wantInfo: Want = {
    22.               deviceId: '', // deviceId为空表示本设备
    23.               bundleName: 'com.samples.stagemodelabilitydevelop',
    24.               moduleName: 'entry', // moduleName非必选
    25.               abilityName: 'FuncAbilityA',
    26.               parameters: {
    27.                 // 自定义信息
    28.                 info: '来自EntryAbility Page_UIAbilityComponentsInteractive页面'
    29.               },
    30.             };
    31.             // context为调用方UIAbility的UIAbilityContext
    32.             this.context.startAbility(wantInfo).then(() => {
    33.               hilog.info(DOMAIN_NUMBER, TAG, 'startAbility success.');
    34.             }).catch((error: BusinessError) => {
    35.               hilog.error(DOMAIN_NUMBER, TAG, 'startAbility failed.');
    36.             });
    37.           })
    38.         }
    39.         //...
    40.       }
    41.       //...
    42.     }
    43.     //...
    44.   }
    45. }
    复制代码
  • 在FuncAbility的onCreate()或者onNewWant()生命周期回调文件中吸取EntryAbility传递过来的参数。
    1. import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
    2. export default class FuncAbilityA extends UIAbility {
    3.   onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    4.     // 接收调用方UIAbility传过来的参数
    5.     let funcAbilityWant = want;
    6.     let info = funcAbilityWant?.parameters?.info;
    7.   }
    8.   //...
    9. }
    复制代码
  • 在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。
    1. import { common } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. const TAG: string = '[Page_FromStageModel]';
    4. const DOMAIN_NUMBER: number = 0xFF00;
    5. @Entry
    6. @Component
    7. struct Page_FromStageModel {
    8.   build() {
    9.     Column() {
    10.       //...
    11.       Button('FuncAbilityB')
    12.         .onClick(() => {
    13.           let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
    14.           // context为需要停止的UIAbility实例的AbilityContext
    15.           context.terminateSelf((err) => {
    16.             if (err.code) {
    17.               hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
    18.               return;
    19.             }
    20.           });
    21.         })
    22.     }
    23.     //...
    24.   }
    25. }
    复制代码
调用terminateSelf()方法停止当前UIAbility实例时,默认会保留该实例的快照(Snapshot),即在最近任务列表中仍旧能检察到该实例对应的任务。如不需要保留该实例的快照,可以在其对应UIAbility的module.json5设置文件中,将abilities标签的removeMissionAfterTerminate字段设置为true。

  • 如需要关闭应用所有的UIAbility实例,可以调用ApplicationContext的killAllProcesses()方法实现关闭应用所有的进程。
启动应用内的UIAbility并获取返回结果

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

  • 在EntryAbility中,调用startAbilityForResult()接口启动FuncAbility,异步回调中的data用于吸取FuncAbility停止自身后返回给EntryAbility的信息。
    1. import { common, Want } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { promptAction } from '@kit.ArkUI';
    4. import { BusinessError } from '@kit.BasicServicesKit';
    5. const TAG: string = '[Page_UIAbilityComponentsInteractive]';
    6. const DOMAIN_NUMBER: number = 0xFF00;
    7. @Entry
    8. @Component
    9. struct Page_UIAbilityComponentsInteractive {
    10.   build() {
    11.     Column() {
    12.       //...
    13.       List({ initialIndex: 0 }) {
    14.         ListItem() {
    15.           Row() {
    16.             //...
    17.           }
    18.           .onClick(() => {
    19.             let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
    20.             const RESULT_CODE: number = 1001;
    21.             let want: Want = {
    22.               deviceId: '', // deviceId为空表示本设备
    23.               bundleName: 'com.samples.stagemodelabilitydevelop',
    24.               moduleName: 'entry', // moduleName非必选
    25.               abilityName: 'FuncAbilityA',
    26.               parameters: {
    27.                 // 自定义信息
    28.                 info: '来自EntryAbility UIAbilityComponentsInteractive页面'
    29.               }
    30.             };
    31.             context.startAbilityForResult(want).then((data) => {
    32.               if (data?.resultCode === RESULT_CODE) {
    33.                 // 解析被调用方UIAbility返回的信息
    34.                 let info = data.want?.parameters?.info;
    35.                 hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
    36.                 if (info !== null) {
    37.                   promptAction.showToast({
    38.                     message: JSON.stringify(info)
    39.                   });
    40.                 }
    41.               }
    42.               hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '');
    43.             }).catch((err: BusinessError) => {
    44.               hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);
    45.             });
    46.           })
    47.         }
    48.         //...
    49.       }
    50.       //...
    51.     }
    52.     //...
    53.   }
    54. }
    复制代码
  • 在FuncAbility停止自身时,需要调用terminateSelfWithResult()方法,入参abilityResult为FuncAbility需要返回给EntryAbility的信息。
    1. import { common } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. const TAG: string = '[Page_FuncAbilityA]';
    4. const DOMAIN_NUMBER: number = 0xFF00;
    5. @Entry
    6. @Component
    7. struct Page_FuncAbilityA {
    8.   build() {
    9.     Column() {
    10.       //...
    11.       List({ initialIndex: 0 }) {
    12.         ListItem() {
    13.           Row() {
    14.             //...
    15.           }
    16.           .onClick(() => {
    17.             let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
    18.             const RESULT_CODE: number = 1001;
    19.             let abilityResult: common.AbilityResult = {
    20.               resultCode: RESULT_CODE,
    21.               want: {
    22.                 bundleName: 'com.samples.stagemodelabilitydevelop',
    23.                 moduleName: 'entry', // moduleName非必选
    24.                 abilityName: 'FuncAbilityB',
    25.                 parameters: {
    26.                   info: '来自FuncAbility Index页面'
    27.                 },
    28.               },
    29.             };
    30.             context.terminateSelfWithResult(abilityResult, (err) => {
    31.               if (err.code) {
    32.                 hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`);
    33.                 return;
    34.               }
    35.             });
    36.           })
    37.         }
    38.         //...
    39.       }
    40.       //...
    41.     }
    42.     //...
    43.   }
    44. }
    复制代码
  • FuncAbility停止自身后,EntryAbility通过startAbilityForResult()方法回调吸取被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。
    1. import { common, Want } from '@kit.AbilityKit';
    2. import { hilog } from '@kit.PerformanceAnalysisKit';
    3. import { promptAction } from '@kit.ArkUI';
    4. import { BusinessError } from '@kit.BasicServicesKit';
    5. const TAG: string = '[Page_UIAbilityComponentsInteractive]';
    6. const DOMAIN_NUMBER: number = 0xFF00;
    7. @Entry
    8. @Component
    9. struct Page_UIAbilityComponentsInteractive {
    10.   build() {
    11.     Column() {
    12.       //...
    13.       List({ initialIndex: 0 }) {
    14.         ListItem() {
    15.           Row() {
    16.             //...
    17.           }
    18.           .onClick(() => {
    19.             let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
    20.             const RESULT_CODE: number = 1001;
    21.             let want: Want = {
    22.               deviceId: '', // deviceId为空表示本设备
    23.               bundleName: 'com.samples.stagemodelabilitydevelop',
    24.               moduleName: 'entry', // moduleName非必选
    25.               abilityName: 'FuncAbilityA',
    26.               parameters: {
    27.                 // 自定义信息
    28.                 info: '来自EntryAbility UIAbilityComponentsInteractive页面'
    29.               }
    30.             };
    31.             context.startAbilityForResult(want).then((data) => {
    32.               if (data?.resultCode === RESULT_CODE) {
    33.                 // 解析被调用方UIAbility返回的信息
    34.                 let info = data.want?.parameters?.info;
    35.                 hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(info) ?? '');
    36.                 if (info !== null) {
    37.                   promptAction.showToast({
    38.                     message: JSON.stringify(info)
    39.                   });
    40.                 }
    41.               }
    42.               hilog.info(DOMAIN_NUMBER, TAG, JSON.stringify(data.resultCode) ?? '');
    43.             }).catch((err: BusinessError) => {
    44.               hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ability for result. Code is ${err.code}, message is ${err.message}`);
    45.             });
    46.           })
    47.         }
    48.         //...
    49.       }
    50.       //...
    51.     }
    52.     //...
    53.   }
    54. }
    复制代码
启动UIAbility的指定页面

一个UIAbility可以对应多个页面,在差别的场景下启动该UIAbility时需要展示差别的页面,可以通过如下方式进行。
调用方

调用方UIAbility启动另外一个UIAbility时,通常需要跳转到指定的页面。此时需要在传入的want参数中设置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。
目标UIAbility冷启动

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

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

天空闲话

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表