未验证 提交 a65f8bb5 编写于 作者: O openharmony_ci 提交者: Gitee

!22472 删除废弃的ability指南目录-3.2Release

Merge pull request !22472 from 时睿/OpenHarmony-3.2-Release
# Ability开发
> **说明:**<br/>
> 当前目录内容已废弃,相关指南请查阅[应用模型](../application-models/Readme-CN.md)。
- [Ability框架概述](ability-brief.md)
- [应用上下文Context概述](context-userguide.md)
- FA模型
- [FA模型综述](fa-brief.md)
- [PageAbility开发指导](fa-pageability.md)
- [ServiceAbility开发指导](fa-serviceability.md)
- [DataAbility开发指导](fa-dataability.md)
- [FA卡片开发指导](fa-formability.md)
- Stage模型
- [Stage模型综述](stage-brief.md)
- [Ability开发指导](stage-ability.md)
- [ServiceExtensionAbility开发指导](stage-serviceextension.md)
- [跨端迁移开发指导](stage-ability-continuation.md)
- [Call调用开发指导](stage-call.md)
- [Stage卡片开发指导](stage-formextension.md)
- 其他
- [WantAgent开发指导](wantagent.md)
- [Ability助手使用指导](ability-assistant-guidelines.md)
- [ContinuationManager开发指导](continuationmanager.md)
- [测试框架使用指导](ability-delegator.md)
# Ability助手使用指导
Ability assistant(Ability助手,简称为aa)是实现应用、原子化服务、及测试用例启动功能的工具,为开发者提供基本的应用调试和测试的能力。通过该工具,开发者可以在hdc shell中,发送命令以执行各种系统操作,比如启动Ability、强制停止进程、打印Ability相关信息等。
## 查询相关
- **help**
用于显示aa相关的帮助信息。
**返回值:**
返回对应的帮助信息。
**使用方法:**
```
aa help
```
## Ability相关
- **start**
用于启动一个ability。
| 参数 | 参数说明 |
| --------- | -------------------------- |
| -h/--help | 帮助信息。 |
| -d | 可选参数,`device id`。 |
| -a | 必选参数,`ability name`。 |
| -b | 必选参数,`bundle name`。 |
| -D | 可选参数,调试模式。 |
**返回值:**
当成功启动Ability时,返回“start ability successfully.”;当启动失败时,返回“error: failed to start ability.”。
**使用方法:**
```
aa start [-d <device-id>] -a <ability-name> -b <bundle-name> [-D]
```
- **stop-service**
用于停止Service Ability。
| 参数 | 参数说明 |
| --------- | ------------------------ |
| -h/--help | 帮助信息。 |
| -d | 可选参数,device id。 |
| -a | 必选参数,ability name。 |
| -b | 必选参数,bundle name。 |
**返回值:**
当成功停止Service Ability时,返回“stop service ability successfully.”;当停止失败时,返回“error: failed to stop service ability.”。
**使用方法:**
```
aa stop-service [-d <device-id>] -a <ability-name> -b <bundle-name>
```
- **dump**
用于打印Ability的相关信息。
| 参数 | 二级参数 | 参数说明 |
| ----------------- | -------------------- | ------------------------------------------------------------ |
| -h/--help | - | 帮助信息。 |
| -a/--all | - | 打印所有mission内的Ability。 |
| -l/--mission-list | type(缺省打印全部) | 打印任务栈信息。<br />type:<br />NORMAL <br />DEFAULT_STANDARD<br />DEFAULT_SINGLE<br />LAUNCHER |
| -e/--extension | elementName | 打印扩展组件信息。 |
| -u/--userId | UserId | 打印指定UserId的栈信息,需要和其他参数组合使用,例如 `aa dump -a -u 100``aa dump -d -u 100` |
| -d/--data | - | 打印Data Ability。 |
| -i/--ability | AbilityRecord ID | 打印指定Ability详细信息。 |
| -c/--client | - | 打印Ability详细信息,需要和其他参数组合使用,例如 `aa dump -a -c``aa dump -i 21 -c` |
**使用方法:**
```
aa dump -a
```
![aa-dump-a](figures/aa-dump-a.PNG)
```
aa dump -l
```
![aa-dump-l](figures/aa-dump-l.PNG)
```
aa dump -i 12
```
![aa-dump-i](figures/aa-dump-i.PNG)
- **force-stop**
通过bundle name强制停止一个进程。
**返回值:**
当成功强制停止该进程时,返回“force stop process successfully.”;当强制停止失败时,返回“error: failed to force stop process.”。
**使用方法:**
```
aa force-stop <bundle-name>
```
\ No newline at end of file
# Ability框架概述
Ability是OpenHarmony系统对应用的基本抽象。
每个Ability是完成独立业务的应用组件,是系统调度应用的最小单元。一个应用可以包含一个或多个Ability。
Ability框架模型具有两种形态:
- 第一种形态称为FA模型。API 8及其更早版本的应用只能使用FA模型。FA模型将Ability分为Page Ability、Service Ability以及Data Ability几种类型。
- 第二种形态称为Stage模型,这是自API 9新增的模型。Stage模型将Ability分为UIAbility和ExtensionAbility两大类,其中ExtensionAbility又被扩展为ServiceExtensionAbility、FormExtensionAbility、DataShareExtensionAbility等一系列ExtensionAbility,以便满足更多的使用场景。
自API 9开始,Stage模型是主推的开发模型。
Stage模型的设计,主要是为了开发者更加方便地开发出分布式环境下的复杂应用。下表给出了两种模型在设计上的差异:
| 对比 | FA模型 | Stage模型 |
| -------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
| 应用组件开发方式 | 类Web的开发方式。 | 面向对象的开发方式。 |
| 引擎实例 | 每个Ability实例独占一个虚拟机实例。 | 多个Ability实例可以共享同一个虚拟机实例。 |
| 进程内对象共享 | 不支持。 | 支持。 |
| 包描述文件 | 使用`config.json`描述HAP包和组件信息,组件必须使用固定的文件名。 | 使用`module.json5`描述HAP包和组件信息,可以指定入口文件名。 |
| 组件 | 提供PageAbility(页面展示),ServiceAbility(服务),DataAbility(数据分享)以及FormAbility(卡片)。 | 提供UIAbility(页面展示)、Extension(基于场景的服务扩展)。 |
除了上述设计上的差异外,对于开发者而言,两种模型的主要区别在于:
* Ability类型存在差异;
![favsstage](figures/favsstage.png)
* Ability生命周期存在差异;
![lifecycle](figures/lifecycle.png)
两种模型的基本介绍,详见[FA模型综述](fa-brief.md)[Stage模型综述](stage-brief.md)
## 相关实例
### Stage 模型
- [Stage模型介绍(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/BasicFeature/ApplicationModels/StageModel)
- [窗口扩展(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/WindowManagement/WindowExtAbility)
- [系统任务管理(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/ApplicationModels/MissionManager)
- [仿桌面应用(ArkTS)(API9)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/ApplicationModels/Launcher)
# 测试框架使用指导
## 概述
Delegator测试框架是OpenHarmony提供的一套开发者应用自测试框架,旨在为开发者提供针对应用的自测试环境。开发者可以通过Delegator类启动对应Ability,并通过Delegator类提供的能力对Ability进行生命周期切换和监听,同时支持shellCMD输入和测试结果打印显示等功能。
## 约束与限制
测试框架相关接口只能在测试hap包中使用,只有通过`aa test`命令或者DevEco Studio启动测试环境后相关接口才能生效。
## 测试框架启动
测试框架启动有两种方式:
- 方式一:通过`aa test`命令启动。
- 方式二:通过DevEco Studio启动。
### aa test启动
开发者可通过 `aa test` 命令启动测试框架,开发者可以自行指定使用的TestRunner以及TestRunner所在hap包的package name或module name,具体命令示例如下:
**FA模型:**
```javascript
aa test -b BundleName -p com.example.myapplicationfaets -s unittest OpenHarmonyTestRunner -s class ActsAbilityTest -w 20
```
**Stage模型:**
```javascript
aa test -b BundleName -m com.example.myapplicationfaets -s unittest OpenHarmonyTestRunner -s class ActsAbilityTest -w 20
```
| 参数 | 是否必选 | 参数说明 |
| --------------- | -------- | ------------------------------------------------------------ |
| -b | 是 | TestRunner所在hap包的bundle name。 |
| -p | 是 | TestRunner所在hap包的package name,FA模型使用。 |
| -m | 是 | TestRunner所在hap包的module name,Stage模型使用。 |
| -s unittest | 是 | 启用的TestRunner名称,TestRunner名称和文件名需要保持一致。 |
| -w | 否 | 测试用例超时时间,单位为秒,如果未指定或指定小于等于0的整数,测试框架会一直等待测试代码调用finishTest才退出。 |
| -s \<key>\<value> | 否 | 支持以key-value的方式输入任何参数,输入的参数可通过AbilityDelegatorArgs.parameters以key-value的方式获取。示例:-s classname myTest,key为"-s classname",value为"myTest"。 |
| -D | 否 | 以Debug模式启动被测试应用。 |
| -h | 否 | 输出帮助信息。 |
### DevEco Studio启动
DevEco Studio启动相关介绍见 [OpenHarmony测试框架](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-openharmony-test-framework-0000001263160453#section1034420367508)
## TestRunner介绍
TestRunner是测试框架测试流程入口类。当测试流程启动时,系统会调用TestRunner内相关接口,开发者需要派生该类,并重写onPrepare、onRun方法。DevEco Studio在创建应用模板时会初始化一个默认TestRunner,并在onRun方法启动默认的TestAbility。开发者也可以修改TestAbility测试代码内容,也可以修改默认的TestRunner内onPrepare、onRun方法,自行实现测试代码。具体详细内容请参考TestRunnerAPI接口说明[TestRunner](../reference/apis/js-apis-application-testRunner.md)
## AbilityDelegatorRegistry介绍
AbilityDelegatorRegistry是测试框架提供的AbilityDelegator仓库类。开发者可以使用AbilityDelegatorRegistry获取AbilityDelegator实例以及执行此次测试时传入和生成的相关参数AbilityDelegatorArgs。开发者可以使用AbilityDelegator调用测试框架提供的函数集进行测试验证。具体详细内容请参考AbilityDelegatorRegistry API接口说明[AbilityDelegatorRegistry](../reference/apis/js-apis-application-abilityDelegatorRegistry.md)
## AbilityDelegatorArgs介绍
AbilityDelegatorArgs是测试框架提供的测试参数类。开发者可以使用AbilityDelegatorArgs获取执行此次测试时传入和生成的相关参数。具体详细内容请参考AbilityDelegatorArgs API接口说明[AbilityDelegatorArgs](../reference/apis/js-apis-inner-application-abilityDelegatorArgs.md)
## AbilityMonitor介绍
AbilityMonitor是测试框架提供用来绑定并监听Ability类。开发者可以使用AbilityMonitor绑定Ability,并将AbilityMonitor添加到监听列表。绑定后Ability的创建、生命周期变化等会触发AbilityMonitor内相关回调函数,开发者可以在对应回调函数内进行测试验证。具体详细内容请参考AbilityMonitor API接口说明[AbilityMonitor](../reference/apis/js-apis-inner-application-abilityMonitor.md)
**示例**
```javascript
import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry'
function onAbilityCreateCallback(data) {
console.info("onAbilityCreateCallback");
}
var monitor = {
abilityName: "abilityname",
onAbilityCreate: onAbilityCreateCallback
}
var abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.addAbilityMonitor(monitor).then(() => {
console.info("addAbilityMonitor promise");
});
```
## AbilityDelegator介绍
AbilityDelegator是测试框架主要功能类,提供了启动Ability、获取Ability示例、调度Ability生命周期、对Ability生命周期进行监听、打印测试结果等功能。
**导入模块**
```javascript
import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry'
```
```javascript
var abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
```
### 启动Ability并监听Ability状态变化
配合AbilityMonitor使用,可以实现对Ability启动、Ability实例获取、监听状态变化功能。
**示例:**
```javascript
var abilityDelegator;
var ability;
var timeout = 100;
function onAbilityCreateCallback(data) {
console.info("onAbilityCreateCallback");
}
var monitor = {
abilityName: "abilityname",
onAbilityCreate: onAbilityCreateCallback
}
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.waitAbilityMonitor(monitor, timeout, (err, data) => {
ability = data;
console.info("waitAbilityMonitor callback");
});
var want = {
bundleName: "bundleName",
abilityName: "abilityName"
};
abilityDelegator.startAbility(want, (err, data) => {
console.info("startAbility callback");
});
```
### 调度Ability生命周期
AbilityDelegator提供对Ability生命周期进行显示调度的能力,支持Foreground、Background,配合AbilityMonitor中对Ability生命周期监听方法,可以完整的测试Ability生命周期变化。具体详细内容请参考AbilityDelegator API接口说明[AbilityDelegator](../reference/apis/js-apis-inner-application-abilityDelegator.md)
### 执行shellCMD命令
AbilityDelegator提供执行shellCMD命令功能,开发者可以在测试代码中执行shell命令,该功能仅在测试环境中生效,非测试环境该接口调用无效果。
**示例:**
```javascript
var abilityDelegator;
var cmd = "cmd";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.executeShellCommand(cmd, (err, data) => {
console.info("executeShellCommand callback");
});
```
### 打印日志信息
AbilityDelegator提供打印日志信息功能,开发者可以在测试代码中输出过程日志信息到终端控制台。
**示例:**
```javascript
var abilityDelegator;
var msg = "msg";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.print(msg, (err) => {
console.info("print callback");
});
```
### 结束测试并打印日志信息
AbilityDelegator提供主动结束测试的功能,开发者可以在测试代码中主动结束测试过程并输出日志码及信息到终端控制台。
**示例:**
```javascript
var abilityDelegator;
var msg = "msg";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.finishTest(msg, 0, (err) => {
console.info("finishTest callback");
});
```
\ No newline at end of file
# 应用上下文Context使用指导
## Context概述
​ Context是应用中对象的上下文,提供获取应用程序环境信息的能力。
​ OpenHarmony的应用框架分为FA模型和Stage两种模型。对应存在两套Context机制适配两种应用框架模型,其中application/BaseContext属于通用的Context基类,里面包含一个属性stageMode,用来区分开发模型是FA还是Stage。
- FA模型
只有app/Context中的方法属于FA模型对应的Context。该模式下,应用级别的Context和Ability级别的Context都是该类型的实例,如果在应用级别的Context里面调用了Ability级别的方法,会产生错误。所以开发者需要注意Context实例所代表的实际含义。
- Stage模型
除了app/Context之外的Context都属于Stage模型,分别有application/Context、application/ApplicationContext、application/AbilityStageContext、application/ExtensionContext、application/AbilityContext、application/FormExtensionContext等Context。这些Context的介绍及使用方式将会在[Stage模型的Context详细介绍](#stage模型的context详细介绍)中进行说明。
![contextIntroduction](figures/contextIntroduction.png)
## FA模型的Context详细介绍
​ 只有app/Context中的方法属于FA模型对应的Context。
​ FA模型只有一个Context定义。Context中所有的功能都是通过方法来提供的,它提供了一些featureAbility中不存在的方法,相当于featureAbility的一个扩展和补全。
**d.ts声明**
​ https://gitee.com/openharmony/interface_sdk-js/blob/master/api/app/context.d.ts
**示例**
```javascript
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')
},
}
```
### FA模型Context常用方法介绍
方法:
```javascript
setDisplayOrientation(orientation: bundle.DisplayOrientation, callback: AsyncCallback<void>): void
setDisplayOrientation(orientation: bundle.DisplayOrientation): Promise<void>;
```
简介:设置当前ability的显示方向。
示例:
```javascript
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.log("Set display orientation.")
})
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
## Stage模型的Context详细介绍
​ Stage模型有如下几类Context:
### application/Context
​ application/Context是基类Context。里面提供了应用的一些基础信息:resourceManager、applicationInfo、cacheDir、area等,还有应用的一些基本方法:createModuleContext等。
**d.ts声明**
​ https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/Context.d.ts
### application/ApplicationContext
​ application/ApplicationContext是应用级别的Context。和基类Context相比,应用级别的Context中提供了监听进程内组件的生命周期的能力,包括registerAbilityLifecycleCallback和unregisterAbilityLifecycleCallback两种方法。
**获取方法**
​ 在Ability中通过context.getApplicationContext()方法获取。
**示例**
```javascript
import Ability from "@ohos.application.Ability";
var lifecycleid;
export default class MainAbility extends Ability {
onCreate() {
console.log("MainAbility onCreate")
let AbilityLifecycleCallback = {
onAbilityCreate(ability){
console.log("AbilityLifecycleCallback onAbilityCreate ability:" + JSON.stringify(ability));
},
onWindowStageCreate(ability, windowStage){
console.log("AbilityLifecycleCallback onWindowStageCreate ability:" + JSON.stringify(ability));
console.log("AbilityLifecycleCallback onWindowStageCreate windowStage:" + JSON.stringify(windowStage));
},
onWindowStageActive(ability, windowStage){
console.log("AbilityLifecycleCallback onWindowStageActive ability:" + JSON.stringify(ability));
console.log("AbilityLifecycleCallback onWindowStageActive windowStage:" + JSON.stringify(windowStage));
},
onWindowStageInactive(ability, windowStage){
console.log("AbilityLifecycleCallback onWindowStageInactive ability:" + JSON.stringify(ability));
console.log("AbilityLifecycleCallback onWindowStageInactive windowStage:" + JSON.stringify(windowStage));
},
onWindowStageDestroy(ability, windowStage){
console.log("AbilityLifecycleCallback onWindowStageDestroy ability:" + JSON.stringify(ability));
console.log("AbilityLifecycleCallback onWindowStageDestroy windowStage:" + JSON.stringify(windowStage));
},
onAbilityDestroy(ability){
console.log("AbilityLifecycleCallback onAbilityDestroy ability:" + JSON.stringify(ability));
},
onAbilityForeground(ability){
console.log("AbilityLifecycleCallback onAbilityForeground ability:" + JSON.stringify(ability));
},
onAbilityBackground(ability){
console.log("AbilityLifecycleCallback onAbilityBackground ability:" + JSON.stringify(ability));
},
onAbilityContinue(ability){
console.log("AbilityLifecycleCallback onAbilityContinue ability:" + JSON.stringify(ability));
}
}
// 1.通过context属性获取applicationContext
let applicationContext = this.context.getApplicationContext();
// 2.通过applicationContext注册监听应用内生命周期
lifecycleid = applicationContext.registerAbilityLifecycleCallback(AbilityLifecycleCallback);
console.log("registerAbilityLifecycleCallback number: " + JSON.stringify(lifecycleid));
},
onDestroy() {
let applicationContext = this.context.getApplicationContext();
applicationContext.unregisterAbilityLifecycleCallback(lifecycleid, (error, data) => {
console.log("unregisterAbilityLifecycleCallback success, err: " + JSON.stringify(error));
});
}
}
```
**d.ts声明**
​ https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/ApplicationContext.d.ts
### application/AbilityStageContext
​ application/AbilityStageContext是Hap包级别的Context。和基类Context相比,Hap包级别的Context中多了HapModuleInfo和Configuration两个信息。
**获取方法**
​ 可以直接在AbilityStage中通过context属性获取。
**示例**
```javascript
export default class MyAbilityStage extends AbilityStage {
onCreate() {
// 属性context就是AbilityStageContext类型的
console.log('HapModuleInfo is ' + this.context.currentHapModuleInfo);
}
}
```
**d.ts声明**
​ https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/AbilityStageContext.d.ts
### application/AbilityContext
​ Stage模型下,每个Ability中都包含了一个Context属性。
​ Ability功能主要是处理生命周期,其余操作Ability的方法(如startAbility、connectAbility等)都是在AbilityContext中实现的。
**获取方法**
​ 在Ability中通过context属性获取。
**示例**
```javascript
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate")
globalThis.abilityWant = want;
}
onDestroy() {
console.log("[Demo] MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
// Main window is created, set main page for this ability
console.log("[Demo] MainAbility onWindowStageCreate")
// 在这里获取AbilityContext,打印ability的信息
let context = this.context;
console.log("[Demo] MainAbility bundleName " + context.abilityInfo.bundleName)
windowStage.loadContent("pages/index", (err, data) => {
if (err.code) {
console.error('Failed to load the content. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in loading the content. Data: ' + JSON.stringify(data))
});
}
onWindowStageDestroy() {
// Main window is destroyed, release UI related resources
console.log("[Demo] MainAbility onWindowStageDestroy")
}
onForeground() {
// Ability has brought to foreground
console.log("[Demo] MainAbility onForeground")
}
onBackground() {
// Ability has back to background
console.log("[Demo] MainAbility onBackground")
}
};
```
### application/FormExtensionContext
卡片业务相关,点下面链接了解。
[FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md)
### 在ArkTS页面中访问Context
Stage模型下,在Ability的`onWindowStageCreate`生命周期中,可以通过WindowStage的`SetUIContent`方法加载一个ArkTS页面。在一些场景中,需要在页面内获取Context调用相关API。
**获取方法**
在ArkTS页面中通过以下全局方法获取当前页面关联的Context。
| 接口名 | 描述 |
| :------------------------------------ | :----------------------------------------------------------- |
| getContext(component: Object): Object | 获取页面中component所关联的Context对象。<br/>从API version 9开始,该接口支持在ArkTS卡片中使用。 |
**示例**
```ts
// MainAbility.ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate")
}
onDestroy() {
console.log("[Demo] MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
// 加载index页面,并传入当前Context
windowStage.setUIContent(this.context, "pages/index", null)
}
onWindowStageDestroy() {}
onForeground() {}
onBackground() {}
};
```
```ts
// pages/index.ets
import context from '@ohos.application.context'
type Context = context.Context
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Text('GetContext')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
// 获取当前component关联的Context
var context : Context = getContext(this) as Context
console.info("CacheDir:" + context.cacheDir)
})
}
.width('100%')
}
.height('100%')
}
}
```
## 常见错误使用方式
**错误1:Stage模型通过globalThis去获取Context**
**原因**
​ 应用框架在API9上推出了新的应用模型(Stage模型)。在老的模型(FA模型)下,每个Ability实例有一个js虚拟机实例,所以可以从js引擎的global对象上,获取到一个全局的Ability实例,但是在新的模型(Stage模型)下,整个应用进程共用一个js虚拟机实例,其中可以运行多个Ability实例,这样就不存在一个全局的Ability实例。如果开发者在新的模型(Stage模型)下,调用的API实现仍然走到了获取全局Ability实例的方法,就可能会发生错误或者崩溃。
\ No newline at end of file
# ContinuationManager开发指导
> 说明:本模块接口用于拉起系统中的设备选择模块,由于该模块功能暂不完备,因此**流转能力整体暂不支持用于应用开发**。
## 场景简介
随着全场景多设备生活方式的不断深入,用户拥有的设备越来越多,每个设备都能在适合的场景下提供良好的体验,但是,每个设备也有使用场景的局限。流转能力致力于打破设备界限,多设备联动,使用户应用程序可分可合、可流转,实现体验升级。
continuationManager作为流转能力的入口,主要用于拉起系统中的设备选择模块供用户选择,待选择设备后,会给用户返回已选择的远端设备信息,用户可以根据远端设备信息发起跨端迁移或多端协同操作。
![continuationManager](figures/continuationManager.png)
## 接口说明
| 接口名 | 接口描述 |
| ---------------------------------------------------------------------------------------------- | ----------- |
| registerContinuation(callback: AsyncCallback\<number>): void | 注册流转管理服务,并获取对应的注册token,无过滤条件(AsyncCallback)。 |
| registerContinuation(options: ContinuationExtraParams, callback: AsyncCallback\<number>): void | 注册流转管理服务,并获取对应的注册token(AsyncCallback)。 |
| registerContinuation(options?: ContinuationExtraParams): Promise\<number> | 连接流转管理服务,并获取对应的注册token(Promise)。 |
| on(type: "deviceSelected", token: number, callback: Callback\<Array\<ContinuationResult>>): void | 监听设备连接状态(Callback)。 |
| on(type: "deviceUnselected", token: number, callback: Callback\<Array\<ContinuationResult>>): void | 监听设备断开状态(Callback)。 |
| off(type: "deviceSelected", token: number): void | 取消监听设备连接状态。 |
| off(type: "deviceUnselected", token: number): void | 取消监听设备断开状态。 |
| startContinuationDeviceManager(token: number, callback: AsyncCallback\<void>): void | 拉起设备选择模块,可显示组网内可选择设备列表信息,无过滤条件(AsyncCallback)。 |
| startContinuationDeviceManager(token: number, options: ContinuationExtraParams, callback: AsyncCallback\<void>): void | 拉起设备选择模块,可显示组网内可选择设备列表信息(AsyncCallback)。 |
| startContinuationDeviceManager(token: number, options?: ContinuationExtraParams): Promise\<void> | 拉起设备选择模块,可显示组网内可选择设备列表信息(Promise)。 |
| updateContinuationState(token: number, deviceId: string, status: DeviceConnectState, callback: AsyncCallback\<void>): void | 通知设备选择模块,更新当前的流转状态(AsyncCallback)。 |
| updateContinuationState(token: number, deviceId: string, status: DeviceConnectState): Promise\<void> | 通知设备选择模块,更新当前的流转状态(Promise)。 |
| unregisterContinuation(token: number, callback: AsyncCallback\<void>): void | 取消注册流转管理服务,传入注册时获取的token进行取消注册(AsyncCallback)。 |
| unregisterContinuation(token: number): Promise\<void> | 取消注册流转管理服务,传入注册时获取的token进行取消注册(Promise)。 |
## 开发步骤
1. 导入continuationManager模块。
```ts
import continuationManager from '@ohos.continuation.continuationManager';
```
2. 申请分布式权限 DISTRIBUTED_DATASYNC。
权限申请在FA平台和Stage平台有区别,FA平台需要在`config.json`里面进行配置请求权限,示例代码如下:
```json
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
```
这个权限还需要在应用首次启动的时候弹窗获取用户授权,可以通过如下代码实现:
```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
import bundle from '@ohos.bundle';
import featureAbility from '@ohos.ability.featureAbility';
async function requestPermission() {
let permissions: Array<string> = [
"ohos.permission.DISTRIBUTED_DATASYNC"
];
let needGrantPermission: boolean = false;
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let applicationInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', 0, 100);
for (let i = 0; i < permissions.length; i++) {
let result = await atManager.verifyAccessToken(applicationInfo.accessTokenId, permissions[i]);
//判断是否未申请该权限
if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
needGrantPermission = true;
break;
}
}
// 如果未申请该权限,则需要调用requestPermissionsFromUser接口申请权限
if (needGrantPermission) {
await featureAbility.getContext().requestPermissionsFromUser(permissions, 1);
} else {
console.info('app permission already granted');
}
}
```
Stage平台需要在`module.json5`里面进行配置请求权限,示例代码如下:
```json
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
```
```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
import bundle from '@ohos.bundle';
async function requestPermission() {
let permissions: Array<string> = [
"ohos.permission.DISTRIBUTED_DATASYNC"
];
let needGrantPermission: boolean = false;
let atManger: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let applicationInfo = await bundle.getApplicationInfo('ohos.samples.continuationmanager', 0, 100);
for (const permission of permissions) {
try {
let grantStatus = await atManger.verifyAccessToken(applicationInfo.accessTokenId, permission);
//判断是否未申请该权限
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
needGrantPermission = true;
break;
}
} catch (err) {
console.error('app permission query grant status error' + JSON.stringify(err));
needGrantPermission = true;
break;
}
}
// 如果未申请该权限,则需要调用requestPermissionsFromUser接口申请权限
if (needGrantPermission) {
try {
// globalThis.context即Ability.context,需提前在MainAbility.ts文件中赋值
await atManger.requestPermissionsFromUser(globalThis.context, permissions);
} catch (err) {
console.error('app permission request permissions error' + JSON.stringify(err));
}
} else {
console.info('app permission already granted');
}
}
```
3. 注册流转管理服务,获取对应的注册token。
注册流转管理服务的代码示例如下:
```ts
let token: number = -1; // 用于保存注册成功并返回的token,后续使用其完成监听设备连接/断开状态、拉起设备选择模块以及更新流转状态的动作
try {
continuationManager.registerContinuation().then((data) => {
console.info('registerContinuation finished, ' + JSON.stringify(data));
token = data; // 获取到对应的注册token,并赋值给token变量
}).catch((err) => {
console.error('registerContinuation failed, cause: ' + JSON.stringify(err));
});
} catch (err) {
console.error('registerContinuation failed, cause: ' + JSON.stringify(err));
}
```
4. 监听设备状态。
监听设备连接状态的代码示例如下:
```ts
let remoteDeviceId: string = ""; // 用于保存用户选择的远端设备信息,后续使用其完成跨端迁移或多端协同操作
try {
// 参数token为注册token
continuationManager.on("deviceSelected", token, (continuationResults) => {
console.info('registerDeviceSelectedCallback len: ' + continuationResults.length);
if (continuationResults.length <= 0) {
console.info('no selected device');
return;
}
remoteDeviceId = continuationResults[0].id; // 将选择的第一个远端设备deviceId赋值给remoteDeviceId变量
// 将remoteDeviceId参数传给want
let want = {
deviceId: remoteDeviceId,
bundleName: 'ohos.samples.continuationmanager',
abilityName: 'MainAbility'
};
globalThis.abilityContext.startAbility(want).then((data) => {
console.info('StartRemoteAbility finished, ' + JSON.stringify(data));
}).catch((err) => {
console.error('StartRemoteAbility failed, cause: ' + JSON.stringify(err));
});
});
} catch (err) {
console.error('on failed, cause: ' + JSON.stringify(err));
}
```
上述多端协同操作为Stage平台的跨设备拉起,FA平台详情见[PageAbility开发指导](fa-pageability.md)。
同时用户还可通知设备选择模块,更新当前的流转状态,代码示例如下:
```ts
// 设置设备流转状态
let deviceConnectStatus: continuationManager.DeviceConnectState = continuationManager.DeviceConnectState.CONNECTED;
// 参数token为注册token,参数remoteDeviceId为获取到的remoteDeviceId
try {
continuationManager.updateContinuationState(token, remoteDeviceId, deviceConnectStatus).then((data) => {
console.info('updateContinuationState finished, ' + JSON.stringify(data));
}).catch((err) => {
console.error('updateContinuationState failed, cause: ' + JSON.stringify(err));
});
} catch (err) {
console.error('updateContinuationState failed, cause: ' + JSON.stringify(err));
}
```
监听设备断开状态,方便用户终止跨端迁移或多端协同操作,代码示例如下:
```ts
try {
// 参数token为注册token
continuationManager.on("deviceUnselected", token, (continuationResults) => {
console.info('onDeviceUnselected len: ' + continuationResults.length);
if (continuationResults.length <= 0) {
console.info('no unselected device');
return;
}
// 更新设备流转状态
let unselectedDeviceId: string = continuationResults[0].id; // 将取消选择的第一个远端设备deviceId赋值给unselectedDeviceId变量
let deviceConnectStatus: continuationManager.DeviceConnectState = continuationManager.DeviceConnectState.DISCONNECTING; // 设备断开状态
// 参数token为注册token,参数unselectedDeviceId为获取到的unselectedDeviceId
continuationManager.updateContinuationState(token, unselectedDeviceId, deviceConnectStatus).then((data) => {
console.info('updateContinuationState finished, ' + JSON.stringify(data));
}).catch((err) => {
console.error('updateContinuationState failed, cause: ' + JSON.stringify(err));
});
});
} catch (err) {
console.error('updateContinuationState failed, cause: ' + JSON.stringify(err));
}
```
5. 拉起设备选择模块,可显示组网内可选择设备列表信息,供用户选择。
拉起设备选择模块的代码示例如下:
```ts
// 过滤参数
let continuationExtraParams = {
deviceType: ["00E"], // 设备类型
continuationMode: continuationManager.ContinuationMode.COLLABORATION_SINGLE // 设备选择模块单选模式
};
try {
// 参数token为注册token
continuationManager.startContinuationDeviceManager(token, continuationExtraParams).then((data) => {
console.info('startContinuationDeviceManager finished, ' + JSON.stringify(data));
}).catch((err) => {
console.error('startContinuationDeviceManager failed, cause: ' + JSON.stringify(err));
});
} catch (err) {
console.error('startContinuationDeviceManager failed, cause: ' + JSON.stringify(err));
}
```
6. 当用户不打算再进行跨端迁移或多端协同操作时,可以传入注册时获取的token进行取消注册。
取消注册流转管理服务的代码示例如下:
```ts
try {
// 参数token为注册token
continuationManager.unregisterContinuation(token).then((data) => {
console.info('unregisterContinuation finished, ' + JSON.stringify(data));
}).catch((err) => {
console.error('unregisterContinuation failed, cause: ' + JSON.stringify(err));
});
} catch (err) {
console.error('unregisterContinuation failed, cause: ' + JSON.stringify(err));
}
```
\ No newline at end of file
# FA模型综述
## 整体架构
OpenHarmony应用的开发,是以Ability为入口展开的。
对于Ability的开发,通常是以生命周期的回调处理为中心。
Ability框架在API 8及更早版本仅支持FA模型。FA模型中Ability分为PageAbility、ServiceAbility、DataAbility、FormAbility几种类型。其中:
- PageAbility使用ArkUI实现用户界面,是用户可见并可以交互的Ability实例。
- ServiceAbility也是Ability一种,但是没有用户界面。它提供了其他Ability调用的自定义服务,ServiceAbility在后台运行。
- DataAbility也是没有界面的Ability,提供其他Ability进行数据的增删查服务,它同样在后台运行。
- FormAbility是实现卡片的Ability,卡片是OpenHarmomny系统上的一种界面展示形式。
> 注:自API 9开始,Stage模型是主推的开发模型。
## 生命周期
在所有Ability中,PageAbility因为具有界面,也是应用的交互入口,因此其生命周期更加复杂。
**PageAbility生命周期回调如下图所示:**
![fa-pageAbility-lifecycle](figures/fa-pageAbility-lifecycle.png)
其他类型Ability的生命周期可参考PageAbility生命周期去除前后台切换以及`onShow``onHide`的部分来理解。
开发者可以在 `app.js/app.ets` 中重写生命周期函数,在对应的生命周期回调内处理应用的相应逻辑。
目前`app.js`仅支持`onCreate``onDestroy`回调,但`app.ets`支持全量生命周期回调。
## 进程线程模型
每个应用运行在不同的进程中,在FA模型中,每个Ability运行在独立的虚拟机中。
应用进程在Ability启动时创建,此时会为Ability创建相应的线程。当一个应用有多个Ability时,每一个Ability在独立线程中运行。在FA模型中,每个Ability绑定一个独立的虚拟机实例,因此Ability之间是隔离的。
![fa-threading-model](figures/fa-threading-model.png)
## 应用包结构
FA模型的应用包的工程目录结构,请参考[OpenHarmony工程介绍](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-project-overview-0000001218440650#section4154183910141)
对FA模型的应用包结构的配置说明,请参考[应用包结构说明(FA模型)](../quick-start/application-configuration-file-overview-fa.md)
# DataAbility开发指导
## 场景介绍
基于Data模板的Ability(以下简称“Data”),有助于应用管理其自身和其他应用存储数据的访问,并提供与其他应用共享数据的方法。Data既可用于同设备不同应用的数据共享,也支持跨设备不同应用的数据共享。
Data提供方可以自定义数据的增、删、改、查,以及文件打开等功能,并对外提供这些接口。
## URI介绍
Data的提供方和使用方都通过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*/*10*
> 说明
>
> 本地设备的“device_id”字段为空,因此在“dataability:”后面有三个“/”。
## 接口说明
**表1** Data中相关生命周期API功能介绍
|接口名|描述|
|:------|:------|
|onInitialized(info: AbilityInfo): void|在Ability初始化调用,通过此回调方法执行RDB等初始化操作。|
|update(uri: string, valueBucket: rdb.ValuesBucket, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<number>): void|更新数据库中的数据。|
|query(uri: string, columns: Array\<string>, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<ResultSet>): void|查询数据库中的数据。|
|delete(uri: string, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<number>): void|删除一条或多条数据。|
|normalizeUri(uri: string, callback: AsyncCallback\<string>): void|对URI进行规范化。一个规范化的URI可以支持跨设备使用、持久化、备份和还原等,当上下文改变时仍然可以引用到相同的数据项。|
|batchInsert(uri: string, valueBuckets: Array\<rdb.ValuesBucket>, callback: AsyncCallback\<number>): void|向数据库中插入多条数据。|
|denormalizeUri(uri: string, callback: AsyncCallback\<string>): void|将一个由normalizeUri生产的规范化URI转换成非规范化的URI。|
|insert(uri: string, valueBucket: rdb.ValuesBucket, callback: AsyncCallback\<number>): void|向数据中插入一条数据。|
|openFile(uri: string, mode: string, callback: AsyncCallback\<number>): void|打开一个文件。|
|getFileTypes(uri: string, mimeTypeFilter: string, callback: AsyncCallback\<Array\<string>>): void|获取文件的MIME类型。|
|getType(uri: string, callback: AsyncCallback\<string>): void|获取URI指定数据相匹配的MIME类型。|
|executeBatch(ops: Array\<DataAbilityOperation>, callback: AsyncCallback\<Array\<DataAbilityResult>>): void|批量操作数据库中的数据。|
|call(method: string, arg: string, extras: PacMap, callback: AsyncCallback\<PacMap>): void|自定义方法。|
## 开发步骤
### 创建Data
1. 实现Data中Insert、Query、Update、Delete接口的业务内容。保证能够满足数据库存储业务的基本需求。BatchInsert与ExecuteBatch接口已经在系统中实现遍历逻辑,依赖Insert、Query、Update、Delete接口逻辑,来实现数据的批量处理。
创建Data的代码示例如下:
```javascript
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)
}
};
```
2. 子模块配置
| Json重要字段 | 备注说明 |
| ------------ | ------------------------------------------------------------ |
| "name" | Ability名称,对应Ability派生的Data类名。 |
| "type" | Ability类型,Data对应的Ability类型为”data“。 |
| "uri" | 通信使用的URI。 |
| "exported" | 对其他应用是否可见,设置为true时,Data才能与其他应用进行通信传输数据。 |
**config.json配置样例**
```json
"abilities":[{
"srcPath": "DataAbility",
"name": ".DataAbility",
"icon": "$media:icon",
"srcLanguage": "ets",
"description": "$string:description_dataability",
"type": "data",
"exported": true,
"uri": "dataability://ohos.samples.etsdataability.DataAbility"
}]
```
### 访问Data
#### 开发前准备
需导入基础依赖包,以及获取与Data子模块通信的URI字符串。
其中,基础依赖包包括:
- @ohos.ability.featureAbility
- @ohos.data.dataAbility
- @ohos.data.rdb
#### DataAbility接口开发指导
1. 创建工具接口类对象。
工具接口类对象DataAbilityHelper相关接口可参考[DataAbilityHelper模块](../reference/apis/js-apis-inner-ability-dataAbilityHelper.md)
```js
// 作为参数传递的URI,与config中定义的URI的区别是多了一个"/",是因为作为参数传递的URI中,在第二个与第三个"/"中间,存在一个DeviceID的参数
import featureAbility from '@ohos.ability.featureAbility'
import ohos_data_ability from '@ohos.data.dataAbility'
import ohos_data_rdb from '@ohos.data.rdb'
var urivar = "dataability:///com.ix.DataAbility"
var DAHelper = featureAbility.acquireDataAbilityHelper(
urivar
);
```
2. 构建数据库相关的RDB数据。
```js
var valuesBucket = {"name": "gaolu"}
var da = new ohos_data_ability.DataAbilityPredicates()
var valArray =new Array("value1");
var cars = new Array({"batchInsert1" : "value1",});
```
3. 调用insert方法向指定的Data子模块插入数据。
```js
// callback方式调用:
DAHelper.insert(
urivar,
valuesBucket,
(error, data) => {
console.log("DAHelper insert result: " + data)
}
);
```
```js
// promise方式调用:
var datainsert = await DAHelper.insert(
urivar,
valuesBucket
);
```
4. 调用delete方法删除Data子模块中指定的数据。
```js
// callback方式调用:
DAHelper.delete(
urivar,
da,
(error, data) => {
console.log("DAHelper delete result: " + data)
}
);
```
```js
// promise方式调用:
var datadelete = await DAHelper.delete(
urivar,
da,
);
```
5. 调用update方法更新指定Data子模块中的数据。
```js
// callback方式调用:
DAHelper.update(
urivar
valuesBucket,
da,
(error, data) => {
console.log("DAHelper update result: " + data)
}
);
```
```js
// promise方式调用:
var dataupdate = await DAHelper.update(
urivar,
valuesBucket,
da,
);
```
6. 调用query方法在指定的Data子模块中查找数据。
```js
// callback方式调用:
DAHelper.query(
urivar,
valArray,
da,
(error, data) => {
console.log("DAHelper query result: " + data)
}
);
```
```js
// promise方式调用:
var dataquery = await DAHelper.query(
urivar,
valArray,
da
);
```
7. 调用batchInsert方法向指定的数据子模块批量插入数据。
```js
// callback方式调用:
DAHelper.batchInsert(
urivar,
cars,
(error, data) => {
console.log("DAHelper batchInsert result: " + data)
}
);
```
```js
// promise方式调用:
var databatchInsert = await DAHelper.batchInsert(
urivar,
cars
);
```
8. 调用executeBatch方法向指定的Data子模块进行数据的批量处理。
```js
// 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.log("DAHelper executeBatch result: " + data)
}
);
```
```js
// promise方式调用:
var dataexecuteBatch = await DAHelper.executeBatch(
urivar,
[
{
uri: urivar,
type: featureAbility.DataAbilityOperationType.TYPE_INSERT,
valuesBucket:
{
"executeBatch" : "value1",
},
predicates: da,
expectedCount:0,
predicatesBackReferences: null,
interrupted:true,
}
]
);
```
# FA卡片开发指导
## 卡片概述
卡片是一种界面展示形式,可以将应用的重要信息或操作前置到卡片,以达到服务直达,减少体验层级的目的。
卡片常用于嵌入到其他应用(当前只支持系统应用)中作为其界面的一部分显示,并支持拉起页面,发送消息等基础的交互功能。卡片使用方负责显示卡片。
卡片的基本概念:
- 卡片提供方:提供卡片显示内容原子化服务,控制卡片的显示内容、控件布局以及控件点击事件。
- 卡片使用方:显示卡片内容的宿主应用,控制卡片在宿主中展示的位置。
- 卡片管理服务:用于管理系统中所添加卡片的常驻代理服务,包括卡片对象的管理与使用,以及卡片周期性刷新等。
> **说明:**
> 卡片使用方和提供方不要求常驻运行,在需要添加/删除/请求更新卡片时,卡片管理服务会拉起卡片提供方获取卡片信息。
开发者仅需作为卡片提供方进行卡片内容的开发,卡片使用方和卡片管理服务由系统自动处理。
卡片提供方控制卡片实际显示的内容、控件布局以及点击事件。
## 场景介绍
FA卡片开发,即基于[FA模型](fa-brief.md)的卡片提供方开发,主要涉及如下功能逻辑:
- 开发卡片生命周期回调函数LifecycleForm。
- 创建卡片数据FormBindingData对象。
- 通过FormProvider更新卡片。
- 开发卡片页面。
## 接口说明
卡片生命周期回调函数LifecycleForm的接口如下:
**表1** LifecycleForm API接口功能介绍
| 接口名 | 描述 |
| :----------------------------------------------------------- | :------------------------------------------- |
| onCreate(want: Want): formBindingData.FormBindingData | 卡片提供方接收创建卡片的通知接口。 |
| onCastToNormal(formId: string): void | 卡片提供方接收临时卡片转常态卡片的通知接口。 |
| onUpdate(formId: string): void | 卡片提供方接收更新卡片的通知接口。 |
| onVisibilityChange(newStatus: { [key: string]: number }): void | 卡片提供方接收修改可见性的通知接口。 |
| onEvent(formId: string, message: string): void | 卡片提供方接收处理卡片事件的通知接口。 |
| onDestroy(formId: string): void | 卡片提供方接收销毁卡片的通知接口。 |
| onAcquireFormState?(want: Want): formInfo.FormState | 卡片提供方接收查询卡片状态的通知接口。 |
FormProvider类具体的API详见[接口文档](../reference/apis/js-apis-application-formProvider.md)
**表2** FormProvider API接口功能介绍
| 接口名 | 描述 |
| :----------------------------------------------------------- | :------------------------------------------------ |
| setFormNextRefreshTime(formId: string, minute: number, callback: AsyncCallback&lt;void&gt;): void; | 设置指定卡片的下一次更新时间。 |
| setFormNextRefreshTime(formId: string, minute: number): Promise&lt;void&gt;; | 设置指定卡片的下一次更新时间,以promise方式返回。 |
| updateForm(formId: string, formBindingData: FormBindingData, callback: AsyncCallback&lt;void&gt;): void; | 更新指定的卡片。 |
| updateForm(formId: string, formBindingData: FormBindingData): Promise&lt;void&gt;; | 更新指定的卡片,以promise方式返回。 |
## 开发步骤
### 创建LifecycleForm
创建FA模型的卡片,需实现LifecycleForm的生命周期接口。具体示例代码如下:
1. 导入相关模块。
```javascript
import formBindingData from '@ohos.application.formBindingData'
import formInfo from '@ohos.application.formInfo'
import formProvider from '@ohos.application.formProvider'
```
2. 实现LifecycleForm生命周期接口。
```javascript
export default {
onCreate(want) {
console.log('FormAbility onCreate');
// 由开发人员自行实现,将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
},
onCastToNormal(formId) {
// 使用方将临时卡片转换为常态卡片触发,提供方需要做相应的处理
console.log('FormAbility onCastToNormal');
},
onUpdate(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.log('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((error) => {
console.log('FormAbility updateForm, error:' + JSON.stringify(error));
});
},
onVisibilityChange(newStatus) {
// 使用方发起可见或者不可见通知触发,提供方需要做相应的处理
console.log('FormAbility onVisibilityChange');
},
onEvent(formId, message) {
// 若卡片支持触发事件,则需要重写该方法并实现对事件的触发
console.log('FormAbility onEvent');
},
onDestroy(formId) {
// 删除卡片实例数据
console.log('FormAbility onDestroy');
},
onAcquireFormState(want) {
console.log('FormAbility onAcquireFormState');
return formInfo.FormState.READY;
},
}
```
### 配置卡片配置文件
卡片需要在应用配置文件config.json中进行配置。
- js模块,用于对应卡片的js相关资源,内部字段结构说明:
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| -------- | ------------------------------------------------------------ | -------- | ------------------------ |
| name | 表示JS Component的名字。该标签不可缺省,默认值为default。 | 字符串 | 否 |
| pages | 表示JS Component的页面用于列举JS Component中每个页面的路由信息[页面路径+页面名称]。该标签不可缺省,取值为数组,数组第一个元素代表JS FA首页。 | 数组 | 否 |
| window | 用于定义与显示窗口相关的配置。 | 对象 | 可缺省 |
| type | 表示JS应用的类型。取值范围如下:<br />normal:标识该JS Component为应用实例。<br />form:标识该JS Component为卡片实例。 | 字符串 | 可缺省,缺省值为“normal” |
| mode | 定义JS组件的开发模式。 | 对象 | 可缺省,缺省值为空 |
配置示例如下:
```json
"js": [{
"name": "widget",
"pages": ["pages/index/index"],
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"type": "form"
}]
```
- abilities模块,用于对应卡片的LifecycleForm,内部字段结构说明:
| 属性名称 | 含义 | 数据类型 | 是否可缺省 |
| ------------------- | ------------------------------------------------------------ | ---------- | ------------------------ |
| 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 * 2:表示1行2列的二宫格。<br />2 * 2:表示2行2列的四宫格。<br />2 * 4:表示2行4列的八宫格。<br />4 * 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,
"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进行持久化管理,以便在后续获取、更新以及拉起时能获取到正确的卡片业务数据。
```javascript
onCreate(want) {
console.log('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"];
// 由开发人员自行实现,将创建的卡片信息持久化,以便在下次获取/更新该卡片实例时进行使用
// storeFormInfo 接口未在此处实现,具体实现请参考:相关实例 章节中的 FormAbility FA模型卡片 实例
storeFormInfo(formId, formName, tempFlag, want);
let obj = {
"title": "titleOnCreate",
"detail": "detailOnCreate"
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
```
同时需要适配onDestroy卡片删除通知接口,在其中实现卡片实例数据的删除。
```javascript
onDestroy(formId) {
console.log('FormAbility onDestroy');
// 由开发人员自行实现,删除之前持久化的卡片实例数据
// deleteFormInfo 接口未在此处实现,具体实现请参考:相关实例 章节中的 FormAbility FA模型卡片 实例
deleteFormInfo(formId);
}
```
具体的持久化方法可以参考[通过用户首选项实现数据持久化](../database/data-persistence-by-preferences.md)
需要注意的是,卡片使用方在请求卡片时传递给提供方应用的Want数据中存在临时标记字段,表示此次请求的卡片是否为临时卡片:
- 常态卡片:卡片使用方会持久化的卡片;
- 临时卡片:卡片使用方不会持久化的卡片;
由于临时卡片的数据具有非持久化的特殊性,某些场景比如卡片服务框架死亡重启,此时临时卡片数据在卡片管理服务中已经删除,且对应的卡片ID不会通知到提供方,所以卡片提供方需要自己负责清理长时间未删除的临时卡片数据。同时对应的卡片使用方可能会将之前请求的临时卡片转换为常态卡片。如果转换成功,卡片提供方也需要对对应的临时卡片ID进行处理,把卡片提供方记录的临时卡片数据转换为常态卡片数据,防止提供方在清理长时间未删除的临时卡片时,把已经转换为常态卡片的临时卡片信息删除,导致卡片信息丢失。
### 卡片数据交互
当卡片应用需要更新数据时(如触发了定时更新或定点更新),卡片应用获取最新数据,并调用updateForm接口更新卡片。示例如下:
```javascript
onUpdate(formId) {
// 若卡片支持定时更新/定点更新/卡片使用方主动请求更新功能,则提供方需要重写该方法以支持数据更新
console.log('FormAbility onUpdate');
let obj = {
"title": "titleOnUpdate",
"detail": "detailOnUpdate"
};
let formData = formBindingData.createFormBindingData(obj);
// 调用updateForm接口去更新对应的卡片,仅更新入参中携带的数据信息,其他信息保持不变
formProvider.updateForm(formId, formData).catch((error) => {
console.log('FormAbility updateForm, error:' + JSON.stringify(error));
});
}
```
### 开发卡片页面
开发者可以使用hml+css+json开发JS卡片页面:
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> 当前仅支持JS扩展的类Web开发范式来实现卡片的UI页面。
- 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">{{title}}</text>
<text class="detail_text" onclick="routerEvent">{{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"
}
}
}
}
```
最终可以得到如下卡片:
![fa-form-example](figures/fa-form-example.png)
### 开发卡片事件
卡片支持为组件设置action,包括router事件和message事件,其中router事件用于Ability跳转,message事件用于卡片开发人员自定义点击事件。关键步骤说明如下:
1. 在hml中为组件设置onclick属性,其值对应到json文件的actions字段中。
2. 若设置router事件,则
- action属性值为"router";
- abilityName为跳转目标的Ability名,如目前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>
```
- 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"
}
}
}
}
```
\ No newline at end of file
# PageAbility开发指导
## 概述
### 功能简介
PageAbility是具备ArkUI实现的Ability,是开发者具体可见并可以与之交互的Ability实例。开发者通过DevEco Studio创建Ability时,DevEco Studio会自动创建相关模板代码。
PageAbility相关能力通过单独的featureAbility实现,生命周期相关回调则通过`app.js/app.ets`中各个回调函数实现。
### PageAbility的生命周期
**PageAbility生命周期介绍**(Ability Lifecycle):
PageAbility生命周期是PageAbility被调度到INACTIVE、ACTIVE、BACKGROUND等各个状态的统称。
PageAbility生命周期流转如下图所示:
![PageAbility-Lifecycle](figures/page-ability-lifecycle.png)
**Ability生命周期状态说明:**
- **UNINITIALIZED**:未初始状态,为临时状态,PageAbility被创建后会由UNINITIALIZED状态进入INITIAL状态。
- **INITIAL**:初始化状态,也表示停止状态,表示当前PageAbility未运行,PageAbility被启动后由INITIAL态进入INACTIVE状态。
- **INACTIVE**:失去焦点状态,表示当前窗口已显示但是无焦点状态。
- **ACTIVE**:前台激活状态,表示当前窗口已显示,并获取焦点。
- **BACKGROUND**:后台状态,表示当前PageAbility退到后台,PageAbility在被销毁后由BACKGROUND状态进入INITIAL状态,或者重新被激活后由BACKGROUND状态进入ACTIVE状态。
**PageAbility生命周期回调与生命周期状态的关系如下图所示:**
![fa-pageAbility-lifecycle](figures/fa-pageAbility-lifecycle.png)
PageAbility提供生命周期回调,开发者可以在`app.js/app.ets`中重写生命周期相关回调函数 。目前`app.js`环境中仅支持onCreate和onDestroy回调,`app.ets`环境支持全量生命周期回调。
### 启动模式
ability支持单实例和多实例两种启动模式。
`config.json`中通过launchType配置项,可以配置具体的启动模式,其中:
**表1** 启动模式介绍
| 启动模式 | 描述 |说明 |
| ----------- | ------- |---------------- |
| standard | 多实例模式 | 每次startAbility都会启动一个新的实例。 |
| singleton | 单实例模式 | 系统中只存在唯一一个实例,startAbility时,如果已存在,则复用系统中的唯一一个实例。 |
缺省情况下是singleton模式。
## 开发指导
### featureAbility接口说明
**表2** featureAbility接口介绍
| 接口名 | 描述 |
| --------------------------------------------------- | --------------- |
| void startAbility(parameter: StartAbilityParameter) | 启动Ability。 |
| Context getContext(): | 获取应用Context。 |
| void terminateSelf() | 结束Ability。 |
| bool hasWindowFocus() | 是否获取焦点。 |
### 启动本地PageAbility
**导入模块**
```js
import featureAbility from '@ohos.ability.featureAbility'
```
**示例**
```javascript
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility({
want: {
action: "",
entities: [""],
type: "",
deviceId: "",
bundleName: "com.example.myapplication",
/* FA模型中abilityName由package + Ability name组成 */
abilityName: "com.example.entry.secondAbility",
uri: ""
}
});
```
### 启动远程PageAbility(当前仅对系统应用开放)
>说明:由于DeviceManager的getTrustedDeviceListSync接口仅对系统应用开放,当前启动远程PageAbility仅支持系统应用。
**导入模块**
```
import featureAbility from '@ohos.ability.featureAbility'
import deviceManager from '@ohos.distributedHardware.deviceManager';
```
**示例**
```ts
function onStartRemoteAbility() {
console.info('onStartRemoteAbility begin');
let params;
let wantValue = {
bundleName: 'ohos.samples.etsDemo',
abilityName: 'ohos.samples.etsDemo.RemoteAbility',
deviceId: 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');
}
```
从DeviceManager获取`deviceId`,具体示例代码如下:
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
let dmClass;
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass != null) {
let list = dmClass.getTrustedDeviceListSync();
if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
return;
}
console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
return list[0].deviceId;
} else {
console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
}
}
```
在跨设备场景下,需要向用户申请数据同步的权限。具体示例代码如下:
```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl";
import bundle from '@ohos.bundle';
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("verifyAccessToken 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, (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');
}
```
### 生命周期接口说明
**表3** 生命周期回调函数介绍
| 接口名 | 描述 |
| ------------ | ------------------------------------------------------------ |
| onShow() | Ability由后台不可见状态切换到前台可见状态调用onShow方法,此时用户在屏幕可以看到该Ability。 |
| onHide() | Ability由前台切换到后台不可见状态时调用onHide方法,此时用户在屏幕看不到该Ability。 |
| onDestroy() | 应用退出,销毁Ability对象前调用onDestroy方法,开发者可以在该方法里做一些回收资源、清空缓存等应用退出前的准备工作。 |
| onCreate() | Ability第一次启动创建Ability时调用onCreate方法,开发者可以在该方法里做一些应用初始化工作。 |
| onInactive() | Ability失去焦点时调用onInactive方法,Ability在进入后台状态时会先失去焦点,再进入后台。 |
| onActive() | Ability切换到前台,并且已经获取焦点时调用onActive方法。 |
**示例**
开发者需要重写`app.js/app.ets`中相关生命周期回调函数,DevEco Studio模板默认生成`onCreate()``onDestroy()`方法,其他方法需要开发者自行实现。
```javascript
export default {
onCreate() {
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
onShow(){
console.info('Application onShow')
},
onHide(){
console.info('Application onHide')
},
onInactive(){
console.info('Application onInactive')
},
onActive(){
console.info('Application onActive')
},
}
```
\ No newline at end of file
# ServiceAbility开发指导
## 场景介绍
基于Service模板的Ability(以下简称“Service”)主要用于后台运行任务(如执行音乐播放、文件下载等),但不提供用户交互界面。Service可由其他应用或Ability启动。即使用户切换到其他应用,Service仍将在后台继续运行。
## 生命周期
**表1** Service中相关生命周期API功能介绍
|接口名|描述|
|:------|:------|
|onStart?(): void|该方法在创建Service的时候调用,用于Service的初始化,在Service的整个生命周期只会调用一次。|
|onCommand?(want: Want, startId: number): void|在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用,开发者可以在该方法中做一些调用统计、初始化类的操作。|
|onConnect?(want: Want): rpc.RemoteObject|在Ability和Service连接时调用。|
|onDisconnect?(want: Want): void|在Ability与绑定的Service断开连接时调用。|
|onStop?(): void|在Service销毁时调用。开发者应通过实现此方法来清理资源,如关闭线程、注册的侦听器等。|
onCommand()与onConnect()的区别在于:
- onCommand()只能被startAbility或startAbilityForResult触发,客户端每次启动Service均会触发该回调
- onConnect()只能被connectAbility触发,客户端每次与Servcie建立新的连接时会触发该回调
## 开发步骤
### 创建注册Service
1. 重写Service的生命周期方法,来添加其他Ability请求与Service Ability交互时的处理方法。
创建Service的代码示例如下:
```ts
export default {
onStart() {
console.log('ServiceAbility onStart');
},
onCommand(want, startId) {
console.log('ServiceAbility onCommand');
},
onConnect(want) {
console.log('ServiceAbility OnConnect');
// ServiceAbilityStub的实现在下文给出
return new ServiceAbilityStub('test');
},
onDisconnect(want) {
console.log('ServiceAbility OnDisConnect');
},
onStop() {
console.log('ServiceAbility onStop');
}
}
```
2. 注册Service。
Service需要在应用配置文件config.json中进行注册,注册类型type需要设置为service。
```json
{
"module": {
"abilities": [
{
"name": ".ServiceAbility",
"type": "service",
"visible": true
...
}
]
...
}
...
}
```
### 启动Service
Ability为开发者提供了startAbility()方法来启动另外一个Ability。因为Service也是Ability的一种,开发者同样可以通过将Want传递给该方法来启动Service。
开发者可以通过构造包含bundleName与abilityName的Want对象来设置目标Service信息。参数的含义如下:
- bundleName:表示对端应用的包名称。
- abilityName:表示待启动的Ability名称。
启动本地设备Service的代码示例如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility(
{
want:
{
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
}
}
).then((err) => {
console.log("startService success");
}).catch (err => {
console.log("startService FAILED");
});
```
执行上述代码后,Ability将通过startAbility() 方法来启动Service。
- 如果Service尚未运行,则系统会先初始化Service,然后回调onStart()来启动Service,再回调onCommand()方法。
- 如果Service正在运行,则系统会直接回调Service的onCommand()方法。
启动远端设备Service的代码示例如下,详见[连接远程Service](fa-serviceability.md#连接远程service当前仅对系统应用开放)
```ts
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility(
{
want:
{
deviceId: remoteDeviceId, // 远端设备Id
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
}
}
).then((err) => {
console.log("startService success");
}).catch (err => {
console.log("startService FAILED");
});
```
### 停止Service
常规情况下,Service可以将自己停止,或者被系统停止,具体场景如下:
- Service调用particleAbility.terminateSelf()方法将自己停止。
- Service所在的应用进程退出,Service将随着进程被回收。
- 若Service仅仅是通过connectAbility()方法被访问的(从未执行过onCommand()回调),那么当最后一个连接被断开后,系统会将Service停止。
### 连接本地Service
如果Service需要与Page Ability或其他应用的Service Ability进行交互,则须创建用于连接的Connection。Service支持其他Ability通过connectAbility()方法与其进行连接。
开发者可使用如下两种方式实现连接Service。
1. 使用IDL自动生成代码
使用OpenHarmony IDL(OpenHarmony Interface Definition Language)来自动生成对应客户端服务端及IRemoteObject代码,具体示例代码和说明请参考:
- [`OpenHarmony IDL`:TS开发步骤](../IDL/idl-guidelines.md#ts)
2. 在对应文件编写代码
在使用connectAbility()时,需要传入目标Service的Want与ConnectOptions的实例,其中ConnectOptions封装了三个回调,分别对应不同情况,开发者需自行实现:
- onConnect():用来处理连接Service成功的回调。
- onDisconnect():用来处理Service断连或异常死亡的回调。
- onFailed():用来处理连接Service失败的回调。
创建连接本地Service回调实例的代码示例如下:
```ts
import prompt from '@system.prompt'
var option = {
onConnect: function onConnectCallback(element, proxy) {
console.log(`onConnectLocalService onConnectDone`);
if (proxy === null) {
prompt.showToast({
message: "Connect service failed"
});
return;
}
// 得到Service的proxy对象后便可以与其进行通信
let data = rpc.MessageParcel.create();
let reply = rpc.MessageParcel.create();
let option = new rpc.MessageOption();
data.writeString("InuptString");
proxy.sendRequest(0, data, reply, option);
prompt.showToast({
message: "Connect service success"
});
},
onDisconnect: function onDisconnectCallback(element) {
console.log(`onConnectLocalService onDisconnectDone element:${element}`);
prompt.showToast({
message: "Disconnect service success"
});
},
onFailed: function onFailedCallback(code) {
console.log(`onConnectLocalService onFailed errCode:${code}`);
prompt.showToast({
message: "Connect local service onFailed"
});
}
};
```
连接本地Service的代码示例如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
let want = {
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
};
let connectId = featureAbility.connectAbility(want, option);
```
同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象。OpenHarmony提供了IRemoteObject的默认实现,开发者可以通过继承rpc.RemoteObject来创建自定义的实现类,从而实现与Service的通信。具体使用方法可参考[ohos.rpc API文档](..\reference\apis\js-apis-rpc.md)。
Service侧把自身的实例返回给调用侧的代码示例如下:
```ts
import rpc from "@ohos.rpc"
class ServiceAbilityStub extends rpc.RemoteObject {
constructor(des: any) {
if (typeof des === 'string') {
super(des);
} else {
console.log("Error, the input param is not string");
return;
}
}
onRemoteRequest(code: number, data: any, reply: any, option: any) {
console.log("onRemoteRequest called");
// 可根据code执行不同的业务逻辑
if (code === 1) {
// 将传入的字符串进行排序
let string = data.readString();
console.log(`Input string = ${string}`);
let result = Array.from(string).sort().join('');
console.log(`Output result = ${result}`);
reply.writeString(result);
} else {
console.log(`Unknown request code`);
}
return true;
}
}
export default {
onStart() {
console.log('ServiceAbility onStart');
},
onCommand(want, startId) {
console.log('ServiceAbility onCommand');
},
onConnect(want) {
console.log('ServiceAbility OnConnect');
return new ServiceAbilityStub('ServiceAbilityRemoteObject');
},
onDisconnect(want) {
console.log('ServiceAbility OnDisConnect');
},
onStop() {
console.log('ServiceAbility onStop');
}
}
```
### 连接远程Service(当前仅对系统应用开放)
连接远程Service,构造ConnectOptions的方法与连接本地Serivce相同,区别在于:
- 应用需要向用户申请数据同步权限
- 目标Service的Want需要包含对端设备的deviceId
> 说明:
> (1) 由于DeviceManager的getTrustedDeviceList等接口仅对系统应用开放,当前仅系统应用支持连接远程Service。
> (2) API定义可见:[deviceManager模块](..\reference\apis\js-apis-device-manager.md)
在跨设备场景下,需要向用户申请数据同步的权限,首先在config.json里配置权限:
```json
{
...
"module": {
...
"reqPermissions": [{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}]
}
}
```
DISTRIBUTED_DATASYNC权限需要用户授予,在应用启动时需要向用户弹框请求授予权限,示例代码如下:
```ts
import abilityAccessCtrl from "@ohos.abilityAccessCtrl"
import bundle from '@ohos.bundle'
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("verifyAccessToken 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, (data)=>{
console.info("data:" + JSON.stringify(data));
});
console.info('RequestPermission end');
}
```
获取deviceId需要导入`@ohos.distributedHardware.deviceManager`模块,其中提供了getTrustedDeviceList等接口用于获取远端设备的deviceId。
- 接口使用可参考[deviceManager模块](..\reference\apis\js-apis-device-manager.md)
连接远程Service,只需要在want内定义deviceId即可,示例代码如下:
```ts
import featureAbility from '@ohos.ability.featureAbility'
let want = {
deviceId: remoteDeviceId,
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
};
let connectId = featureAbility.connectAbility(want, option);
```
其余实现均与本地连接Service相同,参考[连接本地Service](fa-serviceability.md#连接本地service)的示例代码即可。
# 跨端迁移开发指导
## 场景介绍
迁移的主要工作是实现将应用当前任务,包括页面控件状态变量、分布式对象等,迁移到远端设备。页面控件状态变量用于同步页面UI数据,分布式对象用于同步内存中的数据。
## 接口说明
迁移提供的能力如下,具体的API详见[接口文档](../reference/apis/js-apis-application-ability.md)
**表1** 应用迁移API接口功能介绍
|接口名 | 描述|
|:------ | :------|
| onContinue(wantParam : {[key: string]: any}): OnContinueResult | 迁移**发起端**在该回调中保存迁移所需要的数据,同时返回是否同意迁移:AGREE表示同意,REJECT表示拒绝;MISMATCH表示版本不匹配。 |
| onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | 多实例应用迁移**目标端**在该回调中完成数据恢复,并触发页面恢复。 |
| onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; | 单实例应用迁移**目标端**在该回调中完成数据恢复,并触发页面恢复。 |
**图1** 迁移开发示意图
![continuation_dev](figures/continuation-info.png)
迁移实际上是Ability携带数据的跨端启动。触发迁移动作时,会在A设备上通过系统回调应用的onContinue()方法,开发者需要在此方法中实现当前数据的保存。然后系统发起在B设备上的跨端启动,并将数据一同传输到B设备。B设备系统回调onCreate()/onNewWant()方法,开发者需要在此方法中实现传输过来的数据的恢复。
## 开发步骤
下文代码片段来自参考[示例](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/samples/continuationManualTestSuite)
### 迁移应用
1. 配置
- 配置应用支持迁移
在module.json5中配置continuable字段:true表示支持迁移,false表示不支持,默认为false。配置为false的应用将被系统识别为无法迁移。
```javascript
{
"module": {
"abilities": [
{
"continuable": true
}
]
}
}
```
- 配置应用启动类型
多实例应用在module.json5中将launchType字段配置为multiton,目标端将会拉起一个新的应用,并恢复页面;单实例将该字段配置为singleton,如果目标端应用已经打开,迁移将会将已有页面栈清空,并根据迁移数据恢复页面。关于单实例与多实例的更多信息详见[ability开发指导](./stage-ability.md)启动模式。
多实例:
```javascript
{
"module": {
"abilities": [
{
"launchType": "multiton"
}
]
}
}
```
缺省或如下配置为单实例:
```javascript
{
"module": {
"abilities": [
{
"launchType": "singleton"
}
]
}
}
```
- 申请分布式权限
支持跨端迁移的应用需要在module.json5申请分布式权限 DISTRIBUTED_DATASYNC。
```javascript
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
```
这个权限需要在应用首次启动的时候弹窗让用户授予,可以通过在ability的onWindowStageCreate中添加如下代码实现:
```javascript
requestPermissions = async () => {
let permissions: Array<string> = [
"ohos.permission.DISTRIBUTED_DATASYNC"
];
let needGrantPermission = false
let accessManger = accessControl.createAtManager()
Logger.info("app permission get bundle info")
let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100)
Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`)
for (const permission of permissions) {
Logger.info(`app permission query grant status ${permission}`)
try {
let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission)
if (grantStatus === PERMISSION_REJECT) {
needGrantPermission = true
break;
}
} catch (err) {
Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`)
needGrantPermission = true
break;
}
}
if (needGrantPermission) {
Logger.info("app permission needGrantPermission")
try {
await accessManger.requestPermissionsFromUser(this.context, permissions)
} catch (err) {
Logger.error(`app permission ${JSON.stringify(err)}`)
}
} else {
Logger.info("app permission already granted")
}
}
```
2. 实现onContinue接口
onContinue()接口在发起端被调用,主要用于在迁移发起时,通知开发者保存控件状态变量和内存中数据,准备迁移。当应用准备完成后,需要返回OnContinueResult.AGREE(0)表示同意迁移,否则返回相应的错误码拒绝迁移。如果不实现该接口,系统将默认为拒绝迁移。
导入模块
```javascript
import Ability from '@ohos.application.Ability';
import AbilityConstant from '@ohos.application.AbilityConstant';
```
要实现迁移,此接口必须实现并返回AGREE,否则默认为拒绝迁移。
另外,在该接口的入参wantParam中可以获取目标设备的deviceId(key为“targetDevice”),以及目标设备上所安装的应用的版本号(key为“version”)。版本号可用来与本应用版本进行对比,做兼容性判断,如果判定本应用版本与远端不兼容,可以返回OnContinueResult.MISMATCH拒绝迁移。
示例
```javascript
onContinue(wantParam : {[key: string]: any}) {
Logger.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
Logger.info(`onContinue input = ${wantParam["input"]}`);
return AbilityConstant.OnContinueResult.AGREE
}
```
3. 在onCreate/onNewWant接口中实现迁移逻辑
onCreate()接口在迁移目标端被调用,在目标端ability被拉起时,通知开发者同步已保存的内存数据和控件状态,完成后触发页面的恢复。如果不实现该接口中迁移相关逻辑,ability将会作为普通的启动方式拉起,无法恢复页面。
远端设备上,在onCreate()中根据launchReason判断该次启动是否为迁移LaunchReason.CONTINUATION
完成数据恢复后,开发者需要调用restoreWindowStage来触发页面恢复。
在入参want中也可以通过want.parameters.version来获取发起端的应用版本号。
示例
```javascript
import Ability from '@ohos.application.Ability';
import distributedObject from '@ohos.data.distributedDataObject';
export default class MainAbility extends Ability {
storage : LocalStorag;
onCreate(want, launchParam) {
Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// get user data from want params
let workInput = want.parameters.work
Logger.info(`work input ${workInput}`)
AppStorage.SetOrCreate<string>('ContinueWork', workInput)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
如果是单实例应用,则同样的代码实现onNewWant接口即可。
### 迁移数据
使用分布式对象
分布式数据对象提供了与本地变量类似的操作,实现两个设备的数据同步,当设备1的应用A的分布式数据对象增、删、改数据后,设备2的应用A也可以获取到对应的数据变化,同时还能监听数据变更以及对端数据对象的上下线。用法详见[分布式对象指导文档](../database/database-distributedobject-guidelines.md)
迁移场景中,分布式对象(distributedDataObject)主要用于将本机内存数据同步到目标设备。
- 发起端在onContinue()中,将待迁移的数据存入分布式对象中,并调用save接口将数据保存并同步到远端,然后设置好session id,并通过wantParam将session id传到远端设备。
```javascript
import Ability from '@ohos.application.Ability';
import distributedObject from '@ohos.data.distributedDataObject';
var g_object = distributedObject.createDistributedObject({data:undefined});
export default class MainAbility extends Ability {
sessionId : string;
onContinue(wantParam : {[key: string]: any}) {
Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
if (g_object.__sessionId === undefined) {
this.sessionId = distributedObject.genSessionId()
Logger.info(`onContinue generate new sessionId`)
}
else {
this.sessionId = g_object.__sessionId;
}
wantParam["session"] = this.sessionId
g_object.data = AppStorage.Get<string>('ContinueStudy');
Logger.info(`onContinue sessionId = ${this.sessionId}, name = ${g_object.data}`)
g_object.setSessionId(this.sessionId);
g_object.save(wantParam.targetDevice, (result, data)=>{
Logger.info("save callback");
Logger.info("save sessionId " + data.sessionId);
Logger.info("save version " + data.version);
Logger.info("save deviceId " + data.deviceId);
});
```
- 目标设备在onCreate()中,取出发起端传过来的session id,建立分布式对象并关联该session id,这样就能实现分布式对象的同步。需要注意的是,在调用restoreWindowStage之前,迁移需要的分布式对象必须全部关联完,保证能够获取到正确的数据。
```javascript
import Ability from '@ohos.application.Ability';
import distributedObject from '@ohos.data.distributedDataObject';
var g_object = distributedObject.createDistributedObject({data:undefined});
export default class MainAbility extends Ability {
storage : LocalStorag;
onCreate(want, launchParam) {
Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// get distributed data object session id from want params
this.sessionId = want.parameters.session
Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`)
// in order to fetch from remote, reset g_object.data to undefined first
g_object.data = undefined;
// set session id, so it will fetch data from remote
g_object.setSessionId(this.sessionId);
AppStorage.SetOrCreate<string>('ContinueStudy', g_object.data)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
### 其他说明
1. 超时机制:
- 如果目标端迁移应用未安装,系统会去查询在目标端设备上能否安装,这段最大时间为4s,超出此时间,调用者会收到超时错误码,视为不可在目标端安装。若可安装,则系统会提示用户在目标端安装,安装完成后可再次尝试发起迁移。
- 如果目标端迁移应用已安装 ,那么发起迁移后超时时间为20s,若超过此时间,调用者会收到超时错误码,视为此次迁移失败。
2. 当前系统默认支持页面栈信息的迁移,即发起端页面栈会被自动迁移到目标端,无需开发者适配。
### 约束
1. 迁移要求在同ability之间进行,也就是需要相同的bundleName、moduleName和abilityName,具体含义[应用包配置文件说明](../quick-start/module-configuration-file.md)
2. 当前应用只能实现迁移能力,但迁移的动作只能由系统发起。
### 最佳实践
为了获得最佳体验,建议100kb以下的数据直接使用wantParam传输,大于100kb的数据采用分布式对象传输。
\ No newline at end of file
# Ability开发指导
## 场景介绍
Stage模型是区别于FA模型的一种应用开发模型,对此模型的介绍详见[Stage模型综述](stage-brief.md)。开发Stage模型应用时,需要在module.json5和app.json5配置文件中对应用的包结构进行声明,对应用包结构配置文件的说明详见[应用包结构配置文件的说明](../quick-start/application-package-structure-stage.md)。基于Stage模型的Ability应用开发,主要涉及如下功能逻辑:
- 创建支持使用屏幕浏览及人机交互的Ability应用,包括实现Ability的生命周期、获取Ability配置信息、向用户申请授权及环境变化通知等场景。
- 启动Ability应用,包括相同设备启动Ability、跨设备启动Ability以及指定页面启动Ability等场景。
- 通用组件Call功能,详见[Call调用开发指导](stage-call.md)
- 连接ServiceExtensionAbility,与ServiceExtensionAbility断开连接,详见[ServiceExtensionAbility开发指导](stage-serviceextension.md)
- 应用迁移,详见[应用迁移开发指导](stage-ability-continuation.md)
### 启动模式
Ability支持单实例、多实例和指定实例3种启动模式,在module.json5中通过launchType配置。启动模式对应Ability被启动时的行为,对启动模式的详细说明如下:
| 启动模式 | 描述 |说明 |
| ----------- | ------- |---------------- |
| multiton | 多实例模式 | 每次startAbility都会启动一个新的实例。 |
| singleton | 单实例模式 | 系统中只存在唯一一个实例,startAbility时,如果已存在,则复用系统中的唯一一个实例。 |
| specified | 指定实例模式 | 运行时由Ability内部业务决定是否创建多实例。 |
缺省情况下是singleton模式,module.json5示例如下:
```json
{
"module": {
"abilities": [
{
"launchType": "singleton",
}
]
}
}
```
## 创建Ability
### 接口说明
AbilityStage功能如下(AbilityStage类,拥有context属性,具体的API详见[接口文档](../reference/apis/js-apis-app-ability-abilityStage.md)):
**表1** AbilityStage API接口功能介绍
|接口名|描述|
|:------|:------|
|onCreate(): void|AbilityStage初始化时被调用。|
|onAcceptWant(want: Want): string|启动指定Ability时被调用。|
|onConfigurationUpdated(config: Configuration): void|全局配置发生变更时被调用。|
Ability功能如下(Ability类,具体的API详见[接口文档](../reference/apis/js-apis-application-ability.md)):
**表2** Ability API接口功能介绍
|接口名|描述|
|:------|:------|
|onCreate(want: Want, param: AbilityConstant.LaunchParam): void|Ability生命周期回调,Ability启动时被调用。|
|onDestroy(): void|Ability生命周期回调,Ability销毁时被调用。|
|onWindowStageCreate(windowStage: window.WindowStage): void|Ability生命周期回调,创建window stage时被调用,应用开发者可通过window.WindowStage的接口执行页面加载等操作。|
|onWindowStageDestroy(): void|Ability生命周期回调,销毁window stage时被调用。|
|onForeground(): void|Ability生命周期回调,Ability切换至前台时被调用。|
|onBackground(): void|Ability生命周期回调,Ability切换至后台时被调用。|
|onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void|Ability回调,Ability的启动模式设置为单例时被调用。|
|onConfigurationUpdated(config: Configuration): void|Ability回调,Ability的系统配置更新时被调用。|
### 实现AbilityStage及Ability生命周期
创建Stage模型的Page Ability应用,需实现AbilityStage接口及Ability生命周期接口,并使用窗口提供的方法设置页面。具体示例代码如下:
1. 导入AbilityStage模块。
```
import AbilityStage from "@ohos.application.AbilityStage"
```
2. 实现AbilityStage接口,接口生成的默认相对路径:entry\src\main\ets\Application\AbilityStage.ts。
```ts
export default class MyAbilityStage extends AbilityStage {
onCreate() {
console.log("MyAbilityStage onCreate")
}
}
```
3. 导入Ability模块。
```js
import Ability from '@ohos.application.Ability'
```
4. 实现Ability生命周期接口,接口默认生成的相对路径:entry\src\main\ets\MainAbility\MainAbility.ts。
`onWindowStageCreate(windowStage)`中通过loadContent接口设置应用要加载的页面,window接口的使用详见[窗口开发指导](../windowmanager/application-window-stage.md)
```ts
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("MainAbility onCreate")
}
onDestroy() {
console.log("MainAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.log("MainAbility onWindowStageCreate")
windowStage.loadContent("pages/index").then(() => {
console.log("MainAbility load content succeed")
}).catch((error) => {
console.error("MainAbility load content failed with error: " + JSON.stringify(error))
})
}
onWindowStageDestroy() {
console.log("MainAbility onWindowStageDestroy")
}
onForeground() {
console.log("MainAbility onForeground")
}
onBackground() {
console.log("MainAbility onBackground")
}
}
```
### 获取AbilityStage及Ability的配置信息
AbilityStage类及Ability类均拥有context属性,应用可以通过`this.context`获取Ability实例的上下文,进而获取详细的配置信息。
如下示例展示了AbilityStage通过context属性获取包代码路径、hap包名、Ability名以及系统语言的方法。具体示例代码如下:
```ts
import AbilityStage from "@ohos.application.AbilityStage"
export default class MyAbilityStage extends AbilityStage {
onCreate() {
console.log("MyAbilityStage onCreate")
let context = this.context
console.log("MyAbilityStage bundleCodeDir" + context.bundleCodeDir)
let currentHapModuleInfo = context.currentHapModuleInfo
console.log("MyAbilityStage hap module name" + currentHapModuleInfo.name)
console.log("MyAbilityStage hap module mainAbilityName" + currentHapModuleInfo.mainAbilityName)
let config = this.context.config
console.log("MyAbilityStage config language" + config.language)
}
}
```
如下示例展示了Ability通过context属性获取包代码路径、hap包名、Ability名以及系统语言的方法。具体示例代码如下:
```ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
onCreate(want, launchParam) {
console.log("MainAbility onCreate")
let context = this.context
console.log("MainAbility bundleCodeDir" + context.bundleCodeDir)
let abilityInfo = this.context.abilityInfo;
console.log("MainAbility ability bundleName" + abilityInfo.bundleName)
console.log("MainAbility ability name" + abilityInfo.name)
let config = this.context.config
console.log("MainAbility config language" + config.language)
}
}
```
### 系统环境变化通知
环境变化,包括全局配置的变化和Ability配置的变化。全局配置指全局的、系统的配置,目前包括“语言”和“颜色模式”,全局配置的变化一般由“设置”中的配置项或“控制中心”中的图标触发。Ability配置指与单个Ability实例相关的配置,目前包括“displayId”(物理屏幕Id)、“屏幕分辨率”,“横竖屏”,这些配置与Ability所在的Display有关,Ability配置的变化一般由窗口触发。配置项目前均定义在[Configuration](../reference/apis/js-apis-application-configuration.md)类中。
对于Stage模型的应用,配置发生变化时,不会重启Ability,会触发应用的`onConfigurationUpdated(config: Configuration)`回调,若应用希望根据配置变化做相应处理,可以重写`onConfigurationUpdated`回调,若无需处理配置变化,则可以不必实现`onConfigurationUpdated`回调。应该注意的是,回调中的Configuration对象包括当前Ability所有的配置,不仅是发生变化的配置。
如下示例展示了AbilityStage的`onConfigurationUpdated`回调实现,系统语言和颜色模式发生变化时触发该回调。具体示例代码如下:
```ts
import AbilityStage from '@ohos.application.AbilityStage'
import ConfigurationConstant from '@ohos.application.ConfigurationConstant'
export default class MyAbilityStage extends AbilityStage {
onConfigurationUpdated(config) {
if (config.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
console.log('colorMode changed to dark')
}
}
}
```
如下示例展示了Ability的`onConfigurationUpdated`回调实现,系统语言、颜色模式以及Display相关的参数,比如方向、Density,发生变化时触发该回调。具体示例代码如下:
```ts
import Ability from '@ohos.application.Ability'
import ConfigurationConstant from '@ohos.application.ConfigurationConstant'
export default class MainAbility extends Ability {
direction : number;
onCreate(want, launchParam) {
this.direction = this.context.config.direction
}
onConfigurationUpdated(config) {
if (this.direction !== config.direction) {
console.log(`direction changed to ${config.direction}`)
}
}
}
```
## 启动Ability
### 接口说明
Ability类拥有context属性,context属性为AbilityContext类,AbilityContext类拥有abilityInfo、currentHapModuleInfo等属性,启动Ability等方法。具体的API详见[接口文档](../reference/apis/js-apis-inner-application-applicationContext.md)
**表3** AbilityContext API接口功能介绍
|接口名|描述|
|:------|:------|
|startAbility(want: Want, callback: AsyncCallback\<void>): void|启动Ability。|
|startAbility(want: Want, options?: StartOptions): Promise\<void>|启动Ability。|
|startAbilityWithAccount(want: Want, accountId: number, callback: AsyncCallback\<void>): void|带AccountId启动Ability。|
|startAbilityWithAccount(want: Want, accountId: number, options?: StartOptions): Promise\<void>|带AccountId启动Ability。|
|startAbilityForResult(want: Want, callback: AsyncCallback\<AbilityResult>): void|带返回结果启动Ability。|
|startAbilityForResult(want: Want, options?: StartOptions): Promise\<AbilityResult>|带返回结果启动Ability。|
|startAbilityForResultWithAccount(want: Want, accountId: number, callback: AsyncCallback\<AbilityResult>): void|带返回结果及AccountId启动Ability。|
|startAbilityForResultWithAccount(want: Want, accountId: number, options?: StartOptions): Promise\<AbilityResult>|带返回结果及AccountId启动Ability。|
### 相同设备启动Ability
应用可以通过`this.context`获取Ability实例的上下文,进而使用AbilityContext中的StartAbility相关接口启动Ability。启动Ability可指定Want、StartOptions、accountId,通过callback形式或promise形式实现。具体示例代码如下:
```ts
let context = this.context
var want = {
"deviceId": "",
"bundleName": "com.example.MyApplication",
"abilityName": "MainAbility"
};
context.startAbility(want).then(() => {
console.log("Succeed to start ability")
}).catch((error) => {
console.error("Failed to start ability with error: "+ JSON.stringify(error))
})
```
### 跨设备启动Ability(当前仅对系统应用开放)
>说明:由于DeviceManager的getTrustedDeviceListSync接口仅对系统应用开放,当前跨设备启动Ability仅支持系统应用
跨设备场景下,需指定对端设备deviceId,具体示例代码如下:
```ts
let context = this.context
var want = {
"deviceId": getRemoteDeviceId(),
"bundleName": "com.example.MyApplication",
"abilityName": "MainAbility"
};
context.startAbility(want).then(() => {
console.log("Succeed to start remote ability")
}).catch((error) => {
console.error("Failed to start remote ability with error: " + JSON.stringify(error))
})
```
从DeviceManager获取指定设备的deviceId。具体示例代码如下:
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass != null) {
var list = dmClass.getTrustedDeviceListSync();
if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null");
return;
}
console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
return list[0].deviceId;
} else {
console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null");
}
}
```
向用户申请数据同步'ohos.permission.DISTRIBUTED_DATASYNC'的权限。申请授权示例代码见[abilityAccessCtrl.requestPermissionsFromUse](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)
### 指定页面启动Ability
当Ability的启动模式设置为单例时,若Ability已被拉起,再次启动Ability会触发onNewWant回调。应用开发者可以通过want传递启动参数,比如希望指定页面启动Ability,可以通过want中的uri参数或parameters参数传递pages信息。目前,Stage模型中Ability暂时无法直接使用router的能力,可以将启动参数传递给自定义组件,在自定义组件的生命周期中调用router接口显示指定页面。具体示例代码如下:
使用startAbility再次拉起Ability,通过want中的uri参数传递页面信息:
```ts
async function reStartAbility() {
try {
await this.context.startAbility({
bundleName: "com.sample.MyApplication",
abilityName: "MainAbility",
uri: "pages/second"
})
console.log('start ability succeed')
} catch (error) {
console.error(`start ability failed with ${error.code}`)
}
}
```
在Ability的onNewWant回调中获取包含页面信息的want参数:
```ts
import Ability from '@ohos.application.Ability'
export default class MainAbility extends Ability {
onNewWant(want, launchParams) {
globalThis.newWant = want
}
}
```
在自定义组件中获取包含页面信息的want参数并根据uri做路由处理:
```ts
import router from '@ohos.router'
@Entry
@Component
struct Index {
newWant = undefined
onPageShow() {
console.info('Index onPageShow')
let newWant = globalThis.newWant
if (newWant.hasOwnProperty("uri")) {
router.push({ url: newWant.uri });
globalThis.newWant = undefined
}
}
}
```
## 相关实例
基于Stage模型下的Ability开发,有以下相关实例可供参考:
- [Ability内和Ability间页面的跳转(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/StageAbility)
- [Stage模型下Ability的创建和使用(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/StageAbilityDemo)
- [Ability内页面间的跳转(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/Ability/PagesRouter)
\ No newline at end of file
# Stage模型综述
## 设计思想
​Stage模型的设计,是为了提供给开发者一个更好的开发方式,更好的适用于多设备、分布式场景。
​Stage模型的设计思想如下图所示。
![stagedesign](figures/stagedesign.png)
​Stage模型的设计基于如下三个出发点:
- **应用进程的有序管理**
随着设备的内存越来越大,系统中同时运行的进程数量也越来越多。当数百个进程同时运行时,如果没有有效的管理措施,则系统整体的功耗和性能将无法得到保证。Stage模型中,通过短时任务、长时任务、托管任务和延迟任务四种方法对后台进程做了有序约束。这样做使得前台进程的资源得以保障,最终能获得更好的用户体验。
- **原生支持跨端迁移和多端协同**
OpenHarmony是原生分布式的操作系统,应用框架需要从架构设计上使得组件更易于实现跨端迁移和多端协同。Stage模型通过Ability与UI分离及UI展示与服务能力合一等模型特性,实现这一设计目标。
- **支持多种设备的不同窗口形态**
Stage模型重新定义了Ability的生命周期。系统在架构上,将应用组件管理服务和窗口管理服务进行了彼此解耦,这样做可以方便的针对特定设备进行适配,以实现出不同的窗口形态。
## 基本概念
下图展示了Stage模型中的基本概念。
![stageconcept](figures/stageconcept.png)
- **HAP**:OpenHarmony应用编译、分发、加载的基本单位。与开发态的module一一对应。在应用内,moduleName是其唯一标识;
- **Bundle**:通过appid标识的OpenHarmony应用,Bundle可以包含多个HAP,每个应用都有一个bundleName,但是bundleName并不能唯一标识一个应用,appid中包含bundleName以及其他的更多信息,能够唯一标识一个应用;
- **AbilityStage**:对应HAP的运行期对象,在HAP首次加载到进程中时创建,运行期开发者可见;
- **Application**:对应Bundle的运行期对象,运行期开发者不可见;
- **Context**:提供运行期开发者可以调用的各种能力,Ability组件和各种ExtensionAbility都有各自不同的Context类,他们都继承自基类Context,基类提供包名、moduleName、路径等信息;
- **Ability**:提供生命周期回调,持有AbilityContext,支持组件的跨端迁移和多端协同;
- **ExtensionAbility**:基于场景的扩展能力统称,系统定义了多种场景的ExtensionAbility类,它们持有各自的ExtensionContext;
- **WindowStage**:本地窗口管理器;
- **Window**:应用窗口,持有一个ArkUI引擎实例;
- **ArkUI Page**:基于ArkUI开发的用户界面。
## 生命周期
AbilityStage及Ability是关于应用生命周期的关键对象。
[Ability框架概述](ability-brief.md)中,给出了FA模型与Stage模型的生命周期对比,因此这里仅对Ability生命周期切换以及和AbilityStage、WindowStage之间的调度关系进行介绍。
![stageabilitylifecyclecallback](figures/stageabilitylifecyclecallback.png)
为了实现多设备形态上的适配和多窗口的扩展,OpenHarmony对组件管理和窗口管理进行了解耦。
Stage模型定义Ability组件的生命周期,只包含创建、销毁、前后台等状态,而将与界面强相关的获焦、失焦状态都放在WindowStage之中,从而实现Ability与窗口之间的弱耦合;在服务侧,窗口管理服务依赖于组件管理服务,前者通知后者前后台变化,这样组件管理服务仅感知前后台变化,不感知焦点变化。
需要注意的是,在Ability中存在两个与WindowStage相关的生命周期状态onWindowStageCreate和onWindowStageDestroy,这两个生命周期状态的变化仅存在于具有显示能力的设备中。前者表示WindowStage已经创建完成,开发者可以通过执行loadContent的操作设置Ability需要加载的页面;后者在WindowStage销毁后调用,以便开发者对资源进行释放。
## Ability组件实例与任务
Ability组件有三种启动类型:
* **Singleton**:应用进程中只存在一个该类型的Ability实例,如下图Ability1;
* **Standard**:每次startAbility调用,都会在应用进程中创建一个该类型的实例,如下图Ability2的两个实例;
* **Specified**:允许开发者在系统创建Ability实例之前,为该实例创建一个key,后续每次创建该类型的Ability实例都会询问应用使用哪个key对应的Ability实例,来响应startAbility请求,如下图Ability3。
每个Ability实例都对应了一个近期任务中的Mission(任务)。
每个Ability实例对应的Mission都留有该Ability实例的快照,Ability实例销毁后,Mission中会继续保留Ability的类的信息和快照,直到用户删除,或者超过存储上限。
![AbilityComponentInstanceMission](figures/AbilityComponentInstanceMission.png)
## ExtensionAbility机制
不同于页面展示的Ability,ExtensionAbility提供的是一种受限的运行环境。
ExtensionAbility组件具有如下特点:
- 运行在独立于主进程的单独进程中,与主进程无IPC,但共享一个存储沙箱;
- 独立的Context提供基于相应业务场景的API能力;
- 由系统触发创建,应用不能直接创建;
- ExtensionAbility和进程的生命周期受系统管理。
下图以卡片使用场景为例进行展示。系统提供了FormExtensionAbility基类,开发者通过派生提供卡片的具体信息。FormExtensionAbility实例及其所在的ExtensionAbility进程的整个生命周期,都是由系统服务FormManagerService进行管理。
![ExtensionAbility](figures/ExtensionAbility.png)
## 进程模型
OpenHarmony系统对于应用进程是有强管控策略的。对于开发者来说,没有自行配置多进程的能力。应用的所有进程都是由系统创建和管理的。
每个应用的进程可以分为三类:
- 主进程:运行UIAbility组件、页面和业务逻辑;
- Extension进程:运行应用中的ExtensionAbility派生类,该进程由系统中的特定场景的服务管理其生命周期;
- Render进程:专门为webview创建的进程,用于加载webview的渲染库。
下图展示了应用的进程模型。
![stageprocessmodel](figures/stageprocessmodel.png)
## 应用包结构
Stage模型的应用包的工程目录结构,请参考[OpenHarmony工程介绍](https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ohos-project-overview-0000001218440650#section56487581904)
对Stage模型的应用包结构的配置说明,请参考[应用包结构说明(Stage模型)](../quick-start/application-configuration-file-overview-stage.md)
## 相关实例
针对Stage模型下的Ability开发,有以下相关实例可供参考:
- [`StageModel`:Stage模型(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/BasicFeature/ApplicationModels/StageModel)
- [`WindowExtAbility`:窗口扩展(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/WindowManagement/WindowExtAbility)
- [`MissionManager`:系统任务管理(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/ApplicationModels/MissionManager)
- [`Launcher`:仿桌面应用(ArkTS)(API9)(Full SDK)](https://gitee.com/openharmony/applications_app_samples/tree/OpenHarmony-3.2-Release/code/SystemFeature/ApplicationModels/Launcher)
# Call调用开发指导
## 场景介绍
Call调用是Ability能力的扩展,它为Ability提供一种能够被外部调用并与外部进行通信的能力。Call调用支持前台与后台两种启动方式,使Ability既能被拉起到前台展示UI,也可以在后台被创建并运行。Call调用在调用方与被调用方间建立了IPC通信,因此应用开发者可通过Call调用实现不同Ability之间的数据共享。
Call调用的核心接口是startAbilityByCall方法,与startAbility接口的不同之处在于:
- startAbilityByCall支持前台与后台两种启动方式,而startAbility仅支持前台启动。
- 调用方可使用startAbilityByCall所返回的Caller对象与被调用方进行通信,而startAbilty不具备通信能力。
Call调用的使用场景主要包括:
- 需要与被启动的Ability进行通信
- 希望被启动的Ability在后台运行
**表1** Call调用相关名词解释
|名词|描述|
|:------|:------|
|CallerAbility|指代进行Call调用的Ability(调用方)|
|CalleeAbility|指代被Call调用的Ability(被调用方)|
|Caller |实际对象,由startAbilityByCall接口所返回,CallerAbility可使用Caller与CalleeAbility进行通信,具体接口见表2|
|Callee |实际对象,被CalleeAbility持有,可与Caller进行通信|
|IPC |指代进程间通信|
Call调用流程示意图如下:
- CallerAbility调用startAbilityByCall接口获取Caller,并使用Caller对象的call方法向CalleeAbility发送数据
- CalleeAbility持有一个Callee对象,通过Callee的on方法注册回调函数,当接收到Caller发送的数据时将会调用对应的回调函数
![stage-call](figures/stage-call.png)
> ![icon-note.gif](public_sys-resources/icon-note.gif) **说明:**
> CalleeAbility的启动模式需要为单实例。
> 当前仅支持系统应用使用Call调用。
## 接口说明
Caller及Callee功能如下:具体的API详见[接口文档](../reference/apis/js-apis-application-ability.md#caller)
**表2** Call API接口功能介绍
|接口名|描述|
|:------|:------|
|startAbilityByCall(want: Want): Promise\<Caller>|启动指定Ability并获取其Caller通信接口,默认为后台启动,通过配置want可实现前台启动,详见[接口文档](../reference/apis/js-apis-ability-context.md#abilitycontextstartabilitybycall)。AbilityContext与ServiceExtensionContext均支持该接口。|
|on(method: string, callback: CalleeCallBack): void|通用组件Callee注册method对应的callback方法。|
|off(method: string): void|通用组件Callee解注册method的callback方法。|
|call(method: string, data: rpc.Sequenceable): Promise\<void>|向通用组件Callee发送约定序列化数据。|
|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|向通用组件Callee发送约定序列化数据, 并将Callee返回的约定序列化数据带回。|
|release(): void|释放通用组件的Caller通信接口。|
|on(type: "release", callback: OnReleaseCallback): void|注册通用组件通信断开监听通知。|
## 开发步骤
Call调用的开发步骤:
- 创建Callee被调用端。
- 访问Callee被调用端。
### 创建Callee被调用端
Callee被调用端,需要实现指定方法的数据接收回调函数、数据的序列化及反序列化方法。在需要接收数据期间,通过on接口注册监听,无需接收数据时通过off接口解除监听。
**1. 配置Ability的启动模式**
配置module.json5,将CalleeAbility配置为单实例"singleton"。
|Json字段|字段说明|
|:------|:------|
|"launchType"|Ability的启动模式,设置为"singleton"类型。 |
Ability配置标签示例如下:
```json
"abilities":[{
"name": ".CalleeAbility",
"srcEnty": "./ets/CalleeAbility/CalleeAbility.ts",
"launchType": "singleton",
"description": "$string:CalleeAbility_desc",
"icon": "$media:icon",
"label": "$string:CalleeAbility_label",
"exported": true
}]
```
**2. 导入Ability模块**
```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.log('CalleeSortFunc called')
// 获取Caller发送的序列化数据
let receivedData = new MySequenceable(0, '')
data.readSequenceable(receivedData)
console.log(`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.log(`${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. 导入Ability模块**
```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.log(`caller onRelease is called ${msg}`)
})
console.log('caller register OnRelease succeed')
} catch (error) {
console.log(`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.log('get caller failed')
return
}
console.log('get caller success')
this.regOnRelease(this.caller)
} catch (error) {
console.log(`get caller failed with ${error}`)
}
}
```
在跨设备场景下,需指定对端设备deviceId。具体示例代码如下:
```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.log('get remote caller success')
// 注册caller的release监听
caller.on("release", (msg) => {
console.log(`remote caller onRelease is called ${msg}`)
})
console.log('remote caller register OnRelease succeed')
}
}).catch((error) => {
console.error(`get remote caller failed with ${error}`)
})
}
```
从DeviceManager获取指定设备的deviceId,getTrustedDeviceListSync接口仅对系统应用开放。具体示例代码如下:
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
var dmClass;
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass != null) {
var list = dmClass.getTrustedDeviceListSync()
if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
return
}
console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
return list[0].deviceId
} else {
console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
}
}
```
在跨设备场景下,需要向用户申请数据同步的权限。具体示例代码如下:
```ts
import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
requestPermission() {
let context = this.context
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
let atManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(context, permissions).then((data) => {
console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => {
console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
})
}
```
**3. 发送约定序列化数据**
向被调用端发送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.log(`caller call failed with ${error}`)
}
}
```
如下示例调用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.log('caller callWithResult succeed')
let result = new MySequenceable(0, '')
data.readSequenceable(result)
backMsg(result.str)
console.log(`caller result is [${result.num}, ${result.str}]`)
} catch (error) {
console.log(`caller callWithResult failed with ${error}`)
}
}
```
**4. 释放Caller通信接口**
Caller不再使用后,应用开发者可以通过release接口释放Caller。具体示例代码如下:
```ts
releaseCall() {
try {
this.caller.release()
this.caller = undefined
console.log('caller release succeed')
} catch (error) {
console.log(`caller release failed with ${error}`)
}
}
```
\ No newline at end of file
# ServiceExtensionAbility开发指导
## 场景介绍
ExtensionAbility,是Stage模型中新增的扩展组件的基类,一般用于处理无界面的任务,生命周期较简单,没有前后台生命周期。ServiceExtensionAbility是ExtensionAbility的扩展类。
开发者可以自定义类继承ServiceExtensionAbility,通过重写基类中相关生命周期方法,来做初始化、连接中、断开连接时相关业务逻辑操作。
## 接口说明
**表1** ServiceExtensionAbility中相关生命周期API功能介绍
|接口名|描述|
|:------|:------|
|onCreate(want: Want): void|首次调用startAbility、connectAbility时触发,开发者可以进行初始化操作。|
|onRequest(want: Want, startId: number): void|每次调用startAbility都会触发,首次调用时startId为1,重复调用startAbility递增。|
|onConnect(want: Want): rpc.RemoteObject|调用connectAbility触发,重复调用不会再次触发,除非调用disconnectAbility解除绑定后再调用;onConnect返回一个进程通信类RemoteObject。|
|onDisconnect(want: Want): void|调用disconnectAbility触发,Extension如果是用connectAbility拉起的,并且已经没有其他应用绑定这个Extension,则会触发onDestroy生命周期销毁组件。|
|onDestroy(): void|调用停止当前ability接口terminateSelf会触发。|
## 约束与限制
OpenHarmony当前不支持三方应用创建ServiceExtensionAbility。
## 开发步骤
1. 需要在应用配置文件module.json5中进行注册,注册类型type需要设置为service。module.json5配置样例如下所示:
```json
"extensionAbilities":[{
"name": "ServiceExtAbility",
"icon": "$media:icon",
"description": "service",
"type": "service",
"exported": true,
"srcEnty": "./ets/ServiceExtAbility/ServiceExtAbility.ts"
}]
```
2. 开发者在定义Service的目录下创建TS文件,自定义类继承ServiceExtensionAbility,重写基类回调函数,接口生成的默认相对路径:entry\src\main\ets\ServiceExtAbility\ServiceExtAbility.ts,示例如下:
```js
import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility'
import rpc from '@ohos.rpc'
class StubTest extends rpc.RemoteObject {
constructor(des) {
super(des);
}
onRemoteRequest(code, data, reply, option) {
}
}
class ServiceExtAbility extends ServiceExtensionAbility {
onCreate(want) {
console.log('onCreate, want:' + want.abilityName);
}
onRequest(want, startId) {
console.log('onRequest, want:' + want.abilityName);
}
onConnect(want) {
console.log('onConnect , want:' + want.abilityName);
return new StubTest("test");
}
onDisconnect(want) {
console.log('onDisconnect, want:' + want.abilityName);
}
onDestroy() {
console.log('onDestroy');
}
}
```
\ No newline at end of file
# WantAgent开发指导
## 场景简介
WantAgent封装了一个行为意图信息,可以通过WantAgent.trigger接口主动触发,也可以通过与通知绑定被动触发。
具体的行为包括:启动Ability和发布公共事件。
## 接口说明
| 接口名 | 接口描述 |
| ---------------------------------------------------------------------------------------------- | ----------- |
| getWantAgentInfo(info: WantAgentInfo, callback: AsyncCallback\<WantAgent\>) | 以异步回调形式创建WantAgent对象。 |
| getWantAgent(info: WantAgentInfo): Promise\<WantAgent\> | 以Promise形式创建WantAgent对象。 |
| trigger(agent: WantAgent, triggerInfo: TriggerInfo, callback?: Callback\<CompleteData\>) | 触发WantAgent。 |
## 开发步骤
1. 导入WantAgent模块。
```
import wantAgent from '@ohos.wantAgent';
```
2. 创建拉起Ability的WantAgentInfo信息。详细的WantAgentInfo信息数据类型及包含的参数请见[WantAgentInfo文档](../reference/apis/js-apis-wantAgent.md#wantagentinfo)介绍。
```
private wantAgentObj = null // 用于保存创建成功的wantAgent对象,后续使用其完成触发的动作。
// wantAgentInfo
var wantAgentInfo = {
wants: [
{
deviceId: "",
bundleName: "com.example.test",
abilityName: "com.example.test.MainAbility",
action: "",
entities: [],
uri: "",
parameters: {}
}
],
operationType: wantAgent.OperationType.START_ABILITY,
requestCode: 0,
wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
}
```
3. 创建发布公共事件的WantAgentInfo信息。
```
private wantAgentObj = null // 用于保存创建成功的WantAgent对象,后续使用其完成触发的动作。
// wantAgentInfo
var wantAgentInfo = {
wants: [
{
action: "event_name", // 设置事件名。
parameters: {}
}
],
operationType: wantAgent.OperationType.SEND_COMMON_EVENT,
requestCode: 0,
wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
}
```
4. 创建WantAgent,保存返回的WantAgent对象wantAgentObj,用于执行后续触发操作。
```
// 创建WantAgent
wantAgent.getWantAgent(wantAgentInfo, (err, wantAgentObj) => {
if (err.code) {
console.error("[WantAgent]getWantAgent err=" + JSON.stringify(err))
} else {
console.log("[WantAgent]getWantAgent success")
this.wantAgentObj = wantAgentObj
}
})
```
5. 触发WantAgent。
```
// 触发WantAgent。
var triggerInfo = {
code:0
}
wantAgent.trigger(wantAgentObj, triggerInfo, (completeData) => {
console.log("[WantAgent]getWantAgent success, completeData: ", + JSON.stringify(completeData))
})
```
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册