前提:基于官网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"]}],}]}
}
通过json文件来配置,也是和之前
2.UIAbility
是什么?
类似Activity
1.生命周期
onNewIntent等生命周期是否有?
有,见如下
2.启动模式
singleton(单实例模式):与Android一致,配置文件中配置
multiton(多实例模式):对应Android的standard
specified(指定实例模式):对Ability标记key,一致的key则是统一Ability。
针对第三种模式示例:
1.配置
{"module": {// ..."abilities": [{"launchType": "specified",// ...}]}
}
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) => {// ...
})
这里可以看到,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的FuncAbilityif (want.abilityName === 'FuncAbility') {// 返回的字符串Key标识为自定义拼接的字符串内容return `ControlModule_EntryAbilityInstance_${want.parameters.instanceKey}`;}return '';}
}
接受参数是通过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) {// 获取eventHublet 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() {// ...}
}
有些许不同的是订阅对象的获取是内置的:let eventhub = this.context.eventHub;
2. globalThis
因为TS的特性,所以自定义属性难免会出现覆盖值的情况。这里是后来者覆盖前者。(Stage模型使用,而且FA 模型已经不主推了,因此后者不用管了)
3. APPStorage和LocalStorage
此处不再赘述。
4.页面启动
- 启动应用内的UIAbility及获取结果
需要注意的是,提到了如何销毁发起调用的Ability :
在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。
// context为需要停止的UIAbility实例的AbilityContext
this.context.terminateSelf((err) => {// ...
});
是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) => {// ...
})
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) => {// ...
});
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) => {// ...
})
从这里来看,与Android流程一致。
- 启动其他应用的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"]}]}]}
}
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) => {// ...
})
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) => {// ...
});
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) => {// ...
})
- 启动UIAbility的指定页面
其实就是针对onNewWant
回调的处理,可以立即为类似触发Android中的onNewIntent
的场景。(热启动)
3.ExtensionAbility组件
ExtensionAbility组件是基于特定场景提供的应用组件,以便满足更多的使用场景。
每一个具体场景对应一个ExtensionAbilityType,各类型的ExtensionAbility组件均由相应的系统服务统一管理,例如InputMethodExtensionAbility组件由输入法管理服务统一管理。当前支持的ExtensionAbility类型有:
FormExtensionAbility:FORM类型的ExtensionAbility组件,用于提供服务卡片场景相关能力。
WorkSchedulerExtensionAbility:WORK_SCHEDULER类型的ExtensionAbility组件,用于提供延迟任务注册、取消、查询的能力。
倒是不像四大组件的定义,毕竟偏向于UI 层面。
三、总结
UIAbility约等于Activity