• 鸿蒙 Stage模型-应用组件-配置、UIAbility


    前提:基于官网3.1/4.0文档。参考官网文档
    基于Android开发体系来进行比较和思考。(或有偏颇,自行斟酌)

    一、概念

    在这里插入图片描述
    可以看到分为运行期、编译器,主要关注UIAbility(类似Activity,UI相关)、ExtensionAbility(其他非UI相关)。
    另外AbilityState暂时不确定是类似Android 中的Application。

    为什么会单独有个Stage模型?
    其实就是整体架构,只不过为了区分另外一个```Feature模型``,后者已经不主推。

    二、模块

    1.配置

    .json5文件为配置文件,分为:
    app.json5:app级别的配置
    module.json5:模块级别的配置

    示例:

    {
      "module": {
        // ...
        "abilities": [
          {
            // $开头的为资源值
            "icon": "$media:icon",
            "label": "$string:EntryAbility_label",
            "skills": [
              {
                "entities": [
                  "entity.system.home"
                ],
                "actions": [
                  "action.system.home"
                ]
              }
            ],
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    通过json文件来配置,也是和之前

    2.UIAbility

    是什么?
    类似Activity

    1.生命周期

    在这里插入图片描述

    onNewIntent等生命周期是否有?
    有,见如下

    2.启动模式

    singleton(单实例模式):与Android一致,配置文件中配置
    multiton(多实例模式):对应Android的standard
    specified(指定实例模式):对Ability标记key,一致的key则是统一Ability。

    针对第三种模式示例:
    1.配置

    {
      "module": {
        // ...
        "abilities": [
          {
            "launchType": "specified",
            // ...
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.启动

    // 在启动指定实例模式的UIAbility时,给每一个UIAbility实例配置一个独立的Key标识
    // 例如在文档使用场景中,可以用文档路径作为Key标识
    function getInstance() {
        // ...
    }
    
    let want = {
        deviceId: '', // deviceId为空表示本设备
        bundleName: 'com.example.myapplication',
        abilityName: 'FuncAbility',
        moduleName: 'module1', // moduleName非必选
        parameters: { // 自定义信息
            instanceKey: getInstance(),
        },
    }
    // context为调用方UIAbility的AbilityContext
    this.context.startAbility(want).then(() => {
        // ...
    }).catch((err) => {
        // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    这里可以看到,startAbility=startActivity,传递的数据的方式也是类似。

    3.监听

    import AbilityStage from '@ohos.app.ability.AbilityStage';
    
    export default class MyAbilityStage extends AbilityStage {
        onAcceptWant(want): string {
            // 在被调用方的AbilityStage中,针对启动模式为specified的UIAbility返回一个UIAbility实例对应的一个Key值
            // 当前示例指的是module1 Module的FuncAbility
            if (want.abilityName === 'FuncAbility') {
                // 返回的字符串Key标识为自定义拼接的字符串内容
                return `ControlModule_EntryAbilityInstance_${want.parameters.instanceKey}`;
            }
    
            return '';
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    接受参数是通过onAcceptWant获取,名称也是较为直接。

    应用的UIAbility实例已创建,该UIAbility配置为指定实例模式,再次调用startAbility()方法启动该UIAbility实例,且AbilityStage的onAcceptWant()回调匹配到一个已创建的UIAbility实例。此时,再次启动该UIAbility时,只会进入该UIAbility的onNewWant()回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调。

    需要注意的是:Android中有栈顶复用模式,这里并没有提出这样的概念。因此不确认指定specified模式之后任务栈的关系,或者有没有任务栈的概念?

    3.数据传递

    使用EventHub进行数据通信:基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。——EventBus
    使用globalThis进行数据同步:ArkTS引擎实例内部的一个全局对象,在ArkTS引擎实例内部都能访问。——全局静态变量
    使用AppStorage/LocalStorage进行数据同步:ArkUI提供了AppStorage和LocalStorage两种应用级别的状态管理方案,可用于实现应用级别和UIAbility级别的数据同步。——本地缓存

    1.EventHub
    订阅:eventhub.on(‘event1’, this.func1);
    取消订阅:this.context.eventHub.off(‘event1’);
    发送事件: this.context.eventHub.emit(‘event1’);
    监听事件: eventhub.on(‘event1’, this.func1);

    核心要素和EventBus并无二致。
    示例:

    import UIAbility from '@ohos.app.ability.UIAbility';
    
    const TAG: string = '[Example].[Entry].[EntryAbility]';
    
    export default class EntryAbility extends UIAbility {
        func1(...data) {
            // 触发事件,完成相应的业务操作
            console.info(TAG, '1. ' + JSON.stringify(data));
        }
    
        onCreate(want, launch) {
            // 获取eventHub
            let eventhub = this.context.eventHub;
            // 执行订阅操作
            eventhub.on('event1', this.func1);
            eventhub.on('event1', (...data) => {
                // 触发事件,完成相应的业务操作
                console.info(TAG, '2. ' + JSON.stringify(data));
            });
        }
    }
    
    import common from '@ohos.app.ability.common';
    
    @Entry
    @Component
    struct Index {
      private context = getContext(this) as common.UIAbilityContext;
    
      eventHubFunc() {
        // 不带参数触发自定义“event1”事件
        this.context.eventHub.emit('event1');
        // 带1个参数触发自定义“event1”事件
        this.context.eventHub.emit('event1', 1);
        // 带2个参数触发自定义“event1”事件
        this.context.eventHub.emit('event1', 2, 'test');
        // 开发者可以根据实际的业务场景设计事件传递的参数
      }
    
      // 页面展示
      build() {
        // ...
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    有些许不同的是订阅对象的获取是内置的:let eventhub = this.context.eventHub;

    2. globalThis
    在这里插入图片描述

    在这里插入图片描述
    因为TS的特性,所以自定义属性难免会出现覆盖值的情况。这里是后来者覆盖前者。(Stage模型使用,而且FA 模型已经不主推了,因此后者不用管了)

    3. APPStorage和LocalStorage

    此处不再赘述。

    4.页面启动

    1. 启动应用内的UIAbility及获取结果

    需要注意的是,提到了如何销毁发起调用的Ability :

    在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。

    // context为需要停止的UIAbility实例的AbilityContext
    this.context.terminateSelf((err) => {
     // ...
    });
    
    • 1
    • 2
    • 3
    • 4

    是finish方法?还是回调销毁发起startAbility的Ability?
    类似Android中的finish方法。

    获取返回结果的回调

    let wantInfo = {
        deviceId: '', // deviceId为空表示本设备
        bundleName: 'com.example.myapplication',
        abilityName: 'FuncAbility',
        moduleName: 'module1', // moduleName非必选
        parameters: { // 自定义信息
            info: '来自EntryAbility Index页面',
        },
    }
    // context为调用方UIAbility的AbilityContext
    this.context.startAbilityForResult(wantInfo).then((data) => {
        // ...
    }).catch((err) => {
        // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    const RESULT_CODE: number = 1001;
    let abilityResult = {
        resultCode: RESULT_CODE,
        want: {
            bundleName: 'com.example.myapplication',
            abilityName: 'FuncAbility',
            moduleName: 'module1',
            parameters: {
                info: '来自FuncAbility Index页面',
            },
        },
    }
    // context为被调用方UIAbility的AbilityContext
    this.context.terminateSelfWithResult(abilityResult, (err) => {
        // ...
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    const RESULT_CODE: number = 1001;
    
    // ...
    
    // context为调用方UIAbility的AbilityContext
    this.context.startAbilityForResult(want).then((data) => {
        if (data?.resultCode === RESULT_CODE) {
            // 解析被调用方UIAbility返回的信息
            let info = data.want?.parameters?.info;
            // ...
        }
    }).catch((err) => {
        // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    从这里来看,与Android流程一致。

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

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

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

    这点与Android理念一致,但是Android高版本好像强制显示启动了?

    示例:
    1.设置action

    {
      "module": {
        "abilities": [
          {
            // ...
            "skills": [
              {
                "entities": [
                  // ...
                  "entity.system.default"
                ],
                "actions": [
                  // ...
                  "ohos.want.action.editData"
                ]
              }
            ]
          }
        ]
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    2.发起调用

    let wantInfo = {
        deviceId: '', // deviceId为空表示本设备
        // uncomment line below if wish to implicitly query only in the specific bundle.
        // bundleName: 'com.example.myapplication',
        action: 'ohos.want.action.editData',
        // entities can be omitted.
        entities: ['entity.system.default'],
    }
    
    // context为调用方UIAbility的AbilityContext
    this.context.startAbilityForResult(wantInfo).then((data) => {
        // ...
    }).catch((err) => {
        // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3.被调用时处理

    const RESULT_CODE: number = 1001;
    let abilityResult = {
        resultCode: RESULT_CODE,
        want: {
            bundleName: 'com.example.myapplication',
            abilityName: 'EntryAbility',
            moduleName: 'entry',
            parameters: {
                payResult: 'OKay',
            },
        },
    }
    // context为被调用方UIAbility的AbilityContext
    this.context.terminateSelfWithResult(abilityResult, (err) => {
        // ...
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    4.调用方处理返回数据

    const RESULT_CODE: number = 1001;
    
    let want = {
      // Want参数信息
    };
    
    // context为调用方UIAbility的AbilityContext
    this.context.startAbilityForResult(want).then((data) => {
        if (data?.resultCode === RESULT_CODE) {
            // 解析被调用方UIAbility返回的信息
            let payResult = data.want?.parameters?.payResult;
            // ...
        }
    }).catch((err) => {
        // ...
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 启动UIAbility的指定页面
      其实就是针对onNewWant回调的处理,可以立即为类似触发Android中的onNewIntent的场景。(热启动)

    3.ExtensionAbility组件

    ExtensionAbility组件是基于特定场景提供的应用组件,以便满足更多的使用场景。
    每一个具体场景对应一个ExtensionAbilityType,各类型的ExtensionAbility组件均由相应的系统服务统一管理,例如InputMethodExtensionAbility组件由输入法管理服务统一管理。当前支持的ExtensionAbility类型有:
    FormExtensionAbility:FORM类型的ExtensionAbility组件,用于提供服务卡片场景相关能力。
    WorkSchedulerExtensionAbility:WORK_SCHEDULER类型的ExtensionAbility组件,用于提供延迟任务注册、取消、查询的能力。

    倒是不像四大组件的定义,毕竟偏向于UI 层面。

    三、总结

    UIAbility约等于Activity

  • 相关阅读:
    【Python_PySide2学习笔记(十七)】QPlainTextEdit末尾增加文本时设置文本颜色
    linux vscode git 相关记录
    10:00面试,10:04就出来了 ,问的实在是太...
    交易履约之结算平台实践 | 京东云技术团队
    物联网浏览器(IoTBrowser)-整合机器学习yolo框架实现车牌识别
    Transformer解码层用mask解释
    LIEF:修改安卓.so后报 dlopen failed:has invalid shdr offset/size
    docker简单介绍 和 常用命令
    DSA之图(2):图的存储结构
    MongoDB索引与查询优化
  • 原文地址:https://blog.csdn.net/ganshenml/article/details/136329663