提交 832ea7b1 编写于 作者: Z zengyawen

application-models

Signed-off-by: Nzengyawen <zengyawen1@huawei.com>
上级 055176c4
# 应用模型
- 应用模型概述
- [OpenHarmony应用模型的构成要素](application-model-composition.md)
- [OpenHarmony应用模型解读](application-model-description.md)
- Stage模型开发指导
- [Stage模型开发概述](stage-model-development-overview.md)
- Stage模型应用组件
- [应用/组件级配置](application-component-configuration-stage.md)
- UIAbility组件
- [UIAbility组件概述](uiability-overview.md)
- [UIAbility组件生命周期](uiability-lifecycle.md)
- [UIAbility组件启动模式](uiability-launch-type.md)
- [UIAbility组件基本用法](uiability-usage.md)
- [UIAbility组件与UI的数据同步](uiability-data-sync-with-ui.md)
- [UIAbility组件间交互(设备内)](uiability-intra-device-interaction.md)
- ExtensionAbility组件
- [ExtensionAbility组件概述](extensionability-overview.md)
- [ServiceExtensionAbility](serviceextensionability.md)
- [DataShareExtensionAbility(仅对系统应用开放)](datashareextensionability.md)
- [FormExtensionAbility(服务卡片)](widget-development-stage.md)
- [AbilityStage组件容器](abilitystage.md)
- [应用上下文Context](application-context-stage.md)
- 信息传递载体Want
- [Want概述](want-overview.md)
- [显式Want与隐式Want匹配规则](explicit-implicit-want-mappings.md)
- [常见action与entities](actions-entities.md)
- [使用显式Want启动Ability](ability-startup-with-explicit-want.md)
- [使用隐式Want打开网址](ability-startup-with-implicit-want.md)
- [应用间使用Want分享数据](data-share-via-want.md)
- [组件启动规则](component-startup-rules.md)
- 应用组件跨设备交互(流转)
- [流转概述](inter-device-interaction-hop-overview.md)
- [跨端迁移(仅对系统应用开放)](hop-cross-device-migration.md)
- [多端协同(仅对系统应用开放)](hop-multi-device-collaboration.md)
- 进程间通信
- [进程模型](process-model-stage.md)
- 公共事件
- [公共事件简介](common-event-overview.md)
- [公共事件订阅](common-event-subscription.md)
- [公共事件发布](common-event-publish.md)
- [公共事件取消订阅](common-event-unsubscription.md)
- [后台服务](background-services.md)
- 线程间通信
- [线程模型](thread-model-stage.md)
- [使用Emitter进行线程间通信](itc-with-emitter.md)
- [使用Worker进行线程间通信](itc-with-worker.md)
- 任务管理
- [任务管理场景介绍](mission-management-overview.md)
- [任务管理与启动模式](mission-management-launch-type.md)
- [页面栈及任务链](page-mission-stack.md)
- FA模型开发指导
- [FA模型开发概述](fa-model-development-overview.md)
- FA模型应用组件
- [应用/组件级配置](application-component-configuration-fa.md)
- PageAbility组件开发指导
- [PageAbility组件概述](pageability-overview.md)
- [PageAbility组件配置](pageability-configuration.md)
- [PageAbility的生命周期](pageability-lifecycle.md)
- [PageAbility的启动模式](pageability-launch-type.md)
- [创建PageAbility](create-pageability.md)
- [启动本地PageAbility](start-local-pageability.md)
- [停止PageAbility](stop-pageability.md)
- [启动远程PageAbility(仅对系统应用开放)](start-remote-pageability.md)
- [启动指定页面](start-page.md)
- [窗口属性](window-properties.md)
- [申请授权](request-permissions.md)
- [跳转规则](redirection-rules.md)
- ServiceAbility组件开发指导
- [ServiceAbility组件概述](serviceability-overview.md)
- [ServiceAbility组件配置](serviceability-configuration.md)
- [ServiceAbility的生命周期](serviceability-lifecycle.md)
- [创建ServiceAbility](create-serviceability.md)
- [启动ServiceAbility](start-serviceability.md)
- [连接ServiceAbility](connect-serviceability.md)
- DataAbility组件开发指导
- [DataAbility组件概述](dataability-overview.md)
- [DataAbility组件配置](dataability-configuration.md)
- [DataAbility的生命周期](dataability-lifecycle.md)
- [创建DataAbility](create-dataability.md)
- [启动DataAbility](start-dataability.md)
- [访问DataAbility](access-dataability.md)
- [DataAbility权限控制](dataability-permission-control.md)
- [服务卡片开发指导](widget-development-fa.md)
- [FA模型的Context](application-context-fa.md)
- [信息传递载体Want](want-fa.md)
- 进程间通信
- [进程模型](process-model-fa.md)
- [公共事件](common-event-fa.md)
- [后台服务](rpc.md)
- 线程间通信
- [线程模型](thread-model-fa.md)
- [线程间通信](itc-fa-overview.md)
- [任务管理](mission-management-fa.md)
- FA模型与Stage模型应用组件互通指导
- [FA模型与Stage模型应用组件互通综述](fa-stage-interaction-overview.md)
- [FA模型启动Stage模型UIAbility](start-uiability-from-fa.md)
- [FA模型绑定Stage模型ServiceExtensionAbility](bind-serviceextensionability-from-fa.md)
- [FA模型访问Stage模型DataShareExtensionAbility](access-datashareextensionability-from-fa.md)
- [Stage模型启动FA模型PageAbility](start-pageability-from-stage.md)
- [Stage模型绑定FA模型ServiceAbility](bind-serviceability-from-stage.md)
- FA模型切换Stage模型指导
- [模型切换概述](model-switch-overview.md)
- 配置文件切换
- [配置文件的差异](configuration-file-diff.md)
- [app和deviceConfig的切换](app-deviceconfig-switch.md)
- [module的切换](module-switch.md)
- 组件切换
- [PageAbility切换](pageability-switch.md)
- [ServiceAbility切换](aerviceability-switch.md)
- [DataAbility切换](dataability-switch.md)
- [卡片切换](widget-switch.md)
- API切换
- [API切换概述](api-switch-overview.md)
- [Context接口切换](context-switch.md)
- [featureAbility接口切换](featureability-switch.md)
- [particleAbility接口切换](particleability-switch.md)
- [LifecycleForm接口切换](lifecycleform-switch.md)
- [LifecycleApp接口切换](lifecycleapp-switch.md)
- [LifecycleService接口切换](lifecycleservice-switch.md)
- [LifecycleData接口切换](lifecycledata-switch.md)
- [DataAbilityHelper接口切换](dataabilityhelper-switch.md)
- [mediaLibrary接口切换](medialibrary-switch.md)
- [request接口切换](request-switch.md)
- [resourceManager接口切换](resourcemanager-switch.md)
- [window接口切换](window-switch.md)
- [Storage接口切换](storage-switch.md)
# 使用显式Want启动Ability
在应用使用场景中,当用户点击某个按钮时,应用经常需要拉起指定UIAbility组件来完成某些特定任务。下面介绍如何通过显式Want拉起应用内一个指定UIAbility组件。
## 开发步骤
1. Stage模型工程内,创建一个Ability(此示例内命名为callerAbility)与相应Page(此示例中名为Index.ets),并在callerAbility.ts文件内的onWindowStageCreate函数内通过windowStage.loadContent()方法将两者绑定。
```ts
// ...
// callerAbility.ts
onWindowStageCreate(windowStage) {
// Main window is created, set main page for this ability
console.info('[Demo] EntryAbility onWindowStageCreate')
// Bind callerAbility with a paged named Index
windowStage.loadContent('pages/Index')
}
// ...
```
2. 同上方法再创建一个Ability,此示例内命名为“calleeAbility”。
3. 在callerAbility的“Index.ets”页面内新增一个按钮。
```ts
// ...
build() {
Row() {
Column() {
Text('hello')
.fontSize(50)
.fontWeight(FontWeight.Bold)
// A new button with will call explicitStartAbility() when clicked.
Button("CLICKME")
.onClick(this.explicitStartAbility) // explicitStartAbility见下面示例代码
// ...
}
.width('100%')
}
.height('100%')
}
// ...
```
4. 补充相对应的onClick方法,并使用**显式Want**在方法内启动calleeAbility。bundleName字段可在工程AppScope&gt;app.json5文件内获取;abilityName可在对应模块内的“yourModuleName &gt; src &gt; main &gt; module.json5”文件查看。
```ts
import common from '@ohos.app.ability.common';
// ...
async explicitStartAbility() {
try {
// Explicit want with abilityName specified.
let want = {
deviceId: "",
bundleName: "com.example.myapplication",
abilityName: "calleeAbility"
};
let context = getContext(this) as common.UIAbilityContext;
await context.startAbility(want);
console.info(`explicit start ability succeed`);
} catch (error) {
console.info(`explicit start ability failed with ${error.code}`);
}
}
// ...
```
5. 至此,当您点击CLICKME按钮时,应看到页面的跳转。
![startAbilityWtExplicitWant](figures/startAbilityWtExplicitWant.PNG)
# 使用隐式Want打开网址
## 前提条件
设备上安装了一个或多个浏览器。
浏览器应用中通过module.json5配置如下:
```json
"skills": [
{
"entities": [
"entity.system.browsable"
// ...
],
"actions": [
"ohos.want.action.viewData"
// ...
],
"uris": [
{
"scheme": "https",
"host": "www.test.com",
"port": "8080",
// prefix matching
"pathStartWith": "query",
"type": "text/*"
},
{
"scheme": "http",
// ...
}
// ...
]
},
]
```
## 开发步骤
1. 在自定义函数implicitStartAbility内使用**隐式Want**启动Ability。
```ts
async implicitStartAbility() {
try {
let want = {
// uncomment line below if wish to implicitly query only in the specific bundle.
// bundleName: "com.example.myapplication",
"action": "ohos.want.action.viewData",
// entities can be omitted.
"entities": [ "entity.system.browsable" ],
"uri": "https://www.test.com:8080/query/student",
"type": "text/plain"
}
let context = getContext(this) as common.UIAbilityContext;
await context.startAbility(want)
console.info(`explicit start ability succeed`)
} catch (error) {
console.info(`explicit start ability failed with ${error.code}`)
}
let context = getContext(this) as common.UIAbilityContext;
await context.startAbility(want)
console.info(`explicit start ability succeed`)
} catch (error) {
console.info(`explicit start ability failed with ${error.code}`)
}
}
```
匹配过程如下:
1. want内action不为空,且被skills内action包括,匹配成功。
2. want内entities不为空,且被skills内entities包括,匹配成功。
3. skills内uris拼接为https://www.test.com:8080/query\* (\*为通配符)包含want内uri,匹配成功。
4. want内type不为空,且被skills内type包含,匹配成功。
2. 当有多个匹配应用时,会被应用选择器展示给用户进行选择。![stage-want1](figures/stage-want1.png)
# AbilityStage组件容器
AbilityStage是一个[Module](../quick-start/application-package-structure-stage.md)级别的组件容器,应用的HAP在首次加载时会创建一个AbilityStage实例,可以对该Module进行初始化等操作。
AbilityStage与Module一一对应,即一个Module拥有一个AbilityStage。
DevEco Studio默认工程中未自动生成AbilityStage,如需要使用AbilityStage的能力,可以手动新建一个AbilityStage文件,具体步骤如下。
1. 在工程Module对应的ets目录下,右键选择“New &gt; Directory”,新建一个目录并命名为myabilitystage。
2. 在myabilitystage目录,右键选择“New &gt; ts File”,新建一个TS文件并命名为MyAbilityStage.ts。
3. 打开MyAbilityStage.ts文件,导入AbilityStage的依赖包,自定义类继承AbilityStage并加上需要的生命周期回调,示例中增加了一个onCreate()生命周期回调。
```ts
import AbilityStage from '@ohos.app.ability.AbilityStage';
export default class MyAbilityStage extends AbilityStage {
onCreate() {
// 应用的HAP在首次加载的时,为该Module初始化操作
}
onAcceptWant(want) {
// 仅specified模式下触发
return "MyAbilityStage";
}
}
```
[AbilityStage](../reference/apis/js-apis-application-abilitystage.md)拥有[onCreate()](../reference/apis/js-apis-application-abilitystage.md#abilitystageoncreate)生命周期回调和[onAcceptWant()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant)[onConfigurationUpdated()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonconfigurationupdated)[onMemoryLevel()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonmemorylevel)事件回调。
- [onCreate()](../reference/apis/js-apis-application-abilitystage.md#abilitystageoncreate)生命周期回调:在开始加载对应Module的第一个UIAbility实例之前会先创建AbilityStage,并在AbilityStage创建完成之后执行其onCreate()生命周期回调。AbilityStage模块提供在Module加载的时候,通知开发者,可以在此进行该Module的初始化(如资源预加载,线程创建等)能力。
- [onAcceptWant()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant)事件回调:UIAbility[指定实例模式(specified)](uiability-launch-type.md#specified启动模式)启动时候触发的事件回调,具体使用请参见[UIAbility启动模式综述](uiability-launch-type.md)
- [onConfigurationUpdated()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonconfigurationupdated)事件回调:当系统全局配置发生变更时触发的事件,系统语言、深浅色等,配置项目前均定义在[Configuration](../reference/apis/js-apis-configuration.md)类中。
- [onMemoryLevel()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonmemorylevel)事件回调:当系统调整内存时触发的事件。
应用被切换到后台时,系统会将在后台的应用保留在缓存中。即使应用处于缓存中,也会影响系统整体性能。当系统资源不足时,系统会通过多种方式从应用中回收内存,必要时会完全停止应用,从而释放内存用于执行关键任务。为了进一步保持系统内存的平衡,避免系统停止用户的应用进程,可以在AbilityStage中的onMemoryLevel()生命周期回调中订阅系统内存的变化情况,释放不必要的资源。
```ts
import AbilityStage from '@ohos.app.ability.AbilityStage';
export default class MyAbilityStage extends AbilityStage {
onMemoryLevel(level) {
// 根据系统可用内存的变化情况,释放不必要的内存
}
}
```
# 访问DataAbility
访问DataAbility需导入基础依赖包,以及获取与DataAbility子模块通信的URI字符串。
其中,基础依赖包包括:
- @ohos.ability.featureAbility
- @ohos.data.dataAbility
- @ohos.data.rdb
访问DataAbility的示例代码如下:
1. 创建工具接口类对象。
```ts
// 作为参数传递的URI,与config中定义的URI的区别是多了一个"/",有三个"/"
import featureAbility from '@ohos.ability.featureAbility'
import ohos_data_ability from '@ohos.data.dataAbility'
import ohos_data_rdb from '@ohos.data.rdb'
let urivar = "dataability:///com.ix.DataAbility"
let DAHelper = featureAbility.acquireDataAbilityHelper(urivar);
```
2. 构建数据库相关的RDB数据。
```ts
let valuesBucket = {"name": "gaolu"}
let da = new ohos_data_ability.DataAbilityPredicates()
let valArray =new Array("value1");
let cars = new Array({"batchInsert1" : "value1",});
```
注:关于DataAbilityPredicates的详细内容,请参考[DataAbility谓词](../reference/apis/js-apis-data-ability.md)
3. 调用insert方法向指定的DataAbility子模块插入数据。
```ts
// callback方式调用:
DAHelper.insert(
urivar,
valuesBucket,
(error, data) => {
console.info("DAHelper insert result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let datainsert = await DAHelper.insert(urivar, valuesBucket).then((data) => {
console.info("insert success.");
}).catch((error) => {
console.error("insert failed.");
});
```
4. 调用delete方法删除DataAbility子模块中指定的数据。
```ts
// callback方式调用:
DAHelper.delete(
urivar,
da,
(error, data) => {
console.info("DAHelper delete result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let datadelete = await DAHelper.delete(
urivar,
da,
);
```
5. 调用update方法更新指定DataAbility子模块中的数据。
```ts
// callback方式调用:
DAHelper.update(
urivar,
valuesBucket,
da,
(error, data) => {
console.info("DAHelper update result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let dataupdate = await DAHelper.update(
urivar,
valuesBucket,
da,
);
```
6. 调用query方法在指定的DataAbility子模块中查找数据。
```ts
// callback方式调用:
DAHelper.query(
urivar,
valArray,
da,
(error, data) => {
console.info("DAHelper query result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let dataquery = await DAHelper.query(
urivar,
valArray,
da
);
```
7. 调用batchInsert方法向指定的DataAbility子模块批量插入数据。
```ts
// callback方式调用:
DAHelper.batchInsert(
urivar,
cars,
(error, data) => {
console.info("DAHelper batchInsert result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let databatchInsert = await DAHelper.batchInsert(
urivar,
cars
);
```
8. 调用executeBatch方法向指定的DataAbility子模块进行数据的批量处理。
```ts
// callback方式调用:
DAHelper.executeBatch(
urivar,
[
{
uri: urivar,
type: featureAbility.DataAbilityOperationType.TYPE_INSERT,
valuesBucket: {"executeBatch" : "value1",},
predicates: da,
expectedCount:0,
predicatesBackReferences: null,
interrupted:true,
}
],
(error, data) => {
console.info("DAHelper executeBatch result: " + data)
}
);
```
```ts
// promise方式调用(await需要在async方法中使用):
let dataexecuteBatch = await DAHelper.executeBatch(
urivar,
[
{
uri: urivar,
type: featureAbility.DataAbilityOperationType.TYPE_INSERT,
valuesBucket:
{
"executeBatch" : "value1",
},
predicates: da,
expectedCount:0,
predicatesBackReferences: null,
interrupted:true,
}
]
);
```
DataAbility的客户端的接口是由工具接口类对象DataAbilityHelper向外提供,相关接口可参考[DataAbilityHelper模块](../reference/apis/js-apis-dataAbilityHelper.md)
# FA模型访问Stage模型DataShareExtensionAbility
## 概述
无论FA模型还是Stage模型,数据读写功能都包含客户端和服务端两部分。
- FA模型中,客户端是由DataAbilityHelper提供对外接口,服务端是由DataAbility提供数据库的读写服务。
- Stage模型中,客户端是由DataShareHelper提供对外接口,服务端是由DataShareExtensionAbility提供数据库的读写服务。
服务端由FA模型升级到Stage模型后,会导致FA模型的客户端在API 9(含)之后的版本上无法访问服务端。
为了解决上述问题,OpenHarmony在框架侧提供了一个解决方案,让开发者平滑过渡到API 9(含)之后的版本。
## 基本原理
一种兼容方法是DataAbilityHelper根据传入的URI的前缀是DataAbility还是DataShare来决定是否调DataShareHelper的接口。但是这种方法需要开发者修改原客户端代码的URI,做不到无感知切换。
因此DataAbilityHelper不能仅依赖URI的前缀决定访问DataAbility还是DataShareExtensionAbility,OpenHarmony采用的方法是:
1. 先按照传入的URI拉起DataAbility;如果拉起失败,再将传入的URI的前缀转换成DataShare再去尝试拉起DataShareExtensionAbility。
2. 如果URI无对应的DataAbility和DataShareExtensionAbility,则拉起失败;反之,必定会拉起DataAbility或者DataShareExtensionAbility。
## 约束与限制
1. 由DataAbility切换到DataShareExtensionAbility时,只能修改URI的前缀,不能修改URI的其他部分。![FAvsStage-uri](figures/FAvsStage-uri.png)
2. DataShareHelper并没有实现原DataAbilityHelper对外API接口的所有功能,因此有部分接口是无法兼容的,具体如表1所示。
**表1** FA模型访问stage模型DataShareExtensionAbility接口支持情况
| 接口 | DataAbilityHelper是否提供 | DataShareHelper是否提供 | 是否兼容 |
| -------- | -------- | -------- | -------- |
| on | 是 | 是 | 是 |
| off | 是 | 是 | 是 |
| notifyChange | 是 | 是 | 是 |
| insert | 是 | 是 | 是 |
| delete | 是 | 是 | 是 |
| query | 是 | 是 | 是 |
| update | 是 | 是 | 是 |
| batchInsert | 是 | 是 | 是 |
| getType | 是 | 否 | 否 |
| getFileTypes | 是 | 否 | 否 |
| normalizeUri | 是 | 是 | 是 |
| denormalizeUri | 是 | 是 | 是 |
| openFile | 是 | 否 | 否 |
| call | 是 | 否 | 否 |
| executeBatch | 是 | 否 | 否 |
# 常见action与entities
**[action](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction)**:表示调用方要执行的通用操作(如查看、分享、应用详情)。在隐式Want中,您可定义该字段,配合uri或parameters来表示对数据要执行的操作。如打开,查看该uri数据。例如,当uri为一段网址,action为ohos.want.action.viewData则表示匹配可查看该网址的Ability。在Want内声明action字段表示希望被调用方应用支持声明的操作。在被调用方应用配置文件skills字段内声明actions表示该应用支持声明操作。
**常见action**
- ACTION_HOME:启动应用入口组件的动作,需要和ENTITY_HOME配合使用;系统桌面应用图标就是显式的入口组件,点击也是启动入口组件;入口组件可以配置多个。
- ACTION_CHOOSE:选择本地资源数据,例如联系人、相册等;系统一般对不同类型的数据有对应的Picker应用,例如联系人和图库。
- ACTION_VIEW_DATA:查看数据,当使用网址uri时,则表示显示该网址对应的内容。
- ACTION_VIEW_MULTIPLE_DATA:发送多个数据记录的操作。
**[entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity)**:表示目标Ability的类别信息(如浏览器、视频播放器),在隐式Want中是对action的补充。在隐式Want中,开发者可定义该字段,来过滤匹配应用的类别,例如必须是浏览器。在Want内声明entities字段表示希望被调用方应用属于声明的类别。在被调用方应用配置文件skills字段内声明entites表示该应用支持的类别。
**常用entities**
- ENTITY_DEFAULT:默认类别无实际意义。
- ENTITY_HOME:主屏幕有图标点击入口类别。
- ENTITY_BROWSABLE:指示浏览器类别。
# API切换概述
FA模型和Stage模型由于线程模型和进程模型的差异,部分接口仅在FA模型下才能使用,针对这部分接口在SDK的接口中有FAModelOnly的标记,用于提醒开发者这部分接口仅能在FA模型下使用。因此在切换到Stage模型时,需要将应用中用到的FAModelOnly接口替换成Stage模型下对应的接口。下面是startAbility的接口切换示例,全量接口列表请查看后续章节:
![api-switch-overview](figures/api-switch-overview.png)
startAbility接口由FA模型切换到Stage模型的示例:
- FA模型示例
```ts
import fa from '@ohos.ability.featureAbility';
let parameter = {
"want": {
bundleName: "ohos.samples.demo",
abilityName: "ohos.samples.demo.MainAbility"
}
}
fa.startAbility(parameter).then((data) => {
console.info('startAbility success');
}).catch((error) => {
console.error('startAbility failed.');
})
```
- Stage示例示例
```ts
// context为Ability对象的成员,在非Ability对象内部调用需要
// 将Context对象传递过去
let wantInfo = {
bundleName: "ohos.samples.demo",
abilityName: "ohos.samples.demo.MainAbility"
};
this.context.startAbility(wantInfo).then((data) => {
console.info('startAbility success.');
}).catch((error) => {
console.error('startAbility failed.');
})
```
# app和deviceConfig的切换
为了便于开发者维护应用级别的属性配置,Stage模型将config.json中的app和deviceConfig标签提取到了app.json5中进行配置,并对部分标签名称进行了修改,具体差异见下表。
**表1** 配置文件app标签差异对比
| 配置项 | FA模型配置文件config.json中app标签 | Stage模型配置文件app.json5中app标签 |
| -------- | -------- | -------- |
| 应用的版本号 | "version": {<br/> "code": 1,<br/>} | "versionCode":&nbsp;1&nbsp;, |
| 标识版本号的文字描述,用于向用户展示 | "version": {<br/> "name": "1.0.0",<br/>} | "versionName"&nbsp;:&nbsp;"1.0.0"&nbsp;, |
| 标识应用可兼容的最低版本号 | "version": {<br/> "minCompatibleVersionCode": 1,<br/>} | "minCompatibleVersionCode"&nbsp;:&nbsp;1&nbsp;, |
| 运行应用所需要的最低API版本 | "apiVersion": {<br/> "compatible": 7,<br/>} | "minAPIVersion"&nbsp;:&nbsp;7&nbsp;, |
| 应用运行所需的目标API版本 | "apiVersion": {<br/> "target": 8,<br/>} | "targetApiVersion"&nbsp;:&nbsp;8&nbsp;, |
| 应用运行所需的目标API版本的类型 | "apiVersion": {<br/> "releaseType": Release,<br/>} | "apiReleaseType":&nbsp;"Release"&nbsp;, |
app.json5中对原先config.json中的[deviceConfig](../quick-start/deviceconfig-structure.md)标签进行了重构,将deviceConfig下的标签的设备信息整合到了[app.json5](../quick-start/app-configuration-file.md)的app标签下,具体差异见下表
**表2** 配置文件deviceConfig标签差异对比
| FA中deviceConfig标签 | 描述 | stage模型中 | 差异比对 |
| -------- | -------- | -------- | -------- |
| deviceConfig标签 | deviceConfig标签配置了设备信息 | / | Stage模型中没有该标签,直接在app标签下配置设备信息 |
| process | 标识应用或者Ability的进程名。如果在deviceConfig标签下配置了process标签,则该应用的所有Ability都运行在这个进程中。如果在abilities标签下也为某个Ability配置了process标签,则该Ability就运行在这个进程中。 | / | Stage模型不支持配置进程名称 |
| keepAlive | 标识应用是否始终保持运行状态,仅支持系统应用配置,三方应用配置不生效。 | / | Stage模型不支持系统应用模型管控方式变更 |
| supportBackup | 标识应用是否支持备份和恢复。 | / | Stage模型不支持 |
| compressNativeLibs | 标识libs库是否以压缩存储的方式打包到HAP包。 | / | Stage模型不支持 |
| network | 标识网络安全性配置。 | / | Stage模型不支持 |
# 应用/组件级配置
开发者在开发应用时,需要配置应用的一些标签,例如应用的包名、图标等标识特征的属性。这一章节描述了开发者在开发应用时需要配置的一些关键标签。
- **应用包名配置**
应用包名需在config.json文件中app标签下配置bundleName字段,该字段用于指定应用的包名,需保证唯一性。包名是由字母、数字、下划线(_)和点号(.)组成的字符串,必须以字母开头。支持的字符串长度为7~127字节。包名通常采用反向域名形式表示(例如,"com.example.myapplication")。建议第一级为域名后缀"com",第二级为厂商/个人名,也可以采用多级。应用名称配置可以参考[app标签说明](../quick-start/app-structure.md)
- **应用图标和标签配置**
FA模型不支持直接配置应用图标和标签,会以符合规则的PageAbility的图标和标签作为应用图标和标签。PageAbility的图标和标签配置请参见[PageAbility组件配置](pageability-configuration.md)。需在config.json文件的abilities标签下配置icon字段,标签值为资源文件的索引。图标需要在配置IDE的资源文件中,路径为/resource/base/media。取值示例:$media:ability_icon。标签值为资源文件的索引,标识Ability对用户显示的名称。取值可以是Ability名称,也可以是对该名称的资源索引,以支持多语言。如果在该Ability的skills属性中,actions的取值包含 "action.system.home",entities取值中包含"entity.system.home",则该Ability的icon和label将同时作为应用的icon和label。如果存在多个符合条件的Ability,则取位置靠前的Ability的icon和label作为应用的icon和label。应用图标和标签配置可以参考[ablities标签说明](../quick-start/module-structure.md)
```json
"abilities": [
"icon": "$media:icon",
"label": "$string:MainAbility_label",
"skills": [
{
"entities": ["entity.system.home"],
"actions": ["action.system.home"]
}
]
// ...
}
```
- **应用版本声明配置**
应用版本声明配置需在config.json中的app标签下配置version字段,以说明应用当前的版本号和版本名称以及应用能够兼容的最低历史版本号。应用版本配置说明可以参考[version标签说明](../quick-start/module-structure.md)
- **Module支持的设备类型配置**
Module支持的设备类型需要在config.json文件中配置deviceType字段,如果deviceType标签中添加了某种设备,则表明当前的module支持在改设备上运行。具体的deviceType配置规则可以参考[deviceType标签说明](../quick-start/module-structure.md)
- **组件权限申请配置**
组件权限申请配置需在confog.json中的module标签下配置reqPermission字段。来声明需要申请权限的名称,申请权限的原因以及权限使用的场景。组件权限申请可以参考[reqPermission标签说明](../quick-start/module-structure.md)
# 应用/组件级配置
在开发应用时,需要配置应用的一些标签,例如应用的包名、图标等标识特征的属性。本文描述了在开发应用需要配置的一些关键标签。图标和标签通常一起配置,可以分为应用图标、应用标签和入口图标、入口标签,分别对应[app.json5配置文件](../quick-start/app-configuration-file.md)[module.json5配置文件](../quick-start/module-configuration-file.md)文件中的icon和label标签。应用图标和标签是在设置应用中使用,例如设置应用中的应用列表。入口图标是应用安装完成后在设备桌面上显示出来的,如图一所示。入口图标是以[UIAbility](uiability-overview.md)为粒度,支持同一个应用存在多个入口图标和标签,点击后进入对应的UIAbility界面。
**图1** 应用图标和标签  
![application-component-configuration-stage](figures/application-component-configuration-stage.png)
- **应用包名配置**
应用需要在工程的AppScope目录下的[app.json5配置文件](../quick-start/app-configuration-file.md)中配置bundleName标签,该标签用于标识应用的唯一性。推荐采用反域名形式命名(如com.example.demo,建议第一级为域名后缀com,第二级为厂商/个人名,第三级为应用名,也可以多级)。
- **应用图标和标签配置**
应用图标需要在工程的AppScope目录下的[app.json5配置文件](../quick-start/app-configuration-file.md)中配置icon标签。应用图标需配置为图片的资源索引,配置完成后,该图片即为应用的图标。应用图标通常用于显示在应用列表中,例如设置中的应用列表。
应用标签需要在工程的AppScope模块下的[app.json5配置文件](../quick-start/app-configuration-file.md)中配置label标签。标识应用对用户显示的名称,需要配置为字符串资源的索引。
Stage模型的应用支持单独配置应用图标和标签,在[app.json5配置文件](../quick-start/app-configuration-file.md)中配置。应用图标和标签是在设置应用中使用,例如设置应用中的应用列表,会显示出对应的图标和标签。在app字段下面分别有icon和label字段,根据对应的规则进行配置。
```json
{
"app": {
"icon": "$media:app_icon",
"label": "$string:app_name"
// ...
}
}
```
- **入口图标和标签配置**
入口图标和标签需要在[app.json5配置文件](../quick-start/app-configuration-file.md)中配置,在abilities标签下面分别有icon和label标签。例如希望在桌面上显示该UIAbility的图标和标签,则需要在skills标签下面的entities中添加"entity.system.home"、actions中添加"action.system.home"。同一个应用有多个UIAbility配置上述字段时,桌面上会显示出多个图标和标签,分别对应各自的UIAbility。
```json
{
"module": {
// ...
"abilities": [
{
// $开头的为资源值
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
}
]
}
}
```
- **应用版本声明配置**
应用版本声明需要在工程的AppScope目录下的[app.json5配置文件](../quick-start/app-configuration-file.md)中配置versionCode标签和versionName标签。versionCode用于标识应用的版本号,该标签值为32位非负整数。此数字仅用于确定某个版本是否比另一个版本更新,数值越大表示版本越高。versionName标签标识版本号的文字描述。
- **Module支持的设备类型配置**
Module支持的设备类型需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中配置deviceTypes标签,如果deviceTypes标签中添加了某种设备,则表明当前的Module支持在该设备上运行。
- **Module权限配置**
Module访问系统或其他应用受保护部分所需的权限信息需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中配置requestPermission标签。该标签用于声明需要申请权限的名称、申请权限的原因以及权限使用的场景。
\ No newline at end of file
# FA模型的Context
FA模型下只有一个Context。Context中的所有功能都是通过方法来提供的,它提供了一些featureAbility中不存在的方法,相当于featureAbility的一个扩展和补全。
## 接口说明
FA模型下使用Context,需要通过featureAbility下的接口getContext来获取,而在此之前,需要先导入对应的包:
```ts
import featureAbility from "@ohos.ability.featureAbility";
```
然后使用如下方式获取对应的Context对象:
```ts
let context = featureAbility.getContext()
```
最终返回的对象为Context,其对应的接口说明请参见[接口文档](../reference/apis/js-apis-Context.md)
## 开发步骤
1. 查询Bundle信息。
```ts
import featureAbility from '@ohos.ability.featureAbility'
export default {
onCreate() {
// 获取context并调用相关方法
let context = featureAbility.getContext();
context.getBundleName((data, bundleName)=>{
console.info("ability bundleName:" + bundleName)
});
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
2. 设置当前featureAbility的显示方向。
```ts
import featureAbility from '@ohos.ability.featureAbility'
import bundle from '@ohos.bundle';
export default {
onCreate() {
// 获取context并调用相关方法
let context = featureAbility.getContext();
context.setDisplayOrientation(bundle.DisplayOrientation.LANDSCAPE).then(() => {
console.info("Set display orientation.")
})
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
# 应用上下文Context
## 概述
[Context](../reference/apis/js-apis-application-context.md)是应用中对象的上下文,其提供了应用的一些基础信息,例如resourceManager(资源管理)、applicationInfo(当前应用信息)、dir(应用开发路径)、area(文件分区)等,以及应用的一些基本方法,例如createBundleContext()、getApplicationContext()等。UIAbility组件和各种ExtensionAbility派生类组件都有各自不同的Context类。分别有基类Context、ApplicationContext、AbilityStageContext、UIAbilityContext、ExtensionContext、ServiceExtensionContext等Context。
- 各类Context的继承关系
![context-inheritance](figures/context-inheritance.png)
- 各类Context的持有关系
![context-holding](figures/context-holding.png)
- 各类Context的获取方式
- 获取[UIAbilityContext](../reference/apis/js-apis-ability-context.md)。每个UIAbility中都包含了一个Context属性,提供操作Ability、获取Ability的配置信息、应用向用户申请授权等能力。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let uiAbilityContext = this.context;
// ...
}
}
```
- 获取特定场景[ExtensionContext](../reference/apis/js-apis-extension-context.md)。以ServiceExtensionContext为例,表示后台服务的上下文环境,继承自ExtensionContext,提供后台服务相关的接口能力。
```ts
import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
export default class MyService extends ServiceExtensionAbility {
onCreate(want) {
let serviceExtensionContext = this.context;
// ...
}
}
```
- 获取[AbilityStageContext](../reference/apis/js-apis-abilitystagecontext.md)。Module级别的Context,和基类Context相比,额外提供HapModuleInfo、Configuration等信息。
```ts
import AbilityStage from "@ohos.app.ability.AbilityStage";
export default class MyAbilityStage extends AbilityStage {
onCreate() {
let abilityStageContext = this.context;
// ...
}
}
```
- 获取[ApplicationContext](../reference/apis/js-apis-application-applicationContext.md)。应用级别的Context。ApplicationContext在基类Context的基础上提供了订阅应用内Ability的生命周期的变化、订阅系统内存变化和订阅应用内系统环境的变化的能力,在UIAbility、ExtensionAbility、AbilityStage中均可以获取。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let applicationContext = this.context.getApplicationContext();
// ...
}
}
```
## Context的典型使用场景
本章节通过如下典型场景来介绍Context的用法:
- [获取应用开发路径](#获取应用开发路径)
- [获取和修改加密分区](#获取和修改加密分区)
- [创建其他应用/其他Module的Context](#创建其他应用其他module的context)
- [订阅进程内Ability生命周期变化](#订阅进程内ability生命周期变化)
- [通过AbilityContext向用户申请授权](#通过uiabilitycontext向用户申请授权)
### 获取应用开发路径
从Context中获取的应用开发路径如下表所示。
**表1** 应用开发路径说明
| 属性名称 | 参数类型 | 可读 | 可写 | 说明 |
| -------- | -------- | -------- | -------- | -------- |
| cacheDir | string | 是 | 否 | 应用在内部存储上的缓存路径。<br/>对应于"设置&nbsp;&gt;&nbsp;应用管理",找到对应应用的"存储"中的缓存内容。 |
| tempDir | string | 是 | 否 | 应用的临时文件路径。<br/>在应用卸载后,系统会删除存储在此目录中的文件。 |
| filesDir | string | 是 | 否 | 应用在内部存储上的文件路径。<br/>本目录下存放的文件可能会被应用迁移或者备份的时候同步到其他目录中。 |
| databaseDir | string | 是 | 否 | 获取本地数据库存储路径。 |
| bundleCodeDir | string | 是 | 否 | 应用在内部存储上的安装路径。 |
| distributedFilesDir | string | 是 | 否 | 应用的分布式文件路径。 |
| preferencesDir | string | 是 | 是 | 指示应用程序首选项目录。 |
获取路径的能力是基类Context中提供的能力,因此在ApplicationContext、AbilityStageContext、UIAbilityContext和ExtensionContext中均可以获取,在各类Context中获取到的路径会有一些差别,具体差别如下图所示。
**图1** Context中获取的应用开发路径  
![context-dir](figures/context-dir.png)
- 通过ApplicationContext获取的应用级别路径。应用全局信息建议存放的路径,存放在此路径的文件内容仅在应用卸载时会被删除。
| 属性 | 路径 |
| -------- | -------- |
| bundleCodeDir | {路径前缀}/el1/bundle/ |
| cacheDir | {路径前缀}/{加密等级}/base/cache/ |
| filesDir | {路径前缀}/{加密等级}/base/files/ |
| preferencesDir | {路径前缀}/{加密等级}/base/preferences/ |
| tempDir | {路径前缀}/{加密等级}/base/temp/ |
| databaseDir | {路径前缀}/{加密等级}/database/ |
| distributedFilesDir | {路径前缀}/el2/distributedFiles/ |
- 通过AbilityStageContext、UIAbilityContext、ExtensionContext获取的HAP级别路径。HAP对应的信息建议存放的路径,存放在此路径的文件内容会跟随HAP的卸载而删除,不会影响应用级别路径的文件内容,除非该应用的HAP已全部卸载。
| 属性 | 路径 |
| -------- | -------- |
| bundleCodeDir | {路径前缀}/el1/bundle/ |
| cacheDir | {路径前缀}/{加密等级}/base/**haps/{moduleName}/**cache/ |
| filesDir | {路径前缀}/{加密等级}/base/**haps/{moduleName}/**files/ |
| preferencesDir | {路径前缀}/{加密等级}/base/**haps/{moduleName}/**preferences/ |
| tempDir | {路径前缀}/{加密等级}/base/**haps/{moduleName}/**temp/ |
| databaseDir | {路径前缀}/{加密等级}/database/**{moduleName}/** |
| distributedFilesDir | {路径前缀}/el2/distributedFiles/**{moduleName}/** |
获取应用开发路径的示例代码如下所示。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let cacheDir = this.context.cacheDir;
let tempDir = this.context.tempDir;
let filesDir = this.context.filesDir;
let databaseDir = this.context.databaseDir;
let bundleCodeDir = this.context.bundleCodeDir;
let distributedFilesDir = this.context.distributedFilesDir;
let preferencesDir = this.context.preferencesDir;
// ...
}
}
```
### 获取和修改加密分区
上一个场景中,引入了加密等级的概念,通过对[Context的area属性](../reference/apis/js-apis-application-context.md)的读写来实现获取和设置当前加密分区,支持如下两种加密等级:
- AreaMode.EL1:设备级加密区,设备开机后可访问的数据区。
- AreaMode.EL2:用户级加密区,设备开机,首次输入密码后才能够访问的数据区。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
// 存储普通信息前,切换到EL1设备机加密
if (this.context.area === 1) { // 获取area
this.context.area = 0; // 修改area
}
// 存储普通信息
// 存储敏感信息前,切换到EL2用户级加密
if (this.context.area === 0) { // 获取area
this.context.area = 1; // 修改area
}
// 存储敏感信息
}
}
```
### 创建其他应用/其他Module的Context
基类Context提供创建其他应用/其他Module的Context的方法有[createBundleContext(bundleName:string)](../reference/apis/js-apis-application-context.md#contextcreatebundlecontext)[createModuleContext(moduleName:string)](../reference/apis/js-apis-application-context.md#contextcreatemodulecontext)[createModuleContext(bundleName:string, moduleName:string)](../reference/apis/js-apis-application-context.md#contextcreatemodulecontext-1)接口,创建其他应用或者其他Module的Context,从而通过该Context获取相应的资源信息(例如获取其他Module的[获取应用开发路径](#获取应用开发路径)信息)。
- 调用createBundleContext(bundleName:string)方法,创建其他应用的Context信息。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当获取的是其他应用的Context时:
>
> - 申请`ohos.permission.GET_BUNDLE_INFO_PRIVILEGED`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)。
>
> - 接口为系统接口,三方应用不支持调用。
例如在桌面上显示的应用信息,包括应用名称和应用图标等,桌面应用可以通过调用上述的方法获取相应应用的Context信息从而获取到相应的应用名称、图标等资源信息。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let bundleName2 = "com.example.application";
let context2 = this.context.createBundleContext(bundleName2);
let label2 = context2.applicationInfo.label;
// ...
}
}
```
- 调用createModuleContext(bundleName:string, moduleName:string)方法,获取指定应用指定Module的上下文信息。获取到指定应用指定Module的Context之后,即可获取到相应应用Module的资源信息。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当获取的是其他应用的指定Module的Context时:
>
> - 申请`ohos.permission.GET_BUNDLE_INFO_PRIVILEGED`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)。
>
> - 接口为系统接口,三方应用不支持调用。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let bundleName2 = "com.example.application";
let moduleName2 = "module1";
let context2 = this.context.createModuleContext(bundleName2, moduleName2);
// ...
}
}
```
- 调用createModuleContext(moduleName:string)方法,获取本应用中其他Module的Context。获取到其他Module的Context之后,即可获取到相应Module的资源信息。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
let moduleName2 = "module1";
let context2 = this.context.createModuleContext(moduleName2);
// ...
}
}
```
### 订阅进程内Ability生命周期变化
在应用内的DFX统计场景,如需要统计对应页面停留时间和访问频率等信息,可以使用订阅进程内Ability生命周期变化功能。
在进程内Ability生命周期变化时,如创建、可见/不可见、获焦/失焦、销毁等,会触发进入相应的回调,其中返回的此次注册监听生命周期的ID(每次注册该ID会自增+1,当超过监听上限数量2^63-1时,返回-1),以在[UIAbilityContext](../reference/apis/js-apis-ability-context.md)中使用为例进行说明。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
const TAG: string = "[Example].[Entry].[EntryAbility]";
export default class EntryAbility extends UIAbility {
lifecycleId: number;
onCreate(want, launchParam) {
let abilityLifecycleCallback = {
onAbilityCreate(ability) {
console.info(TAG, "onAbilityCreate ability:" + JSON.stringify(ability));
},
onWindowStageCreate(ability, windowStage) {
console.info(TAG, "onWindowStageCreate ability:" + JSON.stringify(ability));
console.info(TAG, "onWindowStageCreate windowStage:" + JSON.stringify(windowStage));
},
onWindowStageActive(ability, windowStage) {
console.info(TAG, "onWindowStageActive ability:" + JSON.stringify(ability));
console.info(TAG, "onWindowStageActive windowStage:" + JSON.stringify(windowStage));
},
onWindowStageInactive(ability, windowStage) {
console.info(TAG, "onWindowStageInactive ability:" + JSON.stringify(ability));
console.info(TAG, "onWindowStageInactive windowStage:" + JSON.stringify(windowStage));
},
onWindowStageDestroy(ability, windowStage) {
console.info(TAG, "onWindowStageDestroy ability:" + JSON.stringify(ability));
console.info(TAG, "onWindowStageDestroy windowStage:" + JSON.stringify(windowStage));
},
onAbilityDestroy(ability) {
console.info(TAG, "onAbilityDestroy ability:" + JSON.stringify(ability));
},
onAbilityForeground(ability) {
console.info(TAG, "onAbilityForeground ability:" + JSON.stringify(ability));
},
onAbilityBackground(ability) {
console.info(TAG, "onAbilityBackground ability:" + JSON.stringify(ability));
},
onAbilityContinue(ability) {
console.info(TAG, "onAbilityContinue ability:" + JSON.stringify(ability));
}
}
// 1. 通过context属性获取applicationContext
let applicationContext = this.context.getApplicationContext();
// 2. 通过applicationContext注册监听应用内生命周期
this.lifecycleId = applicationContext.on("abilityLifecycle", abilityLifecycleCallback);
console.info(TAG, "register callback number: " + JSON.stringify(this.lifecycleId));
}
onDestroy() {
let applicationContext = this.context.getApplicationContext();
applicationContext.off("abilityLifecycle", this.lifecycleId, (error, data) => {
console.info(TAG, "unregister callback success, err: " + JSON.stringify(error));
});
}
}
```
### 通过UIAbilityContext向用户申请授权
每个Ability中都包含了一个Context属性。Ability功能主要是处理生命周期,其余操作Ability的方法(例如startAbility()、connectServiceExtensionAbility()、terminateSelf()等)都是在对应的Context中实现的,同时Context也提供了获取Ability的配置信息、向用户申请授权等能力,如何获取Context请参见[获取UIAbility的上下文信息](uiability-usage.md#获取uiability的上下文信息)
应用需要获取用户的隐私信息或使用系统能力时,例如获取位置信息、访问日历、使用相机拍摄照片或录制视频等,需要向用户申请授权,示意效果如下图所示。具体使用请参见[访问控制授权申请指导](../security/accesstoken-guidelines.md)
**图2** 向用户申请日历访问授权
![application-context-stage](figures/application-context-stage.png)
\ No newline at end of file
# OpenHarmony应用模型的构成要素
应用模型是OpenHarmony为开发者提供的应用程序所需能力的抽象提炼,它提供了应用程序必备的组件和运行机制。有了应用模型,开发者可以基于一套统一的模型进行应用开发,使应用开发更简单、高效。
OpenHarmony应用模型的构成要素包括:
1. 应用组件
应用组件是应用的基本组成单位,是应用的运行入口。用户启动、使用和退出应用过程中,应用组件会在不同的状态间切换,这些状态称为应用组件的生命周期。应用组件提供生命周期的回调函数,开发者通过应用组件的生命周期回调感知应用的[状态变化](uiability-lifecycle.md)。应用开发者在编写应用时,首先需要编写的就是应用组件,同时还需编写应用组件的生命周期回调函数,并在应用配置文件中配置相关信息。这样,操作系统在运行期间通过配置文件创建应用组件的实例,并调度它的生命周期回调函数,从而执行开发者的代码。
2. 应用进程模型
应用进程模型定义应用进程的创建和销毁方式,以及进程间的通信方式。
3. 应用线程模型
应用线程模型定义应用进程内线程的创建和销毁方式、主线程和UI线程的创建方式、线程间的通信方式。
4. 应用任务管理模型
应用任务管理模型定义任务(Mission)的创建和销毁方式,以及任务与组件间的关系。所谓任务,即用户使用一个应用组件实例的记录。每次用户启动一个新的应用组件实例,都会生成一个新的任务。例如,用户启动一个视频应用,此时在“最近任务”界面,将会看到视频应用这个任务,当用户点击这个任务时,系统会把该任务切换到前台,如果这个视频应用中的视频编辑功能也是通过应用组件编写的,那么在用户启动视频编辑功能时,会创建视频编辑的应用组件实例,在“最近任务”界面中,将会展示视频应用、视频编辑两个任务。
5. 应用配置文件
应用配置文件中包含应用配置信息、应用组件信息、权限信息、开发者自定义信息等,这些信息在编译构建、分发和运行阶段分别提供给编译工具、应用市场和操作系统使用。
# OpenHarmony应用模型解读
## OpenHarmony应用模型概况
随着系统的演进发展,OpenHarmony先后提供了两种应用模型:
- FA(Feature Ability)模型:OpenHarmony API 7开始支持的模型,已经不再主推。
- Stage模型:OpenHarmony API 9开始新增的模型,是目前主推且会长期演进的模型。在该模型中,由于提供了AbilityStage、WindowStage等类作为应用组件和Window窗口的“舞台”,因此称这种应用模型为Stage模型。
Stage模型之所以成为主推模型,源于其设计思想。Stage模型的设计基于如下4个出发点。
1. **为复杂应用而设计**
- 多个应用组件共享同一个ArkTS引擎(运行ArkTS语言的虚拟机)实例,应用组件之间可以方便的共享对象和状态,同时减少复杂应用运行对内存的占用。
- 采用面向对象的开发方式,使得复杂应用代码可读性高、易维护性好、可扩展性强。
2. **原生支持应用组件级的[跨端迁移](hop-cross-device-migration.md)和[多端协同](hop-multi-device-collaboration.md)**
Stage模型实现了应用组件与UI解耦:
- 在跨端迁移场景下,系统在多设备的应用组件之间迁移数据/状态后,UI便可利用ArkUI的声明式特点,通过应用组件中保存的数据/状态恢复用户界面,便捷实现跨端迁移。
- 在多端协同场景下,应用组件具备组件间通信的RPC调用能力,天然支持跨设备应用组件的交互。
3. **支持多设备和多窗口形态**
应用组件管理和窗口管理在架构层面解耦:
- 便于系统对应用组件进行裁剪(无屏设备可裁剪窗口)。
- 便于系统扩展窗口形态。
- 在多设备(如桌面设备和移动设备)上,应用组件可使用同一套生命周期。
4. **平衡应用能力和系统管控成本**
Stage模型重新定义应用能力的边界,平衡应用能力和系统管控成本。
- 提供特定场景(如卡片、输入法)的应用组件,以便满足更多的使用场景。
- 规范化后台进程管理:为保障用户体验,Stage模型对后台应用进程进行了有序治理,应用程序不能随意驻留在后台,同时应用后台行为受到严格管理,防止恶意应用行为。
## 通过对比认识FA模型与Stage模型
**Stage模型与FA模型最大的区别在于**:Stage模型中,多个应用组件共享同一个ArkTS引擎实例;而FA模型中,每个应用组件独享一个ArkTS引擎实例。因此在Stage模型中,应用组件之间可以方便的共享对象和状态,同时减少复杂应用运行对内存的占用。Stage模型作为主推的应用模型,开发者通过它能够更加便利地开发出分布式场景下的复杂应用。
可通过如下对比表格了解两种模型的整体概况。
**表1** FA模型与Stage模型差异概览
| 项目 | FA模型 | Stage模型 |
| -------- | -------- | -------- |
| **应用组件** | 1.&nbsp;组件分类<br/>&nbsp;&nbsp;&nbsp;-&nbsp;PageAbility组件:包含UI界面,提供展示UI的能力。详细介绍请参见[PageAbility组件概述](pageability-overview.md)。<br/>&nbsp;&nbsp;&nbsp;-&nbsp;ServiceAbility组件:提供后台服务的能力,无UI界面。详细介绍请参见[ServiceAbility组件概述](serviceability-overview.md)。<br/>&nbsp;&nbsp;&nbsp;-&nbsp;DataAbility组件:提供数据分享的能力,无UI界面。详细介绍请参见[DataAbility组件概述](dataability-overview.md)。<br/>2.&nbsp;开发方式<br/>&nbsp;&nbsp;&nbsp;通过导出匿名对象、固定入口文件的方式指定应用组件。开发者无法进行派生,不利于扩展能力。 | 1.&nbsp;组件分类<br/>&nbsp;&nbsp;&nbsp;-&nbsp;UIAbility组件:包含UI界面,提供展示UI的能力,主要用于和用户交互。详细介绍请参见[UIAbility组件概述](uiability-overview.md)。<br/>&nbsp;&nbsp;&nbsp;-&nbsp;ExtensionAbility组件:提供特定场景(如卡片、输入法)的扩展能力,满足更多的使用场景。详细介绍请参见[ExtensionAbility组件概述](extensionability-overview.md)。<br/>2.&nbsp;开发方式<br/>&nbsp;&nbsp;&nbsp;采用面向对象的方式,将应用组件以类接口的形式开放给开发者,可以进行派生,利于扩展能力。 |
| **进程模型** | 有两类进程:<br/>1.&nbsp;主进程<br/>2.&nbsp;渲染进程<br/>详细介绍请参见[进程模型](process-model-fa.md)。 | 有三类进程:<br/>1.&nbsp;主进程<br/>2.&nbsp;ExtensionAbility进程<br/>3.&nbsp;渲染进程<br/>详细介绍请参见[进程模型](process-model-stage.md)。 |
| **线程模型** | 1.&nbsp;ArkTS引擎实例的创建<br/>&nbsp;&nbsp;&nbsp;一个进程可以运行多个应用组件实例,每个应用组件实例运行在一个单独的ArkTS引擎实例中。<br/>2.&nbsp;线程模型<br/>&nbsp;&nbsp;&nbsp;每个ArkTS引擎实例都在一个单独线程(非主线程)上创建,主线程没有ArkTS引擎实例。<br/>3.&nbsp;进程内对象共享:不支持。<br/>详细介绍请参见[线程模型](thread-model-fa.md)。 | 1.&nbsp;ArkTS引擎实例的创建<br/>&nbsp;&nbsp;&nbsp;一个进程可以运行多个应用组件实例,所有应用组件实例共享一个ArkTS引擎实例。<br/>2.&nbsp;线程模型<br/>&nbsp;&nbsp;&nbsp;ArkTS引擎实例在主线程上创建。<br/>3.&nbsp;进程内对象共享:支持。<br/>详细介绍请参见[线程模型](thread-model-stage.md)。 |
| **任务管理模型** | -&nbsp;每个PageAbility组件实例创建一个任务。<br/>-&nbsp;任务会持久化存储,直到超过最大任务个数(根据产品配置自定义)或者用户主动删除任务。<br/>-&nbsp;PageAbility组件之间不会形成栈的结构。<br/>详细介绍请参见[任务管理场景介绍](mission-management-overview.md)。 | -&nbsp;每个UIAbility组件实例创建一个任务。<br/>-&nbsp;任务会持久化存储,直到超过最大任务个数(根据产品配置自定义)或者用户主动删除任务。<br/>-&nbsp;UIAbility组件之间不会形成栈的结构。<br/>详细介绍请参见[任务管理场景介绍](mission-management-overview.md)。 |
| **应用配置文件** | 使用config.json描述应用信息、HAP信息和应用组件信息。<br/>详细介绍请参见[应用配置文件概述(FA模型)](../quick-start/application-configuration-file-overview-fa.md)。 | 使用app.json5描述应用信息,module.json5描述HAP信息、应用组件信息。<br/>详细介绍请参见[应用配置文件概述(Stage模型)](../quick-start/application-configuration-file-overview-stage.md)。 |
# 后台服务
Stage模型提供了ServiceExtensionAbility来提供后台服务的能力,支持系统应用实现一个后台服务并对外提供相应的能力;系统应用A实现了一个后台服务,三方应用B可以通过连接系统应用A的后台服务与其进行进程间通信。
ServiceExtensionAbility的详细介绍请参见[后台服务开发指导](serviceextensionability.md)
# Stage模型绑定FA模型ServiceAbility
本小节介绍Stage模型的两种应用组件如何绑定FA模型ServiceAbility组件。
## UIAbility关联访问ServiceAbility
UIAbility关联访问ServiceAbility和UIAbility关联访问ServiceExtensionAbility的方式完全相同。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class MainAbility extends UIAbility {
onCreate(want, launchParam) {
console.info("MainAbility onCreate");
}
onDestroy() {
console.info("MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.info("MainAbility onWindowStageCreate")
let want = {
bundleName: "com.ohos.fa",
abilityName: "ServiceAbility",
};
let options = {
onConnect:function (elementName, proxy) {
console.info("onConnect called.");
},
onDisconnect:function (elementName) {
console.info("onDisconnect called.");
},
onFailed:function (code) {
console.info("onFailed code is: " + code);
}
};
let connectionId = this.context.connectServiceExtensionAbility(want, options);
}
onWindowStageDestroy() {
console.info("MainAbility onWindowStageDestroy")
}
onForeground() {
console.info("MainAbility onForeground")
}
onBackground() {
console.info("MainAbility onBackground")
}
}
```
## ExtensionAbility关联访问ServiceAbility
下面以ServiceExtensionAbility为例来说明ExtensionAbility关联访问ServiceAbility。ServiceExtensionAbility关联访问ServiceAbility和ServiceExtensionAbility关联访问ServiceExtensionAbility的方式完全相同。
```ts
import Extension from '@ohos.app.ability.ServiceExtensionAbility'
export default class ServiceExtension extends Extension {
onCreate(want) {
console.info("ServiceExtension onCreate")
}
onDestroy() {
console.info("ServiceExtension onDestroy")
}
onRequest(want, startId) {
console.info("ServiceExtension onRequest")
let wantFA = {
bundleName: "com.ohos.fa",
abilityName: "ServiceAbility",
};
let options = {
onConnect:function (elementName, proxy) {
console.info("onConnect called.");
},
onDisconnect:function (elementName) {
console.info("onDisconnect called.");
},
onFailed:function (code) {
console.info("onFailed code is: " + code);
}
};
let connectionId = this.context.connectServiceExtensionAbility(wantFA, options);
}
}
```
# FA模型绑定Stage模型ServiceExtensionAbility
本文介绍FA模型的三种应用组件如何绑定Stage模型的ServiceExtensionAbility组件。
## PageAbility关联访问ServiceExtensionAbility
PageAbility关联访问ServiceExtensionAbility和PageAbility关联访问ServiceAbility的方式完全相同。
```ts
import featureAbility from '@ohos.ability.featureAbility';
let want = {
bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.ServiceExtensionAbility"
};
let faConnect = {
onConnect:function (elementName, proxy) {
console.info("Faconnection onConnect called.");
},
onDisconnect:function (elementName) {
console.info("Faconnection onDisconnect called.");
},
onFailed:function (code) {
console.info("Faconnection onFailed code is: " + code);
}
};
let connectionId = featureAbility.connectAbility(want, faConnect);
```
## ServiceAbility/DataAbility关联访问ServiceExtensionAbility
ServiceAbility/DataAbility关联访问ServiceExtensionAbility和ServiceAbility/DataAbility关联访问ServiceAbility的方式完全相同。
```ts
import particleAbility from '@ohos.ability.particleAbility';
let want = {
bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.ServiceExtensionAbility"
};
let faConnect = {
onConnect:function (elementName, proxy) {
console.info("Faconnection onConnect called.");
},
onDisconnect:function (elementName) {
console.info("Faconnection onDisconnect called.");
},
onFailed:function (code) {
console.info("Faconnection onFailed code is: " + code);
}
};
let connectionId = particleAbility.connectAbility(want, faConnect);
```
# 公共事件
请参见Stage模型的"[公共事件](common-event-overview.md)"。
# 公共事件简介
OpenHarmony通过CES(Common Event Service,公共事件服务)为应用程序提供订阅、发布、退订公共事件的能力。
公共事件从系统角度可分为:系统公共事件和自定义公共事件。
- 系统公共事件:CES内部定义的公共事件,只有系统应用和系统服务才能发布,例如HAP安装,更新,卸载等公共事件。目前支持的系统公共事件详见[系统公共事件列表](../reference/apis/js-apis-commonEvent.md#support)
- 自定义公共事件:应用自定义一些公共事件用来实现跨进程的事件通信能力。
公共事件按发送方式可分为:无序公共事件、有序公共事件和粘性公共事件。
- 无序公共事件:CES转发公共事件时,不考虑订阅者是否接收到,按订阅者订阅先后顺序转发。
- 有序公共事件:CES转发公共事件时,按订阅者订阅先后顺序,在接收到前一个订阅者回复后,再转发下一个订阅者。
- 粘性公共事件:能够让订阅者收到在订阅前已经发送的公共事件就是粘性公共事件。普通的公共事件只能在订阅后发送才能收到,而粘性公共事件的特殊性就是可以先发送后订阅。发送粘性事件必须是系统应用或系统服务,且需要申请`ohos.permission.COMMONEVENT_STICKY`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)
每个应用都可以按需订阅公共事件,订阅成功,当公共事件发布时,系统会将其发送给对应的应用。这些公共事件可能来自系统、其他应用和应用自身。
**图1** 公共事件示意图
![common-event](figures/common-event.png)
# 公共事件发布
## 场景介绍
当需要发布某个自定义公共事件时,可以通过publish()方法发布事件。发布的公共事件可以携带数据,供订阅者解析并进行下一步处理。
> ![icon-notice.gif](public_sys-resources/icon-notice.gif) **须知:**
> 已发出的粘性公共事件后来订阅者也可以接收到,其他公共事件都需要先订阅再接收,订阅参考[公共事件订阅章节](common-event-subscription.md)。
## 接口说明
详细接口见[接口文档](../reference/apis/js-apis-commonEvent.md#commoneventpublish)
| 接口名 | 接口描述 |
| -------- | -------- |
| publish(event:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback) | 发布公共事件。 |
| publish(event:&nbsp;string,&nbsp;options:&nbsp;[CommonEventPublishData](../reference/apis/js-apis-commonEvent.md#commoneventpublishdata),&nbsp;callback:&nbsp;AsyncCallback) | 指定发布信息并发布公共事件。 |
## 发布不携带信息的公共事件
不携带信息的公共事件,只能发布无序公共事件。
1. 导入CommonEvent模块。
```ts
import commonEvent from '@ohos.commonEventManager';
```
2. 传入需要发布的事件名称和回调函数,发布事件。
```ts
// 发布公共事件
commonEvent.publish("usual.event.SCREEN_OFF", (err) => {
if (err) {
console.error(`[CommonEvent] PublishCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] Publish success`);
}
})
```
## 发布携带信息的公共事件
携带信息的公共事件,可以发布为无序公共事件、有序公共事件和粘性事件,可以通过参数[CommonEventPublishData](../reference/apis/js-apis-commonEvent.md#commoneventpublishdata)的isOrdered、isSticky的字段进行设置。
1. 导入CommonEvent模块。
```ts
import commonEvent from '@ohos.commonEventManager';
```
2. 传入需要发布的事件名称和回调函数,发布事件。
```ts
// 公共事件相关信息
let options = {
code: 1, // 公共事件的初始代码
data: "initial data", // 公共事件的初始数据
}
```
3. 传入需要发布的事件名称、需要发布的指定信息和回调函数,发布事件。
```ts
// 发布公共事件
commonEvent.publish("usual.event.SCREEN_OFF", options, (err) => {
if (err) {
console.error('[CommonEvent] PublishCallBack err=' + JSON.stringify(err));
} else {
console.info('[CommonEvent] Publish success')
}
})
```
# 公共事件订阅
## 场景介绍
当需要订阅某个公共事件,获取该公共事件传递的参数时,需要创建一个订阅者对象,用于作为订阅公共事件的载体,订阅公共事件并获取公共事件传递而来的参数。订阅部分系统公共事件需要先[申请权限](../security/accesstoken-guidelines.md/),订阅这些事件所需要的权限请见[公共事件权限列表](../reference/apis/js-apis-commonEvent.md#%E6%9D%83%E9%99%90%E5%88%97%E8%A1%A8)
## 接口说明
详细接口见[接口文档](../reference/apis/js-apis-commonEvent.md#commoneventcreatesubscriber)
| 接口名 | 接口描述 |
| -------- | -------- |
| createSubscriber(subscribeInfo:&nbsp;[CommonEventSubscribeInfo](../reference/apis/js-apis-commonEvent.md#commoneventsubscribeinfo),&nbsp;callback:&nbsp;AsyncCallback&lt;[CommonEventData](../reference/apis/js-apis-commonEvent.md#commoneventdata)&gt;):&nbsp;void | 创建订阅者对象(callback) |
| createSubscriber(subscribeInfo:&nbsp;CommonEventSubscribeInfo):&nbsp;Promise&lt;CommonEventSubscriber&gt; | 创建订阅者对象(promise) |
| subscribe(subscriber:&nbsp;CommonEventSubscriber,&nbsp;callback:&nbsp;AsyncCallback):&nbsp;void | 订阅公共事件 |
## 开发步骤
1. 导入CommonEvent模块。
```ts
import commonEvent from '@ohos.commonEventManager';
```
2. 创建订阅者信息,详细的订阅者信息数据类型及包含的参数请见[CommonEventSubscribeInfo](../reference/apis/js-apis-commonEvent.md#commoneventsubscribeinfo)文档介绍。
```ts
// 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
let subscriber = null;
// 订阅者信息
let subscribeInfo = {
events: ["usual.event.SCREEN_OFF"], // 订阅灭屏公共事件
}
```
3. 创建订阅者,保存返回的订阅者对象subscriber,用于执行后续的订阅、退订等操作。
```ts
// 创建订阅者回调
commonEvent.createSubscriber(subscribeInfo, (err, data) => {
if (err) {
console.error(`[CommonEvent] CreateSubscriberCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] CreateSubscriber success`);
subscriber = data;
// 订阅公共事件回调
}
})
```
4. 创建订阅回调函数,订阅回调函数会在接收到事件时触发。订阅回调函数返回的data内包含了公共事件的名称、发布者携带的数据等信息,公共事件数据的详细参数和数据类型请见[CommonEventData](../reference/apis/js-apis-commonEvent.md#commoneventdata)文档介绍。
```ts
// 订阅公共事件回调
if (subscriber !== null) {
commonEvent.subscribe(subscriber, (err, data) => {
if (err) {
console.error(`[CommonEvent] SubscribeCallBack err=${JSON.stringify(err)}`);
} else {
console.info(`[CommonEvent] SubscribeCallBack data=${JSON.stringify(data)}`);
}
})
} else {
console.error(`[CommonEvent] Need create subscriber`);
}
```
# 公共事件取消订阅
## 场景介绍
订阅者完成业务需要时,需要主动取消订阅,订阅者通过下面方法取消订阅事件。
## 接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| unsubscribe(subscriber:&nbsp;CommonEventSubscriber,&nbsp;callback?:&nbsp;AsyncCallback) | 取消订阅公共事件 |
## 开发步骤
1. 导入CommonEvent模块。
```ts
import commonEvent from '@ohos.commonEventManager';
```
2. 根据[事件订阅章节](common-event-subscription.md)的步骤来订阅某个事件。
3. 调用CommonEvent中的unsubscribe方法取消订阅某事件。
```ts
// subscriber为订阅事件时创建的订阅者对象
if (subscriber !== null) {
commonEvent.unsubscribe(subscriber, (err) => {
if (err) {
console.error(`[CommonEvent] UnsubscribeCallBack err=${JSON.stringify(err)}`)
} else {
console.info(`[CommonEvent] Unsubscribe`)
subscriber = null
}
})
}
```
# 组件启动规则
启动组件是指一切启动或连接Ability的行为:
- 启动Ability,如使用startAbility()、startServiceExtensionAbility()、startAbilityByCall()等相关接口。
- 连接Ability,如使用connectAbility()、connectServiceExtensionAbility()、acquireDataAbilityHelper()、createDataShareHelper()等相关接口。
在OpenHarmony中,为了保证用户具有更好的使用体验,对以下几种易影响用户体验与系统安全的行为做了限制:
- 后台应用任意弹框,如各种广告弹窗,影响用户使用。
- 后台应用相互唤醒,不合理的占用系统资源,导致系统功耗增加或系统卡顿。
- 前台应用任意跳转至其他应用,如随意跳转到其他应用的支付Ability,存在安全风险。
鉴于此,OpenHarmony制订了一套组件启动规则,主要包括:
- 应用位于后台时,启动组件需鉴权。
- 跨应用启动visible为false的组件,需鉴权。
- 跨应用启动FA模型的ServiceAbility组件或DataAbility组件,对端应用需配置关联启动。
- 使用startAbilityByCall接口,需鉴权(使用方式参考:[Call调用开发指南](hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 1. 组件启动管控自OpenHarmony v3.2 Release版本开始落地。
>
> 2. 与原本的启动规则不同,新的组件启动规则较为严格,开发者需熟知启动规则,避免业务功能异常。
## 相关概念说明
为方便开发者理解管控规则,此处对所涉及的相关概念进行解释。
- **应用APL(Ability Privilege Level)**,表示应用的权限申请优先级的定义,不同APL等级的应用所能申请的权限不同,APL分为3个等级:
- 操作系统核心能力APL="system_core" (APL = 3)
- 系统基础服务APL="system_basic" (APL = 2)
- 三方应用程序APL="normal" (APL = 1)
- **注:三方应用默认都是APL="normal"**
- **BACKGROUND校验**
- 对发起端应用进程的状态进行判断,若应用进程获焦或所属的Ability位于前台则判定为前台应用,否则为后台应用
- 若发起端为后台应用,则需校验START_ABILITIES_FROM_BACKGROUND权限
- **VISIBLE校验**
- 只针对跨应用启动的场景
- 若目标组件visible配置为false,则需校验START_INVISIBLE_ABILITY权限
- **CALL校验**
- 只针对startAbilityByCall接口
- 需校验ABILITY_BACKGROUND_COMMUNICATION权限
- **关联启动(AssociateWakeUp)**
- 只有系统预置应用才允许配置AssociateWakeUp字段,其余应用AssociateWakeUp默认为**false**
- 只针对跨应用场景
- 只针对目标组件为**FA**模型的**ServiceAbility****DataAbility**生效
- 目标应用的AssociateWakeUp为**ture**,其提供的ServiceAbility与DataAbility才允许被其他应用访问
- **相关权限**
```json
{
"name": "ohos.permission.START_ABILITIES_FROM_BACKGROUND",
"grantMode": "system_grant",
"availableLevel": "system_basic", // APL等级至少为system_basic才可申请
"since": 9,
"deprecated": "",
"provisionEnable": true, // 支持ACL方式申请权限
"distributedSceneEnable": false
},
{
"name": "ohos.permission.START_INVISIBLE_ABILITY",
"grantMode": "system_grant",
"availableLevel": "system_core", // APL等级至少为system_core才可申请
"since": 9,
"deprecated": "",
"provisionEnable": true, // 支持ACL方式申请权限
"distributedSceneEnable": false
},
{
"name": "ohos.permission.ABILITY_BACKGROUND_COMMUNICATION",
"grantMode": "system_grant",
"availableLevel": "system_basic", // APL等级至少为system_basic才可申请
"since": 9,
"deprecated": "",
"provisionEnable": true, // 支持ACL方式申请权限
"distributedSceneEnable": false
}
```
## 同设备组件启动管控
不同场景下的管控规则不同,可分为如下三种场景:
- 启动一般组件:(FA模型)PageAbility,(Stage模型)UIAbility、ServiceExtensionAbility、DataShareExtensionAbility。
- 启动FA模型的ServiceAbility或DataAbility。
- 通过startAbilityByCall接口启动(Stage模型)UIAbility。
![startup-rule](figures/startup-rule.png)
## 分布式跨设备组件启动管控
不同场景下的管控规则不同,可分为如下三种场景:
- 启动一般组件:(FA模型)PageAbility,(Stage模型)UIAbility、ServiceExtensionAbility。
- 启动FA模型的ServiceAbility。
- 通过startAbilityByCall接口启动(Stage模型)UIAbility。
![component-startup-rules](figures/component-startup-rules.jpg)
# 配置文件的差异
FA模型应用在[config.json文件](../quick-start/application-configuration-file-overview-fa.md)中描述应用的基本信息,一个应用工程中可以创建多个Module,每个Module中都有一份config.json文件。config.json由app、deviceConfig和module三部分组成,app标签用于配置应用级别的属性,如果一个应用有多个Module,需要由开发者保证各个config.json文件中app标签配置的一致性。
Stage模型应用在[app.json5](../quick-start/app-configuration-file.md)[module.json](../quick-start/module-configuration-file.md)文件中描述应用的基本信息,和FA模型应用类似,一个Stage模型应用工程中同样可以创建多个[Module],但是一个应用工程中仅存在一份app.json5,用于配置应用级别的属性,对每个Module都生效;每个Module中都有一份module.json5配置文件,用于配置Module级别的属性,仅对当前Module生效。
**图1** 配置文件差异
![comparison-of-configuration-file](figures/comparison-of-configuration-file.png)
# 连接ServiceAbility
如果ServiceAbility需要与PageAbility或其他应用的ServiceAbility进行交互,则须创建用于连接的Connection。ServiceAbility支持其他Ability通过connectAbility()方法与其进行连接。PageAbility的connectAbility方法定义在featureAbility中,ServiceAbility的connectAbility方法定义在particleAbility中。连接ServiceAbility的规则详见[组件启动规则](component-startup-rules.md)章节。在使用connectAbility()处理回调时,需要传入目标Service的Want与IAbilityConnection的实例。IAbilityConnection提供了以下方法供开发者实现。
**表1** IAbilityConnection接口说明
| 接口名 | 描述 |
| -------- | -------- |
| onConnect() | 用于处理连接Service成功的回调。 |
| onDisconnect() | 用来处理Service异常死亡的回调。 |
| onFailed() | 用来处理连接Service失败的回调。 |
PageAbility创建连接本地ServiceAbility回调实例的代码以及连接本地ServiceAbility的示例代码如下:
```ts
import rpc from "@ohos.rpc"
import prompt from '@system.prompt'
import featureAbility from '@ohos.ability.featureAbility'
let option = {
onConnect: function onConnectCallback(element, proxy) {
console.info(`onConnectLocalService onConnectDone`)
if (proxy === null) {
prompt.showToast({
message: "Connect service failed"
})
return
}
let data = rpc.MessageParcel.create()
let reply = rpc.MessageParcel.create()
let option = new rpc.MessageOption()
data.writeInterfaceToken("connect.test.token")
proxy.sendRequest(0, data, reply, option)
prompt.showToast({
message: "Connect service success"
})
},
onDisconnect: function onDisconnectCallback(element) {
console.info(`onConnectLocalService onDisconnectDone element:${element}`)
prompt.showToast({
message: "Disconnect service success"
})
},
onFailed: function onFailedCallback(code) {
console.info(`onConnectLocalService onFailed errCode:${code}`)
prompt.showToast({
message: "Connect local service onFailed"
})
}
}
let request = {
bundleName: "com.example.myapplication",
abilityName: "com.example.myapplication.ServiceAbility",
}
let connId = featureAbility.connectAbility(request, option)
```
同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类。
Service侧把自身的实例返回给调用侧的代码示例如下:
```ts
import rpc from "@ohos.rpc"
class FirstServiceAbilityStub extends rpc.RemoteObject {
constructor(des: any) {
if (typeof des === 'string') {
super(des)
} else {
return
}
}
onRemoteRequest(code: number, data: any, reply: any, option: any) {
console.info(`onRemoteRequest called`)
if (code === 1) {
let string = data.readString()
console.info(`string=${string}`)
let result = Array.from(string).sort().join('')
console.info(`result=${result}`)
reply.writeString(result)
} else {
console.info(`unknown request code`)
}
return true
}
}
```
# Context接口切换
| FA接口 | Stage模型接口对应d.ts文件 | Stage对应接口或字段 |
| -------- | -------- | -------- |
| [getOrCreateLocalDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetorcreatelocaldir7)<br/>[getOrCreateLocalDir():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetorcreatelocaldir7-1) | Stage模型无对应接口 | Stage模型应用在应用根目录下没有操作权限,不提供对应接口 |
| [verifyPermission(permission:string,options:PermissionOptions,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-Context.md#contextverifypermission7)<br/>[verifyPermission(permission:string,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-Context.md#contextverifypermission7-1)<br/>[verifyPermission(permission:string,options?:PermissionOptions):Promise&lt;number&gt;;](../reference/apis/js-apis-Context.md#contextverifypermission7-2) | \@ohos.abilityAccessCtrl.d.ts | [verifyAccessTokenSync(tokenID:&nbsp;number,&nbsp;permissionName:&nbsp;Permissions):&nbsp;GrantStatus;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)<br/>[verifyAccessToken(tokenID:&nbsp;number,&nbsp;permissionName:&nbsp;Permissions):&nbsp;Promise&lt;GrantStatus&gt;;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstoken9) |
| [requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number,resultCallback:AsyncCallback&lt;PermissionRequestResult&gt;):void;](../reference/apis/js-apis-Context.md#contextrequestpermissionsfromuser7)<br/>[requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number):Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-Context.md#contextrequestpermissionsfromuser7-1) | application\UIAbilityContext.d.ts | [requestPermissionsFromUser(permissions:&nbsp;Array&lt;string&gt;,&nbsp;requestCallback:&nbsp;AsyncCallback&lt;PermissionRequestResult&gt;)&nbsp;:&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser)<br/>[requestPermissionsFromUser(permissions:&nbsp;Array&lt;string&gt;)&nbsp;:&nbsp;Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser-1) |
| [getApplicationInfo(callback:AsyncCallback&lt;ApplicationInfo&gt;):void;](../reference/apis/js-apis-Context.md#contextgetapplicationinfo7)<br/>[getApplicationInfo():Promise&lt;ApplicationInfo&gt;;](../reference/apis/js-apis-Context.md#contextgetapplicationinfo7-1) | application\Context.d.ts | [applicationInfo:&nbsp;ApplicationInfo;](../reference/apis/js-apis-inner-application-context.md#属性) |
| [getBundleName(callback&nbsp;:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-Context.md#contextgetbundlename7)<br/>[getBundleName():&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetbundlename7-1) | application\UIAbilityContext.d.ts | [abilityInfo.bundleName:&nbsp;string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性) |
| [getDisplayOrientation(callback&nbsp;:&nbsp;AsyncCallback&lt;bundle.DisplayOrientation&gt;):&nbsp;void;](../reference/apis/js-apis-Context.md#contextgetdisplayorientation7)<br/>[getDisplayOrientation():&nbsp;Promise&lt;bundle.DisplayOrientation&gt;;](../reference/apis/js-apis-Context.md#contextgetdisplayorientation7-1) | \@ohos.screen.d.ts | [readonly&nbsp;orientation:&nbsp;Orientation;](../reference/apis/js-apis-screen.md#orientation) |
| [setDisplayOrientation(orientation:bundle.DisplayOrientation,&nbsp;callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-Context.md#contextsetdisplayorientation7)<br/>[setDisplayOrientation(orientation:bundle.DisplayOrientation):Promise&lt;void&gt;;](../reference/apis/js-apis-Context.md#contextsetdisplayorientation7-1) | \@ohos.screen.d.ts | [setOrientation(orientation:&nbsp;Orientation,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-screen.md#setorientation)<br/>[setOrientation(orientation:&nbsp;Orientation):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-screen.md#setorientation-1) |
| [setShowOnLockScreen(show:boolean,&nbsp;callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-Context.md#contextsetshowonlockscreen7)<br/>[setShowOnLockScreen(show:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-Context.md#contextsetshowonlockscreen7-1) | \@ohos.window.d.ts | [setShowOnLockScreen(showOnLockScreen:&nbsp;boolean):&nbsp;void;](../reference/apis/js-apis-window.md#setshowonlockscreen9) |
| [setWakeUpScreen(wakeUp:boolean,&nbsp;callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-Context.md#contextsetwakeupscreen7)<br/>[setWakeUpScreen(wakeUp:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-Context.md#contextsetwakeupscreen7-1) | \@ohos.window.d.ts | [setWakeUpScreen(wakeUp:&nbsp;boolean):&nbsp;void;](../reference/apis/js-apis-window.md#setwakeupscreen9) |
| [getProcessInfo(callback:AsyncCallback&lt;ProcessInfo&gt;):void;](../reference/apis/js-apis-Context.md#contextgetprocessinfo7)<br/>[getProcessInfo():Promise&lt;ProcessInfo&gt;;](../reference/apis/js-apis-Context.md#contextgetprocessinfo7-1) | \@ohos.app.ability.abilityManager.d.ts | [getAbilityRunningInfos(callback:&nbsp;AsyncCallback&lt;Array&lt;AbilityRunningInfo&gt;&gt;):&nbsp;void;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos)<br/>[getAbilityRunningInfos():&nbsp;Promise&lt;Array&lt;AbilityRunningInfo&gt;&gt;;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos-1) |
| [getElementName(callback:AsyncCallback&lt;ElementName&gt;):void;](../reference/apis/js-apis-Context.md#contextgetelementname7)<br/>[getElementName():Promise&lt;ElementName&gt;;](../reference/apis/js-apis-Context.md#contextgetelementname7-1) | application\UIAbilityContext.d.ts | [abilityInfo.name:&nbsp;string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性)<br/>[abilityInfo.bundleName:&nbsp;string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性) |
| [getProcessName(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetprocessname7)<br/>[getProcessName():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetprocessname7-1) | \@ohos.app.ability.abilityManager.d.ts | [getAbilityRunningInfos(callback:&nbsp;AsyncCallback&lt;Array&lt;AbilityRunningInfo&gt;&gt;):&nbsp;void;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos)<br/>[getAbilityRunningInfos():&nbsp;Promise&lt;Array&lt;AbilityRunningInfo&gt;&gt;;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos-1) |
| [getCallingBundle(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetcallingbundle7)<br/>[getCallingBundle():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetcallingbundle7-1) | Stage模型无对应接口 | Stage模型应用可以使用Want.parameters的ohos.aafwk.param.callerUid参数,获取调用方的应用信息 |
| [getFilesDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetfilesdir)<br/>[getFilesDir():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetfilesdir-1) | application\Context.d.ts | [filesDir:&nbsp;string;](../reference/apis/js-apis-inner-application-context.md#属性) |
| [getCacheDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetcachedir)<br/>[getCacheDir():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetcachedir-1) | application\Context.d.ts | [cacheDir:&nbsp;string;](../reference/apis/js-apis-inner-application-context.md#属性) |
| [getOrCreateDistributedDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetorcreatedistributeddir7)<br/>[getOrCreateDistributedDir():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetorcreatedistributeddir7-1) | application\Context.d.ts | [distributedFilesDir:&nbsp;string;](../reference/apis/js-apis-inner-application-context.md#属性) |
| [getAppType(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-Context.md#contextgetapptype7)<br/>[getAppType():Promise&lt;string&gt;;](../reference/apis/js-apis-Context.md#contextgetapptype7-1) | application\UIAbilityContext.d.ts | 通过abilityInfo字段的type属性获取<br/>[abilityInfo.type:&nbsp;bundleManager.AbilityType;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性) |
| [getHapModuleInfo(callback:AsyncCallback&lt;HapModuleInfo&gt;):void;](../reference/apis/js-apis-Context.md#contextgethapmoduleinfo7)<br/>[getHapModuleInfo():Promise&lt;HapModuleInfo&gt;;](../reference/apis/js-apis-Context.md#contextgethapmoduleinfo7-1) | application\UIAbilityContext.d.ts | [currentHapModuleInfo:&nbsp;HapModuleInfo;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性) |
| [getAppVersionInfo(callback:AsyncCallback&lt;AppVersionInfo&gt;):void;](../reference/apis/js-apis-Context.md#contextgetappversioninfo7)<br/>[getAppVersionInfo():Promise&lt;AppVersionInfo&gt;;](../reference/apis/js-apis-Context.md#contextgetappversioninfo7-1) | bundle\bundleInfo.d.ts | [readonly&nbsp;name:&nbsp;string;](../reference/apis/js-apis-bundleManager-bundleInfo.md#bundleinfo-1)<br/>[readonly&nbsp;versionCode:&nbsp;number;](../reference/apis/js-apis-bundleManager-bundleInfo.md#bundleinfo-1)<br/>[readonly&nbsp;versionName:&nbsp;string;](../reference/apis/js-apis-bundleManager-bundleInfo.md#bundleinfo-1) |
| [getApplicationContext():Context;](../reference/apis/js-apis-Context.md#contextgetapplicationcontext7) | application\Context.d.ts | [getApplicationContext():&nbsp;ApplicationContext;](../reference/apis/js-apis-inner-application-context.md#contextgetapplicationcontext) |
| [getAbilityInfo(callback:AsyncCallback&lt;AbilityInfo&gt;):void;](../reference/apis/js-apis-Context.md#contextgetabilityinfo7)<br/>[getAbilityInfo():Promise&lt;AbilityInfo&gt;;](../reference/apis/js-apis-Context.md#contextgetabilityinfo7-1) | application\UIAbilityContext.d.ts | [abilityInfo:&nbsp;AbilityInfo;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#属性) |
| [isUpdatingConfigurations(callback:AsyncCallback&lt;boolean&gt;):void;](../reference/apis/js-apis-Context.md#contextisupdatingconfigurations7)<br/>[isUpdatingConfigurations():Promise&lt;boolean&gt;;](../reference/apis/js-apis-Context.md#contextisupdatingconfigurations7-1) | Stage模型无对应接口 | OpenHarmony在系统环境变化时,应用不会重启,调用onConfigurationUpdated接口通知应用,该接口在FA模型是空实现接口,Stage模型不提供对应接口 |
| [printDrawnCompleted(callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-Context.md#contextprintdrawncompleted7)<br/>[printDrawnCompleted():Promise&lt;void&gt;;](../reference/apis/js-apis-Context.md#contextprintdrawncompleted7-1) | Stage模型无对应接口 | 该接口在FA模型是空实现接口,不影响应用功能,Stage模型不提供对应接口 |
# 创建DataAbility
实现DataAbility中Insert、Query、Update、Delete接口的业务内容。保证能够满足数据库存储业务的基本需求。BatchInsert与ExecuteBatch接口已经在系统中实现遍历逻辑,依赖Insert、Query、Update、Delete接口逻辑,来实现数据的批量处理。
创建DataAbility的代码示例如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
import dataAbility from '@ohos.data.dataAbility'
import dataRdb from '@ohos.data.rdb'
const TABLE_NAME = 'book'
const STORE_CONFIG = { name: 'book.db' }
const SQL_CREATE_TABLE = 'CREATE TABLE IF NOT EXISTS book(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, introduction TEXT NOT NULL)'
let rdbStore: dataRdb.RdbStore = undefined
export default {
onInitialized(abilityInfo) {
console.info('DataAbility onInitialized, abilityInfo:' + abilityInfo.bundleName)
let context = featureAbility.getContext()
dataRdb.getRdbStore(context, STORE_CONFIG, 1, (err, store) => {
console.info('DataAbility getRdbStore callback')
store.executeSql(SQL_CREATE_TABLE, [])
rdbStore = store
});
},
insert(uri, valueBucket, callback) {
console.info('DataAbility insert start')
rdbStore.insert(TABLE_NAME, valueBucket, callback)
},
batchInsert(uri, valueBuckets, callback) {
console.info('DataAbility batch insert start')
for (let i = 0;i < valueBuckets.length; i++) {
console.info('DataAbility batch insert i=' + i)
if (i < valueBuckets.length - 1) {
rdbStore.insert(TABLE_NAME, valueBuckets[i], (err: any, num: number) => {
console.info('DataAbility batch insert ret=' + num)
})
} else {
rdbStore.insert(TABLE_NAME, valueBuckets[i], callback)
}
}
},
query(uri, columns, predicates, callback) {
console.info('DataAbility query start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.query(rdbPredicates, columns, callback)
},
update(uri, valueBucket, predicates, callback) {
console.info('DataAbilityupdate start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.update(valueBucket, rdbPredicates, callback)
},
delete(uri, predicates, callback) {
console.info('DataAbilitydelete start')
let rdbPredicates = dataAbility.createRdbPredicates(TABLE_NAME, predicates)
rdbStore.delete(rdbPredicates, callback)
}
};
```
# 创建PageAbility
开发者需要重写app.js/app.ets中的生命周期回调函数,开发者通过DevEco Studio开发平台创建PageAbility时,DevEco Studio会在app.js/app.ets中默认生成onCreate()和onDestroy()方法,其他方法需要开发者自行实现。接口说明参见前述章节,创建PageAbility示例如下:
```ts
export default {
onCreate() {
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
onShow() {
console.info('Application onShow')
},
onHide() {
console.info('Application onHide')
},
onActive() {
console.info('Application onActive')
},
onInactive() {
console.info('Application onInactive')
},
onNewWant() {
console.info('Application onNewWant')
},
}
```
PageAbility创建成功后,其abilities相关的配置项在config.json中体现,一个名字为MainAbility的config.json配置文件示例如下:
```json
{
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"orientation": "unspecified",
"visible": true,
"srcPath": "MainAbility",
"name": ".MainAbility",
"srcLanguage": "ets",
"icon": "$media:icon",
"description": "$string:MainAbility_desc",
"formsEnabled": false,
"label": "$string:MainAbility_label",
"type": "page",
"launchType": "singleton"
}
]
}
```
FA模型中,可以通过featureAbility的getContext接口获取应用上下文,进而使用上下文提供的能力。
**表1** featureAbility接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| getContext() | 获取应用上下文。 |
通过getContext获取应用上下文并获取分布式目录的示例如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
import fileIo from '@ohos.fileio'
(async () => {
let dir: string
try {
console.info('Begin to getOrCreateDistributedDir')
dir = await featureAbility.getContext().getOrCreateDistributedDir()
console.info('distribute dir is ' + dir)
} catch (error) {
console.error('getOrCreateDistributedDir failed with ' + error)
}
let fd: number;
let path = dir + "/a.txt";
fd = fileIo.openSync(path, 0o2 | 0o100, 0o666);
fileIo.close(fd);
})()
```
# 创建ServiceAbility
1. 创建ServiceAbility。
重写ServiceAbility的生命周期方法,添加其他Ability请求与ServiceAbility交互时的处理方法。
```ts
import rpc from "@ohos.rpc"
class FirstServiceAbilityStub extends rpc.RemoteObject {
constructor(des: any) {
if (typeof des === 'string') {
super(des)
} else {
return
}
}
}
export default {
onStart() {
console.info('ServiceAbility onStart')
},
onStop() {
console.info('ServiceAbility onStop')
},
onCommand(want, startId) {
console.info('ServiceAbility onCommand')
},
onConnect(want) {
console.info('ServiceAbility onConnect' + want)
return new FirstServiceAbilityStub('test')
},
onDisconnect(want) {
console.info('ServiceAbility onDisconnect' + want)
}
}
```
2. 注册ServiceAbility。
ServiceAbility需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。"visible"属性表示ServiceAbility是否可以被其他应用调用,true表示可以被其他应用调用,false表示不能被其他应用调用(仅应用内可以调用)。若ServiceAbility需要被其他应用调用,注册ServiceAbility时需要设置"visible"为true,同时需要设置支持关联启动。ServiceAbility的启动规则详见[组件启动规则](component-startup-rules.md)章节。
```json
{
"module": {
"abilities": [
{
"name": ".ServiceAbility",
"srcLanguage": "ets",
"srcPath": "ServiceAbility",
"icon": "$media:icon",
"description": "hap sample empty service",
"type": "service",
"visible": true
}
]
}
}
```
# 应用间使用Want分享数据
在应用使用场景中,用户经常需要将一个应用内的数据(如文字、图片等)分享至另一个应用内继续操作。下面以PDF文件分享为例,介绍应用间使用Want分享数据的方法。
## 前提条件
1. 数据分享涉及2个UIAbility组件(分享方和被分享方)和1个系统部件(应用选择框)。当分享方通过startAbility接口发起数据分享后,将拉起应用选择框。其将**隐式匹配并展示**所有支持接受分享数据类型的应用,由用户主动选取,并由系统拉起点击应用完成数据的分享。
2. 在本章节中,将继续以按钮形式来触发分享,实际开发场景中并不局限于此,此章节着重讲解分享时Want的配置。
3. 本章节涉及的action:
- ACTION_SELECT (ohos.want.action.select):指示显示应用程序选择框的操作。用于拉起应用选择框。
- ACTION_SEND_DATA (ohos.want.action.sendData):指示发送单个数据记录的操作。用于传递数据至分享方。
## 开发步骤
- 分享方
1. Stage模型下经常会遇到需要分享文件的场景,在这种场景下我们需要使用[文件描述符(FD)](../reference/apis/js-apis-fileio.md#fileioopensync)来传递文件。此示例中,默认已获取分享文件的路径。
```ts
import fileIO from '@ohos.fileio';
// let path = ...
// file open where path is a variable contains the file path.
let fileFd = fileIO.openSync(path, 0o102, 0o666);
```
2. 在前提条件中介绍了分享的流程。分享方需先拉起应用选择框,并将数据分享给应用选择框,并由应用选择框代理传递至被分享方,完成分享。因此分享方的Want需使用2层嵌套,在第1层中使用隐式Want并配合“ohos.want.action.select”action拉起应用选择框,并在自定义字段parameters内声明一个完整的want作为第2层,其中声明传递给被分享方的数据。
```ts
import wantConstant from '@ohos.app.ability.wantConstant';
// let path = ...
// let fileFd = ...
// let fileSize = ...
let want = {
// This action is used to implicitly match the application selctor.
action: wantConstant.Action.ACTION_SELECT,
// This is the custom parameter in the first layer of want
// which is intended to add info to application selector.
parameters: {
// The MIME type of pdf
"ability.picker.type": "application/pdf",
"ability.picker.fileNames": [path],
"ability.picker.fileSizes": [fileSize],
// This a nested want which will be directly send to the user selected application.
"ability.want.params.INTENT": {
"action": "ohos.want.action.sendData",
"type": "application/pdf",
"parameters": {
"keyFd": {"type": "FD", "value": fileFd}
}
}
}
}
```
以上代码中使用Want自定义字段paramters,其中第一层paramters中的“ability.picker.\*”字段用于传递展示信息给应用选择器,具体字段表示为:
- "ability.picker.type":应用选择器根据该字段渲染相应的文件类型图标。
- "ability.picker.fileNames":应用选择器根据该字段展示文件名。
- "ability.picker.fileSizes":应用选择器根据该字段展示文件大小。以字节为单位。
- "ability.picker.fileNames"与"ability.picker.fileSizes"为数组,其有一一对应关系。
例如:当"ability.picker.type"为“application/pdf”,"ability.picker.fileNames"为“["接口文档.pdf"]”,"ability.picker.fileSizes"为“[350 \* 1024]”时,应用选择器将以下形式展示。![stage-want2](figures/stage-want2.png)
示例代码中“ability.want.params.INTENT”字段是一个嵌套Want,内部所含action、type等字段将由应用选择器进行隐式匹配,具体隐式匹配规则可参考[隐式Want匹配原理详解](explicit-implicit-want-mappings.md#隐式want匹配原理详解)。当用户选择具体应用后,“ability.want.params.INTENT”字段的嵌套Want将传递至所选应用。
- 被分享方:
1. 上文中提到,应用选择器通过“ability.want.params.INTENT”字段进行隐式匹配。因此被分享方Ability配置文件内(stage模型下的module.json5)skills字段需配置如下。
```ts
"skills": [
{
"entities": [
// ...
],
"actions": [
"ohos.want.action.sendData"
// ...
],
"uris": [
{
"type": "application/pdf"
},
// ...
]
},
]
```
其中"actions"字段和“uris”内“type”字段分别与“ability.want.params.INTENT”内“action”,“type”字段匹配。
注意:当前文件传递不支持uri方式传递,仅支持FD方式,但隐式匹配中,Want内的“type”字段需与被分享方配置文件skills内“uris”字段下的“type”字段匹配,因此skills内的“uris”字段建议只声明“type”字段,增加“host”,“port”等字段在上述示例中将匹配失败。因为应用选择框通过“ability.want.params.INTENT”发起隐式匹配,所以在“ability.want.params.INTENT”字段内增加uri字段,且与skills内的“uris”字段匹配时,仍可匹配成功且传递额外数据。
2. 应用选择器拉起被分享方后,系统将调用其“onCreate”接口,并传入“ability.want.params.INTENT”至其入参want内。
```ts
onCreate(want, launchParam) {
// note when keyFd is undefined, app crash will happen.
if (want["parameters"]["keyFd"] !== undefined) {
// receive file descriptor
let fd = want["parameters"]["keyFd"].value;
// ...
}
}
```
# DataAbility组件配置
## URI介绍
DataAbility的提供方和使用方都通过URI(Uniform Resource Identifier)来标识一个具体的数据,例如数据库中的某个表或磁盘上的某个文件。OpenHarmony的URI仍基于URI通用标准,格式如下:
![fa-dataability-uri](figures/fa-dataability-uri.png)
- scheme:协议方案名,固定为"dataability",代表Data Ability所使用的协议类型。
- authority:设备ID。如果为跨设备场景,则为目标设备的ID;如果为本地设备场景,则不需要填写。
- path:资源的路径信息,代表特定资源的位置信息。
- query:查询参数。
- fragment:可以用于指示要访问的子资源。
URI示例:
- 跨设备场景:dataability://_device_id_/_com.domainname.dataability.persondata_/_person_/_10_
- 本地设备:dataability:///_com.domainname.dataability.persondata_/_person_/_1_
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 本地设备的"device_id"字段为空,因此在"dataability:"后面有三个"/"。
## 部分配置项介绍
与PageAbility类似,DataAbility的相关配置在config.json配置文件的"module"对象的"abilities"对象中,与PageAbility的区别在于"type"属性及"uri"属性。
**表1** DataAbility的部分配置项说明
| Json重要字段 | 备注说明 |
| -------- | -------- |
| "name" | Ability名称。 |
| "type" | UIAbility类型,DataAbility的类型为"data"。 |
| "uri" | 通信使用的URI。 |
| "visible" | 对其他应用是否可见,设置为true时,DataAbility才能与其他应用进行通信传输数据。 |
config.json配置样例
```json
"abilities": [{
"srcPath": "DataAbility",
"name": ".DataAbility",
"icon": "$media:icon",
"srcLanguage": "ets",
"description": "$string:description_dataability",
"type": "data",
"visible": true,
"uri": "dataability://ohos.samples.etsdataability.DataAbility"
}]
```
DataAbility支持的配置项及详细说明详见[module对象内部结构](../quick-start/module-structure.md)
# DataAbility的生命周期
应用开发者可以根据业务场景重写生命周期相关接口。DataAbility生命周期接口说明见下表。
**表1** DataAbility相关生命周期API功能介绍
| 接口名 | 描述 |
| -------- | -------- |
| onInitialized?(info:&nbsp;AbilityInfo):&nbsp;void | 在Ability初始化调用,通过此回调方法执行RDB等初始化操作。 |
| update?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 更新数据库中的数据。 |
| query?(uri:&nbsp;string,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void | 查询数据库中的数据。 |
| delete?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 删除一条或多条数据。 |
| normalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | 对URI进行规范化。一个规范化的URI可以支持跨设备使用、持久化、备份和还原等,当上下文改变时仍然可以引用到相同的数据项。 |
| batchInsert?(uri:&nbsp;string,&nbsp;valueBuckets:&nbsp;Array&lt;rdb.ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 向数据库中插入多条数据。 |
| denormalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | 将一个由normalizeUri生产的规范化URI转换成非规范化的URI。 |
| insert?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 向数据中插入一条数据。 |
| openFile?(uri:&nbsp;string,&nbsp;mode:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 打开一个文件。 |
| getFileTypes?(uri:&nbsp;string,&nbsp;mimeTypeFilter:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;string&gt;&gt;):&nbsp;void | 获取文件的MIME类型。 |
| getType?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | 获取URI指定数据相匹配的MIME类型。 |
| executeBatch?(ops:&nbsp;Array&lt;DataAbilityOperation&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;DataAbilityResult&gt;&gt;):&nbsp;void | 批量操作数据库中的数据。 |
| call?(method:&nbsp;string,&nbsp;arg:&nbsp;string,&nbsp;extras:&nbsp;PacMap,&nbsp;callback:&nbsp;AsyncCallback&lt;PacMap&gt;):&nbsp;void | 自定义方法。 |
# DataAbility组件概述
DataAbility,即"使用Data模板的Ability",主要用于对外部提供统一的数据访问抽象,不提供用户交互界面。DataAbility可由PageAbility、ServiceAbility或其他应用启动,即使用户切换到其他应用,DataAbility仍将在后台继续运行。
使用DataAbility有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。DataAbility既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
数据的存放形式多样,可以是数据库,也可以是磁盘上的文件。DataAbility对外提供对数据的增、删、改、查,以及打开文件等接口,这些接口的具体实现由开发者提供。
# DataAbility权限控制
DataAbility提供数据服务,并不是所有的Ability都有权限读写它,DataAbility有一套权限控制机制来保证数据安全。分为静态权限控制和动态权限控制两部分。
## 静态权限控制
DataAbility作为服务端,在被拉起的时候,会根据config.json里面配置的权限来进行校验,有"readPermission"、"writePermission"和"Permission"三个配置项,可以不配或者为空。示例如下:
```json
"abilities": [{
"srcPath": "DataAbility",
"name": ".DataAbility",
"icon": "$media:icon",
"srcLanguage": "ets",
"description": "$string:description_dataability",
"type": "data",
"visible": true,
"uri": "dataability://ohos.samples.etsdataability.DataAbility",
"readPermission":"ohos.permission.READ_CONTACTS",
"writePermission":"ohos.permission.WRITE_CONTACTS"
}]
```
客户端在拉起DataAbility的时候,需要校验客户端是否有权限拉起该DataAbility。客户端的权限配置在config.json配置文件的"module"对象的"reqPermissions"对象中,示例如下:
```json
{
"module": {
"reqPermissions":{
{
"name":"ohos.permission.READ_CONTACTS"
},
{
"name":"ohos.permission.WRITE_CONTACTS"
}
}
}
}
```
## 动态权限控制
静态权限校验只能控制某个DataAbility是否能被另一个Ability或应用拉起,无法精确校验每个读写接口的权限,因为拉起DataAbility的时候,还不知道应用是否需要读写它的数据。
动态权限控制是校验每个数据操作的接口是否有对应的权限。客户端调用数据操作接口所需的权限如下表所示。
**表1** 接口对应的读写权限配置
| 需要配置读权限的接口 | 需要配置写权限的接口 | 据实际操作配置读写权限的接口 |
| -------- | -------- | -------- |
| query、normalizeUri、denormalizeUri、openfile(传入mode有'r') | insert、batchInsert、delete、update、openfile(传入mode有'w') | executeBatch |
对于需要配置读权限的接口,服务端需要配置readPermission,客户端必须申请相应的读权限才能调用相关的接口。
对于需要配置写权限的接口,服务端需要配置writePermission,客户端必须申请相应的写权限才能调用相关的接口。
# DataAbility切换
FA模型中的DataAbility对应Stage模型中的DataShareExtensionAbility。
与ServiceExtensionAbility类似,Stage模型下的DataShareExtensionAbility为系统API,只有系统应用才可以创建。因此,FA模型的DataAbility的切换,对于系统应用和三方应用策略有所不同。下面分别介绍这两种场景。
## 系统应用DataAbility切换至DataShareExtensionAbility
迁移步骤和PageAbility基本一致。
1. 在Stage应用中创建DataShareExtensionAbility。
2. 将FA应用中DataAbility的业务代码迁移到新创建的DataShareExtensionAbility中。
DataAbility和DataShareExtensionAbility生命周期对比见下表。
| FA的DataAbility | Stage的DataShareExtensionAbility | 对比描述 |
| -------- | -------- | -------- |
| onInitialized?(info:&nbsp;AbilityInfo):&nbsp;void | onCreate?(want:&nbsp;Want,&nbsp;callback:<br/>AsyncCallback&lt;void&gt;):&nbsp;void | 两者调用时机一致,函数名即入参都不一样,Stage模型下增加了入参want以便开发者在创建时获取参数。 |
| update?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | update?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;value:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 两者意义和调用时机一致,参数顺序和参数类型略有不同,需要简单改造。 |
| query?(uri:&nbsp;string,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void | query?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Object&gt;):&nbsp;void; | 两者意义和调用时机一致,参数顺序和参数类型略有不同,需要简单改造。 |
| delete?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | delete?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;): | 两者意义和调用时机一致,参数类型略有不同,需要简单改造。 |
| normalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | normalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
| batchInsert?(uri:&nbsp;string,&nbsp;valueBuckets:&nbsp;Array&lt;rdb.ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | batchInsert?(uri:&nbsp;string,&nbsp;values:&nbsp;Array&lt;ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 两者意义和调用时机一致,参数类型略有不同,需要简单改造。 |
| denormalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | denormalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
| insert?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | insert?(uri:&nbsp;string,&nbsp;value:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | 两者意义和调用时机一致,参数类型略有不同,需要简单改造。 |
| openFile?(uri:&nbsp;string,&nbsp;mode:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void | NA | Stage模型不支持uri跨进程访问,建议通过[want携带FD和文件信息](../application-models/data-share-via-want.md)进行跨进程文件访问 |
| getFileTypes?(uri:&nbsp;string,&nbsp;mimeTypeFilter:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;string&gt;&gt;):&nbsp;void | NA | Stage模型不支持uri跨进程访问,建议通过[want携带FD和文件信息](../application-models/data-share-via-want.md)进行跨进程文件访问 |
| getType?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void | NA | Stage模型不支持uri跨进程访问,建议通过[want携带FD和文件信息](../application-models/data-share-via-want.md)进行跨进程文件访问 |
| executeBatch?(ops:&nbsp;Array&lt;DataAbilityOperation&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;DataAbilityResult&gt;&gt;):&nbsp;void | NA | DataShareExtensonAbility不提供该接口,开发者需根据业务功能重新实现。 |
| call?(method:&nbsp;string,&nbsp;arg:&nbsp;string,&nbsp;extras:&nbsp;PacMap,&nbsp;callback:&nbsp;AsyncCallback&lt;PacMap&gt;):&nbsp;void | NA | DataShareExtensonAbility不提供该接口,开发者需根据业务功能重新实现。 |
## 三方应用DataAbility改造为公共模块
Stage模型三方应用不能对其他三方提供数据,应用需要根据具体业务选择切换方案。
| DataAbility业务类型 | 切换DataShareExtension策略 |
| -------- | -------- |
| 对三方提供数据 | 需根据业务场景匹配到系统对应的场景化[ExtensionAbility](../reference/apis/js-apis-Bundle.md#extensionabilitytype9)。 |
| 应用内使用的数据 | 对应用内其他组件提供数据,建议提取公共模块。 |
# DataAbilityHelper接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [openFile(uri:&nbsp;string,&nbsp;mode:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperopenfile)<br/>[openFile(uri:&nbsp;string,&nbsp;mode:&nbsp;string):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperopenfile-1) | \@ohos.data.fileAccess.d.ts | [openFile(uri:&nbsp;string,&nbsp;flags:&nbsp;OPENFLAGS)&nbsp;:&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-fileAccess.md#fileaccesshelperopenfile)<br/>[openFile(uri:&nbsp;string,&nbsp;flags:&nbsp;OPENFLAGS,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;)&nbsp;:&nbsp;void;](../reference/apis/js-apis-fileAccess.md#fileaccesshelperopenfile) |
| [on(type:&nbsp;'dataChange',&nbsp;uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperon) | \@ohos.data.dataShare.d.ts | [on(type:&nbsp;'dataChange',&nbsp;uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#ondatachange) |
| [off(type:&nbsp;'dataChange',&nbsp;uri:&nbsp;string,&nbsp;callback?:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperoff) | \@ohos.data.dataShare.d.ts | [off(type:&nbsp;'dataChange',&nbsp;uri:&nbsp;string,&nbsp;callback?:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#offdatachange) |
| [getType(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpergettype)<br/>[getType(uri:&nbsp;string):&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpergettype-1) | Stage模型无对应接口 | Stage模型不支持uri跨进程访问,建议通过[want携带fd和文件信息](data-share-via-want.md#开发步骤)进行跨进程文件访问 |
| [getFileTypes(uri:&nbsp;string,&nbsp;mimeTypeFilter:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;string&gt;&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpergetfiletypes)<br/>[getFileTypes(uri:&nbsp;string,&nbsp;mimeTypeFilter:&nbsp;string):&nbsp;Promise&lt;Array&lt;string&gt;&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpergetfiletypes-1) | Stage模型无对应接口 | Stage模型不支持uri跨进程访问,建议通过[want携带fd和文件信息](data-share-via-want.md#开发步骤)进行跨进程文件访问 |
| [normalizeUri(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpernormalizeuri)<br/>[normalizeUri(uri:&nbsp;string):&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpernormalizeuri-1) | \@ohos.data.dataShare.d.ts | [normalizeUri(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#normalizeuri)<br/>[normalizeUri(uri:&nbsp;string):&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-data-dataShare.md#normalizeuri-1) |
| [denormalizeUri(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperdenormalizeuri)<br/>[denormalizeUri(uri:&nbsp;string):&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperdenormalizeuri-1) | \@ohos.data.dataShare.d.ts | [denormalizeUri(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#denormalizeuri)<br/>[denormalizeUri(uri:&nbsp;string):&nbsp;Promise&lt;string&gt;;](../reference/apis/js-apis-data-dataShare.md#denormalizeuri-1) |
| [notifyChange(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpernotifychange)<br/>[notifyChange(uri:&nbsp;string):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpernotifychange-1) | \@ohos.data.dataShare.d.ts | [notifyChange(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#notifychange)<br/>[notifyChange(uri:&nbsp;string):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-data-dataShare.md#notifychange-1) |
| [insert(uri:&nbsp;string,&nbsp;valuesBucket:&nbsp;rdb.ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperinsert)<br/>[insert(uri:&nbsp;string,&nbsp;valuesBucket:&nbsp;rdb.ValuesBucket):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperinsert-1) | \@ohos.data.dataShare.d.ts | [insert(uri:&nbsp;string,&nbsp;value:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#insert)<br/>[insert(uri:&nbsp;string,&nbsp;value:&nbsp;ValuesBucket):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-data-dataShare.md#insert-1) |
| [batchInsert(uri:&nbsp;string,&nbsp;valuesBuckets:&nbsp;Array&lt;rdb.ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperbatchinsert)<br/>[batchInsert(uri:&nbsp;string,&nbsp;valuesBuckets:&nbsp;Array&lt;rdb.ValuesBucket&gt;):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperbatchinsert-1) | \@ohos.data.dataShare.d.ts | [batchInsert(uri:&nbsp;string,&nbsp;values:&nbsp;Array&lt;ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#batchinsert)<br/>[batchInsert(uri:&nbsp;string,&nbsp;values:&nbsp;Array&lt;ValuesBucket&gt;):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-data-dataShare.md#batchinsert-1) |
| [delete(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperdelete)<br/>[delete(uri:&nbsp;string,&nbsp;predicates?:&nbsp;dataAbility.DataAbilityPredicates):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperdelete-1)<br/>[delete(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperdelete) | \@ohos.data.dataShare.d.ts | [delete(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#delete)<br/>[delete(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-data-dataShare.md#delete-1) |
| [update(uri:&nbsp;string,&nbsp;valuesBucket:&nbsp;rdb.ValuesBucket,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperupdate)<br/>[update(uri:&nbsp;string,&nbsp;valuesBucket:&nbsp;rdb.ValuesBucket,&nbsp;predicates?:&nbsp;dataAbility.DataAbilityPredicates):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperupdate-1)<br/>[update(uri:&nbsp;string,&nbsp;valuesBucket:&nbsp;rdb.ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperupdate) | \@ohos.data.dataShare.d.ts | [update(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;value:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#update)<br/>[update(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;value:&nbsp;ValuesBucket):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-data-dataShare.md#update-1) |
| [query(uri:&nbsp;string,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperquery)<br/>[query(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperquery)<br/>[query(uri:&nbsp;string,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperquery)<br/>[query(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperquery)<br/>[query(uri:&nbsp;string,&nbsp;columns?:&nbsp;Array&lt;string&gt;,&nbsp;predicates?:&nbsp;dataAbility.DataAbilityPredicates):&nbsp;Promise&lt;ResultSet&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperquery-1) | \@ohos.data.dataShare.d.ts | [query(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;DataShareResultSet&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#query)<br/>[query(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;columns:&nbsp;Array&lt;string&gt;):&nbsp;Promise&lt;DataShareResultSet&gt;;](../reference/apis/js-apis-data-dataShare.md#query-1) |
| [call(uri:&nbsp;string,&nbsp;method:&nbsp;string,&nbsp;arg:&nbsp;string,&nbsp;extras:&nbsp;PacMap,&nbsp;callback:&nbsp;AsyncCallback&lt;PacMap&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpercall-1)<br/>[call(uri:&nbsp;string,&nbsp;method:&nbsp;string,&nbsp;arg:&nbsp;string,&nbsp;extras:&nbsp;PacMap):&nbsp;Promise&lt;PacMap&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelpercall) | Stage模型无对应接口 | 暂时未提供对应接口 |
| [executeBatch(uri:&nbsp;string,&nbsp;operations:&nbsp;Array&lt;DataAbilityOperation&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;DataAbilityResult&gt;&gt;):&nbsp;void;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperexecutebatch)<br/>[executeBatch(uri:&nbsp;string,&nbsp;operations:&nbsp;Array&lt;DataAbilityOperation&gt;):&nbsp;Promise&lt;Array&lt;DataAbilityResult&gt;&gt;;](../reference/apis/js-apis-dataAbilityHelper.md#dataabilityhelperexecutebatch-1) | Stage模型无对应接口 | 暂时未提供对应接口 |
# DataShareExtensionAbility(仅对系统应用开放)
DataShareExtensionAbility提供了数据分享的能力,系统应用可以实现一个DataShareExtensionAbility,也可以访问系统中已有的DataShareExtensionAbility,针对三方应用仅开放访问系统中已有DataShareExtensionAbility的能力,详细介绍请参见[数据共享开发指导](../database/database-datashare-guidelines.md)
# 显式Want与隐式Want匹配规则
在启动目标Ability时,会通过显式Want和隐式Want进行目标Ability的匹配,这里说的匹配规则就是调用方Want中设置的参数如何与目标Ability声明的配置文件进行匹配。
- **显式Want匹配原理**
| 名称 | 类型 | 匹配项 | 必选 | 规则 |
| -------- | -------- | -------- | -------- | -------- |
| deviceId | string | 是 | 否 | 留空将仅匹配本设备内Ability。 |
| bundleName | string | 是 | 是 | 如果指定abilityName,而不指定bundleName,则匹配失败。 |
| moduleName | string | 是 | 否 | 留空时当同一个应用内存在多个模块且模块间存在重名Ability,将默认匹配第一个。 |
| abilityName | string | 是 | 是 | 该字段必须设置表示显式匹配。 |
| uri | string | 否 | 否 | 系统匹配时将忽略该参数,但仍可作为参数传递给目标Ability。 |
| type | string | 否 | 否 | 系统匹配时将忽略该参数,但仍可作为参数传递给目标Ability。 |
| action | string | 否 | 否 | 系统匹配时将忽略该参数,但仍可作为参数传递给目标Ability。 |
| entities | Array&lt;string&gt; | 否 | 否 | 系统匹配时将忽略该参数,但仍可作为参数传递给目标Ability。 |
| flags | number | 否 | 否 | 不参与匹配,直接传递给系统处理,一般用来设置运行态信息,例如URI数据授权等。 |
| parameters | {[key:&nbsp;string]:&nbsp;any} | 否 | 否 | 不参与匹配,应用自定义数据将直接传递给目标Ability。 |
- **隐式Want匹配原理**
| 名称 | 类型 | 匹配项 | 必选 | 规则 |
| -------- | -------- | -------- | -------- | -------- |
| deviceId | string | 是 | 否 | 跨设备目前不支持隐式调用。 |
| abilityName | string | 否 | 否 | 该字段必须留空表示隐式匹配。 |
| bundleName | string | 是 | 否 | -&nbsp;声明bundleName时,隐式搜索将仅限于对应应用包内。<br/>-&nbsp;声明bundleName与moduleName时,隐式搜索将仅限于对应应用的对应Module内。<br/>-&nbsp;单独声明moduleName时,该字段无效。<br/>-&nbsp;同时声明bundleName与moduleName时,隐式搜索将仅限于对应应用包内的对应模块内。<br/>这些字段将用来隐式匹配,具体规则可参考[隐式Want匹配原理详解](#隐式want匹配原理详解)。 |
| moduleName | string | 是 | 否 |
| uri | string | 是 | 否 |
| type | string | 是 | 否 |
| action | string | 是 | 否 |
| entities | Array&lt;string&gt; | 是 | 否 |
| flags | number | 否 | 否 | 不参与匹配,直接传递给系统处理,一般用来设置运行态信息,例如URI数据授权等。 |
| parameters | {[key:&nbsp;string]:&nbsp;any} | 否 | 否 | 不参与匹配,应用自定义数据将直接传递给目标Ability。 |
## 隐式Want匹配原理详解
从隐式Want的定义,可得知:
- 调用方传入的want参数,表明调用方需要执行的操作,并提供相关数据以及其他应用类型限制。
- 待匹配Ability的skills配置,声明其具备的能力([module.json5配置文件](../quick-start/module-configuration-file.md)中的[skills标签](../quick-start/module-configuration-file.md#skills标签)参数)。
系统将调用方传入的want参数(包含action、entities、uri和type属性)与已安装待匹配的应用Ability的skills配置(包含actions、entities、uris和type属性)依次进行匹配。当四个属性匹配均通过,则此应用才会被应用选择器展示给用户进行选择。
### want参数的action匹配规则
将调用方传入的want参数的action与待匹配Ability的skills配置中的actions进行匹配。
- 调用方传入的want参数的action不为空,待匹配Ability的skills配置中的actions为空,则action匹配失败。
- 调用方传入的want参数的action为空,待匹配Ability的skills配置中的actions不为空,则action匹配成功。
- 调用方传入的want参数的action不为空,待匹配Ability的skills配置中的actions不为空且包含调用方传入的want参数的action,则action匹配成功。
- 调用方传入的want参数的action不为空,待匹配Ability的skills配置中的actions不为空且不包含调用方传入的want参数的action,则action匹配失败。
**图1** want参数的action匹配规则  
![want-action](figures/want-action.png)
### want参数的entities匹配规则
将调用方传入的want参数的entities与待匹配Ability的skills配置中的entities进行匹配。
- 调用方传入的want参数的entities为空,待匹配Ability的skills配置中的entities不为空,则entities匹配成功。
- 调用方传入的want参数的entities为空,待匹配Ability的skills配置中的entities为空,则entities匹配成功。
- 调用方传入的want参数的entities不为空,待匹配Ability的skills配置中的entities为空,则entities匹配失败。
- 调用方传入的want参数的entities不为空,待匹配Ability的skills配置中的entities不为空且包含调用方传入的want参数的entities,则entities匹配成功。
- 调用方传入的want参数的entities不为空,待匹配Ability的skills配置中的entities不为空且不完全包含调用方传入的want参数的entities,则entities匹配失败。
**图2** want参数的entities匹配规则  
![want-entities](figures/want-entities.png)
### want参数的uri和type匹配规则
调用方传入的want参数中设置uri和type参数发起组件启动请求,系统会遍历当前系统已安装的组件列表,并逐个匹配待匹配Ability的skills配置中的uris数组,如果待匹配Ability的skills配置中的uris数组中只要有一个可以匹配调用方传入的want参数中设置的uri和type即为匹配成功。
**图3** want参数中uri和type皆不为空时的匹配规则  
![want-uri-type1](figures/want-uri-type1.png)
实际应用中,uri和type共存在四种情况,下面将讲解四种情况的具体匹配规则:
- 调用方传入的want参数的uri和type都为空。
1. 如果待匹配Ability的skills配置中的uris数组为空,匹配成功。
2. 如果待匹配Ability的skills配置中的uris数组中存在uri的scheme和type都为空的元素,匹配成功。
3. 除以上两种情况,其他情况均为匹配失败。
- 调用方传入的want参数的uri不为空,type为空。
1. 如果待匹配Ability的skills配置中的uris数组为空,匹配失败。
2. 如果待匹配Ability的skills配置中的uris数组存在一条数据[uri匹配](#uri匹配规则)成功且type为空,则匹配成功,否则匹配失败。
- 调用方传入的want参数的uri为空,type不为空。
1. 如果待匹配Ability的skills配置中的uris数组为空,匹配失败。
2. 如果待匹配Ability的skills配置中的uris数组存在一条数据uri的scheme为空且[type匹配](#type匹配规则)成功,则匹配成功,否则匹配失败。
- 调用方传入的want参数的uri和type都不为空,如图3所示。
1. 如果待匹配Ability的skills配置中的uris数组为空,匹配失败。
2. 如果待匹配Ability的skills配置中的uris数组存在一条数据[uri匹配](#uri匹配规则)[type匹配](#type匹配规则)需要均匹配成功,则匹配成功,否则匹配失败。
下图为了简化描述,称want中传入的uri为w_uri,称want中传入的type为w_type, 待匹配Ability的skills配置中uris为s_uris,其中每个元素为s_uri;按自上而下顺序匹配。
**图4** want参数中uri和type的具体匹配规则  
![want-uri-type2](figures/want-uri-type2.png)
### uri匹配规则
这里为了简化描述,称want中传入的uri为w_uri,待匹配Ability的skills配置中uri为s_uri,具体的匹配规则如下:
- 如果s_uri的scheme为空,当w_uri为空时匹配成功,否则匹配失败;
- 如果s_uri的host为空,当w_uri和s_uri的scheme相同时匹配成功,否则匹配失败;
- 如果s_uri的path、pathStartWith和pathRegex都为空,当w_uri和s_uri完全相同时匹配成功,否则匹配失败;
- 如果s_uri的path不为空,当w_uri和s_uri**全路径表达式**相同时匹配成功,否则继续进行pathStartWith的匹配;
- 如果s_uri的pathStartWith不为空,当w_uri包含s_uri**前缀表达式**时匹配成功,否则继续进行pathRegex的匹配;
- 如果s_uri的pathRegex不为空,当w_uri满足s_uri**正则表达式**时匹配成功,否则匹配失败;
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 待匹配Ability的skills配置的uris中scheme、host、port、path、pathStartWith和pathRegex属性拼接,如果依次声明了path、pathStartWith和pathRegex属性时,uris将分别拼接为如下三种表达式:
>
> - **全路径表达式**:scheme://host:port/path
>
> - **前缀表达式**:scheme://host:port/pathStartWith
>
> - **正则表达式**:scheme://host:port/pathRegex
### type匹配规则
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 此小节所述的type匹配规则的适用性需建立在want参数内type不为空的基础上。当want参数内type为空时请参考[want参数的uri和type匹配规则](#want参数的uri和type匹配规则)。
这里为了简化描述,称want中传入的uri为w_type,待匹配Ability的skills数组中uris的type数据为s_type,具体的匹配规则如下:
- 如果s_type为空,则匹配失败。
- 如果s_type或者w_type为通配符"\*/\*",则匹配成功。
- 如果s_type最后一个字符为通配符'\*',如"prefixType/\*",则当w_type包含"prefixType/"时匹配成功,否则匹配失败。
- 如果w_type最后一个字符为通配符'\*',如"prefixType/\*",则当s_type包含"prefixType/"时匹配成功,否则匹配失败。
# ExtensionAbility组件概述
ExtensionAbility组件是基于特定场景(例如服务卡片、输入法等)提供的应用组件,以便满足更多的使用场景。
每一个具体场景对应一个[ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionabilitytype),各类型的ExtensionAbility组件均由相应的系统服务统一管理,例如InputMethodExtensionAbility组件由输入法管理服务统一管理。当前支持的ExtensionAbility类型有:
- [FormExtensionAbility](../reference/apis/js-apis-formextension.md):FORM类型的ExtensionAbility组件,用于提供服务卡片场景相关能力。
- [WorkSchedulerExtensionAbility](../reference/apis/js-apis-resourceschedule-workScheduler.md):WORK_SCHEDULER类型的ExtensionAbility组件,用于提供延迟任务注册、取消、查询的能力。
- [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod.md):INPUT_METHOD类型的ExtensionAbility组件,提供对输入法框架的管理,包括隐藏输入法、查询已安装的输入法列表和显示输入法选择对话框。
- [ServiceExtensionAbility](../reference/apis/js-apis-service-extension-ability.md):SERVICE类型的ExtensionAbility组件,用于提供后台服务场景相关能力。
- [AccessibilityExtensionAbility](../reference/apis/js-apis-application-AccessibilityExtensionAbility.md):ACCESSIBILITY类型的ExtensionAbility组件,用于提供辅助功能业务的能力。
- [DataShareExtensionAbility](../reference/apis/js-apis-application-DataShareExtensionAbility.md):DATA_SHARE类型的ExtensionAbility组件,用于提供支持数据共享业务的能力。
- [StaticSubscriberExtensionAbility](../reference/apis/js-apis-application-staticSubscriberExtensionAbility.md):STATIC_SUBSCRIBER类型的ExtensionAbility组件,用于提供静态广播的能力。
- [WindowExtensionAbility](../reference/apis/js-apis-application-WindowExtensionAbility.md):WINDOW类型的ExtensionAbility组件,用于提供界面组合扩展能力,允许系统应用进行跨应用的界面拉起和嵌入。
- [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md):ENTERPRISE_ADMIN类型的ExtensionAbility组件,用于提供企业管理时处理管理事件的能力,比如设备上应用安装事件、锁屏密码输入错误次数过多事件等。
## 使用指定类型的ExtensionAbility组件
所有类型的ExtensionAbility组件均不能被应用直接启动,而是由相应的系统管理服务拉起,以确保其生命周期受系统管控,使用时拉起,使用完销毁。ExtensionAbility组件的调用方无需关心目标ExtensionAbility组件的生命周期。
[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod.md)组件为例进行说明,如下图所示,调用方应用发起对InputMethodExtensionAbility组件的调用,此时将先调用输入法管理服务,由输入法管理服务拉起[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod.md)组件,返回给调用方,同时开始管理其生命周期。
**图1** 使用InputMethodExtensionAbility组件
![ExtensionAbility-start](figures/ExtensionAbility-start.png)
## 实现指定类型的ExtensionAbility组件
以实现卡片[FormExtensionAbility](../reference/apis/js-apis-formextension.md)为例进行说明。卡片框架提供了[FormExtensionAbility](../reference/apis/js-apis-formextension.md)基类,开发者通过派生此基类(如MyFormExtensionAbility),实现回调(如创建卡片的onCreate()回调、更新卡片的onUpdateForm()回调等)来实现具体卡片功能,具体见开发指导见[服务卡片FormExtensionAbility](widget-development-stage.md)
卡片FormExtensionAbility实现方不用关心使用方何时去请求添加、删除卡片,FormExtensionAbility实例及其所在的ExtensionAbility进程的整个生命周期,都是由卡片管理系统服务FormManagerService进行调度管理。
![form_extension](figures/form_extension.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 同一应用内的所有同类型的ExtensionAbility运行在同一独立进程(除ServiceExtensionAbility、DataShareExtensionAbility外),跟UIAbility组件不在同一进程,Stage模型的进程模型请参见[进程模型](process-model-stage.md)。
>
> 例如一个应用有1个UIAbility组件、1个ServiceExtensionAbility、1个DataShareExtensionAbility、2个FormExtensionAbility、1个ImeExtensionAbility。则该应用在运行时,有三个进程:
>
> - UIAbility、ServiceExtensionAbility、DataShareExtensionAbility运行在同一个进程。
>
> - FormExtensionAbility运行在一个独立进程。
>
> - ImeExtensionAbility运行在一个独立进程。
# FA模型开发概述
基于FA模型开发应用时,在应用模型部分,涉及如下开发过程。
**表1** FA模型开发流程
| 任务 | 简介 | 相关指导 |
| -------- | -------- | -------- |
| 应用组件开发 | 本章节介绍了如何使用FA模型的PageAbility、ServiceAbility、DataAbility以及服务卡片进行应用开发。 | [应用/组件级配置](application-component-configuration-fa.md)<br/>[PageAbility开发指导](pageability-overview.md)<br/>[ServiceAbility开发指导](serviceability-overview.md)<br/>[DataAbility开发指导](dataability-overview.md)<br/>[服务卡片开发指导](widget-development-fa.md)<br/>[FA模型的Context](application-context-fa.md)<br/>[信息传递载体Want](want-fa.md) |
| 应用配置文件 | 本章节介绍了FA模型的配置文件中所有的字段以及各个字段的说明。 | [app对象内部结构](../quick-start/app-structure.md)<br/>[deviceConfig内部结构](../quick-start/deviceconfig-structure.md)<br/>[module对象内部结构](../quick-start/module-structure.md) |
| 进程间通信 | 本章节介绍了FA模型的进程模型以及几种常用的进程间通信方式。 | [公共事件](common-event-fa.md)<br/>[RPC通信](rpc.md) |
| 线程间通信 | 本章节介绍了FA模型的线程模型以及几种常用的线程间通信方式。 | [线程间通信](itc-fa-overview.md) |
| 任务管理 | 本章节介绍了FA模型中任务管理的基本概念和典型场景。 | [任务管理](mission-management-fa.md) |
# FA模型与Stage模型应用组件互通综述
API 8及以前的接口基于FA模型提供;从API 9开始,OpenHarmony主推Stage模型。FA模型与Stage模型是两套不同的应用模型,他们拥有各自的组件。FA模型提供三种应用组件,分别是PageAbility、ServiceAbility和DataAbility。Stage模型提供了两种应用组件,分别是UIAbility和ExtensionAbility。
由于FA模型与Stage模型不能在应用内混合开发(见下图),当一个设备(系统)内包含两种模型应用时(下图中"场景三"),可能涉及两种模型应用组件间的互通,本文将介绍相关互通指导。
**图1** FA模型与Stage模型应用组件共存场景  
![coexistence-of-FAandStage](figures/coexistence-of-FAandStage.png)
FA模型与Stage模型应用组件互通场景及开发者关注点请参考下表。
**表1** FA模型与Stage模型应用组件互通概览
| 互通场景 | 开发者关注点 |
| -------- | -------- |
| [FA模型启动Stage模型UIAbility](start-uiability-from-fa.md) | 只需要把want中的bundleName和abilityName替换成Stage模型UIAbility的bundleName和abilityName。 |
| [FA模型绑定Stage模型ServiceExtensionAbility](bind-serviceextensionability-from-fa.md) | 只需要把want中的bundleName和abilityName替换成Stage模型ServiceExtensionAbility的bundleName和abilityName。 |
| [FA模型访问Stage模型DataShareExtensionAbility](access-datashareextensionability-from-fa.md) | 无需做代码修改。但需了解DataShareHelper和DataAbilityHelper对外接口的兼容情况。 |
| [Stage模型启动FA模型PageAbility](start-pageability-from-stage.md) | 只需要把want中的bundleName和abilityName替换成FA模型PageAbility的bundleName和abilityName。 |
| [Stage模型绑定FA模型ServiceAbility](bind-serviceability-from-stage.md) | 只需要把want中的bundleName和abilityName替换成FA模型ServiceAbility的bundleName和abilityName。 |
| Stage模型访问FA模型DataAbility | 不支持此种访问。 |
# featureAbility接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [getWant(callback:&nbsp;AsyncCallback&lt;Want&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilitygetwant)<br/>[getWant():&nbsp;Promise&lt;Want&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilitygetwant-1) | \@ohos.app.ability.UIAbility.d.ts | [launchWant:&nbsp;Want;](../reference/apis/js-apis-app-ability-uiAbility.md#属性) |
| [startAbility(parameter:&nbsp;StartAbilityParameter,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilitystartability)<br/>[startAbility(parameter:&nbsp;StartAbilityParameter):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilitystartability-1) | application\UIAbilityContext.d.ts | [startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartability)<br/>[startAbility(want:&nbsp;Want,&nbsp;options:&nbsp;StartOptions,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartability-1)<br/>[startAbility(want:&nbsp;Want,&nbsp;options?:&nbsp;StartOptions):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartability-2) |
| [getContext():&nbsp;Context;](../reference/apis/js-apis-featureAbility.md#featureabilitygetcontext) | \@ohos.app.ability.UIAbility.d.ts | [context:&nbsp;UIAbilityContext;](../reference/apis/js-apis-app-ability-uiAbility.md#属性) |
| [startAbilityForResult(parameter:&nbsp;StartAbilityParameter,&nbsp;callback:&nbsp;AsyncCallback&lt;AbilityResult&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilitystartabilityforresult7)<br/>[startAbilityForResult(parameter:&nbsp;StartAbilityParameter):&nbsp;Promise&lt;AbilityResult&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilitystartabilityforresult7-1) | application\UIAbilityContext.d.ts | [startAbilityForResult(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;AbilityResult&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilityforresult)<br/>[startAbilityForResult(want:&nbsp;Want,&nbsp;options:&nbsp;StartOptions,&nbsp;callback:&nbsp;AsyncCallback&lt;AbilityResult&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilityforresult-1)<br/>[startAbilityForResult(want:&nbsp;Want,&nbsp;options?:&nbsp;StartOptions):&nbsp;Promise&lt;AbilityResult&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilityforresult-2) |
| [terminateSelfWithResult(parameter:&nbsp;AbilityResult,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilityterminateselfwithresult7)<br/>[terminateSelfWithResult(parameter:&nbsp;AbilityResult):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilityterminateselfwithresult7-1) | application\UIAbilityContext.d.ts | [terminateSelfWithResult(parameter:&nbsp;AbilityResult,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextterminateselfwithresult)<br/>[terminateSelfWithResult(parameter:&nbsp;AbilityResult):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextterminateselfwithresult-1) |
| [terminateSelf(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilityterminateself7)<br/>[terminateSelf():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilityterminateself7-1) | application\UIAbilityContext.d.ts | [terminateSelf(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextterminateself)<br/>[terminateSelf():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextterminateself-1) |
| [acquireDataAbilityHelper(uri:&nbsp;string):&nbsp;DataAbilityHelper;](../reference/apis/js-apis-featureAbility.md#featureabilityacquiredataabilityhelper7) | \@ohos.data.dataShare.d.ts<br/>\@ohos.data.fileAccess.d.ts | [createDataShareHelper(context:&nbsp;Context,&nbsp;uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;DataShareHelper&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper)<br/>[createDataShareHelper(context:&nbsp;Context,&nbsp;uri:&nbsp;string):&nbsp;Promise&lt;DataShareHelper&gt;;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper-1)<br/>[createFileAccessHelper(context:&nbsp;Context):&nbsp;FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper-1)<br/>[createFileAccessHelper(context:&nbsp;Context,&nbsp;wants:&nbsp;Array&lt;Want&gt;):&nbsp;FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper) |
| [hasWindowFocus(callback:&nbsp;AsyncCallback&lt;boolean&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilityhaswindowfocus7)<br/>[hasWindowFocus():&nbsp;Promise&lt;boolean&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilityhaswindowfocus7-1) | \@ohos.window.d.ts | [on(eventType:&nbsp;'windowStageEvent',&nbsp;callback:&nbsp;Callback&lt;WindowStageEventType&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#onwindowstageevent9)<br/>监听[Active](../reference/apis/js-apis-window.md#windowstageeventtype9)获焦状态 |
| [connectAbility(request:&nbsp;Want,&nbsp;options:ConnectOptions&nbsp;):&nbsp;number;](../reference/apis/js-apis-featureAbility.md#featureabilityconnectability7) | application\UIAbilityContext.d.ts | [connectServiceExtensionAbility(want:&nbsp;Want,&nbsp;options:&nbsp;ConnectOptions):&nbsp;number;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability) |
| [disconnectAbility(connection:&nbsp;number,&nbsp;callback:AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilitydisconnectability7)<br/>[disconnectAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilitydisconnectability7-1) | application\UIAbilityContext.d.ts | [disconnectAbility(connection:&nbsp;number,&nbsp;callback:AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextdisconnectserviceextensionability-1)<br/>[disconnectAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextdisconnectserviceextensionability) |
| [getWindow(callback:&nbsp;AsyncCallback&lt;window.Window&gt;):&nbsp;void;](../reference/apis/js-apis-featureAbility.md#featureabilitygetwindow7)<br/>[getWindow():&nbsp;Promise&lt;window.Window&gt;;](../reference/apis/js-apis-featureAbility.md#featureabilitygetwindow7-1) | \@ohos.window.d.ts | [getLastWindow(ctx:&nbsp;BaseContext,&nbsp;callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#windowgetlastwindow9)<br/>[getLastWindow(ctx:&nbsp;BaseContext):&nbsp;Promise&lt;Window&gt;;](../reference/apis/js-apis-window.md#windowgetlastwindow9-1) |
# 跨端迁移(仅对系统应用开放)
## 功能描述
跨端迁移的主要工作是实现将应用当前任务(包括页面控件状态变量等)迁移到目标设备,能在目标设备上接续。主要功能包括:
- 支持用户自定义数据存储及恢复。
- 支持页面路由信息和页面控件状态数据的存储及恢复。
- 支持应用兼容性检测。
## 跨端迁移流程
跨端迁移流程如下图所示。
**图1** 跨端迁移流程图  
![hop-cross-device-migration](figures/hop-cross-device-migration.png)
## 约束限制
- 由于“跨端迁移任务管理”能力尚未具备,开发者当前只能开发具备跨端迁移能力的应用,但不能发起迁移。
- 跨端迁移要求在同UIAbility之间进行,也就是需要相同的bundleName、abilityName和签名。
## 最佳实践
为了获得最佳体验,使用wantParam传输的数据建议在100KB以下。
## 接口说明
跨端迁移主要接口如下。详细接口介绍请参见[API参考](../reference/apis/js-apis-application-ability.md)
**表1** 跨端迁移接口
| **接口名** | **描述** |
| -------- | -------- |
| onContinue(wantParam&nbsp;:&nbsp;{[key:&nbsp;string]:&nbsp;any}):&nbsp;OnContinueResult | 迁移发起端在该回调中保存迁移所需要的数据,同时返回是否同意迁移:<br/>-&nbsp;AGREE:表示同意。<br/>-&nbsp;REJECT:表示拒绝。<br/>-&nbsp;MISMATCH:表示版本不匹配。 |
| onCreate(want:&nbsp;Want,&nbsp;param:&nbsp;AbilityConstant.LaunchParam):&nbsp;void; | 多实例应用迁移接收端在该回调中完成数据恢复,并触发页面恢复。 |
| onNewWant(want:&nbsp;Want,&nbsp;launchParams:&nbsp;AbilityConstant.LaunchParam):&nbsp;void; | 单实例应用迁移接收端在该回调中完成数据恢复,并触发页面恢复。 |
## 开发步骤
1. 在module.json5配置数据同步权限,示例代码如下。
```json
{
"module": {
"requestPermissions":[
{
"name" : "ohos.permission.DISTRIBUTED_DATASYNC",
}
]
}
}
```
2. 在配置文件中配置跨端迁移相关标签字段。
- 配置应用支持迁移
在module.json5中配置continuable标签:true表示支持迁移,false表示不支持,默认为false。配置为false的UIAbility将被系统识别为无法迁移。
```json
{
"module": {
// ...
"abilities": [
{
// ...
"continuable": true,
}
]
}
}
```
- 根据需要配置应用启动模式类型,配置详情请参照[UIAbility组件启动模式](uiability-launch-type.md)
3. 申请数据同步权限,弹框示例代码。
```ts
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
context.requestPermissionsFromUser(permissions).then((data) => {
console.info("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
4. 在发起端UIAbility中实现[onContinue()](../reference/apis/js-apis-application-ability.md#abilityoncontinue)接口。
当应用触发迁移时,[onContinue()](../reference/apis/js-apis-application-ability.md#abilityoncontinue)接口在发起端被调用,开发者可以在该接口中保存迁移数据,实现应用兼容性检测,决定是否支持此次迁移。
- 保存迁移数据:开发者可以将要迁移的数据通过键值对的方式保存在wantParam中。
- 应用兼容性检测:开发者可以通过从wantParam中获取目标应用的版本号与本应用版本号做兼容性校验。
- 迁移决策:开发者可以通过onContinue接口的返回值决定是否支持此次迁移,返回值信息见[接口说明](#接口说明)
示例如下:
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
onContinue(wantParam : {[key: string]: any}) {
console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
let workInput = AppStorage.Get<string>('ContinueWork');
// set user input data into want params
wantParam["work"] = workInput // set user input data into want params
console.info(`onContinue input = ${wantParam["input"]}`);
return AbilityConstant.OnContinueResult.AGREE
}
```
5. 在目标端设备UIAbility中实现onCreate()/onNewWant()接口,恢复迁移数据。
- 多实例场景onCreate实现示例
- 目标端设备上,在onCreate中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION。
- 开发者可以从want中获取保存的迁移数据。
- 完成数据恢复后,开发者需要调用restoreWindowStage来触发页面恢复。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import distributedObject from '@ohos.data.distributedDataObject';
export default class EntryAbility extends UIAbility {
storage : LocalStorage;
onCreate(want, launchParam) {
console.info(`EntryAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// get user data from want params
let workInput = want.parameters.work
console.info(`work input ${workInput}`)
AppStorage.SetOrCreate<string>('ContinueWork', workInput)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
- 如果是单实例应用,则采用同样的代码实现onNewWant()接口即可。
# 多端协同(仅对系统应用开放)
## 功能描述
多端协同主要包括如下场景:
- [通过跨设备启动UIAbility和ServiceExtensionAbility组件实现多端协同(无返回数据)](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)
- [通过跨设备启动UIAbility组件实现多端协同(获取返回数据)](#通过跨设备启动uiability组件实现多端协同获取返回数据)
- [通过跨设备连接ServiceExtensionAbility组件实现多端协同](#通过跨设备连接serviceextensionability组件实现多端协同)
- [通过跨设备Call调用实现多端协同](#通过跨设备call调用实现多端协同)
## 多端协同流程
多端协同流程如下图所示。
**图1** 多端协同流程图  
![hop-multi-device-collaboration](figures/hop-multi-device-collaboration.png)
## 约束限制
- 由于“多端协同任务管理”能力尚未具备,开发者当前只能通过开发系统应用获取设备列表,不支持三方应用接入。
- 多端协同需遵循[分布式跨设备组件启动规则](component-startup-rules.md#分布式跨设备组件启动管控)
- 为了获得最佳体验,使用want传输的数据建议在100KB以下。
## 通过跨设备启动UIAbility和ServiceExtensionAbility组件实现多端协同(无返回数据)
在设备A上通过发起端应用提供的启动按钮,启动设备B上指定的UIAbility。
### 接口说明
**表1** 跨设备启动API接口功能介绍
| **接口名** | **描述** |
| -------- | -------- |
| startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 启动UIAbility和ServiceExtensionAbility(callback形式)。 |
### 开发步骤
1. 申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)
2. 申请数据同步权限,弹框示例代码。
```ts
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
context.requestPermissionsFromUser(permissions).then((data) => {
console.info("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
3. 获取目标设备的设备ID。
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
let dmClass;
function initDmClass() {
// 其中createDeviceManager接口为系统API
deviceManager.createDeviceManager('ohos.samples.demo', (err, dm) => {
if (err) {
// ...
return
}
dmClass = dm
})
}
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass !== null) {
let list = dmClass.getTrustedDeviceListSync()
if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {
console.info('EntryAbility onButtonClick getRemoteDeviceId err: list is null')
return;
}
return list[0].deviceId
} else {
console.info('EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null')
}
}
```
4. 设置目标组件参数,调用startAbility()接口,启动UIAbility或ServiceExtensionAbility。
```ts
let want = {
deviceId: getRemoteDeviceId(),
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1', // moduleName非必选
}
// context为发起端UIAbility的AbilityContext
this.context.startAbility(want).then(() => {
// ...
}).catch((err) => {
// ...
})
```
## 通过跨设备启动UIAbility组件实现多端协同(获取返回数据)
在设备A上通过应用提供的启动按钮,启动设备B上指定的UIAbility,当设备B上的UIAbility退出后,会将返回值发回设备A上的发起端应用。
### 接口说明
**表2** 跨设备启动,返回结果数据API接口功能描述
| 接口名 | 描述 |
| -------- | -------- |
| startAbilityForResult(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;AbilityResult&gt;):&nbsp;void; | 启动UIAbility并在该Ability退出的时候返回执行结果(callback形式)。 |
| terminateSelfWithResult(parameter:&nbsp;AbilityResult,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(callback形式)。 |
| terminateSelfWithResult(parameter:&nbsp;AbilityResult):&nbsp;Promise&lt;void&gt;; | 停止UIAbility,配合startAbilityForResult使用,返回给接口调用方AbilityResult信息(promise形式)。 |
### 开发步骤
1. 申请`ohos.permission.DISTRIBUTED_DATASYNC`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)
2. 申请数据同步权限,弹框示例代码。
```ts
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
context.requestPermissionsFromUser(permissions).then((data) => {
console.info("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
3. 在发起端设置目标组件参数,调用startAbilityForResult()接口启动目标端UIAbility,异步回调中的data用于接收目标端UIAbility停止自身后返回给调用方UIAbility的信息。getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)
```ts
let want = {
deviceId: getRemoteDeviceId(),
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1', // moduleName非必选
}
// context为发起端UIAbility的AbilityContext
this.context.startAbilityForResult(want).then((data) => {
// ...
}).catch((err) => {
// ...
})
```
4. 在目标端UIAbility任务完成后,调用terminateSelfWithResult()方法,将数据返回给发起端的UIAbility。
```ts
const RESULT_CODE: number = 1001;
let abilityResult = {
resultCode: RESULT_CODE,
want: {
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1',
},
}
// context为目标端UIAbility的AbilityContext
this.context.terminateSelfWithResult(abilityResult, (err) => {
// ...
});
```
5. 发起端UIAbility接收到目标端UIAbility返回的信息,对其进行处理。
```ts
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) => {
// ...
})
```
## 通过跨设备连接ServiceExtensionAbility组件实现多端协同
系统应用可以通过[connectServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextconnectserviceextensionability)跨设备连接一个服务,实现跨设备远程调用。比如:分布式游戏场景,平板作为遥控器,智慧屏作为显示器。
### 接口说明
**表3** 跨设备连接API接口功能介绍
| 接口名 | 描述 |
| -------- | -------- |
| connectServiceExtensionAbility(want:&nbsp;Want,&nbsp;options:&nbsp;ConnectOptions):&nbsp;number; | 连接ServiceExtensionAbility。 |
| disconnectServiceExtensionAbility(connection:&nbsp;number,&nbsp;callback:AsyncCallback&lt;void&gt;):&nbsp;void; | 断开连接(callback形式)。 |
| disconnectServiceExtensionAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;; | 断开连接(promise形式)。 |
### 开发步骤
1. 在module.json5配置数据同步权限,示例代码如下。
```json
{
"module": {
"requestPermissions":[
{
"name" : "ohos.permission.DISTRIBUTED_DATASYNC",
}
]
}
}
```
2. 申请数据同步权限,弹框示例代码。
```ts
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
context.requestPermissionsFromUser(permissions).then((data) => {
console.info("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
3. 如果已有后台服务,请直接进入下一步;如果没有,则[实现一个后台服务](serviceextensionability.md#实现一个后台服务仅对系统应用开放)
4. 连接一个后台服务。
- 实现IAbilityConnection接口。IAbilityConnection提供了以下方法供开发者实现:onConnect()是用来处理连接Service成功的回调,onDisconnect()是用来处理Service异常终止的回调,onFailed()是用来处理连接Service失败的回调。
- 设置目标组件参数,包括目标设备ID、包名、ability名。
- 调用connectServiceExtensionAbility发起连接。
- 连接成功,收到目标设备返回的服务句柄。
- 进行跨设备调用,获得目标端服务返回的结果。
```ts
import rpc from '@ohos.rpc';
const REQUEST_CODE = 99;
let want = {
"deviceId": getRemoteDeviceId(),
"bundleName": "com.example.myapplication",
"abilityName": "ServiceExtAbility"
};
let options = {
onConnect(elementName, remote) {
console.info('onConnect callback');
if (remote === null) {
console.info(`onConnect remote is null`);
return;
}
let option = new rpc.MessageOption();
let data = new rpc.MessageParcel();
let reply = new rpc.MessageParcel();
data.writeInt(1);
data.writeInt(99); // 开发者可发送data到目标端应用进行相应操作
// @param code 表示客户端发送的服务请求代码。
// @param data 表示客户端发送的{@link MessageParcel}对象。
// @param reply 表示远程服务发送的响应消息对象。
// @param options 指示操作是同步的还是异步的。
//
// @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
remote.sendRequest(REQUEST_CODE, data, reply, option).then((ret) => {
let msg = reply.readInt(); // 在成功连接的情况下,会收到来自目标端返回的信息(100)
console.info(`sendRequest ret:${ret} msg:${msg}`);
}).catch((error) => {
console.info('sendRequest failed');
});
},
onDisconnect(elementName) {
console.info('onDisconnect callback')
},
onFailed(code) {
console.info('onFailed callback')
}
}
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let connectionId = this.context.connectServiceExtensionAbility(want, options);
```
getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)。
5. 断开连接。调用disconnectServiceExtensionAbility()断开与后台服务的连接。
```ts
let connectionId = 1 // 在通过connectServiceExtensionAbility绑定服务时返回的Id
this.context.disconnectServiceExtensionAbility(connectionId).then((data) => {
console.info('disconnectServiceExtensionAbility success');
}).catch((error) => {
console.error('disconnectServiceExtensionAbility failed');
})
```
## 通过跨设备Call调用实现多端协同
跨设备Call调用的基本原理与设备内Call调用相同,请参见[通过Call调用实现UIAbility交互(仅对系统应用开放)](uiability-intra-device-interaction.md#通过call调用实现uiability交互仅对系统应用开放)
下面介绍跨设备Call调用实现多端协同的方法。
### 接口说明
**表4** Call API接口功能介绍
| 接口名 | 描述 |
| -------- | -------- |
| startAbilityByCall(want:&nbsp;Want):&nbsp;Promise&lt;Caller&gt;; | 启动指定UIAbility至前台或后台,同时获取其Caller通信接口,调用方可使用Caller与被启动的Ability进行通信。 |
| on(method:&nbsp;string,&nbsp;callback:&nbsp;CalleeCallBack):&nbsp;void | 通用组件Callee注册method对应的callback方法。 |
| off(method:&nbsp;string):&nbsp;void | 通用组件Callee解注册method的callback方法。 |
| call(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Sequenceable):&nbsp;Promise&lt;void&gt; | 向通用组件Callee发送约定序列化数据。 |
| callWithResult(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Sequenceable):&nbsp;Promise&lt;rpc.MessageParcel&gt; | 向通用组件Callee发送约定序列化数据,&nbsp;并将Callee返回的约定序列化数据带回。 |
| release():&nbsp;void | 释放通用组件的Caller通信接口。 |
| on(type:&nbsp;"release",&nbsp;callback:&nbsp;OnReleaseCallback):&nbsp;void | 注册通用组件通信断开监听通知。 |
### 开发步骤
1. 在module.json5配置数据同步权限,示例代码如下。
```json
{
"module": {
"requestPermissions":[
{
"name" : "ohos.permission.DISTRIBUTED_DATASYNC",
}
]
}
}
```
2. 申请数据同步权限,弹框示例代码。
```ts
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
context.requestPermissionsFromUser(permissions).then((data) => {
console.info("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
3. 创建被调用端UIAbility。
被调用端UIAbility需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。
1. 配置UIAbility的启动模式。
配置module.json5,将CalleeAbility配置为单实例"singleton"。
| Json字段 | 字段说明 |
| -------- | -------- |
| “launchType” | Ability的启动模式,设置为"singleton"类型。 |
UIAbility配置标签示例如下:
```json
"abilities":[{
"name": ".CalleeAbility",
"srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts",
"launchType": "singleton",
"description": "$string:CalleeAbility_desc",
"icon": "$media:icon",
"label": "$string:CalleeAbility_label",
"visible": true
}]
```
2. 导入UIAbility模块。
```ts
import Ability from '@ohos.app.ability.UIAbility'
```
3. 定义约定的序列化数据。
调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
```ts
export default class MySequenceable {
num: number = 0
str: string = ""
constructor(num, string) {
this.num = num
this.str = string
}
marshalling(messageParcel) {
messageParcel.writeInt(this.num)
messageParcel.writeString(this.str)
return true
}
unmarshalling(messageParcel) {
this.num = messageParcel.readInt()
this.str = messageParcel.readString()
return true
}
}
```
4. 实现Callee.on监听及Callee.off解除监听。
如下示例在Ability的onCreate注册MSG_SEND_METHOD监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回。应用开发者根据实际业务需要做相应处理。
```ts
const TAG: string = '[CalleeAbility]'
const MSG_SEND_METHOD: string = 'CallSendMsg'
function sendMsgCallback(data) {
console.info('CalleeSortFunc called')
// 获取Caller发送的序列化数据
let receivedData = new MySequenceable(0, '')
data.readSequenceable(receivedData)
console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`)
// 作相应处理
// 返回序列化数据result给Caller
return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
}
export default class CalleeAbility extends Ability {
onCreate(want, launchParam) {
try {
this.callee.on(MSG_SEND_METHOD, sendMsgCallback)
} catch (error) {
console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
}
}
onDestroy() {
try {
this.callee.off(MSG_SEND_METHOD)
} catch (error) {
console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`)
}
}
}
```
4. 获取Caller接口,访问被调用端UIAbility。
1. 导入UIAbility模块。
```ts
import Ability from '@ohos.app.ability.UIAbility'
```
2. 获取Caller通信接口。
Ability的context属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取Ability实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease监听。应用开发者根据实际业务需要做相应处理。
```ts
async onButtonGetRemoteCaller() {
var caller = undefined
var context = this.context
context.startAbilityByCall({
deviceId: getRemoteDeviceId(),
bundleName: 'com.samples.CallApplication',
abilityName: 'CalleeAbility'
}).then((data) => {
if (data != null) {
caller = data
console.info('get remote caller success')
// 注册caller的release监听
caller.onRelease((msg) => {
console.info(`remote caller onRelease is called ${msg}`)
})
console.info('remote caller register OnRelease succeed')
}
}).catch((error) => {
console.error(`get remote caller failed with ${error}`)
})
}
```
getRemoteDeviceId方法参照[通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据](#通过跨设备启动uiability和serviceextensionability组件实现多端协同无返回数据)。
5. 向被调用端UIAbility发送约定序列化数据。
1. 向被调用端发送Sequenceable数据有两种方式,一种是不带返回值,一种是获取被调用端返回的数据,method以及序列化数据需要与被调用端协商一致。如下示例调用Call接口,向Callee被调用端发送数据。
```ts
const MSG_SEND_METHOD: string = 'CallSendMsg'
async onButtonCall() {
try {
let msg = new MySequenceable(1, 'origin_Msg')
await this.caller.call(MSG_SEND_METHOD, msg)
} catch (error) {
console.info(`caller call failed with ${error}`)
}
}
```
2. 如下示例调用CallWithResult接口,向Callee被调用端发送待处理的数据originMsg,并将’CallSendMsg’方法处理完毕的数据赋值给backMsg。
```ts
const MSG_SEND_METHOD: string = 'CallSendMsg'
originMsg: string = ''
backMsg: string = ''
async onButtonCallWithResult(originMsg, backMsg) {
try {
let msg = new MySequenceable(1, originMsg)
const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
console.info('caller callWithResult succeed')
let result = new MySequenceable(0, '')
data.readSequenceable(result)
backMsg(result.str)
console.info(`caller result is [${result.num}, ${result.str}]`)
} catch (error) {
console.info(`caller callWithResult failed with ${error}`)
}
}
```
6. 释放Caller通信接口。
Caller不再使用后,应用开发者可以通过release接口释放Caller。
```ts
releaseCall() {
try {
this.caller.release()
this.caller = undefined
console.info('caller release succeed')
} catch (error) {
console.info(`caller release failed with ${error}`)
}
}
```
# 流转概述
## 场景介绍
随着全场景多设备的生活方式不断深入,用户拥有的设备越来越多,不同设备都能在适合的场景下提供良好的体验,例如手表可以提供及时的信息查看能力,电视可以带来沉浸的观影体验。但是,每个设备也有使用场景的局限,例如在电视上输入文本相对移动设备来说是非常糟糕的体验。当多个设备通过分布式操作系统能够相互感知、进而整合成一个超级终端时,设备与设备之间就可以取长补短、相互帮助,为用户提供更加自然流畅的分布式体验。
在OpenHarmony中,将跨多设备的分布式操作统称为**流转**;根据使用场景的不同,流转又分为[跨端迁移](hop-cross-device-migration.md)[多端协同](hop-multi-device-collaboration.md)两种具体场景。要实现应用跨设备流转,需使用应用组件的跨设备交互相关能力,这些能力目前仅对系统应用开放。
## 基本概念
- **流转**
在OpenHarmony中泛指跨多设备的分布式操作。流转能力打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现如邮件跨设备编辑、多设备协同健身、多屏游戏等分布式业务。流转为开发者提供更广的使用场景和更新的产品视角,强化产品优势,实现体验升级。流转按照使用场景可分为**跨端迁移****多端协同**
- **跨端迁移**
在用户使用设备的过程中,当使用情境发生变化时(例如从室内走到户外或者周围有更合适的设备等),之前使用的设备可能已经不适合继续当前的任务,此时,用户可以选择新的设备来继续当前的任务,原设备退出任务,这就是跨端迁移场景。常见的跨端迁移场景实例:在平板上播放的视频,迁移到智慧屏继续播放,从而获得更佳的观看体验;平板上的视频应用退出。在应用开发层面,跨端迁移指在A端运行的UIAbility迁移到B端上,完成迁移后, B端UIAbility继续任务,而A端UIAbility退出。
- **多端协同**
用户拥有的多个设备,可以作为一个整体,为用户提供比单设备更加高效、沉浸的体验,这就是多端协同场景。常见的多端协同场景实例:平板侧应用A做答题板,智慧屏侧应用B做直播,为用户提供更优的上网课体验。在应用开发层面,多端协同指多端上的不同UIAbility/ServiceExtensionAbility同时运行、或者交替运行实现完整的业务;或者多端上的相同UIAbility/ServiceExtensionAbility同时运行实现完整的业务。
## 流转架构
OpenHarmony流转提供了一组API库,可让用户应用程序更轻松、快捷地完成流转体验。OpenHarmony流转架构有如下优势:
- 支持远程服务调用等能力,可轻松设计业务。
- 支持多个应用同时进行流转。
- 支持不同形态设备,如平板、智慧屏、手表等。
流转架构如下图所示。
**图1** 流转架构图  
![hop-structure](figures/hop-structure.png)
- 跨端迁移任务管理:在迁移发起端,接受用户迁移的意图,提供迁移流转入口,迁移结果显示等能力。(该能力尚未构建。)
- 多端协同任务管理:在协同发起端,接受用户应用程序注册,提供协同入口、状态显示、退出流转等管理能力。(该能力尚未构建。)
- 分布式组件管理服务:提供远程服务启动、远程服务连接、远程迁移等能力,并通过不同能力组合,支撑用户应用程序完成跨端迁移或多端协同的业务体验。
- 分布式安全:提供E2E的加密通道,为用户应用程序提供安全的跨端传输机制,保证“正确的人,通过正确的设备,正确地使用数据”。
- 分布式软总线:使用基于平板、智能穿戴、智慧屏等分布式设备的统一通信基座,为设备之间的互联互通提供统一的分布式通信能力。
# 线程间通信
请参见Stage模型的"[线程间通信](thread-model-stage.md)"。
# 使用Emitter进行线程间通信
[Emitter](../reference/apis/js-apis-emitter.md)主要提供线程间发送和处理事件的能力,包括对持续订阅事件或单次订阅事件的处理、取消订阅事件、发送事件到事件队列等。
Emitter的开发步骤如下:
1. 订阅事件
```ts
import emitter from "@ohos.events.emitter";
// 定义一个eventId为1的事件
let event = {
eventId: 1
};
// 收到eventId为1的事件后执行该回调
let callback = (eventData) => {
console.info('event callback');
};
// 订阅eventId为1的事件
emitter.on(event, callback);
```
2. 发送事件
```ts
import emitter from "@ohos.events.emitter";
// 定义一个eventId为1的事件,事件优先级为Low
let event = {
eventId: 1,
priority: emitter.EventPriority.LOW
};
let eventData = {
data: {
"content": "c",
"id": 1,
"isEmpty": false,
}
};
// 发送eventId为1的事件,事件内容为eventData
emitter.emit(event, eventData);
```
# 使用Worker进行线程间通信
[Worker](../reference/apis/js-apis-worker.md)是与主线程并行的独立线程。创建Worker的线程被称为宿主线程,Worker工作的线程被称为Worker线程。创建Worker时传入的脚本文件在Worker线程中执行,通常在Worker线程中处理耗时的操作,需要注意的是,Worker中不能直接更新Page。
Worker的开发步骤如下:
1. 在工程的[模块级build-profile.json5](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-building-configuration-0000001218440654#section6887184182020)文件的buildOption属性中添加配置信息。
```ts
"buildOption": {
"sourceOption": {
"workers": [
"./src/main/ets/workers/worker.ts"
]
}
}
```
2. 根据build-profile.json5中的配置创建对应的worker.js文件。
```ts
import worker from '@ohos.worker';
let parent = worker.workerPort;
// 处理来自主线程的消息
parent.onmessage = function(message) {
console.info("onmessage: " + message)
// 发送消息到主线程
parent.postMessage("message from worker thread.")
}
```
3. 主线程中使用如下方式初始化和使用worker。
- Stage模型:
```ts
import worker from '@ohos.worker';
let wk = new worker.ThreadWorker("entry/ets/workers/worker.ts");
// 发送消息到worker线程
wk.postMessage("message from main thread.")
// 处理来自worker线程的消息
wk.onmessage = function(message) {
console.info("message from worker: " + message)
// 根据业务按需停止worker线程
wk.terminate()
}
```
- FA模型:
```ts
import worker from '@ohos.worker';
let wk = new worker.ThreadWorker("../workers/worker.js");
// 发送消息到worker线程
wk.postMessage("message from main thread.")
// 处理来自worker线程的消息
wk.onmessage = function(message) {
console.info("message from worker: " + message)
// 根据业务按需停止worker线程
wk.terminate()
}
```
**说明:**
- build-profile.json5中配置的worker.ts的相对路径都为`./src/main/ets/workers/worker.ts`时,在Stage模型下创建worker需要传入路径`entry/ets/workers/worker.ts`;在FA模型下创建worker需要传入路径`../workers/worker.js`
- 主线程与Worker线程间支持的数据类型参考[序列化支持类型](../reference/apis/js-apis-worker.md#%E5%BA%8F%E5%88%97%E5%8C%96%E6%94%AF%E6%8C%81%E7%B1%BB%E5%9E%8B)
# LifecycleApp接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| onShow?():&nbsp;void; | \@ohos.window.d.ts | [on(eventType:&nbsp;'windowStageEvent',&nbsp;callback:&nbsp;Callback&lt;WindowStageEventType&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#onwindowstageevent9)<br/>监听[FOREGROUND](../reference/apis/js-apis-window.md#windowstageeventtype9)切到前台状态 |
| onHide?():&nbsp;void; | \@ohos.window.d.ts | [on(eventType:&nbsp;'windowStageEvent',&nbsp;callback:&nbsp;Callback&lt;WindowStageEventType&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#onwindowstageevent9)<br/>监听[BACKGROUND](../reference/apis/js-apis-window.md#windowstageeventtype9)切到后台状态 |
| onDestroy?():&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onDestroy():&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityondestroy) |
| onCreate?():&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want:&nbsp;Want,&nbsp;param:&nbsp;AbilityConstant.LaunchParam):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate) |
| onWindowDisplayModeChanged?(isShownInMultiWindow:&nbsp;boolean,&nbsp;newConfig:&nbsp;resourceManager.Configuration):&nbsp;void; | Stage模型无对应接口 | 暂时未提供对应接口 |
| onStartContinuation?():&nbsp;boolean; | Stage模型无对应接口 | Stage模型上,应用无需感知迁移是否成功(由应用发起迁移请求时感知),onStartContinuation废弃 |
| onSaveData?(data:&nbsp;Object):&nbsp;boolean; | \@ohos.app.ability.UIAbility.d.ts | [onContinue(wantParam&nbsp;:&nbsp;{[key:&nbsp;string]:&nbsp;any}):&nbsp;AbilityConstant.OnContinueResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) |
| onCompleteContinuation?(result:&nbsp;number):&nbsp;void; | application\ContinueCallback.d.ts | [onContinueDone(result:&nbsp;number):&nbsp;void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) |
| onRestoreData?(data:&nbsp;Object):&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want:&nbsp;Want,&nbsp;param:&nbsp;AbilityConstant.LaunchParam):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br/>[onNewWant(want:&nbsp;Want,&nbsp;launchParams:&nbsp;AbilityConstant.LaunchParam):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant)<br/>标准实例模式Ability迁移目标端在onCreate回调中完成数据恢复,单实例应用迁移目标端在onCreate回调中完成数据恢复,回调中通过判断launchParam.launchReason可获取迁移启动的场景,从而可以从Want中获取迁移前保存的数据 |
| onRemoteTerminated?():&nbsp;void; | application\ContinueCallback.d.ts | [onContinueDone(result:&nbsp;number):&nbsp;void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) |
| onSaveAbilityState?(outState:&nbsp;PacMap):&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onSaveState(reason:&nbsp;AbilityConstant.StateType,&nbsp;wantParam&nbsp;:&nbsp;{[key:&nbsp;string]:&nbsp;any}):&nbsp;AbilityConstant.OnSaveResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonsavestate) |
| onRestoreAbilityState?(inState:&nbsp;PacMap):&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want:&nbsp;Want,&nbsp;param:&nbsp;AbilityConstant.LaunchParam):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br/>应用重启后会触发Ability的onCreate方法,通过判断launchParam.launchReason可获取自恢复的场景,从而可以从Want中获取重启前保存的数据 |
| onInactive?():&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onBackground():&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonbackground) |
| onActive?():&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onForeground():&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonforeground) |
| onNewWant?(want:&nbsp;Want):&nbsp;void; | \@ohos.app.ability.UIAbility.d.ts | [onNewWant(want:&nbsp;Want,&nbsp;launchParams:&nbsp;AbilityConstant.LaunchParam):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) |
| onMemoryLevel?(level:&nbsp;number):&nbsp;void | \@ohos.app.ability.UIAbility.d.ts | [onMemoryLevel(level:&nbsp;AbilityConstant.MemoryLevel):&nbsp;void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonmemorylevel) |
# LifecycleData接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| update?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [update?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;valueBucket:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#update) |
| query?(uri:&nbsp;string,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;ResultSet&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [query?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;columns:&nbsp;Array&lt;string&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Object&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#query) |
| delete?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataAbility.DataAbilityPredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [delete?(uri:&nbsp;string,&nbsp;predicates:&nbsp;dataSharePredicates.DataSharePredicates,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#delete) |
| normalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [normalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#normalizeuri) |
| batchInsert?(uri:&nbsp;string,&nbsp;valueBuckets:&nbsp;Array&lt;rdb.ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [batchInsert?(uri:&nbsp;string,&nbsp;valueBuckets:&nbsp;Array&lt;ValuesBucket&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#batchinsert) |
| denormalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [denormalizeUri?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#denormalizeuri) |
| insert?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;rdb.ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [insert?(uri:&nbsp;string,&nbsp;valueBucket:&nbsp;ValuesBucket,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#insert) |
| openFile?(uri:&nbsp;string,&nbsp;mode:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void; | Stage模型无对应接口 | Stage模型不支持uri跨进程访问,建议通过[want携带fd和文件信息](data-share-via-want.md#开发步骤)进行跨进程文件访问 |
| getFileTypes?(uri:&nbsp;string,&nbsp;mimeTypeFilter:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;string&gt;&gt;):&nbsp;void; | Stage模型无对应接口 | Stage模型不支持uri跨进程访问,建议通过[want携带fd和文件信息](data-share-via-want.md#开发步骤)进行跨进程文件访问 |
| onInitialized?(info:&nbsp;AbilityInfo):&nbsp;void; | \@ohos.application.DataShareExtensionAbility.d.ts | [onCreate?(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-application-DataShareExtensionAbility.md#oncreate) |
| getType?(uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;string&gt;):&nbsp;void; | Stage模型无对应接口 | Stage模型不支持uri跨进程访问,建议通过[want携带fd和文件信息](data-share-via-want.md#开发步骤)进行跨进程文件访问 |
| executeBatch?(ops:&nbsp;Array&lt;DataAbilityOperation&gt;,&nbsp;callback:&nbsp;AsyncCallback&lt;Array&lt;DataAbilityResult&gt;&gt;):&nbsp;void; | Stage模型无对应接口 | 暂时未提供对应接口 |
| call?(method:&nbsp;string,&nbsp;arg:&nbsp;string,&nbsp;extras:&nbsp;PacMap,&nbsp;callback:&nbsp;AsyncCallback&lt;PacMap&gt;):&nbsp;void; | Stage模型无对应接口 | 暂时未提供对应接口 |
# LifecycleForm接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| onCreate?(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData; | \@ohos.app.form.FormExtensionAbility.d.ts | [onAddForm(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData;](../reference/apis/js-apis-app-form-formextensionability.md#onaddform) |
| onCastToNormal?(formId:&nbsp;string):&nbsp;void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onCastToNormalForm(formId:&nbsp;string):&nbsp;void;](../reference/apis/js-apis-app-form-formextensionability.md#oncasttonormalform) |
| onUpdate?(formId:&nbsp;string):&nbsp;void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onUpdateForm(formId:&nbsp;string):&nbsp;void;](../reference/apis/js-apis-app-form-formextensionability.md#onupdateform) |
| onVisibilityChange?(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onChangeFormVisibility(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void;](../reference/apis/js-apis-app-form-formextensionability.md#onchangeformvisibility) |
| onEvent?(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onFormEvent(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void;](../reference/apis/js-apis-app-form-formextensionability.md#onformevent) |
| onDestroy?(formId:&nbsp;string):&nbsp;void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onRemoveForm(formId:&nbsp;string):&nbsp;void;](../reference/apis/js-apis-app-form-formextensionability.md#onremoveform) |
| onAcquireFormState?(want:&nbsp;Want):&nbsp;formInfo.FormState; | \@ohos.app.form.FormExtensionAbility.d.ts | [onAcquireFormState?(want:&nbsp;Want):&nbsp;formInfo.FormState;](../reference/apis/js-apis-app-form-formextensionability.md#onacquireformstate) |
| onShare?(formId:&nbsp;string):&nbsp;{[key:&nbsp;string]:&nbsp;any}; | \@ohos.app.form.FormExtensionAbility.d.ts | [onShareForm?(formId:&nbsp;string):&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;any&nbsp;};](../reference/apis/js-apis-app-form-formextensionability.md#onshareform) |
# LifecycleService接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| onStart?():&nbsp;void; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onCreate(want:&nbsp;Want):&nbsp;void;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityoncreate) |
| onCommand?(want:&nbsp;Want,&nbsp;startId:&nbsp;number):&nbsp;void; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onRequest(want:&nbsp;Want,&nbsp;startId:&nbsp;number):&nbsp;void;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonrequest) | |
| onStop?():&nbsp;void; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onDestroy():&nbsp;void;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityondestroy) | |
| onConnect?(want:&nbsp;Want):&nbsp;rpc.RemoteObject; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onConnect(want:&nbsp;Want):&nbsp;rpc.RemoteObject;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonconnect) | |
| onDisconnect?(want:&nbsp;Want):&nbsp;void; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onDisconnect(want:&nbsp;Want):&nbsp;void;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityondisconnect) | |
| onReconnect?(want:&nbsp;Want):&nbsp;void; | \@ohos.app.ability.ServiceExtensionAbility.d.ts | [onReconnect(want:&nbsp;Want):&nbsp;void;](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonreconnect) | |
# mediaLibrary接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [getMediaLibrary():&nbsp;MediaLibrary;](../reference/apis/js-apis-medialibrary.md#medialibrarygetmedialibrary) | \@ohos.multimedia.mediaLibrary.d.ts | [getMediaLibrary(context: Context): MediaLibrary;](../reference/apis/js-apis-medialibrary.md#medialibrarygetmedialibrary8) |
# 任务管理
请参见Stage模型的"[任务管理](mission-management-overview.md)"。
# 任务管理与启动模式
如前文所述,一个UIAbility实例对应一个任务。UIAbility实例个数与UIAbility配置的启动模式有关。在FA模型下,通过config.json配置文件中的“launchType”属性配置;在Stage模型下,通过[module.json5配置文件](../quick-start/module-configuration-file.md)中的“launchType”属性配置。
下面介绍了任务管理如何实现以下三种启动模式UIAbility组件的管理:
- singleton:单实例模式,应用在运行时只存在一个该UIAbility实例。
**图1** 任务与singleton模式  
![mission-and-singleton](figures/mission-and-singleton.png)
- standard:多实例模式,每次调用startAbility()方法,都会在应用进程中创建一个该Ability的实例。
**图2** 任务与standard模式  
![mission-and-standard](figures/mission-and-standard.png)
- specified:指定实例模式,由[AbilityStage](abilitystage.md)的([onAcceptWant](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant))决定是否创建新的实例。
**图3** 任务与specified模式  
![mission-and-specified](figures/mission-and-specified.png)
每个UIAbility实例都对应了一个最近任务列表中看到的Mission(任务)。
每个UIAbility实例对应的Mission都保留有该UIAbility实例的快照(Snapshot),UIAbility实例销毁后,Mission信息(包括Ability信息和任务快照)依然会保留,直到用户删除该任务。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> specified模式只在Stage模型上支持,FA模型不支持。
# 任务管理场景介绍
任务管理相关的基本概念如下:
- AbilityRecord:系统服务侧管理一个UIAbility实例的最小单元,对应一个应用侧的UIAbility组件实例。
- MissionRecord:任务管理的最小单元。一个MissionRecord中仅有一个AbilityRecord,即一个UIAbility组件实例对应一个单独的任务。
- MissionList:一个从桌面开始启动的任务列表,记录了任务之间的启动关系,上一个任务由下一个任务启动,最底部的任务由桌面启动,这里称之为任务链。
- MissionListManager:系统任务管理模块,内部维护了当前所有的任务链,与最近任务列表保持一致。
**图1** 任务管理示意图  
![mission-list-manager](figures/mission-list-manager.png)
任务的管理由系统应用(如桌面应用)负责,三方应用无法管理任务。用户通过最近任务列表进行任务的相关交互。当创建任务后,用户可以对最近任务列表进行如下操作:
- 删除一个指定的任务。
- 加锁或解锁一个指定的任务(加锁后的任务在清理所有任务时不会被清理)。
- 清理最近任务列表中的所有任务。
- 将一个指定的任务切换到前台。
一个UIAbility实例对应一个单独的任务,因此应用调用startAbility()方法启动一个UIAbility时,就是创建了一个任务。
桌面应用调用[missionManager](../reference/apis/js-apis-missionManager.md)的接口管理任务,需要申请`ohos.permission.MANAGE_MISSIONS`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)
利用missionManager进行任务管理(监听任务变化、获取任务信息、获取任务快照、清理任务、任务加锁/解锁等),示例代码如下:
```ts
import missionManager from '@ohos.app.ability.missionManager'
let listener = {
// 任务创建
onMissionCreated: function (mission) {
console.info("--------onMissionCreated-------")
},
// 任务销毁
onMissionDestroyed: function (mission) {
console.info("--------onMissionDestroyed-------")
},
// 任务快照变化
onMissionSnapshotChanged: function (mission) {
console.info("--------onMissionSnapshotChanged-------")
},
// 任务被移动到前台
onMissionMovedToFront: function (mission) {
console.info("--------onMissionMovedToFront-------")
},
// 任务图标变化
onMissionIconUpdated: function (mission, icon) {
console.info("--------onMissionIconUpdated-------")
},
// 任务名称变化
onMissionLabelUpdated: function (mission) {
console.info("--------onMissionLabelUpdated-------")
},
// 任务实例被关闭
onMissionClosed: function (mission) {
console.info("--------onMissionClosed-------")
}
};
// 1.注册任务变化通知
let listenerId = missionManager.on('mission', listener);
// 2.获取系统最近20个任务
missionManager.getMissionInfos("", 20, (error, missions) => {
console.info("getMissionInfos is called, error.code = " + error.code);
console.info("size = " + missions.length);
console.info("missions = " + JSON.stringify(missions));
});
// 3.获取单个任务的详细信息()
let missionId = 11; // 11只是示例,实际是从系统中获取的任务id,下面类似
let mission = missionManager.getMissionInfo("", missionId).catch(function (err) {
console.info(err);
});
// 4.获取任务快照
missionManager.getMissionSnapShot("", missionId, (error, snapshot) => {
console.info("getMissionSnapShot is called, error.code = " + error.code);
console.info("bundleName = " + snapshot.ability.bundleName);
})
// 5.获取低分辨任务快照
missionManager.getLowResolutionMissionSnapShot("", missionId, (error, snapshot) => {
console.info("getLowResolutionMissionSnapShot is called, error.code = " + error.code);
console.info("bundleName = " + snapshot.ability.bundleName);
})
// 6.加锁/解锁任务
missionManager.lockMission(missionId).then(() => {
console.info("lockMission is called ");
});
missionManager.unlockMission(missionId).then(() => {
console.info("unlockMission is called ");
});
// 7.把任务切到前台
missionManager.moveMissionToFront(missionId).then(() => {
console.info("moveMissionToFront is called ");
});
// 8.删除单个任务
missionManager.clearMission(missionId).then(() => {
console.info("clearMission is called ");
});
// 9.删除全部任务
missionManager.clearAllMissions().catch(function (err) {
console.info(err);
});
// 10.解注册任务变化通知
missionManager.off('mission', listenerId, (error) => {
console.info("unregisterMissionListener");
})
```
# 模型切换概述
本文介绍如何将一个FA模型开发的声明式范式应用切换到Stage模型,您需要完成如下动作:
- 工程切换:新建一个Stage模型的应用工程。
![model-switch-overview1](figures/model-switch-overview1.png)
- [配置文件切换](configuration-file-diff.md):config.json切换为app.json5和module.json5。
![model-switch-overview2](figures/model-switch-overview2.png)
- [组件切换](pageability-switch.md):PageAbility/ServiceAbility/DataAbility切换为UIAbility和ExtensionAbility(图片仅展示了FA模型的PageAbility切换成Stage模型的UIAbility:下图左侧为FA模型,app.ets为FA模型的PageAbility组件;下图右侧为Stage模型,EntryAbility.ts为Stage模型的UIAbility组件)。
![model-switch-overview3](figures/model-switch-overview3.png)
- [卡片切换](widget-switch.md):将FA模型的FormAbility切换为Stage模型的FormExtensionAbility(下图中的**Service Widget**在FA中为FormAbility,在Stage中为FormExtensionAbility)。
![model-switch-overview4](figures/model-switch-overview4.png)
![model-switch-overview5](figures/model-switch-overview5.png)
- [API切换](api-switch-overview.md):将FA模型应用中使用到的FAModelOnly接口切换为Stage模型下的推荐接口和写法。
![model-switch-overview6](figures/model-switch-overview6.png)
# module的切换
从FA模型切换到Stage模型时,开发者需要将config.json文件module标签下的配置迁移到module.json5配置文件module标签下,具体差异见下表。
**表1** FA模型module标签与Stage模型module标签差异对比
| FA标签 | 标签说明 | 对应的Stage标签 | 差异说明 |
| -------- | -------- | -------- | -------- |
| mainAbility | 服务中心图标露出的ability,常驻进程拉起时会启动mainAbility。 | mainElement | 标签名称变更,Stage模型不在使用.符号。 |
| package | 标识HAP的包结构名称,在应用内保证唯一性。 | / | Stage模型使用name来保证应用唯一性。 |
| name | 标识HAP的类名 | / | FA模型中实际未使能,Stage模型没有与之对应的标签。 |
| supportedModes | 标识应用支持的运行模式,当前只定义了驾驶模式(drive) | / | Stage模型已废弃。 |
| distro对象中的moduleName | 标识当前HAP的名称<br/>**distro对象**中的moduleName | name | 标签变更。 |
| distro对象中的moduleType | 标识当前HAP的类型,包括两种类型:entry和feature。另外,如果表示HAR包类型,请设置为har | type | 标签变更。 |
| distro对象中的installationFree | 标识当前HAP是否支持免安装特性 | installationFree | 标签变更。 |
| distro对象中的deliveryWithInstall | 标识当前HAP是否支持随应用安装 | deliveryWithInstall | 标签变更。 |
| metaData | 标识HAP的元信息 | metadata | 具体差异如[表2](#表2 FA模型metaData和Stage中metadata对比)。 |
| abilities | 标识当前模块内的所有Ability | abilities | 具体差异如[表5](#表5 FA模型和Stage中abilities差异对比)。 |
| js | 标识基于ArkUI框架开发的JS模块集合,其中的每个元素代表一个JS模块的信息 | pages | Stage模型在module标签下保留该对象中的pages,window配置与pages的下一级。 |
| shortcuts | 标识应用的快捷方式信息 | shortcut_config.json文件 | 在开发视图的resources/base/profile下面定义配置文件shortcut_config.json。 |
| reqPermissions | 标识应用运行时向系统申请的权限 | requestPermissions | 标签名称变更。 |
| colorMode | 标识应用自身的颜色模式 | / | Stage模型不支持。 |
| distroFilter | 标识应用的分发规则 | distroFilter_config.json文件 | Stage模型在开发视图的resources/base/profile下面定义配置文件distroFilter_config.json。 |
| reqCapabilities | 标识运行应用程序所需的设备能力 | / | Stage模型不支持。 |
| commonEvents | 公共事件 | common_event_config.json文件 | Stage模型在开发视图的resources/base/profile下面定义配置文件common_event_config.json。 |
| entryTheme | 此标签标识OpenHarmony内部主题的关键字 | / | Stage模型不支持。 |
### 表2 FA模型metaData和Stage中metadata对比
| FA标签 | 标签说明 | 对应的Stage模型标签 | 差异对比 |
| -------- | -------- | -------- | -------- |
| parameters | 标识调用Ability时所有调用参数的元信息 | / | Stage模型不支持。 |
| results | 标识Ability返回值的元信息 | / | Stage模型不支持。 |
| customizeData | 该标签标识父级组件的自定义元信息,Parameters和results在application不可配 | metadata | 具体差异见[表3](#表3 FA模型metaData的customizeData和Stage中metadata对比)。 |
### 表3 FA模型metaData的customizeData和Stage中metadata对比
| FA标签 | 标签说明 | 对应的Stage模型标签 | 差异对比 |
| -------- | -------- | -------- | -------- |
| name | 标识数据项的键名称。字符串最大长度为255字节 | name | 无。 |
| value | 标识数据项的值。字符串最大长度为255字节。 | value | 无。 |
| extra | 标识当前custom数据的格式,取值为表示extra的资源值。 | resource | 标签变更。具体实例见[表4](#表4 FA模型metaData和Stage中metadata示例)。 |
### 表4 FA模型metaData和Stage中metadata示例
| FA模型示例 | Stage模型示例 |
| -------- | -------- |
| “meteData”: {<br/> "customizeDate": [{<br/> "name": "label",<br/> "value": "string",<br/> "extra": "$string:label",<br/> }]<br/>} | “meteData”: [{<br/> "name": "label",<br/> "value": "string",<br/> "resource": "$string:label",<br/>}] |
### 表5 FA模型和Stage中abilities差异对比
| FA模型的abilities对象标签 | 描述 | Stage模型中abilities对象标签 | 差异对比 |
| -------- | -------- | -------- | -------- |
| process | 运行应用程序或Ability的进程名称 | / | Stage模型不支持abilities中配置,在module标签下配置process |
| uri | 标识Ability的统一资源标识符 | / | Stage模型不支持 |
| deviceCapability | 标识Ability运行时要求设备具有的能力,采用字符串数组的格式表示 | / | Stage模型不支持 |
| metaData | 该标签标识ability的元信息。 | metadata | 具体差异如[表2](#表2 FA模型metaData和Stage中metadata对比) |
| type | 标识Ability的类型 | / | Stage模型不支持 |
| grantPermission | 指定是否可以向Ability内任何数据授予权限 | / | abilities内不支持; |
| readPermission | 标识读取Ability的数据所需的权限。该标签仅适用于data类型的Ability | / | abilities内不支持;在extensionAbilities标签中支持 |
| writePermission | 标识向Ability写数据所需的权限 | / | abilities内不支持;在extensionAbilities标签中支持 |
| configChanges | 标识Ability关注的系统配置集合 | / | Stage模型不支持 |
| mission | 标识Ability指定的任务栈 | / | Stage模型不支持 |
| targetAbility | 标识当前Ability重用的目标Ability | / | Stage模型不支持 |
| multiUserShared | 标识Ability是否支持多用户状态进行共享,该标签仅适用于data类型的Ability | / | Stage模型不支持 |
| supportPipMode | 标识Ability是否支持用户进入PIP模式(用于在页面最上层悬浮小窗口,俗称"画中画",常见于视频播放等场景) | / | Stage模型不支持 |
| formsEnabled | 标识Ability是否支持卡片(forms)功能 | / | Stage模型不支持 |
| forms | 标识服务卡片的属性。该标签仅当formsEnabled为"true"时,才能生效 | form_config.json文件 | Stage模型在开发视图的resources/base/profile下面定义配置文件form_config.json |
| srcLanguage | Ability开发语言的类型 | / | Stage模型不支持 |
| srcPath | 该标签标识Ability对应的JS组件代码路径 | srcEntrance | 该标签标识ability所对应的js代码路径。 |
| uriPermission | 标识该Ability有权访问的应用程序数据 | / | Stage模型不支持 |
# 页面栈及任务链
## 页面栈
单个UIAbility组件可以实现多个页面,并在多个页面之间跳转,这种UIAbility组件内部的页面跳转关系称为“页面栈”,由ArkUI框架统一管理,如下图中的UIAbility1的Page1-&gt;Page2-&gt;Page3和UIAbility2的PageA-&gt;PageB-&gt;PageC。
**图1** 页面栈示意图  
![mission-record](figures/mission-record.png)
- 页面栈的形成(下面2/3/5/6步骤为页面跳转,由ArkUI管理)
1. 点击桌面图标([startAbility](../reference/apis/js-apis-ability-context.md#abilitycontextstartability))启动UIAbility1,UIAbility1的初始页面为Page1。
2. 点击Page1页面按钮([Navigator](../reference/arkui-ts/ts-container-navigator.md))跳转到Page2页面。
3. 点击Page2页面按钮([Navigator](../reference/arkui-ts/ts-container-navigator.md))跳转到Page3页面。
4. 点击Page3页面按钮([startAbility](../reference/apis/js-apis-ability-context.md#abilitycontextstartability))跳转到UIAbility2,UIAbility2的初始页面为PageA。
5. 点击PageA页面按钮([Navigator](../reference/arkui-ts/ts-container-navigator.md))跳转到PageB页面。
6. 点击PageB页面按钮([Navigator](../reference/arkui-ts/ts-container-navigator.md))跳转到PageC页面。
- 页面栈的返回(下面1/2/4/5步骤为页面跳转,由ArkUI管理)
1. 在UIAbility2的PageC页面点击**返回键**回到UIAbility2的PageB页面。
2. 在UIAbility2的PageB页面点击**返回键**回到UIAbility2的PageA页面。
3. 在UIAbility2的PageA页面点击**返回键**跳转到UIAbility1的Page3页面。
4. 在UIAbility1的Page3页面点击**返回键**回到UIAbility1的Page2页面。
5. 在UIAbility1的Page2页面点击**返回键**回到UIAbility1的Page1页面。
6. 在UIAbility1的Page1页面点击**返回键**回到桌面。
## 任务链
上文介绍了页面栈的返回,如果Ability2页面栈一层层通过返回键返回到最底层,再次点击返回键时,会返回到Ability1。因为在MissionList中记录了任务(Mission)之间的启动关系,即如果Ability1通过startAbility启动Ability2,则会形成一个MissionList任务链:Ability1-&gt;Ability2,当Ability2页面栈返回到首页时,再次点击返回键,会返回到Ability1的页面。
MissionList任务链记录了任务之间的拉起关系,但是这个任务链可能会断开,有以下几种情况会导致任务链的断开:
- 进入任务列表,把任务链中间某个任务移动到前台。
![mission-chain1](figures/mission-chain1.png)
- 进入任务列表,把任务链中间某个任务清理掉。
![mission-chain2](figures/mission-chain2.png)
- 单实例UIAbility的任务,被不同的任务反复拉起(AbilityB为单例)。
![mission-chain3](figures/mission-chain3.png)
# PageAbility组件配置
PageAbility的相关配置在config.json配置文件的"module"对象的"abilities"对象中,"icon"属性表示Ability图标资源文件的索引,"lable"属性表示Ability对用户显示的名称,"skills"属性表示Ability能够接收的want的特征。
**表1** PageAbility部分配置项说明
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| icon | 表示Ability图标资源文件的索引。取值示例:$media:ability_icon。如果在该Ability的skills属性中,actions的取值包含&nbsp;"action.system.home",entities取值中包含"entity.system.home",则该Ability的icon将同时作为应用的icon。如果存在多个符合条件的Ability,则取位置靠前的Ability的icon作为应用的icon。<br/>说明:应用的"icon"和"label"是用户可感知配置项,需要区别于当前所有已有的应用"icon"或"label"(至少有一个不同)。 | 字符串 | 可缺省,缺省值为空。 |
| label | 表示Ability对用户显示的名称。取值可以是Ability名称,也可以是对该名称的资源索引,以支持多语言。如果在该Ability的skills属性中,actions的取值包含&nbsp;"action.system.home",entities取值中包含"entity.system.home",则该Ability的label将同时作为应用的label。如果存在多个符合条件的Ability,则取位置靠前的Ability的label作为应用的label。<br/>说明:&nbsp;应用的"icon"和"label"是用户可感知配置项,需要区别于当前所有已有的应用"icon"或"label"(至少有一个不同)。该标签为资源文件中定义的字符串的引用,或以"{}"包括的字符串。该标签最大长度为255字节。 | 字符串 | 可缺省,缺省值为空。 |
| skills | 表示Ability能够接收的want的特征。 | 对象数组 | 可缺省,缺省值为空。 |
# PageAbility的启动模式
启动模式对应PageAbility被启动时的行为,支持单实例模式、标准模式两种启动模式。
**表1** PageAbility的启动模式
| 启动模式 | 描述 | 说明 |
| -------- | -------- | -------- |
| singleton | 单实例模式 | 每次调用startAbility方法时,如果应用进程中该类型的Ability实例已经存在,则复用已有的实例,系统中只存在唯一一个实例。表现为在最近任务列表中只有一个Ability实例。<br/>典型场景:当用户打开视频播放应用并观看视频,回到桌面后,再次打开视频播放应用,应用仍为回到桌面之前正在观看的视频。 |
| standard | 标准模式 | 缺省启动模式。每次调用startAbility方法时,都会在应用进程中创建一个新的Ability实例。表现为在最近任务列表中可以看到有多个该类型的Ability实例。<br/>典型场景:当用户打开文档应用,选择新建文档的时候,每次点击新建文档,都会创建一个新的文档任务,在最近任务列表中可以看到多个新建的文档任务。 |
应用开发者可在config.json配置文件中通过“launchType”配置启动模式。示例如下:
```json
{
"module": {
// ...
"abilities": [
{
// singleton: 单实例模式
// standard: 标准模式
"launchType": "standard",
// ...
}
]
}
}
```
启动PageAbility时,对于标准启动模式(多实例启动模式)以及单实例启动模式首次启动,[PageAbility生命周期回调](pageability-lifecycle.md#table13118194914476)均会被触发。单实例非首次启动时不会再触发onCreate()接口,而是触发onNewWant(),onNewWant()的说明如下表2所示。
**表2** 单实例启动模式特有的回调函数说明
| 接口名 | 接口描述 |
| -------- | -------- |
| onNewWant(want:&nbsp;Want) | 单实例启动模式,PageAbility非首次启动时调用onNewWant方法,开发者可以在该方法中获取want,进而根据want做进一步处理。例如,单实例PageAbility迁移场景,指定页面拉起PageAbility场景。 |
# PageAbility的生命周期
PageAbility生命周期是PageAbility被调度到INACTIVE、ACTIVE、BACKGROUND等各个状态的统称。PageAbility生命周期流转及状态说明见如下图1、表1所示。
**图1** PageAbility生命周期流转  
![page-ability-lifecycle](figures/page-ability-lifecycle.png)
**表1** PageAbility生命周期状态说明
| 生命周期状态 | 生命周期状态说明 |
| -------- | -------- |
| UNINITIALIZED | 未初始状态,为临时状态,PageAbility被创建后会由UNINITIALIZED状态进入INITIAL状态。 |
| INITIAL | 初始化状态,也表示停止状态,表示当前PageAbility未运行,PageAbility被启动后由INITIAL态进入INACTIVE状态。 |
| INACTIVE | 失去焦点状态,表示当前窗口已显示但是无焦点状态。 |
| ACTIVE | 前台激活状态,表示当前窗口已显示,并获取焦点。 |
| BACKGROUND | 后台状态,表示当前PageAbility退到后台,PageAbility在被销毁后由BACKGROUND状态进入INITIAL状态,或者重新被激活后由BACKGROUND状态进入ACTIVE状态。 |
应用开发者可以在app.js/app.ets中重写生命周期相关回调函数,PageAbility生命周期相关回调函数见下表。
**表2** PageAbility生命周期回调接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| onCreate() | Ability第一次启动创建Ability时调用onCreate方法,开发者可以在该方法里做一些应用初始化工作。 |
| onDestroy() | 应用退出,销毁Ability对象前调用onDestroy方法,开发者可以在该方法里做一些回收资源、清空缓存等应用退出前的准备工作。 |
| onActive() | Ability切换到前台,并且已经获取焦点时调用onActive方法。 |
| onInactive() | Ability失去焦点时调用onInactive方法,Ability在进入后台状态时会先失去焦点,再进入后台。 |
| onShow() | Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到该Ability。 |
| onHide() | Ability由前台切换到后台不可见状态时调用onHide方法,此时用户在屏幕看不到该Ability。 |
PageAbility生命周期回调与生命周期状态的关系如下图所示。
**图2** PageAbility生命周期回调与生命周期状态的关系  
![fa-pageAbility-lifecycle](figures/fa-pageAbility-lifecycle.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 1、PageAbility的生命周期回调均为同步接口。
>
> 2、目前app.js环境中仅支持onCreate和onDestroy回调,app.ets环境支持全量生命周期回调。
# PageAbility组件概述
PageAbility是包含UI界面、提供展示UI能力的应用组件,主要用于与用户交互。
开发者通过DevEco Studio开发平台创建PageAbility时,DevEco Studio会自动创建相关模板代码。PageAbility相关能力通过单独的featureAbility实现,生命周期相关回调则通过app.js/app.ets中各个回调函数实现。
# PageAbility切换
FA模型中PageAbility对应Stage模型中的UIAbility。切换步骤如下。
1. 在Stage应用中[创建UIAbility](uiability-usage.md)
2. 将FA应用中PageAbility的代码迁移到新创建的UIAbility中。
FA应用中PageAbility和Stage应用中的UIAbility生命周期基本一致,两者的生命周期详细对比见下表。
| FA的PageAbility | Stage的UIAbility | 对应关系描述 |
| -------- | -------- | -------- |
| onCreate():&nbsp;void | onCreate(want:&nbsp;Want,&nbsp;param:&nbsp;AbilityConstant.LaunchParam):&nbsp;void | 两者的意义和调用时机一致,Stage模型在回调中新增了参数,方便开发者在创建的时候获取启动相关的数据。 |
| NA | onWindowStageCreate(windowStage:&nbsp;window.WindowStage):&nbsp;void | Stage模型新增,窗口创建时由系统回调。 |
| onActive():&nbsp;void | on(eventType:&nbsp;'windowStageEvent',&nbsp;callback:&nbsp;Callback&lt;WindowStageEventType&gt;):&nbsp;void;<br/>WindowStageEventType.ACTIVE | 两者的意义和调用时机一致。Stage模型下移动到了窗口对象中。 |
| onShow():&nbsp;void | onForeground():&nbsp;void | 两者的意义和调用时机一致,参数也一致。 |
| onNewWant(want:&nbsp;Want):&nbsp;void | onNewWant(want:&nbsp;Want,&nbsp;launchParams:&nbsp;AbilityConstant.LaunchParam):&nbsp;void | 两者的意义和调用时机一致,Stage模型多了LaunchParam参数来告知应用启动原因。 |
| onInactive():&nbsp;void | on(eventType:&nbsp;'windowStageEvent',&nbsp;callback:&nbsp;Callback&lt;WindowStageEventType&gt;):&nbsp;void;<br/>WindowStageEventType.INACTIVE | 两者的意义和调用时机一致。Stage模型下移动到了窗口对象中。 |
| onHide():&nbsp;void | onBackground():&nbsp;void | 两者的意义和调用时机一致,参数也一致。 |
| NA | onWindowStageDestroy():&nbsp;void | Stage模型新增,窗口销毁时由系统回调。 |
| onDestroy():&nbsp;void | onDestroy():&nbsp;void | 两者的意义和调用时机一致,参数也一致。 |
![pageability-switch](figures/pageability-switch.png)
3. 对迁移过来的代码进行调整,主要有以下两部分。
1、指定加载页面的方式不同。
- 在FA模型中,通过在config.json中设置页面信息来配置需要加载的页面。
- 在Stage模型中,则是通过在onWindowStageCreate回调中调用windowStage.loadContent实现对页面的加载。
例如,开发者希望Ability启动后加载"pages/Index"页面,在FA模型中,开发者需要在config.json中加入如下代码:
```json
"pages" : [
"pages/Index"
]
```
在Stage模型中,则在MainAbility中实现如下接口:
```ts
import Window from '@ohos.window'
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability
windowStage.loadContent('pages/Index', (err, data) => {
if (err.code) {
console.error("loadContent failed")
return;
}
});
}
```
# particleAbility接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [startAbility(parameter:&nbsp;StartAbilityParameter,&nbsp;callback:&nbsp;AsyncCallback&lt;number&gt;):&nbsp;void;](../reference/apis/js-apis-particleAbility.md#particleabilitystartability)<br/>[startAbility(parameter:&nbsp;StartAbilityParameter):&nbsp;Promise&lt;number&gt;;](../reference/apis/js-apis-particleAbility.md#particleabilitystartability-1) | application\ServiceExtensionContext.d.ts | [startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartability)<br/>[startAbility(want:&nbsp;Want,&nbsp;options:&nbsp;StartOptions,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartability-2)<br/>[startAbility(want:&nbsp;Want,&nbsp;options?:&nbsp;StartOptions):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartability-1)<br/>[startServiceExtensionAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartserviceextensionability)<br/>[startServiceExtensionAbility(want:&nbsp;Want):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartserviceextensionability-1) |
| [terminateSelf(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-particleAbility.md#particleabilityterminateself)<br/>[terminateSelf():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-particleAbility.md#particleabilityterminateself-1) | application\ServiceExtensionContext.d.ts | [terminateSelf(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself)<br/>[terminateSelf():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself-1) |
| [connectAbility(request:&nbsp;Want,&nbsp;options:ConnectOptions&nbsp;):&nbsp;number;](../reference/apis/js-apis-particleAbility.md#particleabilityconnectability) | application\ServiceExtensionContext.d.ts | [connectAbility(want:&nbsp;Want,&nbsp;options:&nbsp;ConnectOptions):&nbsp;number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability)<br/>[connectServiceExtensionAbility(want:&nbsp;Want,&nbsp;options:&nbsp;ConnectOptions):&nbsp;number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability) |
| [disconnectAbility(connection:&nbsp;number,&nbsp;callback:AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-particleAbility.md#particleabilitydisconnectability)<br/>[disconnectAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-particleAbility.md#particleabilitydisconnectability-1) | application\ServiceExtensionContext.d.ts | [disconnectAbility(connection:&nbsp;number,&nbsp;callback:AsyncCallback&lt;void&gt;):&nbsp;void;&nbsp;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br/>[disconnectAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1)<br/>[disconnectServiceExtensionAbility(connection:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br/>[disconnectServiceExtensionAbility(connection:&nbsp;number):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1) |
| [acquireDataAbilityHelper(uri:&nbsp;string):&nbsp;DataAbilityHelper;](../reference/apis/js-apis-particleAbility.md#particleabilityacquiredataabilityhelper) | \@ohos.data.dataShare.d.ts<br/>[\@ohos.data.fileAccess.d.ts | [createDataShareHelper(context:&nbsp;Context,&nbsp;uri:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;DataShareHelper&gt;):&nbsp;void;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper)<br/>[createDataShareHelper(context:&nbsp;Context,&nbsp;uri:&nbsp;string):&nbsp;Promise&lt;DataShareHelper&gt;;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper-1)<br/>[createFileAccessHelper(context:&nbsp;Context):&nbsp;FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper-1)<br/>[createFileAccessHelper(context:&nbsp;Context,&nbsp;wants:&nbsp;Array&lt;Want&gt;):&nbsp;FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper) |
| [startBackgroundRunning(id:&nbsp;number,&nbsp;request:&nbsp;NotificationRequest,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-particleAbility.md#particleabilitystartbackgroundrunning)<br/>[startBackgroundRunning(id:&nbsp;number,&nbsp;request:&nbsp;NotificationRequest):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-particleAbility.md#particleabilitystartbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [startBackgroundRunning(context:&nbsp;Context,&nbsp;bgMode:&nbsp;BackgroundMode,&nbsp;wantAgent:&nbsp;WantAgent,&nbsp;callback:&nbsp;AsyncCallback):&nbsp;void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunningcallback)<br/>[startBackgroundRunning(context:&nbsp;Context,&nbsp;bgMode:&nbsp;BackgroundMode,&nbsp;wantAgent:&nbsp;WantAgent):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunningpromise) |
| [cancelBackgroundRunning(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-particleAbility.md#particleabilitycancelbackgroundrunning)<br/>[cancelBackgroundRunning():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-particleAbility.md#particleabilitycancelbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [stopBackgroundRunning(context:&nbsp;Context,&nbsp;callback:&nbsp;AsyncCallback):&nbsp;void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunningcallback)<br/>[stopBackgroundRunning(context:&nbsp;Context):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunningpromise) |
# 进程模型
OpenHarmony的进程模型如下图所示:
- 应用中(同一包名)的所有PageAbility、ServiceAbility、DataAbility、FormAbility运行在同一个独立进程中,即图中绿色部分的“Main Process”。
- WebView拥有独立的渲染进程,即图中黄色部分的“Render Process”。
**图1** 进程模型示意图
![process-model-fa](figures/process-model-fa.png)
基于OpenHarmony的进程模型,应用间存在多个进程的情况,因此系统提供了如下两种进程间通信机制:
- [公共事件机制](common-event-fa.md):多用于一对多的通信场景,公共事件发布者可能存在多个订阅者同时接收事件。
- [后台服务机制](rpc.md):当前主要通过[ServiceAbility](serviceability-overview.md)的能力实现。
# 进程模型
OpenHarmony的进程模型如下图所示:
- 应用中(同一包名)的所有UIAbility、ServiceExtensionAbility、DataShareExtensionAbility运行在同一个独立进程中,即图中绿色部分的“Main Process”。
- 应用中(同一包名)的同一类型ExtensionAbility(除ServiceExtensionAbility和DataShareExtensionAbility外)运行在一个独立进程中,即图中蓝色部分的“FormExtensionAbility Process”、“InputMethodExtensionAbility Process”、其他ExtensionAbility Process。
- WebView拥有独立的渲染进程,即图中黄色部分的“Render Process”。
**图1** 进程模型示意图
![process-model](figures/process-model.png)
在上述模型基础上,对于系统应用可以通过申请多进程权限(如下图所示),为指定HAP配置一个自定义进程名,该HAP中的UIAbility、DataShareExtensionAbility、ServiceExtensionAbility就会运行在自定义进程中。不同的HAP可以通过配置不同的进程名运行在不同进程中。
**图2** 多进程示意图
![multi-process](figures/multi-process.png)
基于OpenHarmony的进程模型,系统中应用间和应用内都会存在多个进程的情况,因此系统提供了如下两种进程间通信机制:
- [公共事件机制](common-event-overview.md):多用于一对多的通信场景,公共事件发布者可能存在多个订阅者同时接收事件。
- [后台服务机制](background-services.md):当前主要通过[ServiceExtensionAbility](serviceextensionability.md)的能力实现。
# 跳转规则
一般情况下,应用中的界面跳转由用户触发,应用本身通过startAbility启动跳转其他界面。
PageAbility作为可见Ability,可以通过startAbility启动有界面的且对外可见的Ability。
应用可通过在config.json中设置"abilities"中的"visible"属性设置Ability是否可由其他应用的组件启动,"visible"属性的具体参数和意义如下表所示。
**表1** visible属性说明
| 属性名称 | 描述 | 是否可缺省 |
| -------- | -------- | -------- |
| visible | 表示Ability是否可以被其他应用调用。<br/>true:该Ability可以被任何应用调用。<br/>false:该Ability只能被同一应用的其他组件调用。 | 可缺省,缺省时默认属性值为"false"。 |
如果需设置当前Ability可由任何应用访问,对应config.json文件的示例代码如下所示:
```ts
{
"module": {
// ...
"abilities": [
{
"visible": "true",
// ...
}
]
}
}
```
如果应用中的Ability包含skills过滤器,建议此属性设置为"true",以允许其他应用通过[隐式调用](explicit-implicit-want-mappings.md#隐式want匹配原理详解)启动该Ability。如果此属性设为"false",其他应用尝试启动该Ability时系统会返回PERMISSION_DENIED。这种情况下系统应用可以通过申请[START_INVISIBLE_ABILITY](../security/permission-list.md)权限启动visible为false的组件,例如系统桌面、语音助手、搜索助手等。
# 申请授权
应用需要获取用户的隐私信息或使用系统能力时,例如获取位置信息、使用相机拍摄照片或录制视频等,需要向用户申请授权。
在开发过程中,首先需要明确涉及的敏感权限并在config.json中声明需要的权限,同时通过接口requestPermissionsFromUser以动态弹窗的方式向用户申请授权。
在config.json声明需要的权限,在module下添加"reqPermissions",并写入对应权限。
如申请访问日历权限,需要申请`ohos.permission.READ_CALENDAR`权限,配置方式请参阅[访问控制授权申请指导](../security/accesstoken-guidelines.md#stage%E6%A8%A1%E5%9E%8B)
对应config.json文件的示例代码如下所示:
```ts
{
"module": {
// ...
"reqPermissions": [
{
"name": "ohos.permission.READ_CALENDAR"
// ...
}
]
}
}
```
通过动态弹窗向用户申请授权:
```ts
import featureAbility from '@ohos.ability.featureAbility';
let context = featureAbility.getContext();
let permissions: Array<string> = ['ohos.permission.READ_CALENDAR']
context.requestPermissionsFromUser(permissions, 1).then((data) => {
console.info("Succeed to request permission from user with data: " + JSON.stringify(data))
}).catch((error) => {
console.info("Failed to request permission from user with error: " + JSON.stringify(error))
})
```
# request接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [download(config:&nbsp;DownloadConfig,&nbsp;callback:&nbsp;AsyncCallback&lt;DownloadTask&gt;):&nbsp;void;](../reference/apis//js-apis-request.md#requestdownload-1)<br/>[download(config:&nbsp;DownloadConfig):&nbsp;Promise&lt;DownloadTask&gt;;](../reference/apis/js-apis-request.md#requestdownload) | \@ohos.request.d.ts | [downloadFile(context:&nbsp;BaseContext,&nbsp;config:&nbsp;DownloadConfig,&nbsp;callback:&nbsp;AsyncCallback&lt;DownloadTask&gt;):&nbsp;void;](../reference/apis/js-apis-request.md#requestdownloadfile9-1)<br/>[downloadFile(context:&nbsp;BaseContext,&nbsp;config:&nbsp;DownloadConfig):&nbsp;Promise&lt;DownloadTask&gt;;](../reference/apis/js-apis-request.md#requestdownloadfile9) |
| [upload(config:&nbsp;UploadConfig,&nbsp;callback:&nbsp;AsyncCallback&lt;UploadTask&gt;):&nbsp;void;](../reference/apis/js-apis-request.md#requestupload-1)<br/>[upload(config:&nbsp;UploadConfig):&nbsp;Promise&lt;UploadTask&gt;;](../reference/apis/js-apis-request.md#requestupload) | \@ohos.request.d.ts | [uploadFile(context:&nbsp;BaseContext,&nbsp;config:&nbsp;UploadConfig,&nbsp;callback:&nbsp;AsyncCallback&lt;UploadTask&gt;):&nbsp;void;](../reference/apis/js-apis-request.md#requestuploadfile9-1)<br/>[uploadFile(context:&nbsp;BaseContext,&nbsp;config:&nbsp;UploadConfig):&nbsp;Promise&lt;UploadTask&gt;;](../reference/apis/js-apis-request.md#requestuploadfile9) |
# resourceManager接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应字段 |
| -------- | -------- | -------- |
| [getResourceManager(callback:&nbsp;AsyncCallback&lt;ResourceManager&gt;):&nbsp;void;](../reference/apis/js-apis-resource-manager.md#resourcemanagergetresourcemanager)<br/>[getResourceManager(bundleName:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;ResourceManager&gt;):&nbsp;void;](../reference/apis/js-apis-resource-manager.md#resourcemanagergetresourcemanager-1)<br/>[getResourceManager():&nbsp;Promise&lt;ResourceManager&gt;;](../reference/apis/js-apis-resource-manager.md#resourcemanagergetresourcemanager-2)<br/>[getResourceManager(bundleName:&nbsp;string):&nbsp;Promise&lt;ResourceManager&gt;;](../reference/apis/js-apis-resource-manager.md#resourcemanagergetresourcemanager-3) | application\Context.d.ts | [resourceManager:&nbsp;resmgr.ResourceManager;](../reference/apis/js-apis-inner-application-context.md#属性) |
# 后台服务
请参见Stage模型的"[后台服务](background-services.md)"。
# ServiceAbility组件配置
与PageAbility类似,ServiceAbility的相关配置在config.json配置文件的"module"对象的"abilities"对象中,与PageAbility的区别在于"type"属性及"backgroundModes"属性。
**表1** ServiceAbility部分配置项说明
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| type | 表示Ability的类型。取值为"service"时表示该Ability是基于Service模板开发的Ability。 | 字符串 | 否 |
| backgroundModes | 表示后台服务的类型,可以为一个服务配置多个后台服务类型。该标签仅适用于service类型的Ability。取值范围如下:<br/>dataTransfer:通过网络/对端设备进行数据下载、备份、分享、传输等业务。<br/>audioPlayback:音频输出业务。<br/>audioRecording:音频输入业务。<br/>pictureInPicture:画中画、小窗口播放视频业务。<br/>voip:音视频电话、VOIP业务。<br/>location:定位、导航业务。<br/>bluetoothInteraction:蓝牙扫描、连接、传输业务。<br/>wifiInteraction:WLAN扫描、连接、传输业务。<br/>screenFetch:录屏、截屏业务。<br/>multiDeviceConnection:多设备互联业务。 | 字符串数组 | 可缺省,缺省值为空。 |
ServiceAbility支持的配置项及详细说明详见[module对象内部结构](../quick-start/module-structure.md)
# ServiceAbility的生命周期
开发者可以根据业务场景重写生命周期相关接口。ServiceAbility生命周期接口说明见下表。
**表1** ServiceAbility生命周期接口说明
| 接口名 | 描述 |
| -------- | -------- |
| onStart():&nbsp;void | 该方法在创建ServiceAbility的时候调用,用于Service的初始化,在ServiceAbility的整个生命周期只会调用一次。 |
| onCommand(want:&nbsp;Want,&nbsp;startId:&nbsp;number):&nbsp;void | 在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。 |
| onConnect(want:&nbsp;Want):&nbsp;rpc.RemoteObject | 在连接ServiceAbility时调用。 |
| onDisconnect(want:&nbsp;Want):&nbsp;void | 在与已连接的ServiceAbility断开连接时调用。 |
| onStop():&nbsp;void | 在ServiceAbility销毁时调用。开发者应通过实现此方法来清理资源,如关闭线程、注册的侦听器等。 |
# ServiceAbility组件概述
ServiceAbility,即"基于Service模板的Ability",主要用于后台运行任务(如执行音乐播放、文件下载等),不提供用户交互界面。ServiceAbility可由其他应用或PageAbility启动,即使用户切换到其他应用,ServiceAbility仍将在后台继续运行。
# ServiceAbility切换
FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API,只有系统应用才可以创建。因此,FA模型的ServiceAbility的切换,对于系统应用和三方应用策略有所不同。下面分别介绍这两种场景。
## 系统应用ServiceAbility切换
目前系统提供了ServiceExtensionAbility供系统应用使用。切换步骤和PageAbility基本一致。
1. 在Stage应用中[创建ServiceExtensionAbility](serviceextensionability.md)
2. 将FA应用中ServiceAbility的业务代码迁移到新创建的ServiceExtensionAbility中。
ServiceAbility和ServiceExtensionAbility生命周期对比见下表。
| FA的ServiceAbility | Stage的ServiceExtensionAbility | 对比描述 |
| -------- | -------- | -------- |
| onStart():&nbsp;void | onCreate(want:&nbsp;Want):&nbsp;void | 两者调用时机一致,Stage模型下增加了入参want以便开发者在创建时获取参数。 |
| onCommand(want:&nbsp;Want,&nbsp;startId:&nbsp;number):&nbsp;void | onRequest(want:&nbsp;Want,&nbsp;startId:&nbsp;number):&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
| onConnect(want:&nbsp;Want):&nbsp;rpc.RemoteObject | onConnect(want:&nbsp;Want):&nbsp;rpc.RemoteObject | 两者意义和调用时机一致,参数也一致。 |
| onDisconnect(want:&nbsp;Want):&nbsp;void | onDisconnect(want:&nbsp;Want):&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
| onReconnect(want:&nbsp;Want):&nbsp;void | onReconnect(want:&nbsp;Want):&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
| onStop():&nbsp;void | onDestroy():&nbsp;void | 两者意义和调用时机一致,参数也一致。 |
## 三方应用ServiceAbility切换
Stage模型下三方应用不能对其他三方提供服务,应用可以根据具体业务选择切换方案。
| 业务类型 | 切换策略 |
| -------- | -------- |
| 对其他三方应用提供服务 | 需根据业务场景匹配到系统对应的场景化[ExtensionAbility](extensionability-overview.md)。 |
| 应用内:前台运行时公共使用 | 可以将该组件代码抽取成公共模块供其他组件使用。 |
| 应用内:进入后台时继续运行 | 可以将此服务切换为[后台任务](serviceextensionability.md),可参考如下示例。 |
# ServiceExtensionAbility
[ServiceExtensionAbility](../reference/apis/js-apis-service-extension-ability.md)是SERVICE类型的ExtensionAbility组件,提供后台服务相关扩展能力。
[ServiceExtensionAbility](../reference/apis/js-apis-service-extension-ability.md)可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。[ServiceExtensionAbility](../reference/apis/js-apis-service-extension-ability.md)支持以启动和连接两种形式运行,系统应用可以调用[startServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstartserviceextensionability)方法启动后台服务,也可以调用[connectServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextconnectserviceextensionability)方法连接后台服务,而三方应用只能调用[connectServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextconnectserviceextensionability)方法连接后台服务。启动和连接后台服务的差别:
- 启动:AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。
- 连接:AbilityA绑定ServiceB,绑定后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。
每个类型的ExtensionAbility都有自己的Context,ServiceExtensionAbility通过[ServiceExtensionContext](../reference/apis/js-apis-service-extension-context.md)提供相关能力。本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。
本章节将从如下场景来介绍ServiceExtensionAbility的基本使用。
- [实现一个后台服务(仅对系统应用开放)](#实现一个后台服务仅对系统应用开放)
- [启动一个后台服务(仅对系统应用开放)](#启动一个后台服务仅对系统应用开放)
- [连接一个后台服务](#连接一个后台服务)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 1. OpenHarmony当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见[后台任务](../task-management/Readme-CN.md)。
>
> 2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
>
> 3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。
## 实现一个后台服务(仅对系统应用开放)
[ServiceExtensionAbility](../reference/apis/js-apis-service-extension-ability.md)提供了onCreate()、onRequest()、onConnect()、onDisconnect()和onDestory()生命周期回调,根据需要重写对应的回调方法。下图展示了ServiceExtensionAbility的生命周期。
**图1** ServiceExtensionAbility生命周期
![ServiceExtensionAbility-lifecycle](figures/ServiceExtensionAbility-lifecycle.png)
- **onCreate**
服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 如果服务已创建,再次启动该ServiceExtensionAbility不会触发onCreate()回调。
- **onRequest**
当另一个组件调用startServiceExtensionAbility()方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。
- **onConnect**
当另一个组件调用connectServiceExtensionAbility()方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象(IRemoteObject),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信。
- **onDisconnect**
其他组件调用disconnectServiceExtensionAbility()方法时,如果没有任何其他组件连接该服务,触发该回调。
- **onDestroy**
当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。
## 开发步骤
开发者在实现一个后台服务时,需要在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下。
1. 在工程Module对应的ets目录下,右键选择“New &gt; Directory”,新建一个目录并命名为serviceextability。
2. 在serviceextability目录,右键选择“New &gt; ts File”,新建一个TS文件并命名为ServiceExtAbility.ts。
3. 打开ServiceExtAbility.ts文件,导入[RPC通信模块](../reference/apis/js-apis-rpc.md),重载onRemoteMessageRequest()方法,接收客户端传递过来的消息,并将处理的结果返回给客户端。REQUEST_VALUE用于校验客户端发送的服务请求码。
```ts
import rpc from '@ohos.rpc';
const REQUEST_CODE = 99;
class StubTest extends rpc.RemoteObject {
constructor(des) {
super(des);
}
// 接收客户端传递过来的消息处理,以及将处理的结果返回给客户端
onRemoteMessageRequest(code, data, reply, option) {
if (code === REQUEST_CODE) {
// 接收客户端传递过来的数据
// 客户端使用多次调用data.writeInt()写入多个数据时,服务端可以通过多次调用data.readInt()方法接收对应的数据
let optFir = data.readInt();
let optSec = data.readInt();
// 服务端将数据的处理结果返回给客户端
// 示例中为接收了两个数据,并将两个数据的求和返回给客户端
reply.writeInt(optFir + optSec);
}
return true;
}
// 以同步或异步方式向客户端发送消息
sendRequest(code, data, reply, options) {
return null;
}
}
```
4. 在ServiceExtAbility.ts文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并加上需要的生命周期回调。
```ts
import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
import rpc from '@ohos.rpc';
const TAG: string = "[Example].[Entry].[ServiceExtAbility]";
const REQUEST_CODE = 99;
class StubTest extends rpc.RemoteObject {
// ...
}
export default class ServiceExtAbility extends ServiceExtensionAbility {
onCreate(want) {
console.info(TAG, `onCreate, want: ${want.abilityName}`);
}
onRequest(want, startId) {
console.info(TAG, `onRequest, want: ${want.abilityName}`);
}
onConnect(want) {
console.info(TAG, `onConnect, want: ${want.abilityName}`);
return new StubTest("test");
}
onDisconnect(want) {
console.info(TAG, `onDisconnect, want: ${want.abilityName}`);
}
onDestroy() {
console.info(TAG, `onDestroy`);
}
}
```
5. 在工程Module对应的[module.json5配置文件](../quick-start/module-configuration-file.md)中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntrance标签表示当前ExtensionAbility组件所对应的代码路径。
```json
{
"module": {
// ...
"extensionAbilities": [
{
"name": "ServiceExtAbility",
"icon": "$media:icon",
"description": "service",
"type": "service",
"visible": true,
"srcEntrance": "./ets/serviceextability/ServiceExtAbility.ts"
}
]
}
}
```
## 启动一个后台服务(仅对系统应用开放)
系统应用通过[startServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstartserviceextensionability)方法启动一个后台服务,服务的[onRequest()](../reference/apis/js-apis-service-extension-ability.md#serviceextensionabilityonrequest)回调就会被调用,并在该回调方法中接收到调用者传递过来的want对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用ServiceExtensionContext的[terminateSelf()](../reference/apis/js-apis-service-extension-context.md#serviceextensioncontextterminateself)来自行停止,或者由另一个组件调用[stopServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstopserviceextensionability)来将其停止。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> ServiceExtensionContext的[startServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstartserviceextensionability)、[stopServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstopserviceextensionability)和[terminateSelf()](../reference/apis/js-apis-service-extension-context.md#serviceextensioncontextterminateself)为系统接口,三方应用不支持调用。
1. 在系统应用中启动一个新的ServiceExtensionAbility。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
let want = {
"deviceId": "",
"bundleName": "com.example.myapplication",
"abilityName": "ServiceExtAbility"
};
this.context.startServiceExtensionAbility(want).then(() => {
console.info('startServiceExtensionAbility success');
}).catch((error) => {
console.info('startServiceExtensionAbility failed');
})
```
2. 在系统应用中停止一个已启动的ServiceExtensionAbility。
```ts
let want = {
"deviceId": "",
"bundleName": "com.example.myapplication",
"abilityName": "ServiceExtAbility"
};
this.context.stopServiceExtensionAbility(want).then(() => {
console.info('stopServiceExtensionAbility success');
}).catch((error) => {
console.info('stopServiceExtensionAbility failed');
})
```
3. 已启动的ServiceExtensionAbility停止自身。
```ts
// this是当前ServiceExtensionAbility
this.context.terminateSelf().then(() => {
console.info('terminateSelf success');
}).catch((error) => {
console.info('terminateSelf failed');
})
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 后台服务可以在后台长期运行,为了避免资源浪费,需要对后台服务的生命周期进行管理。即一个后台服务完成了请求方的任务,需要及时销毁。销毁已启动的后台服务有两种方式:
>
> - 后台服务自身调用[terminateSelf()](../reference/apis/js-apis-service-extension-context.md#serviceextensioncontextterminateself)方法来自行停止。
>
> - 由其他组件调用[stopServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstopserviceextensionability)方法来停止。
>
> 调用[terminateSelf()](../reference/apis/js-apis-service-extension-context.md#serviceextensioncontextterminateself)或[stopServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextstopserviceextensionability)方法之后,系统将销毁后台服务。
## 连接一个后台服务
系统应用或者三方应用可以通过[connectServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextconnectserviceextensionability)连接一个服务(在Want对象中指定启动的目标服务),服务的[onConnect()](../reference/apis/js-apis-service-extension-ability.md#serviceextensionabilityonconnect)就会被调用,并在该回调方法中接收到调用者传递过来的Want对象,从而建立长连接。
ServiceExtensionAbility服务组件在[onConnect()](../reference/apis/js-apis-service-extension-ability.md#serviceextensionabilityonconnect)中返回IRemoteObject对象,开发者通过该IRemoteObject定义通信接口,用于客户端与服务端进行RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务的交互后,客户端需要通过调用[disconnectServiceExtensionAbility()](../reference/apis/js-apis-ability-context.md#abilitycontextdisconnectserviceextensionability)来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。
- 使用connectServiceExtensionAbility()建立与后台服务的连接。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
import rpc from '@ohos.rpc';
const REQUEST_CODE = 99;
let want = {
"deviceId": "",
"bundleName": "com.example.myapplication",
"abilityName": "ServiceExtAbility"
};
let options = {
onConnect(elementName, remote) {
console.info('onConnect callback');
if (remote === null) {
console.info(`onConnect remote is null`);
return;
}
let option = new rpc.MessageOption();
let data = new rpc.MessageParcel();
let reply = new rpc.MessageParcel();
data.writeInt(100);
data.writeInt(200);
// @param code 表示客户端发送的服务请求代码。
// @param data 表示客户端发送的{@link MessageParcel}对象。
// @param reply 表示远程服务发送的响应消息对象。
// @param options 指示操作是同步的还是异步的。
//
// @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
remote.sendRequest(REQUEST_CODE, data, reply, option).then((ret) => {
let msg = reply.readInt();
console.info(`sendRequest ret:${ret} msg:${msg}`);
}).catch((error) => {
console.info('sendRequest failed');
});
},
onDisconnect(elementName) {
console.info('onDisconnect callback')
},
onFailed(code) {
console.info('onFailed callback')
}
}
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
let connectionId = this.context.connectServiceExtensionAbility(want, options);
```
- 使用disconnectServiceExtensionAbility()断开与后台服务的连接。
```ts
let connectionId = 1 // 在通过connectServiceExtensionAbility绑定服务时返回的Id
this.context.disconnectServiceExtensionAbility(connectionId).then((data) => {
console.info('disconnectServiceExtensionAbility success');
}).catch((error) => {
console.error('disconnectServiceExtensionAbility failed');
})
```
## 相关示例
针对ServiceExtensionAbility开发,有以下相关示例可供参考:
[ServiceExtAbility:StageExtAbility的创建与使用(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/ServiceExtAbility)
# Stage模型开发概述
## 基本概念
下图展示了Stage模型中的基本概念。
**图1** Stage模型概念图  
![stage-concepts](figures/stage-concepts.png)
- [UIAbility组件](uiability-overview.md)[ExtensionAbility组件](extensionability-overview.md)
Stage模型提供UIAbility和ExtensionAbility两种类型的组件,这两种组件都有具体的类承载,支持面向对象的开发方式。他们是Ability抽象概念在Stage模型上的具体实现。他们是Ability管理服务调度的单元,其生命周期都是由Ability管理服务进行调度的。
- UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。例如,图库类应用可以在UIAbility组件中展示图片瀑布流,在用户选择某个图片后,在新的页面中展示图片的详细内容。同时用户可以通过返回键返回到瀑布流页面。UIAbility的生命周期只包含创建/销毁/前台/后台等状态,与显示相关的状态通过WindowStage的事件暴露给开发者。
- ExtensionAbility组件是一种面向特定场景的应用组件。开发者并不直接从ExtensionAbility派生,而是需要使用ExtensionAbility的派生类。目前ExtensionAbility有用于卡片场景的FormExtensionAbility,用于输入法场景的InputMethodExtensionAbility,用于闲时任务场景的WorkSchedulerExtensionAbility等多种派生类,这些派生类都是基于特定场景提供的。例如,用户在桌面创建应用的卡片,需要应用开发者从FormExtensionAbility派生,实现其中的回调函数,并在配置文件中配置该能力。ExtensionAbility派生类实例由用户触发创建,并由系统管理生命周期。在Stage模型上,普通应用开发者不能开发自定义服务,而需要根据自身的业务场景通过ExtensionAbility的派生类来实现。
- [WindowStage](../windowmanager/application-window-stage.md)
每个UIAbility类实例都会与一个WindowStage类实例绑定,该类提供了应用进程内窗口管理器的作用。它包含一个主窗口。也就是说UIAbility通过WindowStage持有了一个窗口,该窗口为ArkUI提供了绘制区域。
- [Context](application-context-stage.md)
在Stage模型上,Context及其派生类向开发者提供在运行期可以调用的各种能力。UIAbility组件和各种ExtensionAbility派生类都有各自不同的Context类,他们都继承自基类Context,但是各自又根据所属组件,提供不同的能力。
- [AbilityStage](abilitystage.md)
每个Entry类型或者Feature类型的HAP在运行期都有一个AbilityStage类实例,当HAP中的代码首次被加载到进程中的时候,系统会先创建AbilityStage实例。每个在该HAP中定义的UIAbility类,在实例化后都会与该实例产生关联。开发者可以使用AbilityStage获取该HAP中UIAbility实例的运行时信息。
## 开发流程
基于Stage模型开发应用时,在应用模型部分,涉及如下开发过程。
**表1** Stage模型开发流程
| 任务 | 简介 | 相关指导 |
| -------- | -------- | -------- |
| 应用组件开发 | 本章节介绍了如何使用Stage模型的UIAbility组件和ExtensionAbility组件开发应用。 | -&nbsp;[应用/组件级配置](application-component-configuration-stage.md)<br/>-&nbsp;[UIAbility组件](uiability-overview.md)<br/>-&nbsp;[ExtensionAbility组件](extensionability-overview.md)<br/>-&nbsp;[AbilityStage组件容器](abilitystage.md)<br/>-&nbsp;[应用上下文Context](application-context-stage.md)<br/>-&nbsp;[组件启动规则](component-startup-rules.md) |
| 应用配置文件 | 本章节介绍了Stage模型的配置文件中所有的字段以及各个字段的说明。 | -&nbsp;[app.json5配置文件](../quick-start/app-configuration-file.md)<br/>-&nbsp;[module.json5配置文件](../quick-start/module-configuration-file.md) |
| 进程间通信 | 本章节介绍了Stage模型的进程模型以及几种常用的进程间通信方式。 | -&nbsp;[公共事件](common-event-overview.md)<br/>-&nbsp;[后台服务](background-services.md) |
| 线程间通信 | 本章节介绍了Stage模型的线程模型以及几种常用的线程间通信方式。 | -&nbsp;[Emitter](itc-with-emitter.md)<br/>-&nbsp;[Worker](itc-with-worker.md) |
| 任务管理 | 本章节介绍了Stage模型中任务管理的基本概念和典型场景。 | -&nbsp;[任务管理场景介绍](mission-management-overview.md)<br/>-&nbsp;[任务管理与启动模式](mission-management-launch-type.md)<br/>-&nbsp;[页面栈和任务链](page-mission-stack.md) |
# 启动DataAbility
启动DataAbility会获取一个工具接口类对象(DataAbilityHelper)。启动DataAbility的示例代码如下:
```ts
// 作为参数传递的URI,与config中定义的URI的区别是多了一个"/",有三个"/",具体原因详见。
import featureAbility from '@ohos.ability.featureAbility'
let urivar = "dataability:///com.ix.DataAbility"
let DAHelper = featureAbility.acquireDataAbilityHelper(urivar);
```
# 启动本地PageAbility
PageAbility相关的能力通过featureAbility提供,启动本地Ability通过featureAbility中的startAbility接口实现。
**表1** featureAbility接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| startAbility(parameter:&nbsp;StartAbilityParameter) | 启动Ability。 |
| startAbilityForResult(parameter:&nbsp;StartAbilityParameter) | 启动Ability,并在该Ability被销毁时返回执行结果。 |
如下示例通过startAbility显式启动PageAbility。启动Ability的参数包含want,关于want的说明详见[对象间信息传递载体Want](want-fa.md),相应的,隐式启动与显式启动也不在此赘述。
```ts
import featureAbility from '@ohos.ability.featureAbility'
(async () => {
try {
console.info('Begin to start ability')
let param = {
want: {
bundleName: "com.example.myapplication",
moduleName: "entry",
abilityName: "com.example.myapplication.MainAbility"
}
}
await featureAbility.startAbility(param)
console.info(`Start ability succeed`)
}
catch (error) {
console.error('Start ability failed with ' + error)
}
})()
```
# 启动指定页面
当PageAbility的启动模式设置为单例时(具体设置方法和典型场景示例见[PageAbility的启动模式](pageability-launch-type.md),缺省情况下是单实例模式),若PageAbility已被拉起,再次启动PageAbility会触发onNewWant回调(即非首次拉起)。应用开发者可以通过want传递启动参数,例如开发者希望指定页面启动PageAbility,可以通过want中的parameters参数传递pages信息,具体示例代码如下:
调用方PageAbility的app.ets中或者page中,使用startAbility再次拉起PageAbility,通过want中的uri参数传递页面信息:
```ts
import featureAbility from '@ohos.ability.featureAbility';
async function restartAbility() {
let wantInfo = {
bundleName: "com.sample.MyApplication",
abilityName: "MainAbility",
parameters: {
page: "pages/second"
}
};
featureAbility.startAbility({
want: wantInfo
}).then((data) => {
console.info('restartAbility success.');
});
}
```
在目标端PageAbility的onNewWant回调中获取包含页面信息的want参数:
```ts
export default {
onNewWant(want) {
globalThis.newWant = want
}
}
```
在目标端页面的自定义组件中获取包含页面信息的want参数并根据uri做路由处理:
```ts
import router from '@ohos.router'
@Entry
@Component
struct Index {
@State message: string = 'Router Page'
newWant = undefined
onPageShow() {
console.info('Index onPageShow')
let newWant = globalThis.newWant
if (newWant.hasOwnProperty("page")) {
router.push({ url: newWant.page });
globalThis.newWant = undefined
}
}
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
```
当PageAbility的启动模式设置为标准模式或为首次启动单例模式的PageAbility时(具体设置方法和典型场景示例见[PageAbility的启动模式](pageability-launch-type.md)),在调用方PageAbility中,通过want中的parameters参数传递要启动的指定页面的pages信息,调用startAbility()方法启动PageAbility。被调用方可以在onCreate中使用featrueAbility的getWant方法获取want,再通过调用router.push实现启动指定页面。
调用方的页面中实现按钮点击触发startAbility方法启动目标端PageAbility,startAbility方法的入参want中携带指定页面信息,示例代码如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
// ...
Button("startAbility")
.onClick(() => {
featureAbility.startAbility({
want: {
bundleName: "com.exm.myapplication",
abilityName: "com.exm.myapplication.MainAbility",
parameters: { page: "pages/page1" }
}
}).then((data) => {
console.info("startAbility finish");
}).catch((err) => {
console.info("startAbility failed errcode:" + err.code)
})
})
// ...
Button("page2")
.onClick(() => {
featureAbility.startAbility({
want: {
bundleName: "com.exm.myapplication",
abilityName: "com.exm.myapplication.MainAbility",
parameters: { page: "pages/page2" }
}
}).then((data) => {
console.info("startAbility finish");
}).catch((err) => {
console.info("startAbility failed errcode:" + err.code)
})
})
// ...
}
}
```
目标端PageAbility的onCreate生命周期回调中通过featrueAbility的getWant方法获取want,并对参数进行解析,实现指定页面拉起:
```ts
import featureAbility from '@ohos.ability.featureAbility';
import router from '@ohos.router';
export default {
onCreate() {
featureAbility.getWant().then((want) => {
if (want.parameters.page) {
router.push({
url: want.parameters.page
})
}
})
},
onDestroy() {
// ...
},
}
```
# Stage模型启动FA模型PageAbility
本小节介绍Stage模型的两种应用组件如何启动FA模型的PageAbility组件。
## UIAbility启动PageAbility
UIAbility启动PageAbility和UIAbility启动UIAbility的方式完全相同。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class MainAbility extends UIAbility {
onCreate(want, launchParam) {
console.info("MainAbility onCreate")
}
onDestroy() {
console.info("MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.info("MainAbility onWindowStageCreate")
windowStage.loadContent('pages/Index', (err, data) => {
// ...
});
let want = {
bundleName: "com.ohos.fa",
abilityName: "MainAbility",
};
this.context.startAbility(want).then(() => {
console.info('Start Ability successfully.');
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
}
onWindowStageDestroy() {
console.info("MainAbility onWindowStageDestroy")
}
onForeground() {
console.info("MainAbility onForeground")
}
onBackground() {
console.info("MainAbility onBackground")
}
}
```
## UIAbility访问PageAbility(startAbilityForResult)
startAbilityForResult和startAbility的区别是当PageAbility销毁的时候会返回执行结果。
UIAbility通过startAbilityForResult启动PageABility和UIAbility通过startAbilityForResult启动UIAbility的代码一样,没有任何区别。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class MainAbility extends UIAbility {
onCreate(want, launchParam) {
console.info("MainAbility onCreate")
}
onDestroy() {
console.info("MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.info("MainAbility onWindowStageCreate")
windowStage.loadContent('pages/Index', (err, data) => {
// ...
});
let want = {
bundleName: "com.ohos.fa",
abilityName: "MainAbility",
};
this.context.startAbilityForResult(want).then((result) => {
console.info('Ability verify result: ' + JSON.stringify(result));
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
}
onWindowStageDestroy() {
console.info("MainAbility onWindowStageDestroy")
}
onForeground() {
console.info("MainAbility onForeground")
}
onBackground() {
console.info("MainAbility onBackground")
}
}
```
## ExtensionAbility启动PageAbility
下面以ServiceExtensionAbility为例来说明ExtensionAbility启动PageAbility。ServiceExtensionAbility启动PageAbility和ServiceExtensionAbility启动UIAbility的方式完全相同。
```ts
import Extension from '@ohos.app.ability.ServiceExtensionAbility'
export default class ServiceExtension extends Extension {
onCreate(want) {
console.info("ServiceExtension onCreate")
}
onDestroy() {
console.info("ServiceExtension onDestroy")
}
onRequest(want, startId) {
console.info("ServiceExtension onRequest")
let wantFA = {
bundleName: "com.ohos.fa",
abilityName: "MainAbility",
};
this.context.startAbility(wantFA).then(() => {
console.info('Start Ability successfully.');
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
}
}
```
# 启动远程PageAbility(仅对系统应用开放)
启动远程PageAbility同样通过featureAbility中的startAbility接口实现。
除引入'\@ohos.ability.featureAbility'外,还需引入'\@ohos.distributedHardware.deviceManager',通过DeviceManager(该组件在OpenHarmony上提供账号无关的分布式设备的认证组网能力)的getTrustedDeviceListSync接口(获取信任设备列表)获取远端的deviceId,写入want中,用于启动远程PageAbility。
由于当前DeviceManager的getTrustedDeviceListSync接口仅对系统应用开放,故现阶段非系统应用无法获取其他设备信息,无远程启动设备选择入口,远程启动Ability开发。
**表1** featureAbility接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| startAbility(parameter:&nbsp;StartAbilityParameter) | 启动Ability。 |
| startAbilityForResult(parameter:&nbsp;StartAbilityParameter) | 启动Ability,并在该Ability被销毁时返回执行结果。 |
**表2** deviceManager接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| getTrustedDeviceListSync():&nbsp;Array&lt;DeviceInfo&gt; | 同步获取所有可信设备列表。 |
在跨设备场景下,启动远程PageAbility首先需要向用户申请数据同步的权限,相关接口说明如下:
**表3** AtManager接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| checkAccessToken(tokenID:&nbsp;number,&nbsp;permissionName:&nbsp;string)<br>:&nbsp;Promise&lt;GrantStatus&gt; | 校验应用是否授予权限。使用Promise异步回调。返回值GrantStatus。建议使用checkAccessToken代替verifyAccessToken(已废弃),verifyAccessToken从API&nbsp;version&nbsp;9开始不再维护。 |
**表4** context接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| requestPermissionsFromUser(permissions:&nbsp;Array&lt;string&gt;,&nbsp;<br>requestCode:&nbsp;number,&nbsp;resultCallback:&nbsp;AsyncCallback&lt;<br>PermissionRequestResult&gt;):&nbsp;void | 以callback形式从系统请求某些权限,详见对应[接口文档](../reference/apis/js-apis-Context.md#contextrequestpermissionsfromuser7-1)。 |
如下示例代码展示了向用户申请数据同步权限的方法:
```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
import featureAbility from '@ohos.ability.featureAbility';
import bundle from '@ohos.bundle.bundleManager';
async function RequestPermission() {
console.info('RequestPermission begin');
let array: Array<string> = ["ohos.permission.DISTRIBUTED_DATASYNC"];
let bundleFlag = 0;
let tokenID = undefined;
let userID = 100;
let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID);
tokenID = appInfo.accessTokenId;
let atManager = abilityAccessCtrl.createAtManager();
let requestPermissions: Array<string> = [];
for (let i = 0;i < array.length; i++) {
let result = await atManager.verifyAccessToken(tokenID, array[i]);
console.info("checkAccessToken result:" + JSON.stringify(result));
if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
requestPermissions.push(array[i]);
}
}
console.info("requestPermissions:" + JSON.stringify(requestPermissions));
if (requestPermissions.length == 0 || requestPermissions == []) {
return;
}
let context = featureAbility.getContext();
context.requestPermissionsFromUser(requestPermissions, 1, (error, data)=>{
console.info("data:" + JSON.stringify(data));
console.info("data requestCode:" + data.requestCode);
console.info("data permissions:" + data.permissions);
console.info("data authResults:" + data.authResults);
});
console.info('RequestPermission end');
}
```
在获取数据同步权限后,需要获取可信设备列表,进行设备选择。
如下示例展示了通过getTrustedDeviceListSync获取可信设备列表,选择设备的方法。
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
let dmClass;
function getDeviceManager() {
deviceManager.createDeviceManager('ohos.example.distributedService', (error, dm) => {
if (error) {
console.info('create device manager failed with ' + error)
}
dmClass = dm;
})
}
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass != null) {
let list = dmClass.getTrustedDeviceListSync();
if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
console.info("MainAbility onButtonClick getRemoteDeviceId err: list is null");
return;
}
console.info("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
return list[0].deviceId;
} else {
console.info("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
}
}
```
设备选择完成后,通过调用startAbility接口,显式启动远程PageAbility。
如下示例展示了通过startAbility显式启动远程PageAbility的方法。
```ts
import featureAbility from '@ohos.ability.featureAbility';
function onStartRemoteAbility() {
console.info('onStartRemoteAbility begin');
let params;
let wantValue = {
bundleName: 'ohos.samples.etsDemo',
abilityName: 'ohos.samples.etsDemo.RemoteAbility',
deviceId: getRemoteDeviceId(), // getRemoteDeviceId的定义在前面的示例代码中
parameters: params
};
console.info('onStartRemoteAbility want=' + JSON.stringify(wantValue));
featureAbility.startAbility({
want: wantValue
}).then((data) => {
console.info('onStartRemoteAbility finished, ' + JSON.stringify(data));
});
console.info('onStartRemoteAbility end');
}
```
# 启动ServiceAbility
ServiceAbility的启动与其他Ability并无区别,应用开发者可以在PageAbility中通过featureAbility的startAbility接口拉起ServiceAbility,在ServiceAbility中通过particleAbility的startAbility接口拉起ServiceAbility。ServiceAbility的启动规则详见[组件启动规则](component-startup-rules.md)章节。
如下示例展示了在PageAbility中通过startAbility启动bundleName为"com.example.myapplication",abilityName为"ServiceAbility"的ServiceAbility的方法。启动FA模型的ServiceAbility时,需要在abilityName前拼接bundleName字符串。
```ts
import featureAbility from '@ohos.ability.featureAbility'
async function startServiceAbility() {
try {
console.info('Begin to start ability')
let param = {
want: {
bundleName: "com.example.myapplication",
abilityName: "com.example.myapplication.ServiceAbility"
}
}
await featureAbility.startAbility(param)
console.info(`Start ability succeed`)
} catch (error) {
console.error('Start ability failed with ' + error)
}
}
```
执行上述代码后,Ability将通过startAbility() 方法来启动ServiceAbility。
- 如果ServiceAbility尚未运行,则系统会先调用onStart()来初始化ServiceAbility,再回调Service的onCommand()方法来启动ServiceAbility。
- 如果ServiceAbility正在运行,则系统会直接回调ServiceAbility的onCommand()方法来启动ServiceAbility。
# FA模型启动Stage模型UIAbility
本文介绍FA模型的三种应用组件如何启动Stage模型的UIAbility组件。
## PageAbility启动UIAbility
在PageAbility中启动UIAbility和在PageAbility中启动PageAbility的方式完全相同。
```ts
import featureAbility from '@ohos.ability.featureAbility';
let parameter = {
"want": {
bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.MainAbility"
}
};
featureAbility.startAbility(parameter).then((code) => {
console.info('Ability verify code: ' + JSON.stringify(code));
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
```
## PageAbility访问UIAbility(startAbilityForResult)
startAbilityForResult和startAbility的区别是当UIAbility销毁的时候会返回执行结果。
在PageAbility中通过startAbilityForResult启动UIAbility和在PageAbility中通过startAbilityForResult启动PageAbility的方式完全相同。
```ts
import featureAbility from '@ohos.ability.featureAbility';
let parameter = {
"want": {
bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.MainAbility"
}
};
featureAbility.startAbilityForResult(parameter).then((result) => {
console.info('Ability verify result: ' + JSON.stringify(result));
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
```
## ServiceAbility/DataAbility启动UIAbility
在ServiceAbility/DataAbility中启动UIAbility和在ServiceAbility/DataAbility中启动PageAbility的方式完全相同。
```ts
import particleAbility from '@ohos.ability.particleAbility';
let parameter = {
"want": {
bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.MainAbility"
}
};
particleAbility.startAbility(parameter).then(() => {
console.info('Start Ability successfully.');
}).catch((error) => {
console.error("Ability failed: " + JSON.stringify(error));
});
```
# 停止PageAbility
停止PageAbility通过featureAbility中的terminateSelf接口实现。
**表1** featureAbility接口说明
| 接口名 | 接口描述 |
| -------- | -------- |
| terminateSelf() | 停止Ability。 |
| terminateSelfWithResult(parameter:&nbsp;AbilityResult) | 设置该PageAbility停止时返回给调用者的结果及数据并停止Ability。 |
如下示例展示了停止Ability的方法。
```ts
import featureAbility from '@ohos.ability.featureAbility'
(async () => {
try {
console.info('Begin to terminateSelf')
await featureAbility.terminateSelf()
console.info('terminateSelf succeed')
}
catch (error) {
console.error('terminateSelf failed with ' + error)
}
})()
```
# Storage接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| GetStorageOptions | Stage模型无对应接口 | Storage接口功能使用Prefereces接口来代替,接口入参已经重新设计 |
| SetStorageOptions | Stage模型无对应接口 | Storage接口功能使用Prefereces接口来代替,接口入参已经重新设计 |
| ClearStorageOptions | Stage模型无对应接口 | Storage接口功能使用Prefereces接口来代替,接口入参已经重新设计 |
| DeleteStorageOptions | Stage模型无对应接口 | Storage接口功能使用Prefereces接口来代替,接口入参已经重新设计 |
| [static&nbsp;get(options:&nbsp;GetStorageOptions):&nbsp;void;](../reference/apis/js-apis-system-storage.md#storageget) | \@ohos.data.preferences.d.ts | [get(key:&nbsp;string,&nbsp;defValue:&nbsp;ValueType,&nbsp;callback:&nbsp;AsyncCallback&lt;ValueType&gt;):&nbsp;void;](../reference/apis/js-apis-data-preferences.md#get)<br/>[get(key:&nbsp;string,&nbsp;defValue:&nbsp;ValueType):&nbsp;Promise&lt;ValueType&gt;;](../reference/apis/js-apis-data-preferences.md#get-1) |
| [static&nbsp;set(options:&nbsp;SetStorageOptions):&nbsp;void;](../reference/apis/js-apis-system-storage.md#storageset) | \@ohos.data.preferences.d.ts | [put(key:&nbsp;string,&nbsp;value:&nbsp;ValueType,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-preferences.md#put)<br/>[put(key:&nbsp;string,&nbsp;value:&nbsp;ValueType):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-data-preferences.md#put-1) |
| [static&nbsp;clear(options?:&nbsp;ClearStorageOptions):&nbsp;void;](../reference/apis/js-apis-system-storage.md#storageclear) | \@ohos.data.preferences.d.ts | [clear(callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-preferences.md#clear)<br/>[clear():&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-data-preferences.md#clear-1) |
| [static&nbsp;delete(options:&nbsp;DeleteStorageOptions):&nbsp;void;](../reference/apis/js-apis-system-storage.md#storagedelete) | \@ohos.data.preferences.d.ts | [delete(key:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void;](../reference/apis/js-apis-data-preferences.md#delete)<br/>[delete(key:&nbsp;string):&nbsp;Promise&lt;void&gt;;](../reference/apis/js-apis-data-preferences.md#delete-1) |
# 线程模型
FA模型下的线程主要有如下三类:
- 主线程
负责管理其他线程
- Ability线程
- 每个Ability一个线程
- 输入事件分发
- UI绘制
- 应用代码回调(事件处理,生命周期)
- 接收Worker发送的消息
- Worker线程
执行耗时操作
基于OpenHarmony的线程模型,不同的业务功能运行在不同的线程上,业务功能的交互就需要线程间通信。线程间通信目前主要有Emitter和Worker两种方式,其中Emitter主要适用于线程间的事件同步, Worker主要用于新开一个线程执行耗时任务。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> FA模型每个ability都有一个独立的线程,Emiter可用于Ability线程内、Ability线程间、Ability线程与Worker线程的事件同步。
# 线程模型
OpenHarmony应用中每个进程都会有一个主线程,主线程有如下职责:
1. 负责管理其他线程
2. 同应用多个UIAbility组件共用一个主线程
3. 输入事件分发
4. UI绘制
5. 应用代码回调(事件处理,生命周期)
6. 接收Worker发送的消息
除主线程外,还有一类与主线程并行的独立线程Worker,主要用于执行耗时操作,但不可以直接操作UI。Worker线程在主线程中创建,与主线程相互独立。最多可以创建7个Worker:
![thread-model-stage](figures/thread-model-stage.png)
基于OpenHarmony的线程模型,不同的业务功能运行在不同的线程上,业务功能的交互就需要线程间通信。线程间通信目前主要有Emitter和Worker两种方式,其中Emitter主要适用于线程间的事件同步, Worker主要用于新开一个线程执行耗时任务。
**说明:**
* Stage模型只提供了主线程和Worker线程,Emitter主要用于主线程内或者主线程和Worker线程的事件同步。
\ No newline at end of file
# UIAbility组件与UI的数据同步
基于OpenHarmony的应用模型,可以通过以下两种方式来实现UIAbility组件与UI之间的数据同步。
1. EventHub:基于发布订阅模式来实现,事件需要先订阅后发布,订阅者收到消息后进行处理。
2. globalThis:ArkTS引擎实例内部的一个全局对象,在ArkTS引擎实例内部都能访问。
## 使用EventHub进行数据通信
EventHub提供了UIAbility组件/ExtensionAbility组件级别的事件机制,以UIAbility组件/ExtensionAbility组件为中心提供了订阅、取消订阅和触发事件的数据通信能力。接口说明请参见[EventHub](../reference/apis/js-apis-eventhub.md)
在使用EventHub之前,首先需要获取EventHub对象。[基类Context](application-context-stage.md)提供了EventHub对象,本章节以使用EventHub实现UIAbility与UI之间的数据通信为例进行说明。
1. 在UIAbility中调用eventHub.on()方法注册一个自定义事件“event1”,eventHub.on()有如下两种调用方式,使用其中一种即可。
```ts
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));
});
}
}
```
2. 在UI界面中通过eventHub.emit()方法触发该事件,在触发事件的同时,根据需要传入参数信息。
```ts
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() {
// ...
}
}
```
3. 在UIAbility的注册事件回调中可以得到对应的触发事件结果,运行日志结果如下所示。
```ts
[]
[1]
[2,'test']
```
4. 在自定义事件“event1”使用完成后,可以根据需要调用eventHub.off()方法取消该事件的订阅。
```ts
// context为UIAbility实例的AbilityContext
this.context.eventHub.off('event1');
```
## 使用globalThis进行数据同步
globalThis是ArkTS引擎实例内部的一个全局对象,引擎内部的UIAbility/ExtensionAbility/Page都可以使用,因此可以使用globalThis全局对象进行数据同步。
**图1** 使用globalThis进行数据同步
![globalThis1](figures/globalThis1.png)
如上图所示,下面从如下三个场景和一个注意点来介绍globalThis的使用:
- [UIAbility和Page之间使用globalThis](#uiability和page之间使用globalthis)
- [UIAbility和UIAbility之间使用globalThis](#uiability和uiability之间使用globalthis)
- [UIAbility和ExtensionAbility之间使用globalThis](#uiability和extensionability之间使用globalthis)
- [globalThis使用的注意事项](#globalthis使用的注意事项)
### UIAbility和Page之间使用globalThis
globalThis为[ArkTS引擎实例](thread-model-stage.md)下的全局对象,可以通过globalThis绑定属性/方法来进行UIAbility组件与UI的数据同步。例如在UIAbility组件中绑定want参数,即可在UIAbility对应的UI界面上使用want参数信息。
1. 调用startAbility()方法启动一个UIAbility实例时,被启动的UIAbility创建完成后会进入onCreate()生命周期回调,且在onCreate()生命周期回调中能够接受到传递过来的want参数,可以将want参数绑定到globalThis上。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class EntryAbility extends UIAbility {
onCreate(want, launch) {
globalThis.entryAbilityWant = want;
// ...
}
// ...
}
```
2. 在UI界面中即可通过globalThis获取到want参数信息。
```ts
let entryAbilityWant;
@Entry
@Component
struct Index {
aboutToAppear() {
entryAbilityWant = globalThis.entryAbilityWant;
}
// 页面展示
build() {
// ...
}
}
```
### UIAbility和UIAbility之间使用globalThis
同一个应用中UIAbility和UIAbility之间的数据传递,可以通过将数据绑定到全局变量globalThis上进行同步,如在AbilityA中将数据保存在globalThis,然后跳转到AbilityB中取得该数据:
1. AbilityA中保存数据一个字符串数据并挂载到globalThis上。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityA extends UIAbility {
onCreate(want, launch) {
globalThis.entryAbilityStr = 'AbilityA'; // AbilityA存放字符串“AbilityA”到globalThis
// ...
}
}
```
2. AbilityB中获取对应的数据。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityB extends UIAbility {
onCreate(want, launch) {
// AbilityB从globalThis读取name并输出
console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr);
// ...
}
}
```
### UIAbility和ExtensionAbility之间使用globalThis
同一个应用中UIAbility和ExtensionAbility之间的数据传递,也可以通过将数据绑定到全局变量globalThis上进行同步,如在AbilityA中保存数据,在ServiceExtensionAbility中获取数据。
1. AbilityA中保存数据一个字符串数据并挂载到globalThis上。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityA extends UIAbility {
onCreate(want, launch) {
// AbilityA存放字符串“AbilityA”到globalThis
globalThis.entryAbilityStr = 'AbilityA';
// ...
}
}
```
2. ExtensionAbility中获取数据。
```ts
import Extension from '@ohos.app.ability.ServiceExtensionAbility'
export default class ServiceExtAbility extends Extension {
onCreate(want) {
// ServiceExtAbility从globalThis读取name并输出
console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr);
// ...
}
}
```
### globalThis使用的注意事项
**图2** globalThis注意事项
![globalThis2](figures/globalThis2.png)
- Stage模型下进程内的UIAbility组件共享ArkTS引擎实例,使用globalThis时需要避免存放相同名称的对象。例如AbilityA和AbilityB可以使用globalThis共享数据,在存放相同名称的对象时,先存放的对象会被后存放的对象覆盖。
- FA模型因为每个UIAbility组件之间引擎隔离,不会存在该问题。
- 对于绑定在globalThis上的对象,其生命周期与ArkTS虚拟机实例相同,建议在使用完成之后将其赋值为null,以减少对应用内存的占用。
Stage模型上同名对象覆盖导致问题的场景举例说明。
1. 在AbilityA文件中使用globalThis中存放了[UIAbilityContext](../reference/apis/js-apis-ability-context.md)
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityA extends UIAbility {
onCreate(want, launch) {
globalThis.context = this.context; // AbilityA存放context到globalThis
// ...
}
}
```
2. 在AbilityA的页面中获取该[UIAbilityContext](../reference/apis/js-apis-ability-context.md)并进行使用。使用完成后将AbilityA实例切换至后台。
```ts
@Entry
@Component
struct Index {
onPageShow() {
let ctx = globalThis.context; // 页面中从globalThis中取出context并使用
let permissions = ['com.example.permission']
ctx.requestPermissionsFromUser(permissions,(result) => {
// ...
});
}
// 页面展示
build() {
// ...
}
}
```
3. 在AbilityB文件中使用globalThis中存放了[UIAbilityContext](../reference/apis/js-apis-ability-context.md),并且命名为相同的名称。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityB extends UIAbility {
onCreate(want, launch) {
// AbilityB覆盖了AbilityA在globalThis中存放的context
globalThis.context = this.context;
// ...
}
}
```
4. 在AbilityB的页面中获取该[UIAbilityContext](../reference/apis/js-apis-ability-context.md)并进行使用。此时获取到的globalThis.context已经表示为AbilityB中赋值的[UIAbilityContext](../reference/apis/js-apis-ability-context.md)内容。
```ts
@Entry
@Component
struct Index {
onPageShow() {
let ctx = globalThis.context; // Page中从globalThis中取出context并使用
let permissions = ['com.example.permission']
ctx.requestPermissionsFromUser(permissions,(result) => {
console.info('requestPermissionsFromUser result:' + JSON.stringify(result));
});
}
// 页面展示
build() {
// ...
}
}
```
5. 在AbilityB实例切换至后台,将AbilityA实例从后台切换回到前台。此时AbilityA的onCreate生命周期不会再次进入。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class AbilityA extends UIAbility {
onCreate(want, launch) { // AbilityA从后台进入前台,不会再走这个生命周期
globalThis.context = this.context;
// ...
}
}
```
6. 在AbilityA的页面再次回到前台时,其获取到的globalThis.context表示的为AbilityB的[UIAbilityContext](../reference/apis/js-apis-ability-context.md),而不是AbilityA的[UIAbilityContext](../reference/apis/js-apis-ability-context.md),在AbilityA的页面中使用则会出错。
```ts
@Entry
@Component
struct Index {
onPageShow() {
let ctx = globalThis.context; // 这时候globalThis中的context是AbilityB的context
let permissions=['com.example.permission'];
ctx.requestPermissionsFromUser(permissions,(result) => { // 使用这个对象就会导致进程崩溃
console.info('requestPermissionsFromUser result:' + JSON.stringify(result));
});
}
// 页面展示
build() {
// ...
}
}
```
# UIAbility组件间交互(设备内)
UIAbility是系统调度的最小单元。在设备内的功能模块之间跳转时,会涉及到启动特定的UIAbility,该UIAbility可以是应用内的其他UIAbility,也可以是其他应用的UIAbility(例如启动三方支付UIAbility)。
本章节将从如下场景分别介绍设备内UIAbility间的交互方式。对于跨设备的应用组件交互,请参见[应用组件跨设备交互(流转)](inter-device-interaction-hop-overview.md)
- [启动应用内的UIAbility](#启动应用内的uiability)
- [启动应用内的UIAbility并获取返回结果](#启动应用内的uiability并获取返回结果)
- [启动其他应用的UIAbility](#启动其他应用的uiability)
- [启动其他应用的UIAbility并获取返回结果](#启动其他应用的uiability并获取返回结果)
- [启动UIAbility的指定页面](#启动uiability的指定页面)
- [通过Call调用实现UIAbility交互(仅对系统应用开放)](#通过call调用实现uiability交互仅对系统应用开放)
## 启动应用内的UIAbility
当一个应用内包含多个UIAbility时,存在应用内启动UIAbility的场景。例如在支付应用中从入口UIAbility启动收付款UIAbility。
假设应用中有两个UIAbility:EntryAbility和FuncAbility(可以在应用的一个Module中,也可以在的不同Module中),需要从EntryAbility的页面中启动FuncAbility。
1. 在EntryAbility中,通过调用startAbility()方法启动UIAbility,[want](../reference/apis/js-apis-application-Want.md)为UIAbility实例启动的入口参数,其中bundleName为待启动应用的Bundle名称,abilityName为待启动的UIAbility名称,moduleName在待启动的UIAbility属于不同的Module时添加,parameters为自定义信息参数。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
let wantInfo = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1', // moduleName非必选
parameters: { // 自定义信息
info: '来自EntryAbility Index页面',
},
}
// context为调用方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
// ...
}).catch((err) => {
// ...
})
```
2. 在FuncAbility的生命周期回调文件中接收EntryAbility传递过来的参数。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class FuncAbility extends UIAbility {
onCreate(want, launchParam) {
// 接收调用方UIAbility传过来的参数
let funcAbilityWant = want;
let info = funcAbilityWant?.parameters?.info;
// ...
}
}
```
3. 在FuncAbility业务完成之后,如需要停止当前UIAbility实例,在FuncAbility中通过调用terminateSelf()方法实现。
```ts
// context为需要停止的UIAbility实例的AbilityContext
this.context.terminateSelf((err) => {
// ...
});
```
## 启动应用内的UIAbility并获取返回结果
在一个EntryAbility启动另外一个FuncAbility时,希望在被启动的FuncAbility完成相关业务后,能将结果返回给调用方。例如在应用中将入口功能和帐号登录功能分别设计为两个独立的UIAbility,在帐号登录UIAbility中完成登录操作后,需要将登录的结果返回给入口UIAbility。
1. 在EntryAbility中,调用startAbilityForResult()接口启动FuncAbility,异步回调中的data用于接收FuncAbility停止自身后返回给EntryAbility的信息。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
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) => {
// ...
})
```
2. 在FuncAbility停止自身时,需要调用terminateSelfWithResult()方法,入参abilityResult为FuncAbility需要返回给EntryAbility的信息。
```ts
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) => {
// ...
});
```
3. FuncAbility停止自身后,EntryAbility通过startAbilityForResult()方法回调接收被FuncAbility返回的信息,RESULT_CODE需要与前面的数值保持一致。
```ts
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) => {
// ...
})
```
## 启动其他应用的UIAbility
启动其他应用的UIAbility,通常用户只需要完成一个通用的操作(例如需要选择一个文档应用来查看某个文档的内容信息),推荐使用[隐式Want启动](want-overview.md#want的类型)。系统会根据调用方的want参数来识别和启动匹配到的应用UIAbility。
启动UIAbility有[显式Want启动和隐式Want启动](want-overview.md)两种方式。
- 显式Want启动:启动一个确定应用的UIAbility,在want参数中需要设置该应用bundleName和abilityName,当需要拉起某个明确的UIAbility时,通常使用显式Want启动方式。
- 隐式Want启动:根据匹配条件由用户选择启动哪一个UIAbility,即不明确指出要启动哪一个UIAbility(abilityName参数未设置),在调用startAbility()方法时,其入参want中指定了一系列的[entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity)字段(表示目标UIAbility额外的类别信息,如浏览器、视频播放器)和[actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction)字段(表示要执行的通用操作,如查看、分享、应用详情等)等参数信息,然后由系统去分析want,并帮助找到合适的UIAbility来启动。当需要拉起其他应用的UIAbility时,开发者通常不知道用户设备中应用的安装情况,也无法确定目标应用的bundleName和abilityName,通常使用隐式Want启动方式。
本章节主要讲解如何通过隐式Want启动其他应用的UIAbility。
1. 将多个待匹配的文档应用安装到设备,在其对应UIAbility的module.json5配置文件中,配置skills的[entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity)字段和[actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction)字段。
```json
{
"module": {
"abilities": [
{
// ...
"skills": [
{
"entities": [
// ...
"entity.system.default"
],
"actions": [
// ...
"ohos.want.action.viewData"
]
}
]
}
]
}
}
```
2. 在调用方want参数中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
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.viewData',
// entities can be omitted.
entities: ['entity.system.default'],
}
// context为调用方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
// ...
}).catch((err) => {
// ...
})
```
效果示意如下图所示,点击“打开PDF文档”时,会弹出选择框供用户选择。![uiability-intra-device-interaction](figures/uiability-intra-device-interaction.png)
3. 在文档应用使用完成之后,如需要停止当前UIAbility实例,通过调用terminateSelf()方法实现。
```ts
// context为需要停止的UIAbility实例的AbilityContext
this.context.terminateSelf((err) => {
// ...
});
```
## 启动其他应用的UIAbility并获取返回结果
当使用隐式Want启动其他应用的UIAbility并希望获取返回结果时,调用方需要使用startAbilityForResult()方法启动目标UIAbility。例如主应用中需要启动三方支付并获取支付结果。
1. 在支付应用对应UIAbility的module.json5配置文件中,配置skills的[entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity)字段和[actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction)字段。
```json
{
"module": {
"abilities": [
{
// ...
"skills": [
{
"entities": [
// ...
"entity.system.default"
],
"actions": [
// ...
"ohos.want.action.editData"
]
}
]
}
]
}
}
```
2. 调用方使用startAbilityForResult()方法启动支付应用的UIAbility,在调用方want参数中的entities和action需要被包含在待匹配UIAbility的skills配置的entities和actions中。异步回调中的data用于后续接收支付UIAbility停止自身后返回给调用方的信息。系统匹配到符合entities和actions参数条件的UIAbility后,会弹出选择框展示匹配到的UIAbility实例列表供用户选择使用。
```ts
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. 在支付UIAbility完成支付之后,需要调用terminateSelfWithResult()方法实现停止自身,并将abilityResult参数信息返回给调用方。
```ts
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. 在调用方startAbilityForResult()方法回调中接收支付应用返回的信息,RESULT_CODE需要与前面terminateSelfWithResult()返回的数值保持一致。
```ts
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的指定页面
一个UIAbility可以对应多个页面,在不同的场景下启动该UIAbility时需要展示不同的页面,例如从一个UIAbility的页面中跳转到另外一个UIAbility时,希望启动目标UIAbility的指定页面。本文主要讲解目标UIAbility首次启动和目标UIAbility非首次启动两种启动指定页面的场景,以及在讲解启动指定页面之前会讲解到在调用方如何指定启动页面。
### 调用方UIAbility指定启动页面
调用方UIAbility启动另外一个UIAbility时,通常需要跳转到指定的页面。例如FuncAbility包含两个页面(Index对应首页,Second对应功能A页面),此时需要在传入的want参数中配置指定的页面路径信息,可以通过want中的parameters参数增加一个自定义参数传递页面跳转信息。示例中的context的获取方式参见[获取UIAbility的Context属性](uiability-usage.md#获取uiability的上下文信息)
```ts
let wantInfo = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'module1', // moduleName非必选
parameters: { // 自定义参数传递页面信息
router: 'funcA',
},
}
// context为调用方UIAbility的AbilityContext
this.context.startAbility(wantInfo).then(() => {
// ...
}).catch((err) => {
// ...
})
```
### 目标UIAbility首次启动
目标UIAbility首次启动时,在目标UIAbility的onWindowStageCreate()生命周期回调中,解析EntryAbility传递过来的want参数,获取到需要加载的页面信息url,传入windowStage.loadContent()方法。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
import Window from '@ohos.window'
export default class FuncAbility extends UIAbility {
funcAbilityWant;
onCreate(want, launchParam) {
// 接收调用方UIAbility传过来的参数
this.funcAbilityWant = want;
}
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability
let url = 'pages/Index';
if (this.funcAbilityWant?.parameters?.router) {
if (this.funcAbilityWant.parameters.router === 'funA') {
url = 'pages/Second';
}
}
windowStage.loadContent(url, (err, data) => {
// ...
});
}
}
```
### 目标UIAbility非首次启动
经常还会遇到一类场景,当应用A已经启动且处于主页面时,回到桌面,打开应用B,并从应用B再次启动应用A,且需要跳转到应用A的指定页面。例如联系人应用和短信应用配合使用的场景。打开短信应用主页,回到桌面,此时短信应用处于已打开状态且当前处于短信应用的主页。再打开联系人应用主页,进入联系人用户A查看详情,点击短信图标,准备给用户A发送短信,此时会再次拉起短信应用且当前处于短信应用的发送页面。
![uiability_not_first_started](figures/uiability_not_first_started.png)
针对以上场景,即当应用A的UIAbility实例已创建,并且处于该UIAbility实例对应的主页面中,此时,从应用B中需要再次启动应用A的该UIAbility,并且需要跳转到不同的页面,这种情况下要如何实现呢?
1. 在目标UIAbility中,默认加载的是Index页面。由于当前UIAbility实例之前已经创建完成,此时会进入UIAbility的onNewWant()回调中且不会进入onCreate()和onWindowStageCreate()生命周期回调,在onNewWant()回调中解析调用方传递过来的want参数,并挂在到全局变量globalThis中,以便于后续在页面中获取。
```ts
import UIAbility from '@ohos.app.ability.UIAbility'
export default class FuncAbility extends UIAbility {
onNewWant(want, launchParam) {
// 接收调用方UIAbility传过来的参数
globalThis.funcAbilityWant = want;
// ...
}
}
```
2. 在FuncAbility中,此时需要在Index页面中通过页面路由Router模块实现指定页面的跳转,由于此时FuncAbility对应的Index页面是处于激活状态,不会重新变量声明以及进入aboutToAppear()生命周期回调中。因此可以在Index页面的onPageShow()生命周期回调中实现页面路由跳转的功能。
```ts
import router from '@ohos.router';
@Entry
@Component
struct Index {
onPageShow() {
let funcAbilityWant = globalThis.funcAbilityWant;
let url2 = funcAbilityWant?.parameters?.router;
if (url2 && url2 === 'funcA') {
router.replaceUrl({
url: 'pages/Second',
})
}
}
// 页面展示
build() {
// ...
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当被调用方[Ability的启动模式](uiability-launch-type.md)设置为standard启动模式时,每次启动都会创建一个新的实例,那么[onNewWant()](../reference/apis/js-apis-application-ability.md#abilityonnewwant)回调就不会被用到。
## 通过Call调用实现UIAbility交互(仅对系统应用开放)
Call调用是UIAbility能力的扩展,它为UIAbility提供一种能够被外部调用并与外部进行通信的能力。Call调用支持前台与后台两种启动方式,使UIAbility既能被拉起到前台展示UI,也可以在后台被创建并运行。Call调用在调用方与被调用方间建立了IPC通信,因此应用开发者可通过Call调用实现不同Ability之间的数据共享。
Call调用的核心接口是startAbilityByCall方法,与startAbility接口的不同之处在于:
- startAbilityByCall支持前台与后台两种启动方式,而startAbility仅支持前台启动。
- 调用方可使用startAbilityByCall所返回的Caller对象与被调用方进行通信,而startAbilty不具备通信能力。
Call调用的使用场景主要包括:
- 需要与被启动的UIAbility进行通信。
- 希望被启动的UIAbility在后台运行。
**表1** Call调用相关名词解释
| 名词 | 描述 |
| -------- | -------- |
| CallerAbility | 进行Call调用的UIAbility(调用方)。 |
| CalleeAbility | 被Call调用的UIAbility(被调用方)。 |
| Caller | 实际对象,由startAbilityByCall接口返回,CallerAbility可使用Caller与CalleeAbility进行通信。 |
| Callee | 实际对象,被CalleeAbility持有,可与Caller进行通信。 |
Call调用示意图如下所示。
**图1** Call调用示意图
![call](figures/call.png)
- CallerAbility调用startAbilityByCall接口获取Caller,并使用Caller对象的call方法向CalleeAbility发送数据。
- CalleeAbility持有一个Callee对象,通过Callee的on方法注册回调函数,当接收到Caller发送的数据时将会调用对应的回调函数。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 1. 当前仅支持系统应用使用Call调用。
>
> 2. CalleeAbility的启动模式需要为单实例。
>
> 3. Call调用既支持本地(设备内)Call调用,也支持跨设备Call调用,下面介绍设备内Call调用方法。跨设备Call调用方法请参见[跨设备Call调用](hop-multi-device-collaboration.md#通过跨设备call调用实现多端协同)。
### 接口说明
Call功能主要接口如下表所示。具体的API详见[接口文档](../reference/apis/js-apis-application-ability.md#caller)
**表2** Call功能主要接口
| 接口名 | 描述 |
| -------- | -------- |
| startAbilityByCall(want:&nbsp;Want):&nbsp;Promise&lt;Caller&gt; | 启动指定UIAbility并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见[接口文档](../reference/apis/js-apis-ability-context.md#abilitycontextstartabilitybycall)。AbilityContext与ServiceExtensionContext均支持该接口。 |
| on(method:&nbsp;string,&nbsp;callback:&nbsp;CalleeCallBack):&nbsp;void | 通用组件Callee注册method对应的callback方法。 |
| off(method:&nbsp;string):&nbsp;void | 通用组件Callee解注册method的callback方法。 |
| call(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Sequenceable):&nbsp;Promise&lt;void&gt; | 向通用组件Callee发送约定序列化数据。 |
| callWithResult(method:&nbsp;string,&nbsp;data:&nbsp;rpc.Sequenceable):&nbsp;Promise&lt;rpc.MessageParcel&gt; | 向通用组件Callee发送约定序列化数据,&nbsp;并将Callee返回的约定序列化数据带回。 |
| release():&nbsp;void | 释放通用组件的Caller通信接口。 |
| on(type:&nbsp;"release",&nbsp;callback:&nbsp;OnReleaseCallback):&nbsp;void | 注册通用组件通信断开监听通知。 |
设备内通过Call调用实现UIAbility交互,涉及如下两部分开发:
- [创建Callee被调用端](#开发步骤创建callee被调用端)
- [访问Callee被调用端](#开发步骤访问callee被调用端)
### 开发步骤(创建Callee被调用端)
在Callee被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。
1. 配置Ability的启动模式。
配置module.json5,将CalleeAbility配置为单实例"singleton"。
| Json字段 | 字段说明 |
| -------- | -------- |
| "launchType" | Ability的启动模式,设置为"singleton"类型。 |
Ability配置标签示例如下:
```json
"abilities":[{
"name": ".CalleeAbility",
"srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts",
"launchType": "singleton",
"description": "$string:CalleeAbility_desc",
"icon": "$media:icon",
"label": "$string:CalleeAbility_label",
"visible": true
}]
```
2. 导入UIAbility模块。
```ts
import Ability from '@ohos.app.ability.UIAbility';
```
3. 定义约定的序列化数据。
调用端及被调用端发送接收的数据格式需协商一致,如下示例约定数据由number和string组成。
```ts
export default class MySequenceable {
num: number = 0
str: string = ""
constructor(num, string) {
this.num = num
this.str = string
}
marshalling(messageParcel) {
messageParcel.writeInt(this.num)
messageParcel.writeString(this.str)
return true
}
unmarshalling(messageParcel) {
this.num = messageParcel.readInt()
this.str = messageParcel.readString()
return true
}
}
```
4. 实现Callee.on监听及Callee.off解除监听。
被调用端Callee的监听函数注册时机,取决于应用开发者。注册监听之前的数据不会被处理,取消监听之后的数据不会被处理。如下示例在Ability的onCreate注册'MSG_SEND_METHOD'监听,在onDestroy取消监听,收到序列化数据后作相应处理并返回,应用开发者根据实际需要做相应处理。具体示例代码如下:
```ts
const TAG: string = '[CalleeAbility]';
const MSG_SEND_METHOD: string = 'CallSendMsg';
function sendMsgCallback(data) {
console.info('CalleeSortFunc called');
// 获取Caller发送的序列化数据
let receivedData = new MySequenceable(0, '');
data.readSequenceable(receivedData);
console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`);
// 作相应处理
// 返回序列化数据result给Caller
return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`);
}
export default class CalleeAbility extends Ability {
onCreate(want, launchParam) {
try {
this.callee.on(MSG_SEND_METHOD, sendMsgCallback);
} catch (error) {
console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`);
}
}
onDestroy() {
try {
this.callee.off(MSG_SEND_METHOD);
} catch (error) {
console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`);
}
}
}
```
### 开发步骤(访问Callee被调用端)
1. 导入UIAbility模块。
```ts
import Ability from '@ohos.app.ability.UIAbility';
```
2. 获取Caller通信接口。
Ability的context属性实现了startAbilityByCall方法,用于获取指定通用组件的Caller通信接口。如下示例通过this.context获取Ability实例的context属性,使用startAbilityByCall拉起Callee被调用端并获取Caller通信接口,注册Caller的onRelease监听。应用开发者根据实际需要做相应处理。
```ts
// 注册caller的release监听
private regOnRelease(caller) {
try {
caller.on("release", (msg) => {
console.info(`caller onRelease is called ${msg}`);
})
console.info('caller register OnRelease succeed');
} catch (error) {
console.info(`caller register OnRelease failed with ${error}`);
}
}
async onButtonGetCaller() {
try {
this.caller = await context.startAbilityByCall({
bundleName: 'com.samples.CallApplication',
abilityName: 'CalleeAbility'
})
if (this.caller === undefined) {
console.info('get caller failed')
return
}
console.info('get caller success')
this.regOnRelease(this.caller)
} catch (error) {
console.info(`get caller failed with ${error}`)
}
}
```
# UIAbility组件启动模式
UIAbility的启动模式是指UIAbility实例在启动时的不同呈现状态。针对不同的业务场景,系统提供了三种启动模式:
- [singleton(单实例模式)](#singleton启动模式)
- [standard(标准实例模式)](#standard启动模式)
- [specified(指定实例模式)](#specified启动模式)
## singleton启动模式
singleton启动模式为单实例模式,也是默认情况下的启动模式。
每次调用startAbility()方法时,如果应用进程中该类型的UIAbility实例已经存在,则复用系统中的UIAbility实例。系统中只存在唯一一个该UIAbility实例,即在最近任务列表中只存在一个该类型的UIAbility实例。
**图1** 单实例模式演示效果
![uiability-launch-type1](figures/uiability-launch-type1.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 应用的UIAbility实例已创建,该UIAbility配置为单实例模式,再次调用startAbility()方法启动该UIAbility实例,此时只会进入该UIAbility的[onNewWant()](../reference/apis/js-apis-application-ability.md#abilityonnewwant)回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调。
如果需要使用singleton启动模式,在[module.json5配置文件](../quick-start/module-configuration-file.md)中的"launchType"字段配置为"singleton"即可。
```json
{
"module": {
// ...
"abilities": [
{
"launchType": "singleton",
// ...
}
]
}
}
```
## standard启动模式
standard启动模式为标准实例模式,每次调用startAbility()方法时,都会在应用进程中创建一个新的该类型UIAbility实例。即在最近任务列表中可以看到有多个该类型的UIAbility实例。这种情况下可以将UIAbility配置为standard(标准实例模式)。
**图2** 标准实例模式演示效果
![standard-mode](figures/standard-mode.png)
standard启动模式的开发使用,在[module.json5配置文件](../quick-start/module-configuration-file.md)中的"launchType"字段配置为"standard"即可。
```json
{
"module": {
// ...
"abilities": [
{
"launchType": "standard",
// ...
}
]
}
}
```
## specified启动模式
specified启动模式为指定实例模式,针对一些特殊场景使用(例如文档应用中每次新建文档希望都能新建一个文档实例,重复打开一个已保存的文档希望打开的都是同一个文档实例)。
在UIAbility实例创建之前,允许开发者为该实例创建一个唯一的字符串Key,创建的UIAbility实例绑定Key之后,后续每次调用startAbility()方法时,都会询问应用使用哪个Key对应的UIAbility实例来响应startAbility()请求。运行时由UIAbility内部业务决定是否创建多实例,如果匹配有该UIAbility实例的Key,则直接拉起与之绑定的UIAbility实例,否则创建一个新的UIAbility实例。
**图3** 指定实例模式演示效果
![uiability-launch-type2](figures/uiability-launch-type2.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 应用的UIAbility实例已创建,该UIAbility配置为指定实例模式,再次调用startAbility()方法启动该UIAbility实例,且[AbilityStage](abilitystage.md)的[onAcceptWant()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant)回调匹配到一个已创建的UIAbility实例。此时,再次启动该UIAbility时,只会进入该UIAbility的[onNewWant()](../reference/apis/js-apis-application-ability.md#abilityonnewwant)回调,不会进入其onCreate()和onWindowStageCreate()生命周期回调。
例如有两个UIAbility:EntryAbility和FuncAbility,FuncAbility配置为specified启动模式,需要从EntryAbility的页面中启动FuncAbility。
1. 在FuncAbility中,将[module.json5配置文件](../quick-start/module-configuration-file.md)的"launchType"字段配置为"specified"。
```json
{
"module": {
// ...
"abilities": [
{
"launchType": "specified",
// ...
}
]
}
}
```
2. 在EntryAbility中,调用startAbility()方法时,在want参数中,增加一个自定义参数来区别UIAbility实例,例如增加一个"instanceKey"自定义参数。
```ts
// 在启动指定实例模式的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) => {
// ...
})
```
3. 由于FuncAbility的启动模式配置为了指定实例启动模式,在FuncAbility启动之前,会先进入其对应的AbilityStage的[onAcceptWant()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant)生命周期回调中,解析传入的want参数,获取"instanceKey"自定义参数。根据业务需要通过AbilityStage的[onAcceptWant()](../reference/apis/js-apis-application-abilitystage.md#abilitystageonacceptwant)生命周期回调返回一个字符串Key标识。[如果返回的Key对应一个已启动的UIAbility](mission-management-launch-type.md#fig14520125175314),则会将之前的UIAbility拉回前台并获焦,而不创建新的实例,否则创建新的实例并启动。
```ts
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 '';
}
}
```
例如在文档应用中,可以对不同的文档实例内容绑定不同的Key值。当每次新建文档的时候,可以传入不同的新Key值(如可以将文件的路径作为一个Key标识),此时AbilityStage中启动UIAbility时都会创建一个新的UIAbility实例;当新建的文档保存之后,回到桌面,或者新打开一个已保存的文档,回到桌面,此时再次打开该已保存的文档,此时AbilityStage中再次启动该UIAbility时,打开的仍然是之前原来已保存的文档界面。
以如下步骤所示进行举例说明。
1. 打开文件A,对应启动一个新的UIAbility实例,例如启动“UIAbility实例1”。
2. 在最近任务列表中关闭文件A的进程,此时UIAbility实例1被销毁,回到桌面,再次打开文件A,此时对应启动一个新的UIAbility实例,例如启动“UIAbility实例2”。
3. 回到桌面,打开文件B,此时对应启动一个新的UIAbility实例,例如启动“UIAbility实例3”。
4. 回到桌面,再次打开文件A,此时对应启动的还是之前的“UIAbility实例2”。
# UIAbility组件生命周期
## 概述
当用户打开、切换和返回到对应应用时,应用中的UIAbility实例会在其生命周期的不同状态之间转换。UIAbility类提供了一系列回调,通过这些回调可以知道当前UIAbility实例的某个状态发生改变,会经过UIAbility实例的创建和销毁,或者UIAbility实例发生了前后台的状态切换。
UIAbility的生命周期包括Create、Foreground、Background、Destroy四个状态,如下图所示。
**图1** UIAbility生命周期状态
![Ability-Life-Cycle](figures/Ability-Life-Cycle.png)
## 生命周期状态说明
### Create状态
Create状态为在应用加载过程中,UIAbility实例创建完成时触发,系统会调用onCreate()回调。可以在该回调中进行应用初始化操作,例如变量定义资源加载等,用于后续的UI界面展示。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
// 应用初始化
}
// ...
}
```
### WindowStageCreate和WindowStageDestory状态
UIAbility实例创建完成之后,在进入Foreground之前,系统会创建一个WindowStage。WindowStage创建完成后会进入onWindowStageCreate()回调,可以在该回调中设置UI界面加载、设置WindowStage的事件订阅。
**图2** WindowStageCreate和WindowStageDestory状态  
![Ability-Life-Cycle-WindowStage](figures/Ability-Life-Cycle-WindowStage.png)
在onWindowStageCreate()回调中通过loadContent()方法设置应用要加载的页面并根据需要订阅WindowStage的[事件](../reference/apis/js-apis-window.md#windowstageeventtype9)(获焦/失焦、可见/不可见)。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: Window.WindowStage) {
// 设置WindowStage的事件订阅(获焦/失焦、可见/不可见)
// 设置UI界面加载
windowStage.loadContent('pages/Index', (err, data) => {
// ...
});
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> WindowStage的相关使用请参见[窗口开发指导](../windowmanager/application-window-stage.md)。
对应于onWindowStageCreate()回调。在UIAbility实例销毁之前,则会先进入onWindowStageDestroy()回调,可以在该回调中释放UI界面资源。例如在onWindowStageDestroy()中注销获焦/失焦等WindowStage事件。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
// ...
onWindowStageDestroy() {
// 释放UI界面资源
}
}
```
### Foreground和Background状态
Foreground和Background状态分别在UIAbility实例切换至前台和切换至后台时触发,对应于onForeground()回调和onBackground()回调。
onForeground()回调,在UIAbility的UI界面可见之前,如UIAbility切换至前台时触发。可以在onForeground()回调中申请系统需要的资源,或者重新申请在onBackground()中释放的资源。
onBackground()回调,在UIAbility的UI界面完全不可见之后,如UIAbility切换至后台时候触发。可以在onBackground()回调中释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作,例如状态保存等。
例如应用在使用过程中需要使用用户定位时,假设应用已获得用户的定位权限授权。在UI界面显示之前,可以在onForeground()回调中开启定位功能,从而获取到当前的位置信息。
当应用切换到后台状态,可以在onBackground()回调中停止定位功能,以节省系统的资源消耗。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onForeground() {
// 申请系统需要的资源,或者重新申请在onBackground中释放的资源
}
onBackground() {
// 释放UI界面不可见时无用的资源,或者在此回调中执行较为耗时的操作
// 例如状态保存等
}
}
```
### Destroy状态
Destroy状态在UIAbility实例销毁时触发。可以在onDestroy()回调中进行系统资源的释放、数据的保存等操作。
例如调用terminateSelf()方法停止当前UIAbility实例,从而完成UIAbility实例的销毁;或者用户使用最近任务列表关闭该UIAbility实例,完成UIAbility的销毁。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onDestroy() {
// 系统资源的释放、数据的保存等
}
}
```
# UIAbility组件概述
## 概述
UIAbility组件是一种包含UI界面的应用组件,主要用于和用户交互。
UIAbility组件是系统调度的基本单元,为应用提供绘制界面的窗口;一个UIAbility组件中可以通过多个页面来实现一个功能模块。每一个UIAbility组件实例,都对应于一个最近任务列表中的任务。
## 声明配置
为使应用能够正常使用UIAbility,需要在[module.json5配置文件](../quick-start/module-configuration-file.md)[abilities标签](../quick-start/module-configuration-file.md#abilities标签)中声明UIAbility的名称、入口、标签等相关信息。
```json
{
"module": {
// ...
"abilities": [
{
"name": "EntryAbility", // UIAbility组件的名称
"srcEntrance": "./ets/entryability/EntryAbility.ts", // UIAbility组件的代码路径
"description": "$string:EntryAbility_desc", // UIAbility组件的描述信息
"icon": "$media:icon", // UIAbility组件的图标
"label": "$string:EntryAbility_label", // UIAbility组件的标签
"startWindowIcon": "$media:icon", // UIAbility组件启动页面图标资源文件的索引
"startWindowBackground": "$color:start_window_background", // UIAbility组件启动页面背景颜色资源文件的索引
// ...
}
]
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> Ability的组成请参见在[模块中添加Ability](https://developer.harmonyos.com/cn/docs/documentation/doc-guides-V3/ohos-adding-ability-0000001218280664-V3)。
# UIAbility组件基本用法
UIAbility组件的基本用法包括:指定UIAbility的启动页面以及获取UIAbility的上下文[UIAbilityContext](../reference/apis/js-apis-ability-context.md)
## 指定UIAbility的启动页面
应用中的UIAbility在启动过程中,需要指定启动页面,否则应用启动后会因为没有默认加载页面而导致白屏。可以在UIAbility的onWindowStageCreate()生命周期回调中,通过[WindowStage](../reference/apis/js-apis-window.md#windowstage9)对象的loadContent()方法设置启动页面。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import Window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: Window.WindowStage) {
// Main window is created, set main page for this ability
windowStage.loadContent('pages/Index', (err, data) => {
// ...
});
}
// ...
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 在DevEco Studio中创建的UIAbility中,该UIAbility实例默认会加载Index页面,根据需要将Index页面路径替换为需要的页面路径即可。
## 获取UIAbility的上下文信息
UIAbility类拥有自身的上下文信息,该信息为[UIAbilityContext](../reference/apis/js-apis-ability-context.md)类的实例,[UIAbilityContext](../reference/apis/js-apis-ability-context.md)类拥有abilityInfo、currentHapModuleInfo等属性。通过UIAbilityContext可以获取UIAbility的相关配置信息,如包代码路径、Bundle名称、Ability名称和应用程序需要的环境状态等属性信息,以及可以获取操作UIAbility实例的方法(如startAbility()、connectServiceExtensionAbility()、terminateSelf()等)。
- 在UIAbility中可以通过this.context获取UIAbility实例的上下文信息。
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
// 获取UIAbility实例的上下文
let context = this.context;
// ...
}
}
```
- 在页面中获取UIAbility实例的上下文信息,包括导入依赖资源context模块和在组件中定义一个context变量两个部分。
```ts
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext;
startAbilityTest() {
let want = {
// Want参数信息
};
this.context.startAbility(want);
}
// 页面展示
build() {
// ...
}
}
```
也可以在导入依赖资源context模块后,在具体使用[UIAbilityContext](../reference/apis/js-apis-ability-context.md)前进行变量定义。
```ts
import common from '@ohos.app.ability.common';
@Entry
@Component
struct Index {
startAbilityTest() {
let context = getContext(this) as common.UIAbilityContext;
let want = {
// Want参数信息
};
context.startAbility(want);
}
// 页面展示
build() {
// ...
}
}
```
# 信息传递载体Want
请参见Stage模型的"[信息传递载体Want](want-overview.md)"。
# Want概述
## Want的定义与用途
[Want](../reference/apis/js-apis-application-Want.md)是对象间信息传递的载体,可以用于应用组件间的信息传递。其使用场景之一是作为startAbility()的参数,包含了指定的启动目标以及启动时需携带的相关数据,如bundleName和abilityName字段分别指明目标Ability所在应用的包名以及对应包内的Ability名称。当UIAbilityA启动UIAbilityB并需要传入一些数据给UIAbilityB时,Want可以作为一个载体将数据传给UIAbilityB。
**图1** Want用法示意  
![usage-of-want](figures/usage-of-want.png)
## Want的类型
- **显式Want**:在启动Ability时指定了abilityName和bundleName的Want称为显式Want。
当有明确处理请求的对象时,通过提供目标Ability所在应用的包名信息(bundleName),并在Want内指定abilityName便可启动目标Ability。显式Want通常用于在当前应用开发中启动某个已知的Ability。参数说明参见[Want参数说明](#Want参数说明)
```ts
let wantInfo = {
deviceId: '', // deviceId为空表示本设备
bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
}
```
- **隐式Want**:在启动UIAbility时未指定abilityName的Want称为隐式Want。
当请求处理的对象不明确时,希望在当前应用中使用其他应用提供的某个能力(通过[skills标签](../quick-start/module-configuration-file.md#skills标签)定义),而不关心提供该能力的具体应用,可以使用隐式Want。例如使用隐式Want描述需要打开一个链接的请求,而不关心通过具体哪个应用打开,系统将匹配声明支持该请求的所有应用。
```ts
let wantInfo = {
// uncomment line below if wish to implicitly query only in the specific bundle.
// bundleName: 'com.example.myapplication',
action: 'ohos.want.action.search',
// entities can be omitted
entities: [ 'entity.system.browsable' ],
uri: 'https://www.test.com:8080/query/student',
type: 'text/plain',
};
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> - 根据系统中待匹配Ability的匹配情况不同,使用隐式Want启动Ability时会出现以下三种情况。
> - 未匹配到满足条件的Ability:启动失败。
> - 匹配到一个满足条件的Ability:直接启动该Ability。
> - 匹配到多个满足条件的Ability(UIAbility):弹出选择框让用户选择。
>
> - 调用方传入的want参数中不带有abilityName和bundleName,则不允许通过隐式Want启动所有应用的ServiceExtensionAbility。
>
> - 调用方传入的want参数中带有bundleName,则允许使用startServiceExtensionAbility()方法隐式Want启动ServiceExtensionAbility,默认返回优先级最高的ServiceExtensionAbility,如果优先级相同,返回第一个。
## Want参数说明
| 名称 | 读写属性 | 类型 | 必填 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
| deviceId | 只读 | string | 否 | 表示目标Ability所在设备ID。如果未设置该字段,则表明本设备。 |
| bundleName | 只读 | string | 否 | 表示目标Ability所在应用名称。 |
| moduleName | 只读 | string | 否 | 表示目标Ability所属的模块名称。 |
| abilityName | 只读 | string | 否 | 表示目标Ability名称。如果未设置该字段,则该Want为隐式。如果在Want中同时指定了bundleName,moduleName和abilityName,则Want可以直接匹配到指定的Ability。 |
| uri | 只读 | string | 否 | 表示携带的数据,一般配合type使用,指明待处理的数据类型。如果在Want中指定了uri,则Want将匹配指定的Uri信息,包括scheme,&nbsp;schemeSpecificPart,&nbsp;authority和path信息。 |
| type | 只读 | string | 否 | 表示携带数据类型,使用[MIME类型](https://www.iana.org/assignments/media-types/media-types.xhtml?utm_source=ld246.com%E3%80%82)规范。例如:"text/plain"、"image/\*"等。 |
| [action](../reference/apis/js-apis-ability-wantConstant.md) | 只读 | string | 否 | 表示要执行的通用操作(如:查看、分享、应用详情)。在隐式Want中,您可定义该字段,配合uri或parameters来表示对数据要执行的操作。如打开,查看该uri数据。例如,当uri为一段网址,action为ohos.want.action.viewData则表示匹配可查看该网址的Ability。 |
| [entities](../reference/apis/js-apis-ability-wantConstant.md) | 只读 | Array&lt;string&gt; | 否 | 表示目标Ability额外的类别信息(如:浏览器,视频播放器),在隐式Want中是对action的补充。在隐式Want中,您可定义该字段,来过滤匹配UIAbility类别,如必须是浏览器。例如,在action字段的举例中,可存在多个应用声明了支持查看网址的操作,其中有应用为普通社交应用,有的为浏览器应用,您可通过entity.system.browsable过滤掉非浏览器的其他应用。 |
| [flags](../reference/apis/js-apis-ability-wantConstant.md#wantconstantflags) | 只读 | number | 否 | 表示处理Want的方式。例如通过wantConstant.Flags.FLAG_ABILITY_CONTINUATION表示是否以设备间迁移方式启动Ability。 |
| parameters | 只读 | {[key:&nbsp;string]:&nbsp;any} | 否 | 此参数用于传递自定义数据,通过用户自定义的键值对进行数据填充,具体支持的数据类型如[Want&nbsp;API](../reference/apis/js-apis-application-Want.md)所示。 |
# 服务卡片开发指导
## 卡片概述
服务卡片(以下简称“卡片”)是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达、减少体验层级的目的。
卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面、发送消息等基础的交互功能。
卡片的基本概念:
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。
- 卡片提供方:提供卡片显示内容原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。
## 运作机制
卡片框架的运作机制如图1所示。
**图1** 卡片框架运作机制(FA模型)
![form-extension](figures/form-extension.png)
卡片使用方包含以下模块:
- 卡片使用:包含卡片的创建、删除、请求更新等操作。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的相关操作到卡片管理服务。
卡片管理服务包含以下模块:
- 周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。
- 卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
- 卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
- 卡片使用方对象管理:对卡片使用方的RPC对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
- 通信适配层:负责与卡片使用方和提供方进行RPC通信。
卡片提供方包含以下模块:
- 卡片服务:由卡片提供方开发者实现,开发者实现生命周期处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
- 卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 实际开发时只需要作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。
## 接口说明
FormAbility生命周期接口如下:
| 接口名 | 描述 |
| -------- | -------- |
| onCreate(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 |
| onCastToNormal(formId:&nbsp;string):&nbsp;void | 卡片提供方接收临时卡片转常态卡片的通知接口 |
| onUpdate(formId:&nbsp;string):&nbsp;void | 卡片提供方接收更新卡片的通知接口。 |
| onVisibilityChange(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void | 卡片提供方接收修改可见性的通知接口。 |
| onEvent(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void | 卡片提供方接收处理卡片事件的通知接口。 |
| onDestroy(formId:&nbsp;string):&nbsp;void | 卡片提供方接收销毁卡片的通知接口。 |
| onAcquireFormState?(want:&nbsp;Want):&nbsp;formInfo.FormState | 卡片提供方接收查询卡片状态的通知接口。 |
| onShare?(formId:&nbsp;string):&nbsp;{[key:&nbsp;string]:&nbsp;any} | 卡片提供方接收卡片分享的通知接口。 |
FormProvider类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formprovider.md)
| 接口名 | 描述 |
| -------- | -------- |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 设置指定卡片的下一次更新时间。 |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number):&nbsp;Promise&lt;void&gt;; | 设置指定卡片的下一次更新时间,以promise方式返回。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 更新指定的卡片。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData):&nbsp;Promise&lt;void&gt;; | 更新指定的卡片,以promise方式返回。 |
formBindingData类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formbindingdata.md)
| 接口名 | 描述 |
| -------- | -------- |
| createFormBindingData(obj?:&nbsp;Object&nbsp;\|&nbsp;string):&nbsp;FormBindingData | 创建一个FormBindingData对象。 |
## 开发步骤
FA卡片开发,即基于[FA模型](../ability/fa-brief.md)的卡片提供方开发,主要涉及如下关键步骤:
- [实现卡片生命周期接口](#实现卡片生命周期接口):开发FormAbility生命周期回调函数。
- [配置卡片配置文件](#配置卡片配置文件):配置应用配置文件config.json。
- [卡片信息的持久化](#卡片信息的持久化):对卡片信息进行持久化管理。
- [卡片数据交互](#卡片数据交互):通过updateForm()更新卡片显示的信息。
- [开发卡片页面](#开发卡片页面):使用HML+CSS+JSON开发JS卡片页面。
- [开发卡片事件](#开发卡片事件):为卡片添加router事件和message事件。
### 实现卡片生命周期接口
创建FA模型的卡片,需实现卡片的生命周期接口。先参考[IDE开发服务卡片指南](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425)生成服务卡片模板。
1. 在form.ts中,导入相关模块
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formInfo from '@ohos.app.form.formInfo';
import formProvider from '@ohos.app.form.formProvider';
import dataStorage from '@ohos.data.storage';
```
2. 在form.ts中,实现卡片生命周期接口
```ts
export default {
onCreate(want) {
console.info('FormAbility onCreate');
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
},
onCastToNormal(formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.info('FormAbility onCastToNormal');
},
onUpdate(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.info('FormAbility updateForm, error:' + JSON.stringify(error));
});
},
onVisibilityChange(newStatus) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理,仅系统应用生效
console.info('FormAbility onVisibilityChange');
},
onEvent(formId, message) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.info('FormAbility onEvent');
},
onDestroy(formId) {
// 删除卡片实例数据
console.info('FormAbility onDestroy');
},
onAcquireFormState(want) {
console.info('FormAbility onAcquireFormState');
return formInfo.FormState.READY;
},
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> FormAbility不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务。
### 配置卡片配置文件
卡片需要在应用配置文件config.json中进行配置。
- js模块,用于对应卡片的js相关资源,内部字段结构说明:
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示JS&nbsp;Component的名字。该标签不可缺省,默认值为default。 | 字符串 | 否 |
| pages | 表示JS&nbsp;Component的页面用于列举JS&nbsp;Component中每个页面的路由信息[页面路径+页面名称]。该标签不可缺省,取值为数组,数组第一个元素代表JS&nbsp;FA首页。 | 数组 | 否 |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| type | 表示JS应用的类型。取值范围如下:<br/>normal:标识该JS&nbsp;Component为应用实例。<br/>form:标识该JS&nbsp;Component为卡片实例。 | 字符串 | 可缺省,缺省值为“normal” |
| mode | 定义JS组件的开发模式。 | 对象 | 可缺省,缺省值为空 |
配置示例如下:
```json
"js": [{
"name": "widget",
"pages": ["pages/index/index"],
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"type": "form"
}]
```
- abilities模块,用于对应卡片的FormAbility,内部字段结构说明:
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示卡片的类名。字符串最大长度为127字节。 | 字符串 | 否 |
| description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
| isDefault | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。<br/>true:默认卡片。<br/>false:非默认卡片。 | 布尔值 | 否 |
| type | 表示卡片的类型。取值范围如下:<br/>JS:JS卡片。 | 字符串 | 否 |
| colorMode | 表示卡片的主题样式,取值范围如下:<br/>auto:自适应。<br/>dark:深色主题。<br/>light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
| supportDimensions | 表示卡片支持的外观规格,取值范围:<br/>1&nbsp;\*&nbsp;2:表示1行2列的二宫格。<br/>2&nbsp;\*&nbsp;2:表示2行2列的四宫格。<br/>2&nbsp;\*&nbsp;4:表示2行4列的八宫格。<br/>4&nbsp;\*&nbsp;4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
| defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
| updateEnabled | 表示卡片是否支持周期性刷新,取值范围:<br/>true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。<br/>false:表示不支持周期性刷新。 | 布尔类型 | 否 |
| scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省值为“0:0”。 |
| updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br/>当取值为0时,表示该参数不生效。<br/>当取值为正整数N时,表示刷新周期为30\*N分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
| formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
| formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
| jsComponentName | 表示JS卡片的Component名称。字符串最大长度为127字节。 | 字符串 | 否 |
| metaData | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
| customizeData | 表示自定义的卡片信息。 | 对象数组 | 可缺省,缺省值为空。 |
配置示例如下:
```json
"abilities": [{
"name": "FormAbility",
"description": "This is a FormAbility",
"formsEnabled": true,
"icon": "$media:icon",
"label": "$string:form_FormAbility_label",
"srcPath": "FormAbility",
"type": "service",
"srcLanguage": "ets",
"formsEnabled": true,
"formConfigAbility": "ability://com.example.entry.MainAbility",
"forms": [{
"colorMode": "auto",
"defaultDimension": "2*2",
"description": "This is a service widget.",
"formVisibleNotify": true,
"isDefault": true,
"jsComponentName": "widget",
"name": "widget",
"scheduledUpdateTime": "10:30",
"supportDimensions": ["2*2"],
"type": "JS",
"updateEnabled": true
}]
}]
```
### 卡片信息的持久化
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) {
// 此处仅对卡片ID:formId,卡片名:formName和是否为临时卡片:tempFlag进行了持久化
let formInfo = {
"formName": formName,
"tempFlag": tempFlag,
"updateCount": 0
};
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// put form info
await storage.put(formId, JSON.stringify(formInfo));
console.info(`storeFormInfo, put form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`failed to storeFormInfo, err: ${JSON.stringify(err)}`);
}
}
// ...
onCreate(want) {
console.info('FormAbility onCreate');
let formId = want.parameters["ohos.extra.param.key.form_identity"];
let formName = want.parameters["ohos.extra.param.key.form_name"];
let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"];
// 将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
storeFormInfo(formId, formName, tempFlag);
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
// ...
```
且需要适配onDestroy卡片删除通知接口,在其中实现卡片实例数据的删除。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function deleteFormInfo(formId: string) {
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// del form info
await storage.delete(formId);
console.info(`deleteFormInfo, del form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`failed to deleteFormInfo, err: ${JSON.stringify(err)}`);
}
}
// ...
onDestroy(formId) {
console.info('FormAbility onDestroy');
// 删除之前持久化的卡片实例数据
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
deleteFormInfo(formId);
}
// ...
```
具体的持久化方法可以参考[轻量级数据存储开发指导](../database/database-preference-guidelines.md)
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片:
- 常态卡片:卡片使用方会持久化的卡片;
- 临时卡片:卡片使用方不会持久化的卡片;
由于临时卡片的数据具有非持久化的特殊性,某些场景例如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
### 卡片数据交互
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm()接口更新主动触发卡片的更新。
```ts
onUpdate(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
// 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
formProvider.updateForm(formId, formData).catch((error) => {
console.info('FormAbility updateForm, error:' + JSON.stringify(error));
});
}
```
### 开发卡片页面
开发者可以使用类Web范式(HML+CSS+JSON)开发JS卡片页面。生成如下卡片页面,可以这样配置卡片页面文件:
![widget-development-fa](figures/widget-development-fa.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI界面。
- HML:使用类Web范式的组件描述卡片的页面信息。
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title">{{title}}</text>
<text class="detail_text" onclick="routerEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS:HML中类Web范式组件的样式信息。
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON:卡片页面中的数据和事件交互。
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "com.example.entry.MainAbility",
"params": {
"message": "add detail"
}
}
}
}
```
### 开发卡片事件
卡片支持为组件设置交互事件(action),包括router事件和message事件,其中router事件用于Ability跳转,message事件用于卡片开发人员自定义点击事件。关键步骤说明如下:
1. 在hml中为组件设置onclick属性,其值对应到json文件的actions字段中。
2. 如何设置router事件:
- action属性值为"router";
- abilityName为跳转目标的Ability名(支持跳转FA模型的PageAbility组件和Stage模型的UIAbility组件),如目前DevEco创建的FA模型的MainAbility默认名为com.example.entry.MainAbility;
- params为传递给跳转目标Ability的自定义参数,可以按需填写。其值可以在目标Ability启动时的want中的parameters里获取。如FA模型MainAbility的onCreate生命周期里可以通过featureAbility.getWant()获取到want,然后在其parameters字段下获取到配置的参数;
3. 如何设置message事件:
- action属性值为"message";
- params为message事件的用户自定义参数,可以按需填写。其值可以在卡片生命周期函数onEvent中的message里获取;
示例如下:
- hml文件
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title" onclick="routerEvent">{{title}}</text>
<text class="detail_text" onclick="messageEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- css文件
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- json文件
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "com.example.entry.MainAbility",
"params": {
"message": "add detail"
}
},
"messageEvent": {
"action": "message",
"params": {
"message": "add detail"
}
}
}
}
```
## 相关实例
针对FA模型卡片提供方的开发,有以下相关实例可供参考:
- [FormAbility:FA模型卡片(ArkTS)(API8)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/FormAbility)
- [FormLauncher:卡片使用方(ArkTS)(API8)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/FormLauncher)
# FormExtensionAbility(服务卡片)
## 卡片概述
FormExtensionAbility就是服务卡片扩展组件(以下简称“卡片”),是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达,减少体验层级的目的。
卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面、发送消息等基础的交互功能。
卡片的基本概念:
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。
- 卡片提供方:提供卡片显示内容原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。
## 运作机制
卡片框架的运作机制如图1所示。
**图1** 卡片框架运作机制(Stage模型)  
![form-extension](figures/form-extension.png)
卡片使用方包含以下模块:
- 卡片使用:包含卡片的创建、删除、请求更新等操作。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的相关操作到卡片管理服务。
卡片管理服务包含以下模块:
- 周期性刷新:在卡片添加后,根据卡片的刷新策略启动定时任务周期性触发卡片的刷新。
- 卡片缓存管理:在卡片添加到卡片管理服务后,对卡片的视图信息进行缓存,以便下次获取卡片时可以直接返回缓存数据,降低时延。
- 卡片生命周期管理:对于卡片切换到后台或者被遮挡时,暂停卡片的刷新;以及卡片的升级/卸载场景下对卡片数据的更新和清理。
- 卡片使用方对象管理:对卡片使用方的RPC对象进行管理,用于使用方请求进行校验以及对卡片更新后的回调处理。
- 通信适配层:负责与卡片使用方和提供方进行RPC通信。
卡片提供方包含以下模块:
- 卡片服务:由卡片提供方开发者实现,开发者实现生命周期处理创建卡片、更新卡片以及删除卡片等请求,提供相应的卡片服务。
- 卡片提供方实例管理模块:由卡片提供方开发者实现,负责对卡片管理服务分配的卡片实例进行持久化管理。
- 通信适配层:由OpenHarmony SDK提供,负责与卡片管理服务通信,用于将卡片的更新数据主动推送到卡片管理服务。
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 实际开发时只需要作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。
## 接口说明
FormExtensionAbility类拥有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formextensionability.md)
| 接口名 | 描述 |
| -------- | -------- |
| onAddForm(want:&nbsp;Want):&nbsp;formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 |
| onCastToNormalForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收临时卡片转常态卡片的通知接口。 |
| onUpdateForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收更新卡片的通知接口。 |
| onChangeFormVisibility(newStatus:&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;number&nbsp;}):&nbsp;void | 卡片提供方接收修改可见性的通知接口。 |
| onFormEvent(formId:&nbsp;string,&nbsp;message:&nbsp;string):&nbsp;void | 卡片提供方接收处理卡片事件的通知接口。 |
| onRemoveForm(formId:&nbsp;string):&nbsp;void | 卡片提供方接收销毁卡片的通知接口。 |
| onConfigurationUpdate(config:&nbsp;Configuration):&nbsp;void | 当系统配置更新时调用。 |
| onShareForm?(formId:&nbsp;string):&nbsp;{&nbsp;[key:&nbsp;string]:&nbsp;any&nbsp;} | 卡片提供方接收卡片分享的通知接口。 |
FormExtensionAbility类还拥有成员context,为FormExtensionContext类,具体的API介绍详见[接口文档](../reference/apis/js-apis-formextensioncontext.md)
| 接口名 | 描述 |
| -------- | -------- |
| startAbility(want:&nbsp;Want,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void | 回调形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
| startAbility(want:&nbsp;Want):&nbsp;Promise&lt;void&gt; | Promise形式拉起一个卡片所属应用的UIAbility(系统接口,三方应用不支持调用,需申请后台拉起权限)。 |
formProvider类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formprovider.md)
| 接口名 | 描述 |
| -------- | -------- |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 设置指定卡片的下一次更新时间。 |
| setFormNextRefreshTime(formId:&nbsp;string,&nbsp;minute:&nbsp;number):&nbsp;Promise&lt;void&gt;; | 设置指定卡片的下一次更新时间,以promise方式返回。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData,&nbsp;callback:&nbsp;AsyncCallback&lt;void&gt;):&nbsp;void; | 更新指定的卡片。 |
| updateForm(formId:&nbsp;string,&nbsp;formBindingData:&nbsp;FormBindingData):&nbsp;Promise&lt;void&gt;; | 更新指定的卡片,以promise方式返回。 |
formBindingData类有如下API接口,具体的API介绍详见[接口文档](../reference/apis/js-apis-app-form-formbindingdata.md)
| 接口名 | 描述 |
| -------- | -------- |
| createFormBindingData(obj?:&nbsp;Object&nbsp;\|&nbsp;string):&nbsp;FormBindingData | 创建一个FormBindingData对象。 |
## 开发步骤
Stage卡片开发,即基于[Stage模型](stage-model-development-overview.md)的卡片提供方开发,主要涉及如下关键步骤:
- [创建卡片FormExtensionAbility](#创建卡片formextensionability):卡片生命周期回调函数FormExtensionAbility开发。
- [配置卡片配置文件](#配置卡片配置文件):配置应用配置文件module.json5和profile配置文件。
- [卡片数据交互](#卡片数据交互):对卡片信息进行持久化管理。
- [卡片数据交互](#卡片数据交互):通过updateForm更新卡片显示的信息。
- [开发卡片页面](#开发卡片页面):使用HML+CSS+JSON开发JS卡片页面。
- [开发卡片事件](#开发卡片事件):为卡片添加router事件和message事件。
### 创建卡片FormExtensionAbility
创建Stage模型的卡片,需实现FormExtensionAbility生命周期接口。先参考[DevEco Studio服务卡片开发指南](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425)生成服务卡片模板。
1. 在EntryFormAbility.ts中,导入相关模块。
```ts
import FormExtension from '@ohos.app.form.FormExtensionAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formInfo from '@ohos.app.form.formInfo';
import formProvider from '@ohos.app.form.formProvider';
import dataStorage from '@ohos.data.storage';
```
2. 在EntryFormAbility.ts中,实现FormExtension生命周期接口。
```ts
export default class EntryFormAbility extends FormExtension {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// 使用方创建卡片时触发,提供方需要返回卡片数据绑定类
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.info('[EntryFormAbility] onCastToNormalForm');
}
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
onChangeFormVisibility(newStatus) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理,仅系统应用生效
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// 删除卡片实例数据
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
console.info('[EntryFormAbility] nConfigurationUpdate, config:' + JSON.stringify(config));
}
onAcquireFormState(want) {
return formInfo.FormState.READY;
}
}
```
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> FormExtensionAbility不能常驻后台,即在卡片生命周期回调函数中无法处理长时间的任务。
### 配置卡片配置文件
1. 卡片需要在[module.json5配置文件](../quick-start/module-configuration-file.md)中的extensionAbilities标签下,配置ExtensionAbility相关信息。FormExtensionAbility需要填写metadata元信息标签,其中键名称为固定字符串"ohos.extension.form",资源为卡片的具体配置信息的索引。
配置示例如下:
```json
{
"module": {
// ...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntrance": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. 卡片的具体配置信息。在上述FormExtensionAbility的元信息("metadata"配置项)中,可以指定卡片具体配置信息的资源索引。例如当resource指定为$profile:form_config时,会使用开发视图的resources/base/profile/目录下的form_config.json作为卡片profile配置文件。内部字段结构说明如下表所示。
**表1** 卡片profile配置文件
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | -------- | -------- | -------- |
| name | 表示卡片的类名,字符串最大长度为127字节。 | 字符串 | 否 |
| description | 表示卡片的描述。取值可以是描述性内容,也可以是对描述性内容的资源索引,以支持多语言。字符串最大长度为255字节。 | 字符串 | 可缺省,缺省为空。 |
| src | 表示卡片对应的UI代码的完整路径。 | 字符串 | 否 |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| isDefault | 表示该卡片是否为默认卡片,每个Ability有且只有一个默认卡片。<br/>true:默认卡片。<br/>false:非默认卡片。 | 布尔值 | 否 |
| colorMode | 表示卡片的主题样式,取值范围如下:<br/>auto:自适应。<br/>dark:深色主题。<br/>light:浅色主题。 | 字符串 | 可缺省,缺省值为“auto”。 |
| supportDimensions | 表示卡片支持的外观规格,取值范围:<br/>1&nbsp;\*&nbsp;2:表示1行2列的二宫格。<br/>2&nbsp;\*&nbsp;2:表示2行2列的四宫格。<br/>2&nbsp;\*&nbsp;4:表示2行4列的八宫格。<br/>4&nbsp;\*&nbsp;4:表示4行4列的十六宫格。 | 字符串数组 | 否 |
| defaultDimension | 表示卡片的默认外观规格,取值必须在该卡片supportDimensions配置的列表中。 | 字符串 | 否 |
| updateEnabled | 表示卡片是否支持周期性刷新,取值范围:<br/>true:表示支持周期性刷新,可以在定时刷新(updateDuration)和定点刷新(scheduledUpdateTime)两种方式任选其一,优先选择定时刷新。<br/>false:表示不支持周期性刷新。 | 布尔类型 | 否 |
| scheduledUpdateTime | 表示卡片的定点刷新的时刻,采用24小时制,精确到分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 字符串 | 可缺省,缺省值为“0:0”。 |
| updateDuration | 表示卡片定时刷新的更新周期,单位为30分钟,取值为自然数。<br/>当取值为0时,表示该参数不生效。<br/>当取值为正整数N时,表示刷新周期为30\*N分钟。<br/>updateDuration参数优先级高于scheduledUpdateTime,两者同时配置时,以updateDuration配置的刷新时间为准。 | 数值 | 可缺省,缺省值为“0”。 |
| formConfigAbility | 表示卡片的配置跳转链接,采用URI格式。 | 字符串 | 可缺省,缺省值为空。 |
| formVisibleNotify | 标识是否允许卡片使用卡片可见性通知。 | 字符串 | 可缺省,缺省值为空。 |
| metaData | 表示卡片的自定义信息,包含customizeData数组标签。 | 对象 | 可缺省,缺省值为空。 |
配置示例如下:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./js/widget/pages/index/index",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
### 卡片信息的持久化
因大部分卡片提供方都不是常驻服务,只有在需要使用时才会被拉起获取卡片信息,且卡片管理服务支持对卡片进行多实例管理,卡片ID对应实例ID,因此若卡片提供方支持对卡片数据进行配置,则需要对卡片的业务数据按照卡片ID进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) {
// 此处仅对卡片ID:formId,卡片名:formName和是否为临时卡片:tempFlag进行了持久化
let formInfo = {
"formName": formName,
"tempFlag": tempFlag,
"updateCount": 0
};
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// put form info
await storage.put(formId, JSON.stringify(formInfo));
console.info(`[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err)}`);
}
}
export default class EntryFormAbility extends FormExtension {
// ...
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
let formId = want.parameters["ohos.extra.param.key.form_identity"];
let formName = want.parameters["ohos.extra.param.key.form_name"];
let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"];
// 将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
storeFormInfo(formId, formName, tempFlag);
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
}
```
且需要适配onRemoveForm卡片删除通知接口,在其中实现卡片实例数据的删除。
```ts
const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store";
async function deleteFormInfo(formId: string) {
try {
const storage = await dataStorage.getStorage(DATA_STORAGE_PATH);
// del form info
await storage.delete(formId);
console.info(`[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`);
await storage.flush();
} catch (err) {
console.error(`[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err)}`);
}
}
// ...
export default class EntryFormAbility extends FormExtension {
// ...
onRemoveForm(formId) {
console.info('[EntryFormAbility] onRemoveForm');
// 删除之前持久化的卡片实例数据
// 此接口请根据实际情况实现,具体请参考:FormExtAbility Stage模型卡片实例
deleteFormInfo(formId);
}
}
```
具体的持久化方法可以参考[轻量级数据存储开发指导](../database/database-preference-guidelines.md)
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片:
- 常态卡片:卡片使用方会持久化的卡片;
- 临时卡片:卡片使用方不会持久化的卡片;
由于临时卡片的数据具有非持久化的特殊性,某些场景例如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
### 卡片数据交互
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm()接口主动触发卡片的更新。
```ts
onUpdateForm(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
// 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
formProvider.updateForm(formId, formData).catch((error) => {
console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
});
}
```
### 开发卡片页面
开发者可以使用类Web范式(HML+CSS+JSON)开发JS卡片页面。生成如下卡片页面,可以这样配置卡片页面文件:
![widget-development-stage](figures/widget-development-stage.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI界面。
- HML:使用类Web范式的组件描述卡片的页面信息。
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title">{{title}}</text>
<text class="detail_text" onclick="routerEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS:HML中类Web范式组件的样式信息。
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON:卡片页面中的数据和事件交互。
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
}
}
}
```
### 开发卡片事件
卡片支持为组件设置交互事件(action),包括router事件和message事件,其中router事件用于Ability跳转,message事件用于卡片开发人员自定义点击事件。
关键步骤说明如下:
1. 在HML中为组件设置onclick属性,其值对应到JSON文件的actions字段中。
2. 设置router事件:
- action属性值为"router"。
- abilityName为跳转目标的Ability名(支持跳转FA模型的PageAbility组件和Stage模型的UIAbility组件),如目前DevEco Studio创建的Stage模型的UIAbility默认名为EntryAbility。
- params为传递给跳转目标Ability的自定义参数,可以按需填写。其值可以在目标Ability启动时的want中的parameters里获取。如Stage模型MainAbility的onCreate生命周期里的入参want的parameters字段下获取到配置的参数。
3. 设置message事件:
- action属性值为"message"。
- params为message事件的用户自定义参数,可以按需填写。其值可以在卡片生命周期函数onFormEvent()中的message里获取。
示例如下。
- HML文件
```html
<div class="container">
<stack>
<div class="container-img">
<image src="/common/widget.png" class="bg-img"></image>
</div>
<div class="container-inner">
<text class="title" onclick="routerEvent">{{title}}</text>
<text class="detail_text" onclick="messageEvent">{{detail}}</text>
</div>
</stack>
</div>
```
- CSS文件
```css
.container {
flex-direction: column;
justify-content: center;
align-items: center;
}
.bg-img {
flex-shrink: 0;
height: 100%;
}
.container-inner {
flex-direction: column;
justify-content: flex-end;
align-items: flex-start;
height: 100%;
width: 100%;
padding: 12px;
}
.title {
font-size: 19px;
font-weight: bold;
color: white;
text-overflow: ellipsis;
max-lines: 1;
}
.detail_text {
font-size: 16px;
color: white;
opacity: 0.66;
text-overflow: ellipsis;
max-lines: 1;
margin-top: 6px;
}
```
- JSON文件
```json
{
"data": {
"title": "TitleDefault",
"detail": "TextDefault"
},
"actions": {
"routerEvent": {
"action": "router",
"abilityName": "EntryAbility",
"params": {
"message": "add detail"
}
},
"messageEvent": {
"action": "message",
"params": {
"message": "add detail"
}
}
}
}
```
## 相关实例
针对Stage模型卡片提供方的开发,有以下相关实例可供参考:
- [FormExtAbility:Stage模型卡片(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/FormExtAbility)
- [GalleryForm:图库卡片(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/ability/GalleryForm)
# 卡片切换
**卡片切换**主要包含如下三部分:
- 卡片页面布局:FA模型卡片和Stage模型卡片的布局都采用类web范式开发可以直接复用。
- 卡片配置文件:FA模型的卡片配置在config.json中,Stage卡片配置在module.json5和form_config.json中(如下图1和图2)。
- 卡片业务逻辑:FA模型和Stage模型在卡片入口文件以及生命周期存在细微的差异(如下图3和图4)。
| 配置项 | FA模型 | Stage模型 |
| -------- | -------- | -------- |
| 配置项位置 | formAbility和forms配置都在config.json文件里 | 一级目录module.json5配置文件中配置extensionAbilities(针对formExtensionAbility的配置),二级目录form_config.json文件中配置forms(针对formExtensionAbility里包含的forms的详细配置) |
| 卡片代码路径 | srcPath,不带文件名 | srcEntrance,带文件名 |
| 语言 | srcLanguage支持配置为js或ets | 无此配置项,只支持ets |
| 是否使能卡片 | formsEnabled | 无,type配置项配置为form即使能 |
| ability类型 | type:service | type:form |
| 二级目录配置标签 | 无 | metadata。包含name、value、resource。resource用于指向卡片二级目录form_config.json文件的位置 |
入口配置差异示意图1:
![widget-switch1](figures/widget-switch1.png)
卡片信息配置差异示意图2:
![widget-switch2](figures/widget-switch2.png)
| 入口及生命周期 | FA模型 | Stage模型 |
| -------- | -------- | -------- |
| 入口文件 | srcPath指向的目录下的form.ts | srcEntrance指向的文件 |
| 生命周期 | export&nbsp;default | import&nbsp;FormExtension&nbsp;from&nbsp;'\@ohos.app.form.FormExtensionAbility';<br/>export&nbsp;default&nbsp;class&nbsp;FormAbility&nbsp;extends&nbsp;FormExtension |
入口文件差异示意图3:
![widget-switch3](figures/widget-switch3.png)
生命周期差异示意图4( 生命周期回调均一致,不需要调整):
![widget-switch4](figures/widget-switch4.png)
# 窗口属性
具体获取窗口实例、设置窗口属性的接口描述及示例见[接口文档](../windowmanager/application-window-fa.md)
# window接口切换
| FA模型接口 | Stage模型接口对应d.ts文件 | Stage模型对应接口 |
| -------- | -------- | -------- |
| [enum&nbsp;WindowType&nbsp;{<br/>TYPE_APP<br/>}](../reference/apis/js-apis-window.md#windowtype7) | \@ohos.window.d.ts | [createSubWindow(name:&nbsp;string,&nbsp;callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#createsubwindow9)<br/>[createSubWindow(name:&nbsp;string):&nbsp;Promise;](../reference/apis/js-apis-window.md#createsubwindow9-1)<br/>FA模型应用通过window.create(id,&nbsp;WindowType.TYPE_APP)接口创建应用子窗口,Stage模型应用可使用WindowStage.CreateSubWindow()接口代替 |
| [create(id:&nbsp;string,&nbsp;type:&nbsp;WindowType,&nbsp;callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#windowcreatedeprecated)<br/>[create(id:&nbsp;string,&nbsp;type:&nbsp;WindowType):&nbsp;Promise&lt;Window&gt;;](../reference/apis/js-apis-window.md#windowcreatedeprecated-1) | \@ohos.window.d.ts | [createWindow(config:&nbsp;Configuration,&nbsp;callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#windowcreatewindow9)<br/>[createWindow(config:&nbsp;Configuration):&nbsp;Promise&lt;Window&gt;;](../reference/apis/js-apis-window.md#windowcreatewindow9-1) |
| [getTopWindow(callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#windowgettopwindowdeprecated)<br/>[getTopWindow():&nbsp;Promise&lt;Window&gt;;](../reference/apis/js-apis-window.md#windowgettopwindowdeprecated-1) | \@ohos.window.d.ts | [getLastWindow(ctx:&nbsp;BaseContext,&nbsp;callback:&nbsp;AsyncCallback&lt;Window&gt;):&nbsp;void;](../reference/apis/js-apis-window.md#windowgetlastwindow9)<br/>[getLastWindow(ctx:&nbsp;BaseContext):&nbsp;Promise&lt;Window&gt;;](../reference/apis/js-apis-window.md#windowgetlastwindow9-1) |
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册