提交 99d72638 编写于 作者: 王鑫 提交者: Gitee

Merge branch 'master' of gitee.com:openharmony/docs into 0824

Signed-off-by: N王鑫 <wangxin601@huawei.com>
...@@ -241,7 +241,7 @@ zh-cn/application-dev/reference/apis/js-apis-data-preferences.md @feng-aiwen @ge ...@@ -241,7 +241,7 @@ zh-cn/application-dev/reference/apis/js-apis-data-preferences.md @feng-aiwen @ge
zh-cn/application-dev/reference/apis/js-apis-data-rdb.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-rdb.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-udmf.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-udmf.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-cloudData.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-cloudData.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-relationStore.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-relationalStore.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-resultset.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-resultset.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-storage.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-storage.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
zh-cn/application-dev/reference/apis/js-apis-data-ValuesBucket.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-data-ValuesBucket.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
...@@ -330,7 +330,7 @@ zh-cn/application-dev/reference/apis/js-apis-prompt.md @HelloCrease @niulihua @t ...@@ -330,7 +330,7 @@ zh-cn/application-dev/reference/apis/js-apis-prompt.md @HelloCrease @niulihua @t
zh-cn/application-dev/reference/apis/js-apis-queue.md @gongjunsong @ge-yafang @flyingwolf @blackstone-oh zh-cn/application-dev/reference/apis/js-apis-queue.md @gongjunsong @ge-yafang @flyingwolf @blackstone-oh
zh-cn/application-dev/reference/apis/js-apis-radio.md @zhang-hai-feng @zengyawen @jyh926 @gaoxi785 zh-cn/application-dev/reference/apis/js-apis-radio.md @zhang-hai-feng @zengyawen @jyh926 @gaoxi785
zh-cn/application-dev/reference/apis/js-apis-reminderAgent.md @jayleehw @RayShih @li-weifeng2 @currydavids zh-cn/application-dev/reference/apis/js-apis-reminderAgent.md @jayleehw @RayShih @li-weifeng2 @currydavids
zh-cn/application-dev/reference/apis/js-apis-request.md @feng-aiwen @ningningW @nagexiucai @murphy1984 zh-cn/application-dev/reference/apis/js-apis-request.md @feng-aiwen @ningningW @hanruofei @murphy1984
zh-cn/application-dev/reference/apis/js-apis-resource-manager.md @Buda-Liu @ningningW @mengjingzhimo @yangqing3 zh-cn/application-dev/reference/apis/js-apis-resource-manager.md @Buda-Liu @ningningW @mengjingzhimo @yangqing3
zh-cn/application-dev/reference/apis/js-apis-router.md @HelloCrease @niulihua @tomatodevboy zh-cn/application-dev/reference/apis/js-apis-router.md @HelloCrease @niulihua @tomatodevboy
zh-cn/application-dev/reference/apis/js-apis-rpc.md @xuepianpian @RayShih @zhaopeng_gitee @vagrant_world zh-cn/application-dev/reference/apis/js-apis-rpc.md @xuepianpian @RayShih @zhaopeng_gitee @vagrant_world
...@@ -365,7 +365,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-notification.md @jayleehw @R ...@@ -365,7 +365,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-notification.md @jayleehw @R
zh-cn/application-dev/reference/apis/js-apis-system-package.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-system-package.md @shuaytao @RayShih @wangzhen107 @inter515
zh-cn/application-dev/reference/apis/js-apis-system-parameter.md @mupceet @zengyawen @handyohos @nan-xiansen zh-cn/application-dev/reference/apis/js-apis-system-parameter.md @mupceet @zengyawen @handyohos @nan-xiansen
zh-cn/application-dev/reference/apis/js-apis-system-prompt.md @HelloCrease @niulihua @tomatodevboy zh-cn/application-dev/reference/apis/js-apis-system-prompt.md @HelloCrease @niulihua @tomatodevboy
zh-cn/application-dev/reference/apis/js-apis-system-request.md @zhang-hai-feng @zengyawen @jyh926 @gaoxi785 zh-cn/application-dev/reference/apis/js-apis-system-request.md @zhang-hai-feng @ningningW @jyh926 @gaoxi785
zh-cn/application-dev/reference/apis/js-apis-system-router.md @HelloCrease @niulihua @tomatodevboy zh-cn/application-dev/reference/apis/js-apis-system-router.md @HelloCrease @niulihua @tomatodevboy
zh-cn/application-dev/reference/apis/js-apis-system-sensor.md @hellohyh001 @ningningW @butterls @star-wind-snow-and-rain zh-cn/application-dev/reference/apis/js-apis-system-sensor.md @hellohyh001 @ningningW @butterls @star-wind-snow-and-rain
zh-cn/application-dev/reference/apis/js-apis-system-storage.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-system-storage.md @feng-aiwen @ge-yafang @gong-a-shi @logic42
......
# Ability Development
> **NOTE**<br/>
> This folder is deprecated. Read [Application Models](../application-models/Readme-EN.md) instead.
- [Ability Framework Overview](ability-brief.md)
- [Context Usage](context-userguide.md)
- FA Model
- [FA Model Overview](fa-brief.md)
- [Page Ability Development](fa-pageability.md)
- [Service Ability Development](fa-serviceability.md)
- [Data Ability Development](fa-dataability.md)
- [FA Widget Development](fa-formability.md)
- Stage Model
- [Stage Model Overview](stage-brief.md)
- [Ability Development](stage-ability.md)
- [Service Extension Ability Development](stage-serviceextension.md)
- [Ability Continuation Development](stage-ability-continuation.md)
- [Ability Call Development](stage-call.md)
- [Stage Widget Development](stage-formextension.md)
- Other
- [WantAgent Development](wantagent.md)
- [Ability Assistant Usage](ability-assistant-guidelines.md)
- [ContinuationManager Development](continuationmanager.md)
- [Test Framework Usage](ability-delegator.md)
# Ability Assistant Usage
The ability assistant enables you to start applications, atomic services, and test cases and debug applications. By using this tool, you can send commands in the hdc shell to perform various system operations, such as starting abilities, forcibly stopping processes, and printing ability information.
## Query-related Commands
- **help**
Displays help information for the ability assistant.
**Return value**
Returns the help information.
**Method**
```
aa help
```
## Ability-related Commands
- **start**
Starts an ability.
| Name | Description |
| --------- | -------------------------- |
| -h/--help | Help information. |
| -d | Device ID. This parameter is optional. |
| -a | Ability name. This parameter is mandatory.|
| -b | Bundle name. This parameter is mandatory. |
| -D | Debugging mode. This parameter is optional. |
**Return value**
Returns "start ability successfully." if the ability is started; returns "error: failed to start ability." otherwise.
**Method**
```
aa start [-d <device-id>] -a <ability-name> -b <bundle-name> [-D]
```
- **stop-service**
Stops a Service ability.
| Name | Description |
| --------- | ------------------------ |
| -h/--help | Help information. |
| -d | Device ID. This parameter is optional. |
| -a | Ability name. This parameter is mandatory.|
| -b | Bundle name. This parameter is mandatory. |
**Return value**
Returns "stop service ability successfully." if the Service ability is stopped; returns "error: failed to stop service ability." otherwise.
**Method**
```
aa stop-service [-d <device-id>] -a <ability-name> -b <bundle-name>
```
- **dump**
Prints ability-related information.
| Name | Level-2 Parameter | Description |
| ----------------- | -------------------- | ------------------------------------------------------------ |
| -h/--help | - | Prints help information. |
| -a/--all | - | Prints ability information in all missions. |
| -l/--mission-list | type (All logs are printed if this parameter is left unspecified.)| Prints mission stack information.<br>The following values are available for **type**:<br>- NORMAL <br>- DEFAULT_STANDARD<br>- DEFAULT_SINGLE<br>- LAUNCHER |
| -e/--extension | elementName | Prints extended component information. |
| -u/--userId | UserId | Prints stack information of a specified user ID. This parameter must be used together with other parameters. <br/>Example commands: aa **dump -a -u 100** and **aa dump -d -u 100**.|
| -d/--data | - | Prints Data ability information. |
| -i/--ability | AbilityRecord ID | Prints detailed information about a specified ability. |
| -c/--client | - | Prints detailed ability information. This parameter must be used together with other parameters. <br/>Example commands: **aa dump -a -c** and **aa dump -i 21 -c**.|
**Method**
```
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**
Forcibly stops a process based on the bundle name.
**Return value**
Returns "force stop process successfully." if the process is forcibly stopped; returns "error: failed to force stop process." otherwise.
**Method**
```
aa force-stop <bundle-name>
```
# Ability Framework Overview
Ability is the basic abstraction of applications in OpenHarmony.
Each ability is an application component that provides an independent service and is the minimum unit for the system to schedule an application. An application can contain one or more **Ability** instances.
The ability framework model has two forms:
- FA model, which is available for application development using API version 8 and earlier versions. In the FA model, there are PageAbility, ServiceAbility, DataAbility, and FormAbility.
- Stage model, which is introduced since API version 9. In the stage model, there are two classes: UIAbility and ExtensionAbility. ExtensionAbility is further extended to ServiceExtensionAbility, FormExtensionAbility, DataShareExtensionAbility, and more.
Starting from API version 9, the stage model is recommended.
The stage model is designed to make it easier to develop complex applications in the distributed environment. The table below lists the design differences between the two models.
| Item | FA Model | Stage Model |
| -------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
| Application component development mode | Web-like development | Object-oriented development |
| Engine instance | Each **Ability** instance exclusively occupies a VM instance. | Multiple **Ability** instances share a VM instance. |
| Intra-process object sharing| Not supported | Supported |
| Bundle description file | The **config.json** file is used to describe the HAP and component information. Each component must use a fixed file name.| The **module.json5** file is used to describe the HAP and component information. The entry file name can be specified.|
| Component | Four types of components are provided: PageAbility (used for UI page display), ServiceAbility (used to provide services), DataAbility (used for data sharing), and FormAbility (used to provide widgets).| Two types of components are provided: UIAbility (used for UI page display) and ExtensionAbility (scenario-based service extension). |
In addition, the following differences exist in the development process:
* Different ability types
![favsstage](figures/favsstage.png)
* Different ability lifecycles
![lifecycle](figures/lifecycle.png)
For details about the two models, see [FA Model Overview](fa-brief.md) and [Stage Model Overview](stage-brief.md).
# Test Framework Usage
## Overview
The delegator test framework provides a self-test environment for OpenHarmony applications. Using this framework, you can start an ability, schedule its lifecycle, listen for its state changes, run a shell command, and print the test result.
## Constraints
The APIs provided by the test framework can be used only in the test HAP. They take effect only after the test framework is started.
## Starting the Test Framework
The test framework can be started in either of the following ways:
- Method 1: Run the `aa test` command.
- Method 2: Use DevEco Studio.
### Running aa test
To start the test framework, specify the **TestRunner** and the package name or module name of the HAP where the **TestRunner** is located.
An example command in the FA model is as follows:
```javascript
aa test -b BundleName -p com.example.myapplicationfaets -s unittest OpenHarmonyTestRunner -s class ActsAbilityTest -w 20
```
An example command in the stage model is as follows:
```javascript
aa test -b BundleName -m com.example.myapplicationfaets -s unittest OpenHarmonyTestRunner -s class ActsAbilityTest -w 20
```
| Parameter | Mandatory| Description |
| --------------- | -------- | ------------------------------------------------------------ |
| -b | Yes | Bundle name of the HAP where the **TestRunner** is located. |
| -p | Yes | Package name of the HAP where the **TestRunner** is located. This parameter is used by the FA model. |
| -m | Yes | Module name of the HAP where the **TestRunner** is located. This parameter is used by the stage model. |
| -s unittest | Yes | Name of the **TestRunner** to be used. The TestRunner name must be the same as the file name. |
| -w | No | Timeout interval of a test case, in seconds. If this parameter is not specified or is set to a value less than or equal to **0**, the test framework exits only after **finishTest** is invoked.|
| -s \<key>\<value> | No | **-s** can be followed by any key-value pair obtained through **AbilityDelegatorArgs.parameters**. For example, in **-s classname myTest**, **-s classname** is the key and **myTest** is the value.|
| -D | No | Debug mode for starting the tested application.|
| -h | No | Help information.|
### Using DevEco Studio
For details about how to use DevEco Studio to start the test framework, see [OpenHarmony Test Framework](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-openharmony-test-framework-0000001263160453#section1034420367508).
## Introduction to TestRunner
**TestRunner** is the entry class of the test framework test process. When the test process is started, the system calls related APIs in **TestRunner**. You need to inherit this class and override the **onPrepare** and **onRun** APIs. When creating an application template, DevEco Studio initializes the default **TestRunner** and starts the default **TestAbility** in the **onRun** API. You can modify the test code of **TestAbility** or override **onPrepare** and **onRun** in **TestRunner** to implement your own test code. For details, see [TestRunner](../reference/apis/js-apis-application-testRunner.md).
## Introduction to AbilityDelegatorRegistry
**AbilityDelegatorRegistry** is the **AbilityDelegator** repository class provided by the test framework. You can use **AbilityDelegatorRegistry** to obtain an **AbilityDelegator** instance and the input and generated parameters **AbilityDelegatorArgs** during the test. You can use **AbilityDelegator** to invoke the function set provided by the test framework for testing and verification. For details, see [AbilityDelegatorRegistry](../reference/apis/js-apis-application-abilityDelegatorRegistry.md).
## Introduction to AbilityDelegatorArgs
**AbilityDelegatorArgs** is a test parameter class provided by the test framework. You can use **AbilityDelegatorArgs** to obtain the parameters passed and generated during the test. For details, see [AbilityDelegatorArgs](../reference/apis/js-apis-inner-application-abilityDelegatorArgs.md).
## Introduction to AbilityMonitor
**AbilityMonitor** is provided by the test framework for binding to and listening for abilities. You can use **AbilityMonitor** to bind to an **Ability** instance and add **AbilityMonitor** to the listening list. When **AbilityMonitor** is bound to an ability, the creation and lifecycle changes of the ability will trigger the related callback in **AbilityMonitor**. You can test and verify the ability in these callbacks. For details, see [AbilityMonitor](../reference/apis/js-apis-inner-application-abilityMonitor.md).
**Example**
```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");
});
```
## Introduction to AbilityDelegator
**AbilityDelegator** is a main function class of the test framework. It provides the functions of starting an ability, obtaining an **Ability** instance, scheduling the ability lifecycle, listening for the ability state, and printing test results.
**Modules to Import**
```javascript
import AbilityDelegatorRegistry from '@ohos.application.abilityDelegatorRegistry'
```
```javascript
var abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
```
### Starting an Ability and Listening for the Ability State
Use **AbilityDelegator** and **AbilityMonitor** to start an ability, obtain an **Ability** instance, and listen for the ability state.
**Example**
```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");
});
```
### Scheduling the Ability Lifecycle
**AbilityDelegator** provides APIs to display and schedule the ability lifecycle and supports the foreground and background. It works with **AbilityMonitor** to listen for the ability lifecycle. For details, see [AbilityDelegator](../reference/apis/js-apis-inner-application-abilityDelegator.md).
### Running a Shell Command
**AbilityDelegator** provides APIs to run shell commands in the test environment.
**Example**
```javascript
var abilityDelegator;
var cmd = "cmd";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.executeShellCommand(cmd, (err, data) => {
console.info("executeShellCommand callback");
});
```
### Printing Log Information
**AbilityDelegator** provides APIs for printing log information. You can call any API in the test code to print process logs to the unit test console.
**Example**
```javascript
var abilityDelegator;
var msg = "msg";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.print(msg, (err) => {
console.info("print callback");
});
```
### Finishing the Test and Printing Log Information
**AbilityDelegator** provides the APIs for actively finishing the test. You can call any API in test code to finish the test and print logs to the unit test console.
**Example**
```javascript
var abilityDelegator;
var msg = "msg";
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator();
abilityDelegator.finishTest(msg, 0, (err) => {
console.info("finishTest callback");
});
```
# Context Usage
## Context Overview
**Context** provides the capability of obtaining contextual information of an application.
The OpenHarmony application framework has two models: Feature Ability (FA) model and stage model. Correspondingly, there are two sets of context mechanisms. **application/BaseContext** is a common context base class. It uses the **stageMode** attribute to specify whether the context is used for the stage model.
- FA model
Only the methods in **app/Context** can be used for the context in the FA model. Both the application-level context and ability-level context are instances of this type. If an ability-level method is invoked in the application-level context, an error occurs. Therefore, you must pay attention to the actual meaning of the **Context** instance.
- Stage model
The stage model has the following types of contexts: **application/Context**, **application/ApplicationContext**, **application/AbilityStageContext**, **application/ExtensionContext**, **application/AbilityContext**, and **application/FormExtensionContext**. For details about these contexts and how to use them, see [Context in the Stage Model](#context-in-the-stage-model).
![contextIntroduction](figures/contextIntroduction.png)
## Context in the FA Model
Only the methods in **app/Context** can be used for the context in the FA model.
The FA model has only one context definition. All capabilities in the context are provided through methods. The context uses these methods to extend the capabilities of the FA.
**d.ts statement**
https://gitee.com/openharmony/interface_sdk-js/blob/master/api/app/context.d.ts
**Example**
```javascript
import featureAbility from '@ohos.ability.featureAbility'
export default {
onCreate() {
// Obtain the context and call related APIs.
let context = featureAbility.getContext();
context.getBundleName((data, bundleName)=>{
console.info("ability bundleName:" + bundleName)
});
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
### Common Context-related Methods in the FA Model
The following context-related methods are available in the FA model:
```javascript
setDisplayOrientation(orientation: bundle.DisplayOrientation, callback: AsyncCallback<void>): void
setDisplayOrientation(orientation: bundle.DisplayOrientation): Promise<void>;
```
The methods are used to set the display orientation of the current ability.
**Example**
```javascript
import featureAbility from '@ohos.ability.featureAbility'
import bundle from '@ohos.bundle';
export default {
onCreate() {
// Obtain the context and call related APIs.
let context = featureAbility.getContext();
context.setDisplayOrientation(bundle.DisplayOrientation.LANDSCAPE).then(() => {
console.log("Set display orientation.")
})
console.info('Application onCreate')
},
onDestroy() {
console.info('Application onDestroy')
},
}
```
## Context in the Stage Model
The following describes the contexts provided by the stage model in detail.
### application/Context
**application/Context** is the base class context. It provides basic application information, such as **resourceManager**, **applicationInfo**, **cacheDir**, and **area**. It also provides basic application methods such as **createModuleContext**.
**d.ts statement**
https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/Context.d.ts
### application/ApplicationContext
**application/ApplicationContext** is an application-level context. In addition to the capabilities provided by the base class context, the application-level context provides **registerAbilityLifecycleCallback** and **unregisterAbilityLifecycleCallback** to monitor the ability lifecycle in a process.
**How to Obtain**
Obtain the context by calling **context.getApplicationContext()** in **Ability**.
**Example**
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
var lifecycleid;
export default class EntryAbility extends UIAbility {
onCreate() {
console.log("EntryAbility 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. Obtain applicationContext through the context attribute.
let applicationContext = this.context.getApplicationContext();
// 2. Use applicationContext to register and listen for the ability lifecycle in the application.
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 statement**
https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/ApplicationContext.d.ts
### application/AbilityStageContext
**application/AbilityStageContext** is the context for the HAP file. In addition to those provided by the base class **application/Context**, this context contains **HapModuleInfo** and **Configuration**.
**How to Obtain**
Obtain the context from the **context** attribute in **AbilityStage**.
**Example**
```javascript
export default class MyAbilityStage extends AbilityStage {
onCreate() {
// The context attribute is of the AbilityStageContext type.
console.log('HapModuleInfo is ' + this.context.currentHapModuleInfo);
}
}
```
**d.ts statement**
https://gitee.com/openharmony/interface_sdk-js/blob/master/api/application/AbilityStageContext.d.ts
### application/AbilityContext
In the stage model, each ability has a context attribute.
**Ability** provides methods to manage the ability lifecycle, and **AbilityContext** provides methods to operate abilities (such as **startAbility** and **connectAbility**).
**How to Obtain**
Obtain the context from the **context** attribute in **Ability**.
**Example**
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.log("[Demo] EntryAbility onCreate")
globalThis.abilityWant = want;
}
onDestroy() {
console.log("[Demo] EntryAbility onDestroy")
}
onWindowStageCreate(windowStage) {
// Set the main page for this ability when the main window is created.
console.log("[Demo] EntryAbility onWindowStageCreate")
// Obtain AbilityContext and print the ability information.
let context = this.context;
console.log("[Demo] EntryAbility 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() {
// Release the UI related resources when the main window is destroyed.
console.log("[Demo] EntryAbility onWindowStageDestroy")
}
onForeground() {
// The ability is switched to run in the foreground.
console.log("[Demo] EntryAbility onForeground")
}
onBackground() {
// The ability is switched to run in the background.
console.log("[Demo] EntryAbility onBackground")
}
};
```
### application/FormExtensionContext
For details, see [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md).
### Obtaining the Context on an ArkTS Page
In the stage model, in the onWindowStageCreate lifecycle of an ability, you can call **SetUIContent** of **WindowStage** to load an ArkTS page. In some scenarios, you need to obtain the context on the page to call related APIs.
**How to Obtain**
Use the API described in the table below to obtain the context associated with an ArkTS page.
| API | Description |
| :------------------------------------ | :----------------------------------------------------------- |
| getContext(component: Object): Object | Obtains the **Context** object associated with a component on the page.<br>Since API version 9, this API is supported in ArkTS widgets.|
**Example**
```ts
// EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.log("[Demo] EntryAbility onCreate")
}
onDestroy() {
console.log("[Demo] EntryAbility onDestroy")
}
onWindowStageCreate(windowStage) {
// Load the index page and pass the current Context object.
windowStage.setUIContent(this.context, "pages/index", null)
}
onWindowStageDestroy() {}
onForeground() {}
onBackground() {}
};
```
```ts
// pages/index.ets
import context from '@ohos.app.ability.context'
type Context = context.Context
@Entry
@Component
struct Index {
build() {
Row() {
Column() {
Text('GetContext')
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
// Obtain the Context object associated with the current component.
var context : Context = getContext(this) as Context
console.info("CacheDir:" + context.cacheDir)
})
}
.width('100%')
}
.height('100%')
}
}
```
## Common Incorrect Usage
**Error 1: Use globalThis to obtain the context in the stage model.**
**Reason**
In the FA model, each ability instance has a JS VM instance. Therefore, a global ability instance can be obtained from the **global** object of the JS engine. In the stage model, where all the processes of an application share a JS VM instance, there is no global ability instance, and using **globalThis** may cause an error or crash.
# ContinuationManager Development
> **NOTE**
>
> Currently, the **ContinuationManager** module is not available for application development. Its APIs are mainly used to start the device selection module.
## When to Use
Users are using two or more devices to experience an all-scenario, multi-device lifestyle. Each type of device has its unique advantages and disadvantages specific to scenarios. The ability continuation capability breaks boundaries of devices and enables multi-device collaboration, achieving precise control, universal coordination, and seamless hops of user applications.
As the entry of the ability continuation capability, **continuationManager** is used to start the device selection module for the user to select the target device. After a device is selected, information about the selected device is returned to the user. The user can then initiate cross-device continuation or collaboration based on the device information.
![continuationManager](figures/continuationManager.png)
## Available APIs
| API | Description|
| ---------------------------------------------------------------------------------------------- | ----------- |
| registerContinuation(callback: AsyncCallback\<number>): void | Registers the continuation management service and obtains a token. This API does not involve any filter parameters and uses an asynchronous callback to return the result.|
| registerContinuation(options: ContinuationExtraParams, callback: AsyncCallback\<number>): void | Registers the continuation management service and obtains a token. This API uses an asynchronous callback to return the result.|
| registerContinuation(options?: ContinuationExtraParams): Promise\<number> | Registers the continuation management service and obtains a token. This API uses a promise to return the result.|
| on(type: "deviceSelected", token: number, callback: Callback\<Array\<ContinuationResult>>): void | Subscribes to device connection events. This API uses an asynchronous callback to return the result.|
| on(type: "deviceUnselected", token: number, callback: Callback\<Array\<ContinuationResult>>): void | Subscribes to device disconnection events. This API uses an asynchronous callback to return the result.|
| off(type: "deviceSelected", token: number): void | Unsubscribes from device connection events.|
| off(type: "deviceUnselected", token: number): void | Unsubscribes from device disconnection events.|
| startContinuationDeviceManager(token: number, callback: AsyncCallback\<void>): void | Starts the device selection module to show the list of available devices. This API does not involve any filter parameters and uses an asynchronous callback to return the result.|
| startContinuationDeviceManager(token: number, options: ContinuationExtraParams, callback: AsyncCallback\<void>): void | Starts the device selection module to show the list of available devices. This API uses an asynchronous callback to return the result.|
| startContinuationDeviceManager(token: number, options?: ContinuationExtraParams): Promise\<void> | Starts the device selection module to show the list of available devices. This API uses a promise to return the result.|
| updateContinuationState(token: number, deviceId: string, status: DeviceConnectState, callback: AsyncCallback\<void>): void | Instructs the device selection module to update the device connection state. This API uses an asynchronous callback to return the result.|
| updateContinuationState(token: number, deviceId: string, status: DeviceConnectState): Promise\<void> | Instructs the device selection module to update the device connection state. This API uses a promise to return the result.|
| unregisterContinuation(token: number, callback: AsyncCallback\<void>): void | Deregisters the continuation management service. This API uses an asynchronous callback to return the result.|
| unregisterContinuation(token: number): Promise\<void> | Deregisters the continuation management service. This API uses a promise to return the result.|
## How to Develop
1. Import the **continuationManager** module.
```ts
import continuationManager from '@ohos.continuation.continuationManager';
```
2. Apply for the **DISTRIBUTED_DATASYNC** permission.
The permission application operation varies according to the ability model in use. In the FA mode, add the required permission in the `config.json` file, as follows:
```json
{
"module": {
"reqPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}
]
}
}
```
This permission must also be granted by the user through a dialog box when the application is started for the first time. The sample code is as follows:
```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]);
// Check whether the permission is granted.
if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
needGrantPermission = true;
break;
}
}
// If the permission is not granted, call requestPermissionsFromUser to apply for the permission.
if (needGrantPermission) {
await featureAbility.getContext().requestPermissionsFromUser(permissions, 1);
} else {
console.info('app permission already granted');
}
}
```
In the stage model, add the required permission in the `module.json5` file. The sample code is as follows:
```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);
// Check whether the permission is granted.
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;
}
}
// If the permission is not granted, call requestPermissionsFromUser to apply for the permission.
if (needGrantPermission) {
try {
// globalThis.context is Ability.context, which must be assigned a value in the MainAbility.ts file in advance.
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. Register the continuation management service and obtain a token.
The sample code is as follows:
```ts
let token: number = -1; // Used to save the token returned after the registration. The token will be used when listening for device connection/disconnection events, starting the device selection module, and updating the device connection state.
try {
continuationManager.registerContinuation().then((data) => {
console.info('registerContinuation finished, ' + JSON.stringify(data));
token = data; // Obtain a token and assign a value to the token variable.
}).catch((err) => {
console.error('registerContinuation failed, cause: ' + JSON.stringify(err));
});
} catch (err) {
console.error('registerContinuation failed, cause: ' + JSON.stringify(err));
}
```
4. Listen for the device connection/disconnection state.
The sample code is as follows:
```ts
let remoteDeviceId: string = ""; // Used to save the information about the remote device selected by the user, which will be used for cross-device continuation or collaboration.
try {
// The token parameter is the token obtained during the registration.
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; // Assign the deviceId of the first selected remote device to the remoteDeviceId variable.
// Pass the remoteDeviceId parameter to want.
let want = {
deviceId: remoteDeviceId,
bundleName: 'ohos.samples.continuationmanager',
abilityName: 'EntryAbility'
};
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));
}
```
The preceding multi-device collaboration operation is performed across devices in the stage model. For details about this operation in the FA model, see [Page Ability Development](fa-pageability.md).
You can also instruct the device selection module to update the device connection state. The sample code is as follows:
```ts
// Set the device connection state.
let deviceConnectStatus: continuationManager.DeviceConnectState = continuationManager.DeviceConnectState.CONNECTED;
// The token parameter is the token obtained during the registration, and the remoteDeviceId parameter is the remoteDeviceId obtained.
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));
}
```
Listen for the device disconnection state so that the user can stop cross-device continuation or collaboration in time. The sample code is as follows:
```ts
try {
// The token parameter is the token obtained during the registration.
continuationManager.on("deviceUnselected", token, (continuationResults) => {
console.info('onDeviceUnselected len: ' + continuationResults.length);
if (continuationResults.length <= 0) {
console.info('no unselected device');
return;
}
// Update the device connection state.
let unselectedDeviceId: string = continuationResults[0].id; // Assign the deviceId of the first deselected remote device to the unselectedDeviceId variable.
let deviceConnectStatus: continuationManager.DeviceConnectState = continuationManager.DeviceConnectState.DISCONNECTING; // Device disconnected.
// The token parameter is the token obtained during the registration, and the unselectedDeviceId parameter is the unselectedDeviceId obtained.
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. Start the device selection module to show the list of available devices on the network.
The sample code is as follows:
```ts
// Filter parameters.
let continuationExtraParams = {
deviceType: ["00E"], // Device type.
continuationMode: continuationManager.ContinuationMode.COLLABORATION_SINGLE // Single-choice mode of the device selection module.
};
try {
// The token parameter is the token obtained during the registration.
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. If you do not need to perform cross-device migration or collaboration operations, you can deregister the continuation management service, by passing the token obtained during the registration.
The sample code is as follows:
```ts
try {
// The token parameter is the token obtained during the registration.
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));
}
```
# FA Model Overview
## Overall Architecture
Ability is the entry for application development in OpenHarmony.
The core of ability development is the processing on ability lifecycle callbacks.
The Feature Ability (FA) model can be used only for application development using API version 8 and earlier versions. In this model, there are PageAbility, ServiceAbility, DataAbility, and FormAbility.
- PageAbility implements the ArkUI and provides the capability for interacting with users.
- ServiceAbility does not have a UI. It runs in the background and provides custom services for other abilities to invoke.
- DataAbility does not have a UI. It runs in the background and enables other abilities to insert, delete, and query data.
- FormAbility is used to implement widgets, a new UI display form available on OpenHarmony devices.
> Note: Starting from API version 9, the stage model is recommended for application development.
## Lifecycle
Among all abilities, PageAbility has the most complex lifecycle, because it has a UI and acts as a touchpoint for interacting with users.
**The following figure shows the lifecycle of PageAbility.**
![fa-pageAbility-lifecycle](figures/fa-pageAbility-lifecycle.png)
The other abilities do not involve foreground and background switch or the **onShow** and **onHide** callbacks.
You can override the lifecycle callbacks in **app.js** or **app.ets** to process application logic.
The **app.js** file provides only the **onCreate** and **onDestroy** callbacks, and the **app.ets** file provides the callbacks covering the entire lifecycle.
## Process and Thread Model
Each application runs in a process. In the FA model, each ability runs in an independent VM.
When an ability is started, an application process as well as a thread for this ability is created. For an application that has multiple abilities, each ability runs in an independent thread. In the FA model, each ability is bound to an independent VM instance. In this way, abilities are isolated from each other.
![fa-threading-model](figures/fa-threading-model.png)
## Application Package Structure
For details about the project directory structure of the FA model, see [OpenHarmony Project Overview](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-project-overview-0000001218440650#section4154183910141).
For details about how to configure the application package structure of the FA model, see [Application Package Structure Configuration File](../quick-start/application-configuration-file-overview-fa.md).
# Data Ability Development
## When to Use
A Data ability helps applications manage access to data stored by themselves and other applications. It also provides APIs for sharing data with other applications either on the same device or across devices.
Data ability providers can customize data access-related APIs such as data inserting, deleting, updating, and querying, as well as file opening, and share data with other applications through these open APIs.
## URI Introduction
A Uniform Resource Identifier (URI) is used to identify a specific data item, such as a table in the database or a file on the disk. URIs used in OpenHarmony comply with the commonly used URI standard. A URI consists of the components:
![fa-dataability-uri](figures/fa-dataability-uri.png)
- **scheme**: name of the scheme used by the Data ability. The value is fixed at **dataability**.
- **authority**: device ID. To access data on a remote device, set this component to the ID of the remote device. To access data on the local device, leave this component empty.
- **path**: location of the specific resource to access.
- **query**: query parameters.
- **fragment**: subordinate resources to access.
Example URIs:
- Cross-device communication: **dataability://***device_id***/***com.domainname.dataability.persondata***/***person***/***10*
- Local-device communication: **dataability:///***com.domainname.dataability.persondata***/***person***/***10*
> **NOTE**
>
> In the case of local-device communication, **device_id** is empty, and therefore, there are three slashes (/) after **dataability:**.
## Available APIs
**Table 1** Data ability lifecycle APIs
|API|Description|
|:------|:------|
|onInitialized(info: AbilityInfo): void|Called during ability initialization to initialize the relational database (RDB).|
|update(uri: string, valueBucket: rdb.ValuesBucket, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<number>): void|Updates data in the database.|
|query(uri: string, columns: Array\<string>, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<ResultSet>): void|Queries data in the database.|
|delete(uri: string, predicates: dataAbility.DataAbilityPredicates, callback: AsyncCallback\<number>): void|Deletes one or more data records from the database.|
|normalizeUri(uri: string, callback: AsyncCallback\<string>): void|Normalizes the URI. A normalized URI applies to cross-device use, persistence, backup, and restore. When the context changes, it ensures that the same data item can be referenced.|
|batchInsert(uri: string, valueBuckets: Array\<rdb.ValuesBucket>, callback: AsyncCallback\<number>): void|Inserts multiple data records into the database.|
|denormalizeUri(uri: string, callback: AsyncCallback\<string>): void|Converts a normalized URI generated by **normalizeUri** into a denormalized URI.|
|insert(uri: string, valueBucket: rdb.ValuesBucket, callback: AsyncCallback\<number>): void|Inserts a data record into the database.|
|openFile(uri: string, mode: string, callback: AsyncCallback\<number>): void|Opens a file.|
|getFileTypes(uri: string, mimeTypeFilter: string, callback: AsyncCallback\<Array\<string>>): void|Obtains the MIME type of a file.|
|getType(uri: string, callback: AsyncCallback\<string>): void|Obtains the MIME type matching the data specified by the URI.|
|executeBatch(ops: Array\<DataAbilityOperation>, callback: AsyncCallback\<Array\<DataAbilityResult>>): void|Operates data in the database in batches.|
|call(method: string, arg: string, extras: PacMap, callback: AsyncCallback\<PacMap>): void|Calls a custom API.|
## How to Develop
### Creating a Data Ability
1. To meet the basic requirements of the database storage service, implement the **Insert**, **Query**, **Update**, and **Delete** APIs in the **Data** class. The **BatchInsert** and **ExecuteBatch** APIs have already implemented the traversal logic, but not batch data processing.
The following code snippet shows how to create a Data ability:
```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. Configure the submodule.
| JSON Field| Description |
| ------------ | ------------------------------------------------------------ |
| "name" | Ability name, corresponding to the **Data** class name derived from **Ability**. |
| "type" | Ability type, which is **Data** for a Data ability. |
| "uri" | URI used for communication. |
| "visible" | Whether the Data ability is visible to other applications. When this parameter is set to **true**, the Data ability can communicate with other applications.|
**config.json configuration example**
```json
"abilities":[{
"srcPath": "DataAbility",
"name": ".DataAbility",
"icon": "$media:icon",
"srcLanguage": "ets",
"description": "$string:description_dataability",
"type": "data",
"visible": true,
"uri": "dataability://ohos.samples.etsdataability.DataAbility"
}]
```
### Accessing a Data ability
#### Development Preparations
Import the basic dependency packages and obtain the URI string for communicating with the Data submodule.
The basic dependency packages include:
- @ohos.ability.featureAbility
- @ohos.data.dataAbility
- @ohos.data.rdb
#### Data Ability API Development
1. Create a Data ability helper.
For details about the APIs provided by **DataAbilityHelper**, see [DataAbilityHelper Module](../reference/apis/js-apis-inner-ability-dataAbilityHelper.md).
```js
// Different from the URI defined in the config.json file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/).
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. Construct RDB data.
```js
var valuesBucket = {"name": "gaolu"}
var da = new ohos_data_ability.DataAbilityPredicates()
var valArray =new Array("value1");
var cars = new Array({"batchInsert1" : "value1",});
```
3. Use **insert** to insert data to the Data submodule.
```js
// Callback mode:
DAHelper.insert(
urivar,
valuesBucket,
(error, data) => {
console.log("DAHelper insert result: " + data)
}
);
```
```js
// Promise mode:
var datainsert = await DAHelper.insert(
urivar,
valuesBucket
);
```
4. Use **delete** to delete data from the Data submodule.
```js
// Callback mode:
DAHelper.delete(
urivar,
da,
(error, data) => {
console.log("DAHelper delete result: " + data)
}
);
```
```js
// Promise mode:
var datadelete = await DAHelper.delete(
urivar,
da,
);
```
5. Use **update** to update data in the Data submodule.
```js
// Callback mode:
DAHelper.update(
urivar
valuesBucket,
da,
(error, data) => {
console.log("DAHelper update result: " + data)
}
);
```
```js
// Promise mode:
var dataupdate = await DAHelper.update(
urivar,
valuesBucket,
da,
);
```
6. Use **query** to query data in the Data submodule.
```js
// Callback mode:
DAHelper.query(
urivar,
valArray,
da,
(error, data) => {
console.log("DAHelper query result: " + data)
}
);
```
```js
// Promise mode:
var dataquery = await DAHelper.query(
urivar,
valArray,
da
);
```
7. Use **batchInsert** to insert data in batches to the Data submodule.
```js
// Callback mode:
DAHelper.batchInsert(
urivar,
cars,
(error, data) => {
console.log("DAHelper batchInsert result: " + data)
}
);
```
```js
// Promise mode:
var databatchInsert = await DAHelper.batchInsert(
urivar,
cars
);
```
8. Use **executeBatch** to process data in batches in the Data submodule.
```js
// Callback mode:
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 mode:
var dataexecuteBatch = await DAHelper.executeBatch(
urivar,
[
{
uri: urivar,
type: featureAbility.DataAbilityOperationType.TYPE_INSERT,
valuesBucket:
{
"executeBatch" : "value1",
},
predicates: da,
expectedCount:0,
predicatesBackReferences: null,
interrupted:true,
}
]
);
```
# Page Ability Development
## Overview
### Concepts
The Page ability implements the ArkUI and provides the capability of interacting with developers. When you create an ability in DevEco Studio, DevEco Studio automatically creates template code.
The capabilities related to the Page ability are implemented through the **featureAbility**, and the lifecycle callbacks are implemented through the callbacks in **app.js** or **app.ets**.
### Page Ability Lifecycle
Introduction to the Page ability lifecycle:
The Page ability lifecycle defines all states of a Page ability, such as **INACTIVE**, **ACTIVE**, and **BACKGROUND**.
The following figure shows the lifecycle state transition of the Page ability.
![PageAbility-Lifecycle](figures/page-ability-lifecycle.png)
Description of ability lifecycle states:
- **UNINITIALIZED**: The Page ability is not initialized. This is a temporary state, from which a Page ability changes directly to the **INITIAL** state upon its creation.
- **INITIAL**: The Page ability is initialized but not running. The Page ability enters the **INACTIVE** state after it is started.
- **INACTIVE**: The Page ability is visible but does not gain focus.
- **ACTIVE**: The Page ability runs in the foreground and has focus.
- **BACKGROUND**: The Page ability runs in the background. After being re-activated, the Page ability enters the **ACTIVE** state. After being destroyed, the Page ability enters the **INITIAL** state.
The following figure shows the relationship between lifecycle callbacks and lifecycle states of the Page ability.
![fa-pageAbility-lifecycle](figures/fa-pageAbility-lifecycle.png)
You can override the lifecycle callbacks provided by the Page ability in the **app.js** or **app.ets** file. Currently, the **app.js** file provides only the **onCreate** and **onDestroy** callbacks, and the **app.ets** file provides the full lifecycle callbacks.
### Launch Type
The ability supports two launch types: singleton and multi-instance.
You can specify the launch type by setting **launchType** in the **config.json** file.
**Table 1** Startup modes
| Launch Type | Description |Description |
| ----------- | ------- |---------------- |
| standard | Multi-instance | A new instance is started each time an ability starts.|
| singleton | Singleton | The ability has only one instance in the system. If an instance already exists when an ability is started, that instance is reused.|
By default, **singleton** is used.
## Development Guidelines
### Available APIs
**Table 2** APIs provided by featureAbility
| API | Description |
| --------------------------------------------------- | --------------- |
| void startAbility(parameter: StartAbilityParameter) | Starts an ability. |
| Context getContext(): | Obtains the application context.|
| void terminateSelf() | Terminates the ability. |
| bool hasWindowFocus() | Checks whether the ability has focus. |
### Starting a Local Page Ability
**Modules to Import**
```js
import featureAbility from '@ohos.ability.featureAbility'
```
**Example**
```javascript
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility({
want: {
action: "",
entities: [""],
type: "",
deviceId: "",
bundleName: "com.example.myapplication",
/* In the FA model, abilityName consists of package and ability name. */
abilityName: "com.example.entry.secondAbility",
uri: ""
}
});
```
### Starting a Remote Page Ability
>NOTE
>
>This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications.
**Modules to Import**
```
import featureAbility from '@ohos.ability.featureAbility'
import deviceManager from '@ohos.distributedHardware.deviceManager';
```
**Example**
```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');
}
```
Obtain **deviceId** from **DeviceManager**. The sample code is as follows:
```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("EntryAbility onButtonClick getRemoteDeviceId err: list is null");
return;
}
console.log("EntryAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
return list[0].deviceId;
} else {
console.log("EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null");
}
}
```
In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows:
```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');
}
```
### Lifecycle APIs
**Table 3** Lifecycle callbacks
| API | Description |
| ------------ | ------------------------------------------------------------ |
| onShow() | Called when the ability is switched from the background to the foreground. In this case, the ability is visible to users.|
| onHide() | Called when the ability is switched from the foreground to the background. In this case, the ability is invisible to users.|
| onDestroy() | Called when the ability is destroyed. In this callback, you can make preparations for application exit, such as recycling resources and clearing the cache.|
| onCreate() | Called when the ability is created for the first time. You can initialize the application in this callback.|
| onInactive() | Called when the ability loses focus. An ability loses focus when it is about to enter the background state.|
| onActive() | Called when the ability is switched to the foreground and gains focus. |
**Example**
You need to override the lifecycle callbacks except **onCreate()** and **onDestroy()** in **app.js** or **app.ets**. The **onCreate()** and **onDestroy()** callbacks are automatically generated in the template code provided by DevEco Studio.
```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')
},
}
```
# Service Ability Development
## When to Use
A Service ability is used to run tasks in the background, such as playing music or downloading files. It does not provide a UI for user interaction. Service abilities can be started by other applications or abilities and can keep running in the background even after the user switches to another application.
## Lifecycle APIs
**Table 1** Service ability lifecycle APIs
|API|Description|
|:------|:------|
|onStart?(): void|Called to initialize a Service ability when the Service ability is being created. This callback is invoked only once in the entire lifecycle of a Service ability.|
|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on the client. You can collect calling statistics and perform initialization operations in this callback.|
|onConnect?(want: Want): rpc.RemoteObject|Called when another ability is connected to the Service ability.|
|onDisconnect?(want: Want): void|Called when another ability is disconnected from the Service ability.|
|onStop?(): void|Called when the Service ability is being destroyed. You should override this callback for your Service ability to clear its resources, such as threads and registered listeners.|
The differences between **onCommand()** and **onConnect()** are as follows:
- The **onCommand()** callback is triggered each time the client starts the Service ability by calling **startAbility** or **startAbilityForResult**.
- The **onConnect()** callback is triggered each time the client establishes a new connection with the Service ability by calling **connectAbility**.
## How to Develop
### Creating and Registering a Service Ability
1. Override the Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests.
```ts
export default {
onStart() {
console.log('ServiceAbility onStart');
},
onCommand(want, startId) {
console.log('ServiceAbility onCommand');
},
onConnect(want) {
console.log('ServiceAbility OnConnect');
// Below lists the implementation of ServiceAbilityStub.
return new ServiceAbilityStub('test');
},
onDisconnect(want) {
console.log('ServiceAbility OnDisConnect');
},
onStop() {
console.log('ServiceAbility onStop');
}
}
```
2. Register a Service ability.
Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**.
```json
{
"module": {
"abilities": [
{
"name": ".ServiceAbility",
"type": "service",
"visible": true
...
}
]
...
}
...
}
```
### Starting a Service Ability
The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object.
To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified.
- **bundleName** specifies the bundle name of the target application.
- **abilityName** specifies the target ability name.
The following code snippet shows how to start a Service ability running on the local device:
```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");
});
```
In the preceding code, the **startAbility()** API is used to start the Service ability.
- If the Service ability is not running, the system initializes the Service ability, and calls **onStart()** and **onCommand()** on the Service ability in sequence.
- If the Service ability is running, the system directly calls **onCommand()** on the Service ability.
The following code snippet shows how to start a Service ability running on the remote device. For details, see [Connecting to a Remote Service Ability](#connecting-to-a-remote-service-ability).
```ts
import featureAbility from '@ohos.ability.featureAbility'
featureAbility.startAbility(
{
want:
{
deviceId: remoteDeviceId, // Remote device ID.
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
}
}
).then((err) => {
console.log("startService success");
}).catch (err => {
console.log("startService FAILED");
});
```
### Stopping a Service Ability
In normal cases, a Service ability can be stopped by itself or by the system.
- The Service ability can call **particleAbility.terminateSelf()** to stop itself.
- If the application process where the Service ability is located exits, the Service ability is reclaimed along with the process.
- If the Service ability is only accessed through **connectAbility()** (the **onCommand()** callback has never been triggered), the system stops the Service ability when the last connection to the Service ability is disconnected.
### Connecting to a Local Service Ability
If a Service ability wants to interact with a Page ability or a Service ability in another application, you must first create a connection. A Service ability allows other abilities to connect to it through **connectAbility()**.
You can use either of the following methods to connect to a Service ability:
1. Using the IDL to automatically generate code
Use OpenHarmony Interface Definition Language (IDL) to automatically generate the corresponding client, server, and **IRemoteObject** code. For details, see [Development Using TS](../IDL/idl-guidelines.md#development-using-ts).
2. Writing code in the corresponding file
When using **connectAbility()**, pass the **Want** and **ConnectOptions** objects of the target Service ability, where **ConnectOptions** encapsulates the following three callbacks that need to be implemented.
- **onConnect()**: callback used for processing when the Service ability is connected.
- **onDisconnect()**: callback used for processing when the Service ability is disconnected.
- **onFailed()**: callback used for processing when the connection to the Service ability fails.
The following code snippet shows how to implement the callbacks:
```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;
}
// After obtaining the proxy of the Service ability, the calling ability can communicate with the Service ability.
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"
});
}
};
```
The following code snippet shows how to connect to a local Service ability:
```ts
import featureAbility from '@ohos.ability.featureAbility'
let want = {
bundleName: "com.jstest.service",
abilityName: "com.jstest.service.ServiceAbility"
};
let connectId = featureAbility.connectAbility(want, option);
```
When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides the default implementation of **IRemoteObject**. You can inherit **rpc.RemoteObject** to create a custom implementation class for interaction with the Service ability. For details, see the [RPC API Reference](..\reference\apis\js-apis-rpc.md).
The following code snippet shows how the Service ability returns itself to the calling ability:
```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");
// Execute the service logic.
if (code === 1) {
// Sort the input strings.
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');
}
}
```
### Connecting to a Remote Service Ability
This feature applies only to system applications. The method of creating a **ConnectOptions** object for connecting to a remote Service ability is similar to that for connecting to a local Service ability. The differences are as follows:
- The application must apply for the data synchronization permission from the user.
- **Want** of the target Service ability must contain the remote device ID.
> **NOTE**
>
> The **getTrustedDeviceList** API of **DeviceManager** is open only to system applications. Currently, only system applications can connect to a remote Service ability.
>
> For details about the API definition, see [Device Management](..\reference\apis\js-apis-device-manager.md).
The data synchronization permission is required in the cross-device scenario. Configure the permission in the **config.json** file.
```json
{
...
"module": {
...
"reqPermissions": [{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
}]
}
}
```
The **DISTRIBUTED_DATASYNC** permission is user granted. Therefore, your application, when being started, must display a dialog box to request the permission. The sample code is as follows:
```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');
}
```
To obtain the device ID, import the **@ohos.distributedHardware.deviceManager** module, which provides **getTrustedDeviceList** to obtain the remote device ID. For details about how to use the API, see [Device Management](..\reference\apis\js-apis-device-manager.md).
To connect to a remote Service ability, you only need to define **deviceId** in **Want**. The sample code is as follows:
```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);
```
The other implementations are the same as those for the connection to a local Service ability. For details, see the sample code provided under [Connecting to a Local Service Ability](#connecting-to-a-local-service-ability).
# Ability Continuation Development
## When to Use
Ability continuation is to continue the current mission of an application, including the UI component state variables and distributed objects, on another device. The UI component state variables are used to synchronize UI data, and the distributed objects are used to synchronize memory data.
## Available APIs
The following table lists the APIs used for ability continuation. For details about the APIs, see [UIAbility](../reference/apis/js-apis-app-ability-uiAbility.md).
**Table 1** Ability continuation APIs
|API| Description|
|:------ | :------|
| onContinue(wantParam : {[key: string]: any}): OnContinueResult | Called by the initiator to store the data required for continuation. The return value indicates whether the continuation request is accepted. The value **AGREE** means that the continuation request is accepted, **REJECT** means that the continuation request is rejected, and **MISMATCH** means a version mismatch.|
| onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the multi-instance ability scenario.|
| onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the singleton ability scenario.|
**Figure 1** Ability continuation development
![continuation_dev](figures/continuation-info.png)
In effect, ability continuation is a cross-device ability startup that carries data. When a continuation action is initiated, the system on device A calls back **onContinue()** of the application. You must implement storage of the current data in this API. Then, the system initiates a cross-device ability startup on device B and transmits the data to device B. The system on device B calls back **onCreate()** or **onNewWant()**. You must implement restoration of the transmitted data in this API.
## How to Develop
The code snippets provided below are all from [Sample](https://gitee.com/openharmony/ability_dmsfwk/tree/master/services/dtbschedmgr/test/samples/continuationManualTestSuite).
### Application Continuation
1. Modify the configuration file.
- Configure the application to support ability continuation.
Set the **continuable** field in the **module.json5** file to **true**. The default value is **false**. If this parameter is set to **false**, the application cannot be continued on another device.
```javascript
{
"module": {
"abilities": [
{
"continuable": true
}
]
}
}
```
- Configure the application startup type.
If **launchType** is set to **multiton** in the **module.json5** file, the application is of the multi-instance launch type. During ability continuation, regardless of whether the application is already open, the target starts the application and restores the UI page. If **launchType** is set to **singleton**, the application is of the singleton launch type. If the application is already open, the target clears the existing page stack and restores the UI page. For more information, see "Launch Type" in [Ability Development](./stage-ability.md).
Configure a multi-instance application as follows:
```javascript
{
"module": {
"abilities": [
{
"launchType": "multiton"
}
]
}
}
```
Configure a singleton application as follows or retain the default settings of **launchType**:
```javascript
{
"module": {
"abilities": [
{
"launchType": "singleton"
}
]
}
}
```
- Apply for the distributed permissions.
Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application.
```javascript
"requestPermissions": [
{
"name": "ohos.permission.DISTRIBUTED_DATASYNC"
},
```
This permission must be granted by the user in a dialog box when the application is started for the first time. To enable the application to display a dialog box to ask for the permission, add the following code to **onWindowStageCreate** of the **Ability** class:
```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. Implement the **onContinue()** API.
The **onContinue()** API is called by the initiator to save the UI component state variables and memory data and prepare for continuation. After the application completes the continuation preparation, the system must return either **OnContinueResult.AGREE(0)** to accept the continuation request or an error code to reject the request. If this API is not implemented, the system rejects the continuation request by default.
Modules to import:
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
```
To implement ability continuation, you must implement this API and have the value **AGREE** returned.
You can obtain the target device ID (identified by the key **targetDevice**) and the version number (identified by the key **version**) of the application installed on the target device from the **wantParam** parameter of this API. The version number can be used for compatibility check. If the current application version is incompatible with that on the target device, **OnContinueResult.MISMATCH** can be returned to reject the continuation request.
Example:
```javascript
onContinue(wantParam : {[key: string]: any}) {
Logger.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
let workInput = AppStorage.Get<string>('ContinueWork');
// Set the user input data into wantParam.
wantParam["work"] = workInput // set user input data into want params
Logger.info(`onContinue input = ${wantParam["input"]}`);
return AbilityConstant.OnContinueResult.AGREE
}
```
3. Implement the continuation logic in the **onCreate()** or **onNewWant()** API.
The **onCreate()** API is called by the target. When the ability is started on the target device, this API is called to instruct the application to synchronize the memory data and UI component state, and triggers page restoration after the synchronization is complete. If the continuation logic is not implemented, the ability will be started in common startup mode and the page cannot be restored.
The target device determines whether the startup is **LaunchReason.CONTINUATION** based on **launchReason** in **onCreate()**.
After data restore is complete, call **restoreWindowStage** to trigger page restoration.
You can also use **want.parameters.version** in the **want** parameter to obtain the application version number of the initiator.
Example:
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
import distributedObject from '@ohos.data.distributedDataObject';
export default class EntryAbility extends UIAbility {
storage : LocalStorag;
onCreate(want, launchParam) {
Logger.info(`EntryAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// Obtain the user data from the want parameter.
let workInput = want.parameters.work
Logger.info(`work input ${workInput}`)
AppStorage.SetOrCreate<string>('ContinueWork', workInput)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
For a singleton ability, use **onNewWant()** to achieve the same implementation.
### Data Continuation
Use distributed objects.
Distributed objects allow cross-device data synchronization like local variables. For two devices that form a Super Device, when data in the distributed data object of an application is added, deleted, or modified on a device, the data for the same application is also updated on the other device. Both devices can listen for the data changes and online and offline states of the other. For details, see [Sharing Distributed Data Objects](../database/data-sync-of-distributed-data-object.md).
In the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device.
- In **onContinue()**, the initiator saves the data to be migrated to the distributed object, calls the **save()** API to save the data and synchronize the data to the target device, sets the session ID, and sends the session ID to the target device through **wantParam**.
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
import distributedObject from '@ohos.data.distributedDataObject';
var g_object = distributedObject.createDistributedObject({data:undefined});
export default class EntryAbility extends UIAbility {
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);
});
```
- The target device obtains the session ID from **onCreate()**, creates a distributed object, and associates the distributed object with the session ID. In this way, the distributed object can be synchronized. Before calling **restoreWindowStage**, ensure that all distributed objects required for continuation have been associated.
```javascript
import UIAbility from '@ohos.app.ability.UIAbility';
import distributedObject from '@ohos.data.distributedDataObject';
var g_object = distributedObject.createDistributedObject({data:undefined});
export default class EntryAbility extends UIAbility {
storage : LocalStorag;
onCreate(want, launchParam) {
Logger.info(`EntryAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// Obtain the session ID of the distributed data object from the want parameter.
this.sessionId = want.parameters.session
Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`)
// Before fetching data from the remote device, reset g_object.data to undefined.
g_object.data = undefined;
// Set the session ID, so the target will fetch data from the remote device.
g_object.setSessionId(this.sessionId);
AppStorage.SetOrCreate<string>('ContinueStudy', g_object.data)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
### More Information
1. Timeout
- If the application to be continued is not installed on the target device, the system checks whether the application can be installed on it and waits for a response for 4 seconds. If no response is received within 4 seconds, the caller receives a timeout error code, which means that the application cannot be installed on the target device. If the application can be installed, the system prompts the consumer to install the application on the target device. The consumer can initiate the continuation again after the installation.
- If the application to be continued has been installed on the target device, the system waits for a response to the continuation request for 20 seconds. If no response is received within 20 seconds, the caller receives a timeout error code, which means that the continuation fails.
2. By default, the system supports page stack information migration, which means that the page stack of the initiator will be automatically migrated to the target device. No adaptation is required.
### Restrictions
1. The continuation must be performed between the same ability, which means the same bundle name, module name, and ability name. For details, see [Application Package Structure Configuration File](../quick-start/module-configuration-file.md).
2. Currently, the application can only implement the continuation capability. The continuation action must be initiated by the system.
### Best Practice
For better user experience, you are advised to use the **wantParam** parameter to transmit data smaller than 100 KB and use distributed objects to transmit data larger than 100 KB.
<!--no_check-->
\ No newline at end of file
# Ability Development
## When to Use
Ability development in the [stage model](stage-brief.md) is significantly different from that in the FA model. The stage model requires you to declare the application package structure in the **module.json5** and **app.json5** files during application development. For details about the configuration file, see [Application Package Structure Configuration File](../quick-start/application-package-structure-stage.md). To develop an ability based on the stage model, implement the following logic:
- Create an ability that supports screen viewing and human-machine interaction. You must implement the following scenarios: ability lifecycle callbacks, obtaining ability configuration, requesting permissions, and notifying environment changes.
- Start an ability. You need to implement ability startup on the same device, on a remote device, or with a specified UI page.
- Call abilities. For details, see [Call Development](stage-call.md).
- Connect to and disconnect from a Service Extension ability. For details, see [Service Extension Ability Development](stage-serviceextension.md).
- Continue the ability on another device. For details, see [Ability Continuation Development](stage-ability-continuation.md).
### Launch Type
An ability can be launched in the **standard**, **singleton**, or **specified** mode, as configured by **launchType** in the **module.json5** file. Depending on the launch type, the action performed when the ability is started differs, as described below.
| Launch Type | Description |Action |
| ----------- | ------- |---------------- |
| multiton | Multi-instance mode| A new instance is started each time an ability starts.|
| singleton | Singleton mode | Default type. The ability has only one instance in the system. If an instance already exists when an ability is started, that instance is reused.|
| specified | Instance-specific| The internal service of an ability determines whether to create multiple instances during running.|
By default, the singleton mode is used. The following is an example of the **module.json5** file:
```json
{
"module": {
"abilities": [
{
"launchType": "singleton",
}
]
}
}
```
## Creating an Ability
### Available APIs
The table below describes the APIs provided by the **AbilityStage** class, which has the **context** attribute. For details about the APIs, see [AbilityStage](../reference/apis/js-apis-app-ability-abilityStage.md).
**Table 1** AbilityStage APIs
|API|Description|
|:------|:------|
|onCreate(): void|Called when an ability stage is created.|
|onAcceptWant(want: Want): string|Called when a specified ability is started.|
|onConfigurationUpdated(config: Configuration): void|Called when the global configuration is updated.|
The table below describes the APIs provided by the **Ability** class. For details about the APIs, see [UIAbility](../reference/apis/js-apis-app-ability-uiAbility.md).
**Table 2** Ability APIs
|API|Description|
|:------|:------|
|onCreate(want: Want, param: AbilityConstant.LaunchParam): void|Called when an ability is created.|
|onDestroy(): void|Called when the ability is destroyed.|
|onWindowStageCreate(windowStage: window.WindowStage): void|Called when a **WindowStage** is created for the ability. You can use the **window.WindowStage** APIs to implement operations such as page loading.|
|onWindowStageDestroy(): void|Called when the **WindowStage** is destroyed for the ability.|
|onForeground(): void|Called when the ability is switched to the foreground.|
|onBackground(): void|Called when the ability is switched to the background.|
|onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void|Called when the ability launch type is set to **singleton**.|
|onConfigurationUpdated(config: Configuration): void|Called when the configuration of the environment where the ability is running is updated.|
### Implementing AbilityStage and Ability Lifecycle Callbacks
To create Page abilities for an application in the stage model, you must implement the **AbilityStage** class and ability lifecycle callbacks, and use the **Window** class to set the pages. The sample code is as follows:
1. Import the **AbilityStage** module.
```ts
import AbilityStage from "@ohos.app.ability.AbilityStage";
```
2. Implement the **AbilityStage** class. The default relative path generated by the APIs is **entry\src\main\ets\Application\AbilityStage.ts**.
```ts
export default class MyAbilityStage extends AbilityStage {
onCreate() {
console.log("MyAbilityStage onCreate")
}
}
```
3. Import the **Ability** module.
```js
import UIAbility from '@ohos.app.ability.UIAbility';
```
4. Implement the lifecycle callbacks of the **UIAbility** class. The default relative path generated by the APIs is **entry\src\main\ets\entryability\EntryAbility.ts**.
In the **onWindowStageCreate(windowStage)** API, use **loadContent** to set the application page to be loaded. For details about how to use the **Window** APIs, see [Window Development](../windowmanager/application-window-stage.md).
```ts
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.log("EntryAbility onCreate")
}
onDestroy() {
console.log("EntryAbility onDestroy")
}
onWindowStageCreate(windowStage) {
console.log("EntryAbility onWindowStageCreate")
windowStage.loadContent("pages/index").then(() => {
console.log("EntryAbility load content succeed")
}).catch((error) => {
console.error("EntryAbility load content failed with error: " + JSON.stringify(error))
})
}
onWindowStageDestroy() {
console.log("EntryAbility onWindowStageDestroy")
}
onForeground() {
console.log("EntryAbility onForeground")
}
onBackground() {
console.log("EntryAbility onBackground")
}
}
```
### Obtaining AbilityStage and Ability Configurations
Both the **AbilityStage** and **Ability** classes have the **context** attribute. An application can obtain the context of an **Ability** instance through **this.context** to obtain the configuration details.
The following example shows how an application obtains the bundle code directory, HAP file name, ability name, and system language through the **context** attribute in the **AbilityStage** class. The sample code is as follows:
```ts
import AbilityStage from "@ohos.app.ability.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)
}
}
```
The following example shows how an application obtains the bundle code directory, HAP file name, ability name, and system language through the **context** attribute in the **Ability** class. The sample code is as follows:
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onCreate(want, launchParam) {
console.log("EntryAbility onCreate")
let context = this.context
console.log("EntryAbility bundleCodeDir" + context.bundleCodeDir)
let abilityInfo = this.context.abilityInfo;
console.log("EntryAbility ability bundleName" + abilityInfo.bundleName)
console.log("EntryAbility ability name" + abilityInfo.name)
let config = this.context.config
console.log("EntryAbility config language" + config.language)
}
}
```
### Notifying of Environment Changes
Environment changes include changes of global configurations and ability configurations. Currently, the global configurations include the system language and color mode. The change of global configurations is generally triggered by configuration items in **Settings** or icons in **Control Panel**. The ability configuration is specific to a single **Ability** instance, including the display ID, screen resolution, and screen orientation. The configuration is related to the display where the ability is located, and the change is generally triggered by the window. Before configuring a project, define the project in the [Configuration](../reference/apis/js-apis-application-configuration.md) class.
For an application in the stage model, when the configuration changes, its abilities are not restarted, but the **onConfigurationUpdated(config: Configuration)** callback is triggered. If the application needs to perform processing based on the change, you can override **onConfigurationUpdated**. Note that the **Configuration** object in the callback contains all the configurations of the current ability, not only the changed configurations.
The following example shows the implementation of the **onConfigurationUpdated** callback in the **AbilityStage** class. The callback is triggered when the system language and color mode are changed.
```ts
import AbilityStage from '@ohos.app.ability.AbilityStage';
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
export default class MyAbilityStage extends AbilityStage {
onConfigurationUpdated(config) {
if (config.colorMode === ConfigurationConstant.ColorMode.COLOR_MODE_DARK) {
console.log('colorMode changed to dark')
}
}
}
```
The following example shows the implementation of the **onConfigurationUpdated** callback in the **Ability** class. The callback is triggered when the system language, color mode, or display parameters (such as the direction and density) change.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant';
export default class EntryAbility extends UIAbility {
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}`)
}
}
}
```
## Starting an Ability
### Available APIs
The **Ability** class has the **context** attribute, which belongs to the **AbilityContext** class. The **AbilityContext** class has the **abilityInfo**, **currentHapModuleInfo**, and other attributes as well as the APIs used for starting abilities. For details, see [AbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md).
**Table 3** AbilityContext APIs
|API|Description|
|:------|:------|
|startAbility(want: Want, callback: AsyncCallback\<void>): void|Starts an ability.|
|startAbility(want: Want, options?: StartOptions): Promise\<void>|Starts an ability.|
|startAbilityWithAccount(want: Want, accountId: number, callback: AsyncCallback\<void>): void|Starts an ability with the account ID.|
|startAbilityWithAccount(want: Want, accountId: number, options?: StartOptions): Promise\<void>|Starts an ability with the account ID.|
|startAbilityForResult(want: Want, callback: AsyncCallback\<AbilityResult>): void|Starts an ability with the returned result.|
|startAbilityForResult(want: Want, options?: StartOptions): Promise\<AbilityResult>|Starts an ability with the returned result.|
|startAbilityForResultWithAccount(want: Want, accountId: number, callback: AsyncCallback\<AbilityResult>): void|Starts an ability with the execution result and account ID.|
|startAbilityForResultWithAccount(want: Want, accountId: number, options?: StartOptions): Promise\<AbilityResult>|Starts an ability with the execution result and account ID.|
### Starting an Ability on the Same Device
An application can obtain the context of an **Ability** instance through **this.context** and then use the **startAbility** API in the **AbilityContext** class to start the ability. The ability can be started by specifying **Want**, **StartOptions**, and **accountId**, and the operation result can be returned using a callback or **Promise** instance. The sample code is as follows:
```ts
let context = this.context
let want = {
"deviceId": "",
"bundleName": "com.example.MyApplication",
"abilityName": "EntryAbility"
};
context.startAbility(want).then(() => {
console.log("Succeed to start ability")
}).catch((error) => {
console.error("Failed to start ability with error: "+ JSON.stringify(error))
})
```
### Starting an Ability on a Remote Device
>This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications.
In the cross-device scenario, you must specify the ID of the remote device. The sample code is as follows:
```ts
let context = this.context
let want = {
"deviceId": getRemoteDeviceId(),
"bundleName": "com.example.MyApplication",
"abilityName": "EntryAbility"
};
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))
})
```
Obtain the ID of a specified device from **DeviceManager**. The sample code is as follows:
```ts
import deviceManager from '@ohos.distributedHardware.deviceManager';
function getRemoteDeviceId() {
if (typeof dmClass === 'object' && dmClass !== null) {
let list = dmClass.getTrustedDeviceListSync();
if (typeof (list) === 'undefined' || typeof (list.length) === 'undefined') {
console.log("EntryAbility onButtonClick getRemoteDeviceId err: list is null");
return;
}
console.log("EntryAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId);
return list[0].deviceId;
} else {
console.log("EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null");
}
}
```
Request the permission **ohos.permission.DISTRIBUTED_DATASYNC** from consumers. This permission is used for data synchronization. For details about the sample code for requesting permissions, see [abilityAccessCtrl.requestPermissionsFromUse](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9).
### Starting an Ability with the Specified Page
If the launch type of an ability is set to **singleton** and the ability has been started, the **onNewWant** callback is triggered when the ability is started again. You can pass start options through the **want**. For example, to start an ability with the specified page, use the **uri** or **parameters** parameter in the **want** to pass the page information. Currently, the ability in the stage model cannot directly use the **router** capability. You must pass the start options to the custom component and invoke the **router** method to display the specified page during the custom component lifecycle management. The sample code is as follows:
When using **startAbility** to start an ability again, use the **uri** parameter in the **want** to pass the page information.
```ts
async function reStartAbility() {
try {
await this.context.startAbility({
bundleName: "com.sample.MyApplication",
abilityName: "EntryAbility",
uri: "pages/second"
})
console.log('start ability succeed')
} catch (error) {
console.error(`start ability failed with ${error.code}`)
}
}
```
Obtain the **want** parameter that contains the page information from the **onNewWant** callback of the ability.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
export default class EntryAbility extends UIAbility {
onNewWant(want, launchParams) {
globalThis.newWant = want
}
}
```
Obtain the **want** parameter that contains the page information from the custom component and process the route based on the 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 Model Overview
## Design Ideas
The stage model is designed to provide a better application development mode in the distributed environment.
The following figure shows the design ideas of the stage model.
![stagedesign](figures/stagedesign.png)
The stage model is designed based on the following considerations:
- Efficient management of application processes
As the device memory becomes larger, the number of processes concurrently running in the system increases. If the number of concurrent processes reaches several hundreds, the overall power consumption and performance of the system will be adversely affected without effective management measures. To restrict the behavior of background processes, the stage model uses four measures: transient task, continuous task, agent task, and Work Scheduler task. With these measures, foreground processes will obtain guaranteed resources, thereby delivering a better user experience.
- Native support for cross-device migration and multi-device collaboration
OpenHarmony is a native distributed OS. Its application framework must be designed for easier component migration and collaboration across devices. The stage model achieves this design objective by providing features such as separation between ability and UI as well as integration of UI display and service capabilities.
- Different window forms for various device types
The stage model redefines the ability lifecycle. In terms of architecture, the component manager and window manager are decoupled. This facilitates adaptation between window forms and device types.
## Basic Concepts
The following figure shows the basic concepts in the stage model.
![stageconcept](figures/stageconcept.png)
- **HAP**: basic unit for building, distributing, and loading OpenHarmony applications. Each HAP corresponds to a module in the development state. In an application, **moduleName** uniquely identifies a module.
- **Bundle**: an OpenHarmony application identified by **appid**. A bundle can contain multiple HAP files. Each application has a **bundleName**. However, **bundleName** must be used together with **appid** and other information to uniquely identify an application.
- **AbilityStage**: runtime object of an HAP. It is created when the HAP is loaded to the process for the first time and is visible to developers in the runtime.
- **Application**: runtime object of a bundle. It is invisible to developers in the runtime.
- **Context**: base class that provides APIs in the runtime to obtain information such as the bundle name, module name, and path. The **Context** classes of the Ability and ExtensionAbility components inherit from this class.
- **Ability**: provides lifecycle callbacks, holds the ability context, and supports cross-device component migration and multi-device collaboration.
- **ExtensionAbility**: general name of scenario-based Extension abilities. The system defines multiple scenario-based **ExtensionAbility** classes, each of which has its own **ExtensionContext**.
- **WindowStage**: local window manager.
- **Window**: application window, which holds an ArkUI engine instance.
- **ArkUI Page**: UI developed based on ArkUI.
## Lifecycle
The ability and ability stage are key objects in the application lifecycle.
For details about the lifecycle differences between the stage model and FA model, see [Ability Framework Overview](ability-brief.md). This section focuses on the ability lifecycle transition and the scheduling relationships between the ability, ability stage, and window stage.
![stageabilitylifecyclecallback](figures/stageabilitylifecyclecallback.png)
To implement device adaptation and multi-window scalability, OpenHarmony decouples the component manager from the window manager.
The ability lifecycle defined in the stage model includes only the creation, destruction, foreground, and background states. The gain focus and lose focus states that are closely related to UI are defined in the window stage. This implements weak coupling between the abilities and windows. On the service side, the window manager notifies the component manager of the foreground and background state changes, so the component manager only senses the foreground and background state changes but not the focus changes.
There are two lifecycle states related to the window stage in **Ability**: **onWindowStageCreate** and **onWindowStageDestroy**. They are valid only for devices with the display capability. **onWindowStageCreate** is invoked when a window stage is created, where you can call **loadContent** to set pages to be loaded for the ability. **onWindowStageDestroy** is invoked when the window stage is destroyed, where you can release resources.
## Ability Instances and Missions
Abilities can be started in any of the following modes:
* **Singleton**: For each type of ability, only one instance exists in the application process. **Ability1** in the figure below is started in singleton mode.
* **Standard**: Each time **startAbility** is called, an instance of the specified ability type is created in the application process. **Ability2** in the figure below is started in standard mode.
* **Specified**: Before creating an **Ability** instance, you can create a key for the instance. Each time **startAbility** is called, the system asks the application which ability instance (corresponding to a key) will be used. **Ability3** in the figure below is started in specified mode.
Each **Ability** instance corresponds to a mission in **Recents**.
The mission corresponding to an ability instance has a snapshot of the ability instance. After the ability instance is destroyed, the ability class information and snapshot are retained in the mission until the user deletes the information or the storage space reaches the upper limit.
![AbilityComponentInstanceMission](figures/AbilityComponentInstanceMission.png)
## ExtensionAbility Mechanism
Different from the ability used for UI display, ExtensionAbility provides a restricted running environment.
ExtensionAbility has the following features:
- Its process runs independently from the main process but shares the same storage sandbox with the main process. There is no inter-process communication (IPC) between the ExtensionAbility process and the main process.
- It has an independent context that provides scenario-specific APIs.
- It is created by the system, rather than by applications.
- The lifecycles of the ExtensionAbility component and process are managed by the system.
The following figure uses the widget an example. **FormExtensionAbility** is the base class. You can inherit from this class to provide widget information. The lifecycle of the **FormExtensionAbility** instance and that of the ExtensionAbility process where the instance is located are managed by a system service named **FormManagerService**.
![ExtensionAbility](figures/ExtensionAbility.png)
## Process Model
OpenHarmony forces strong control policies on application processes. No APIs are provided to configure multiple processes. All application processes are created and managed by the system.
The processes of an application can be classified into three types:
- Main process: runs the **UIAbility** component, UI, and service logic.
- Extension process: runs classes derived from **ExtensionAbility** in the application. The lifecycle of this process is managed by a scenario-specific system service.
- Render process: created for the WebView and used to load the WebView rendering library.
The following figure shows the process model of an application.
![stageprocessmodel](figures/stageprocessmodel.png)
## Application Package Structure
For details about the project directory structure of the stage model, see [OpenHarmony Project Overview](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-project-overview-0000001218440650#section56487581904).
For details about how to configure the application package structure of the stage model, see [Application Package Structure Configuration File](../quick-start/application-configuration-file-overview-stage.md).
# Ability Call Development
## When to Use
Ability call is an extension of the ability capability. It enables an ability to be invoked by and communicate with external systems. The ability invoked can be either started in the foreground or created and run in the background. You can use the ability call to implement data sharing between two abilities (caller ability and callee ability) through inter-process communication (IPC).
The core API used for the ability call is **startAbilityByCall**, which differs from **startAbility** in the following ways:
- **startAbilityByCall** supports ability startup in the foreground and background, whereas **startAbility** supports ability startup in the foreground only.
- The caller ability can use the **Caller** object returned by **startAbilityByCall** to communicate with the callee ability, but **startAbility** does not provide the communication capability.
Ability call is usually used in the following scenarios:
- Communicating with the callee ability
- Starting the callee ability in the background
**Table 1** Terms used in the ability call
|Term|Description|
|:------|:------|
|Caller ability|Ability that triggers the ability call.|
|Callee ability|Ability invoked by the ability call.|
|Caller |Object returned by **startAbilityByCall** and used by the caller ability to communicate with the callee ability.|
|Callee |Object held by the callee ability to communicate with the caller ability.|
|IPC |Inter-process communication.|
The ability call process is as follows:
- The caller ability uses **startAbilityByCall** to obtain a **Caller** object and uses **call()** of the **Caller** object to send data to the callee ability.
- The callee ability, which holds a **Callee** object, uses **on()** of the **Callee** object to register a callback. This callback is invoked when the callee ability receives data from the caller ability.
![stage-call](figures/stage-call.png)
> **NOTE**
>
> The launch type of the callee ability must be **singleton**.
>
> Currently, only system applications can use the ability call.
## Available APIs
The table below describes the ability call APIs. For details, see [UIAbility](../reference/apis/js-apis-app-ability-uiAbility.md#caller).
**Table 2** Ability call APIs
|API|Description|
|:------|:------|
|startAbilityByCall(want: Want): Promise\<Caller>|Starts an ability in the foreground (through the **want** configuration) or background (default) and obtains the **Caller** object for communication with the ability. For details, see [AbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartabilitybycall) or **ServiceExtensionContext**.|
|on(method: string, callback: CalleeCallBack): void|Callback invoked when the callee ability registers a method.|
|off(method: string): void|Callback invoked when the callee ability deregisters a method.|
|call(method: string, data: rpc.Sequenceable): Promise\<void>|Sends agreed sequenceable data to the callee ability.|
|callWithResult(method: string, data: rpc.Sequenceable): Promise\<rpc.MessageParcel>|Sends agreed sequenceable data to the callee ability and obtains the agreed sequenceable data returned by the callee ability.|
|release(): void|Releases the **Caller** object.|
|on(type: "release", callback: OnReleaseCallback): void|Callback invoked when the **Caller** object is released.|
## How to Develop
The procedure for developing the ability call is as follows:
1. Create a callee ability.
2. Access the callee ability.
### Creating a Callee Ability
For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener.
1. **Configure the ability launch type.**
Set **launchType** of the callee ability to **singleton** in the **module.json5** file.
|JSON Field|Description|
|:------|:------|
|"launchType"|Ability launch type. Set this parameter to **singleton**.|
An example of the ability configuration is as follows:
```json
"abilities":[{
"name": ".CalleeAbility",
"srcEntry": "./ets/CalleeAbility/CalleeAbility.ts",
"launchType": "singleton",
"description": "$string:CalleeAbility_desc",
"icon": "$media:icon",
"label": "$string:CalleeAbility_label",
"exported": true
}]
```
2. **Import the UIAbility module.**
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
```
3. **Define the agreed sequenceable data.**
The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. The code snippet is as follows:
```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. **Implement Callee.on and Callee.off.**
The time to register a listener for the callee ability depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The code snippet is as follows:
```ts
const TAG: string = '[CalleeAbility]'
const MSG_SEND_METHOD: string = 'CallSendMsg'
function sendMsgCallback(data) {
console.log('CalleeSortFunc called')
// Obtain the sequenceable data sent by the caller ability.
let receivedData = new MySequenceable(0, '')
data.readSequenceable(receivedData)
console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
// Process the data.
// Return the sequenceable data result to the caller ability.
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)}`)
}
}
}
```
### Accessing the Callee Ability
1. **Import the Ability module.**
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
```
2. **Obtain the Caller object.**
The **context** attribute of the ability implements **startAbilityByCall** to obtain the **Caller** object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the **Caller** object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. The code snippet is as follows:
```ts
// Register the onRelease listener of the caller ability.
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}`)
}
}
```
In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows:
```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')
// Register the onRelease listener of the caller ability.
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}`)
})
}
```
Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The code snippet is as follows:
```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("EntryAbility onButtonClick getRemoteDeviceId err: list is null")
return
}
console.log("EntryAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
return list[0].deviceId
} else {
console.log("EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null")
}
}
```
In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows:
```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. **Send agreed sequenceable data.**
The sequenceable data can be sent to the callee ability with or without a return value. The method and sequenceable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. The code snippet is as follows:
```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}`)
}
}
```
In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. The code snippet is as follows:
```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. **Release the Caller object.**
When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows:
```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
# Service Extension Ability Development
## When to Use
`ExtensionAbility` is the base class of the new Extension component in the stage model. It is used to process missions without UIs. The lifecycle of an Extension ability is simple and does not involve foreground or background states. `ServiceExtensionAbility` is extended from `ExtensionAbility`.
You can customize a class that inherits from `ServiceExtensionAbility` and override the lifecycle callbacks in the base class to perform service logic operations during the initialization, connection, and disconnection processes.
## Available APIs
**Table 1** ServiceExtensionAbility lifecycle APIs
|API|Description|
|:------|:------|
|onCreate(want: Want): void|Called for the initialization when `startAbility` or `connectAbility` is invoked for a given ability for the first time.|
|onRequest(want: Want, startId: number): void|Called each time `startAbility` is invoked for a given ability. The initial value of `startId` is `1`, and the value is incremented by one each time `startAbility` is invoked for that ability.|
|onConnect(want: Want): rpc.RemoteObject|Called when `connectAbility` is invoked for a given ability. This callback is not invoked for repeated calling of `connectAbility` for a specific ability. However, it will be invoked unless `connectAbility` is called after the ability has been disconnected using `disconnectAbility`. The returned result is a `RemoteObject`.|
|onDisconnect(want: Want): void|Called when `disconnectAbility` is called for a given ability. If the Extension ability is started by `connectAbility` and is not bound to other applications, the `onDestroy` callback will also be triggered to destroy the Extension ability.|
|onDestroy(): void|Called when `terminateSelf` is invoked to terminate the ability.|
## Constraints
OpenHarmony does not support creation of a Service Extension ability for third-party applications.
## How to Develop
1. Declare the Service Extension ability in the `module.json5` file by setting its `type` attribute to `service`. The following is a configuration example of the `module.json5` file:
```json
"extensionAbilities":[{
"name": "ServiceExtAbility",
"icon": "$media:icon",
"description": "service",
"type": "service",
"exported": true,
"srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ts"
}]
```
2. Customize a class that inherits from `ServiceExtensionAbility` in the .ts file in the directory where the Service Extension ability is defined (`entry\src\main\ets\ServiceExtAbility\ServiceExtAbility.ts` by default) and override the lifecycle callbacks of the base class. The code sample is as follows:
```js
import ServiceExtensionAbility from '@ohos.app.ability.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');
}
}
```
# WantAgent Development
## When to Use
The **WantAgent** class encapsulates want information that specifies a particular action, which can be starting an ability or publishing a common event. You can either call **wantAgent.trigger** to trigger a **WantAgent** directly or add a **WantAgent** to a notification so that it will be triggered when users tap the notification.
## Available APIs
| API | Description|
| ---------------------------------------------------------------------------------------------- | ----------- |
| getWantAgentInfo(info: WantAgentInfo, callback: AsyncCallback\<WantAgent\>) | Creates a **WantAgent** object. This API uses an asynchronous callback to return the result.|
| getWantAgent(info: WantAgentInfo): Promise\<WantAgent\> | Creates a **WantAgent** object. This API uses a promise to return the result.|
| trigger(agent: WantAgent, triggerInfo: TriggerInfo, callback?: Callback\<CompleteData\>) | Triggers a **WantAgent** object.|
## How to Develop
1. Import the **WantAgent** module.
```ts
import wantAgent from '@ohos.app.ability.wantAgent';
```
2. Create a **WantAgentInfo** object that will be used for starting an ability. For details about the data types and parameters of **WantAgentInfo**, see [WantAgent](../reference/apis/js-apis-wantAgent.md#wantagentinfo).
```ts
private wantAgentObj = null // Save the WantAgent object created. It will be used to complete the trigger operations.
// wantAgentInfo
var wantAgentInfo = {
wants: [
{
deviceId: "",
bundleName: "com.example.test",
abilityName: "com.example.test.EntryAbility",
action: "",
entities: [],
uri: "",
parameters: {}
}
],
operationType: wantAgent.OperationType.START_ABILITY,
requestCode: 0,
wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
}
```
3. Create a **WantAgentInfo** object for publishing a common event.
```ts
private wantAgentObj = null // Save the WantAgent object created. It will be used to complete the trigger operations.
// wantAgentInfo
var wantAgentInfo = {
wants: [
{
action: "event_name", // Set the action name.
parameters: {}
}
],
operationType: wantAgent.OperationType.SEND_COMMON_EVENT,
requestCode: 0,
wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
}
```
4. Create a **WantAgent** object and save the returned **wantAgentObj** for subsequent trigger operations.
```ts
// Create a WantAgent object.
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. Trigger the **WantAgent** object.
```ts
// Trigger the WantAgent object.
var triggerInfo = {
code:0
}
wantAgent.trigger(wantAgentObj, triggerInfo, (completeData) => {
console.log("[WantAgent]getWantAgent success, completeData: ", + JSON.stringify(completeData))
})
```
...@@ -54,7 +54,7 @@ Widget-related configuration includes **FormExtensionAbility** configuration and ...@@ -54,7 +54,7 @@ Widget-related configuration includes **FormExtensionAbility** configuration and
| formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)| | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)|
| metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)| | metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)|
| dataProxyEnabled | Whether the widget supports the [update-through-proxy](./arkts-ui-widget-update-by-proxy.md) feature.<br>- **true**: The widget supports the update-through-proxy feature.<br>- **false**: The widget does not support the update-through-proxy feature.<br>If this tag is set to **true**, the settings for the scheduled update time will still take effect, but the settings for the update interval and next update time will not.| Boolean| Yes (initial value: **false**)| | dataProxyEnabled | Whether the widget supports the [update-through-proxy](./arkts-ui-widget-update-by-proxy.md) feature.<br>- **true**: The widget supports the update-through-proxy feature.<br>- **false**: The widget does not support the update-through-proxy feature.<br>If this tag is set to **true**, the settings for the scheduled update time will still take effect, but the settings for the update interval and next update time will not.| Boolean| Yes (initial value: **false**)|
| isDynamic | Whether the widget is a dynamic widget. This tag only applies to ArkTS widgets.<br>- **true**: The widget is a dynamic widget.<br>- **false**: The widget is a static widget. In this case, the widget is displayed as a static image after being added.| Boolean| Yes (initial value: **true**)| | isDynamic | Whether the widget is a dynamic widget. This tag applies only to ArkTS widgets.<br>- **true**: The widget is a dynamic widget.<br>- **false**: The widget is a static widget.<br>**NOTE**<br>To reduce unnecessary memory resource overhead, widgets are classified as dynamic or static.<br>- Dynamic widgets support interaction events, such as **onClick**. They behave and respond differently based on the user's actions, such as tap and swipe.<br>- Static widgets do not support interaction events. They display static content that does not change and can only be redirected to a specified UIAbility through the [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md) component.| Boolean| Yes (initial value: **true**)|
**Table 2** Internal structure of the window object **Table 2** Internal structure of the window object
......
# Creating an ArkTS Widget # Creating an ArkTS Widget
To create an ArkTS widget in an existing application project, perform the following steps: You can create a widget in either of the following modes:
1. Create a widget. - When creating a project, select **Application**. The project created this way does not have a widget by default. You can right-click a module folder and choose **New** > **Service Widget** to create a widget.
- When creating a project, select **Atomic Service**. The project created this way has a widget by default. You can right-click a module folder and choose **New** > **Service Widget** to create a widget.
![WidgetCreateProject](figures/WidgetCreateProject.png)
To create an ArkTS widget in an existing project, perform the following steps:
1. Right-click a module folder and choose **New** > **Service Widget**.
![WidgetProjectCreate1](figures/WidgetProjectCreate1.png) ![WidgetProjectCreate1](figures/WidgetProjectCreate1.png)
...@@ -14,6 +21,6 @@ To create an ArkTS widget in an existing application project, perform the follow ...@@ -14,6 +21,6 @@ To create an ArkTS widget in an existing application project, perform the follow
![WidgetProjectCreate3](figures/WidgetProjectCreate3.png) ![WidgetProjectCreate3](figures/WidgetProjectCreate3.png)
After an ArkTS widget is created, the following widget-related files are automatically added to the project directory: **EntryFormAbility.ts** (widget lifecycle management file), **WidgetCard.ets** (widget page file), and **form_config.json** (widget configuration file). After an ArkTS widget is created, the following widget-related files are automatically added to the project directory: **EntryFormAbility.ets** (widget lifecycle management file), **WidgetCard.ets** (widget page file), and **form_config.json** (widget configuration file).
![WidgetProjectView](figures/WidgetProjectView.png) ![WidgetProjectView](figures/WidgetProjectView.png)
\ No newline at end of file
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
There may be cases you want to provide in a widget access to features available in your application running in the foreground, for example, the play, pause, and stop buttons in a music application widget. This is where the **call** capability of the **postCardAction** API comes in handy. This capability, when used in a widget, can start the specified UIAbility of the widget provider in the background. It also allows the widget to call the specified method of the application and transfer data so that the application, while in the background, can behave accordingly in response to touching of the buttons on the widget. There may be cases you want to provide in a widget access to features available in your application running in the foreground, for example, the play, pause, and stop buttons in a music application widget. This is where the **call** capability of the **postCardAction** API comes in handy. This capability, when used in a widget, can start the specified UIAbility of the widget provider in the background. It also allows the widget to call the specified method of the application and transfer data so that the application, while in the background, can behave accordingly in response to touching of the buttons on the widget.
> **NOTE**
>
> This topic describes development for dynamic widgets. For static widgets, see [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md).
Typically, the call event is triggered for touching of buttons. Below is an example. Typically, the call event is triggered for touching of buttons. Below is an example.
...@@ -48,7 +51,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam ...@@ -48,7 +51,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam
} }
``` ```
- The UIAbility receives the call event and obtains the transferred parameters. It then executes the target method specified by the **method** parameter. Other data can be obtained in readString mode. Listen for the method required by the call event in the **onCreate** callback of the UIAbility. - The UIAbility receives the call event and obtains the transferred parameters. It then executes the target method specified by the **method** parameter. Other data can be obtained through the **[readString](../reference/apis/js-apis-rpc.md#readstring)** method. Listen for the method required by the call event in the **onCreate** callback of the UIAbility.
```ts ```ts
import UIAbility from '@ohos.app.ability.UIAbility'; import UIAbility from '@ohos.app.ability.UIAbility';
...@@ -65,7 +68,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam ...@@ -65,7 +68,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam
} }
export default class CameraAbility extends UIAbility { export default class CameraAbility extends UIAbility {
// If the UIAbility is started for the first time, onCreate is triggered afte the call event is received. // If the UIAbility is started for the first time, onCreate is triggered after the call event is received.
onCreate(want, launchParam) { onCreate(want, launchParam) {
try { try {
// Listen for the method required by the call event. // Listen for the method required by the call event.
......
# Updating Widget Content Through the message Event # Updating Widget Content Through the message Event
> **NOTE**
>
> This topic describes development for dynamic widgets. For static widgets, see [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md).
On the widget page, the **postCardAction** API can be used to trigger a message event to start a FormExtensionAbility, which then updates the widget content. The following is an example of this widget update mode. On the widget page, the **postCardAction** API can be used to trigger a message event to start a FormExtensionAbility, which then updates the widget content. The following is an example of this widget update mode.
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the FormExtensionAbility. - On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the FormExtensionAbility. Use [LocalStorageProp](../quick-start/arkts-localstorage.md#localstorageprop) to decorate the widget data to be updated.
```ts ```ts
let storage = new LocalStorage(); let storage = new LocalStorage();
@Entry(storage) @Entry(storage)
@Component @Component
struct WidgetCard { struct WidgetCard {
@LocalStorageProp('title') title: string = 'init'; @LocalStorageProp('title') title: string = 'Title default';
@LocalStorageProp('detail') detail: string = 'init'; @LocalStorageProp('detail') detail: string = 'Description default';
build() { build() {
Column() { Column() {
Button ('Update') Column() {
Text(`${this.title}`)
.margin(5).fontWeight(FontWeight.Medium).fontSize('14fp')
Text(`${this.detail}`)
.margin(5).fontColor(Color.Gray).fontSize('12fp').height('25%')
}.width('100%').alignItems(HorizontalAlign.Start)
Button('UPDATE')
.margin(15).width('90%')
.onClick(() => { .onClick(() => {
postCardAction(this, { postCardAction(this, {
'action': 'message', 'action': 'message',
...@@ -25,11 +35,7 @@ On the widget page, the **postCardAction** API can be used to trigger a message ...@@ -25,11 +35,7 @@ On the widget page, the **postCardAction** API can be used to trigger a message
} }
}); });
}) })
Text(`${this.title}`) }.width('90%').height('90%').margin('5%')
Text(`${this.detail}`)
}
.width('100%')
.height('100%')
} }
} }
``` ```
...@@ -42,25 +48,24 @@ On the widget page, the **postCardAction** API can be used to trigger a message ...@@ -42,25 +48,24 @@ On the widget page, the **postCardAction** API can be used to trigger a message
import formProvider from '@ohos.app.form.formProvider'; import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility { export default class EntryFormAbility extends FormExtensionAbility {
onFormEvent(formId, message) { onFormEvent(formId: string, message: string) {
// Called when a specified message event defined by the form provider is triggered. // Called when a specified message event defined by the form provider is triggered.
console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`); console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
let formData = { let formData = new Map<Object, string>();
'title':'Title Update Success.', // Matches the widget layout. formData.set('title', 'Title Update.'); // It matches the widget layout.
'detail':'Detail Update Success.', // Matches the widget layout. formData.set('detail', 'Description update success.'); // It matches the widget layout.
};
let formInfo = formBindingData.createFormBindingData(formData) let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => { formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data)); console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
}) })
} }
... ...
} }
``` ```
The figure below shows the effect. The figure below shows the effect.
![WidgetUpdatePage](figures/WidgetUpdatePage.png) | Initial State | After Clicking |
| ------------------------------------------------------- | ----------------------------------------------------- |
| ![WidgetUpdateBefore](figures/widget-update-before.PNG) | ![WidgetUpdateAfter](figures/widget-update-after.PNG) |
# Widget Event Capability Overview # Widget Event Capability Overview
The ArkTS widget provides the **postCardAction()** API for interaction between the widget internal and the widget provider. Currently, this API supports the router, message, and call events and can be called only in the widget. For dynamic ATS widgets, the **postCardAction()** API is provided for interaction between the widget internal and the provider application. Currently, this API supports the router, message, and call events and can be called only in the widget.
For static ATS widgets, the [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md) component is provided for interaction between the widget internal and the provider application.
## Dynamic Widget Event Capability
![WidgetPostCardAction](figures/WidgetPostCardAction.png) ![WidgetPostCardAction](figures/WidgetPostCardAction.png)
**Definition**: postCardAction(component: Object, action: Object): void **Definition**: postCardAction(component: Object, action: Object): void
...@@ -23,8 +26,11 @@ The ArkTS widget provides the **postCardAction()** API for interaction between t ...@@ -23,8 +26,11 @@ The ArkTS widget provides the **postCardAction()** API for interaction between t
| "bundleName" | string | Name of the target bundle when **action** is **"router"** or **"call"**. This parameter is optional.| | "bundleName" | string | Name of the target bundle when **action** is **"router"** or **"call"**. This parameter is optional.|
| "moduleName" | string | Name of the target module when **action** is **"router"** or **"call"**. This parameter is optional.| | "moduleName" | string | Name of the target module when **action** is **"router"** or **"call"**. This parameter is optional.|
| "abilityName" | string | Name of the target UIAbility when **action** is **"router"** or **"call"**. This parameter is mandatory.| | "abilityName" | string | Name of the target UIAbility when **action** is **"router"** or **"call"**. This parameter is mandatory.|
| "params" | Object | Additional parameters carried in the current action. The value is a key-value pair in JSON format. For the **"call"** action type, the **method** parameter (mandatory) must be set and its value type must be string.| | "params" | Object | Additional parameters carried in the current action. The value is a key-value pair in JSON format. This parameter is mandatory.|
>**NOTE**
>
>When **action** is **"router"** or **"call"**, **'method'** of the string type must be passed to **params** to trigger the corresponding method in the UIAbility.
Sample code of the **postCardAction()** API: Sample code of the **postCardAction()** API:
...@@ -59,5 +65,5 @@ Button ('Start in Background') ...@@ -59,5 +65,5 @@ Button ('Start in Background')
}) })
``` ```
## Static Widget Event Capability
Read on to learn the typical widget development scenarios that can be implemented through widget events. See [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md).
...@@ -4,8 +4,11 @@ The **router** capability of the **postCardAction** API can be used in a widget ...@@ -4,8 +4,11 @@ The **router** capability of the **postCardAction** API can be used in a widget
![WidgerCameraCard](figures/WidgerCameraCard.png) ![WidgerCameraCard](figures/WidgerCameraCard.png)
> **NOTE**
>
> This topic describes development for dynamic widgets. For static widgets, see [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md).
Generally, a button is used to start a page. Generally, a button is used to start a page. Below is an example:
- Design two buttons on the widget page. When one of the buttons is clicked, **postCardAction** is called to send a router event to the specified UIAbility, with the content to be transferred defined in the event. - Design two buttons on the widget page. When one of the buttons is clicked, **postCardAction** is called to send a router event to the specified UIAbility, with the content to be transferred defined in the event.
...@@ -57,7 +60,7 @@ Generally, a button is used to start a page. ...@@ -57,7 +60,7 @@ Generally, a button is used to start a page.
let selectPage = ""; let selectPage = "";
let currentWindowStage = null; let currentWindowStage = null;
export default class CameraAbility extends UIAbility { export default class EntryAbility extends UIAbility {
// If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received. // If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received.
onCreate(want, launchParam) { onCreate(want, launchParam) {
// Obtain the targetPage parameter passed in the router event. // Obtain the targetPage parameter passed in the router event.
......
...@@ -5,7 +5,11 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -5,7 +5,11 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
## Updating Widget Content Through the router Event ## Updating Widget Content Through the router Event
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the router event to the FormExtensionAbility. > **NOTE**
>
> This topic describes development for dynamic widgets. For static widgets, see [FormLink](../../application-dev/reference/arkui-ts/ts-container-formlink.md).
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the router event to start the UIAbility.
```ts ```ts
let storage = new LocalStorage(); let storage = new LocalStorage();
...@@ -17,7 +21,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -17,7 +21,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
build() { build() {
Column() { Column() {
Button ('Redirect') Button ('Redirect')
.margin('20%') .margin(20)
.onClick(() => { .onClick(() => {
console.info('postCardAction to EntryAbility'); console.info('postCardAction to EntryAbility');
postCardAction(this, { postCardAction(this, {
...@@ -28,7 +32,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -28,7 +32,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
} }
}); });
}) })
Text(`${this.detail}`).margin('20%') Text(`${this.detail}`)
} }
.width('100%') .width('100%')
.height('100%') .height('100%')
...@@ -47,31 +51,21 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -47,31 +51,21 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
export default class EntryAbility extends UIAbility { export default class EntryAbility extends UIAbility {
// If the UIAbility is started for the first time, onCreate is triggered after the router event is received. // If the UIAbility is started for the first time, onCreate is triggered after the router event is received.
onCreate(want, launchParam) { onCreate(want, launchParam) {
console.info('Want:' + JSON.stringify(want)); this.handleFormRouterEvent(want);
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message +': onCreate UIAbility.', // Matches the widget layout.
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
} }
// If the UIAbility is running in the background, onNewWant is triggered after the router event is received. // If the UIAbility is running in the background, onNewWant is triggered after the router event is received.
onNewWant(want, launchParam) { onNewWant(want, launchParam) {
console.info('onNewWant Want:' + JSON.stringify(want)); this.handleFormRouterEvent(want);
}
handleFormRouterEvent(want) {
console.info('Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) { if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY]; let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail; let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`); console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = { let formData = {
"detail": message +': onNewWant UIAbility.', // Matches the widget layout. "detail": message + ': UIAbility.', // It matches the widget layout.
}; };
let formMsg = formBindingData.createFormBindingData(formData) let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => { formProvider.updateForm(curFormId, formMsg).then((data) => {
...@@ -81,7 +75,6 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -81,7 +75,6 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
}) })
} }
} }
... ...
} }
``` ```
...@@ -108,7 +101,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o ...@@ -108,7 +101,7 @@ On the widget page, the **postCardAction** API can be used to trigger a router o
}; };
``` ```
- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the UIAbility. - On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to start the UIAbility.
```ts ```ts
let storage = new LocalStorage(); let storage = new LocalStorage();
......
# Widget Data Interaction # Widget Data Interaction
The ArkTS widget framework provides the **updateForm()** and **requestForm()** APIs to proactively trigger widget updates. The ArkTS widget framework provides the **updateForm()** and **requestForm()** APIs to proactively trigger widget updates. You can use [LocalStorageProp](../quick-start/arkts-localstorage.md#localstorageprop) to check the widget data to be updated.
![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png) ![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
When creating an ArkTS widget, you need to implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs. When creating an ArkTS widget, you need to implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs.
1. Import related modules to **EntryFormAbility.ts**. 1. Import related modules to **EntryFormAbility.ets**.
```ts ```ts
import formInfo from '@ohos.app.form.formInfo'; import formInfo from '@ohos.app.form.formInfo';
...@@ -13,7 +13,7 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility]( ...@@ -13,7 +13,7 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility](
import formProvider from '@ohos.app.form.formProvider'; import formProvider from '@ohos.app.form.formProvider';
``` ```
2. In **EntryFormAbility.ts**, implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs, including **onAddForm**, whose **want** parameter can be used to obtain the widget information through [FormParam](../reference/apis/js-apis-app-form-formInfo.md#formparam). 2. In **EntryFormAbility.ets**, implement the [[FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs, including **onAddForm**, whose **want** parameter can be used to obtain the widget information through [FormParam](../reference/apis/js-apis-app-form-formInfo.md#formparam).
```typescript ```typescript
import formInfo from '@ohos.app.form.formInfo'; import formInfo from '@ohos.app.form.formInfo';
...@@ -36,9 +36,8 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility]( ...@@ -36,9 +36,8 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility](
} }
onCastToNormalForm(formId) { onCastToNormalForm(formId) {
// Called when the form provider is notified that a temporary form is successfully // Called when the widget host converts the temporary widget into a normal one.
// converted to a normal form. // The widget provider should do something to respond to the conversion.
// Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion.
console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`); console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`);
} }
...@@ -60,20 +59,20 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility]( ...@@ -60,20 +59,20 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility](
} }
onChangeFormVisibility(newStatus) { onChangeFormVisibility(newStatus) {
// Called when the form provider receives form events from the system. // Called when the widget provider receives form events from the system.
// The callback is performed only when formVisibleNotify is set to true and the application is a system application. // The callback is triggered only when formVisibleNotify is set to true and the application is a system application.
console.info('[EntryFormAbility] onChangeFormVisibility'); console.info('[EntryFormAbility] onChangeFormVisibility');
} }
onFormEvent(formId, message) { onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered. // Called when a specified message event defined by the widget provider is triggered.
// If the widget supports event triggering, override this method and implement the trigger. // If the widget supports event triggering, override this method and implement the trigger.
console.info('[EntryFormAbility] onFormEvent'); console.info('[EntryFormAbility] onFormEvent');
} }
onRemoveForm(formId) { onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed. // Called to notify the widget provider that a specified widget has been destroyed.
// Called when the corresponding widget is deleted. The input parameter is the ID of the deleted card. // The input parameter is the ID of the deleted card.
console.info('[EntryFormAbility] onRemoveForm'); console.info('[EntryFormAbility] onRemoveForm');
} }
...@@ -83,8 +82,8 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility]( ...@@ -83,8 +82,8 @@ When creating an ArkTS widget, you need to implement the [FormExtensionAbility](
} }
onAcquireFormState(want) { onAcquireFormState(want) {
// Called to return a {@link FormState} object. // Called to return a FormState object upon a status query request
// Called when the widget provider receives the status query result of a widget. By default, the initial state of the widget is returned. // from the widget. By default, the initial widget state is returned.
return formInfo.FormState.READY; return formInfo.FormState.READY;
} }
} }
......
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
- [formBindingData](../reference/apis/js-apis-app-form-formBindingData.md): provides APIs for widget data binding. You can use the APIs to create a **FormBindingData** object and obtain related information. - [formBindingData](../reference/apis/js-apis-app-form-formBindingData.md): provides APIs for widget data binding. You can use the APIs to create a **FormBindingData** object and obtain related information.
- [Page layout (Card.ets)](arkts-ui-widget-page-overview.md): provides APIs for a declarative paradigm UI. - [Page layout (WidgetCard.ets)](arkts-ui-widget-page-overview.md): provides APIs for the declarative UI paradigm.
- [Capabilities exclusive to ArkTS widgets](arkts-ui-widget-event-overview.md): include the **postCardAction** API used for interaction between the widget internal and the provider application and can be called only in the widget. - [Capabilities exclusive to ArkTS widgets](arkts-ui-widget-event-overview.md): include the **postCardAction** API used for interaction between the widget internal and the provider application and can be called only in the widget.
- [ArkTS widget capability list](arkts-ui-widget-page-overview.md#page-capabilities-supported-by-arkts-widgets): contain the APIs, components, events, attributes, and lifecycle callbacks that can be used in ArkTS widgets. - [ArkTS widget capability list](arkts-ui-widget-page-overview.md): contain the APIs, components, events, attributes, and lifecycle callbacks that can be used in ArkTS widgets.
- [Widget configuration](arkts-ui-widget-configuration.md): includes FormExtensionAbility configuration and widget configuration. - [Widget configuration](arkts-ui-widget-configuration.md): includes FormExtensionAbility configuration and widget configuration.
- Configure the FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). - Configure the FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
......
...@@ -13,6 +13,10 @@ Compared with the [implementation of the ArkTS widget](../application-models/ark ...@@ -13,6 +13,10 @@ Compared with the [implementation of the ArkTS widget](../application-models/ark
- Data management service: provides a mechanism for data sharing among multiple applications. - Data management service: provides a mechanism for data sharing among multiple applications.
- Data provider: must be a system application that has data sharing enabled. The shared data is identified through the defined **key** + **subscriberId** combination. - Data provider: must be a system application that has data sharing enabled. The shared data is identified through the defined **key** + **subscriberId** combination.
> **NOTE**
>
> This feature can be used when the system provides applications as data providers and publicly available shared data identifiers.
Processing flow of the widget provider (indicated by the blue arrows in the figure): Processing flow of the widget provider (indicated by the blue arrows in the figure):
1. The widget provider sets the **dataProxyEnabled** field to **true** in the **form_config.json** file to enable the update-through-proxy feature. 1. The widget provider sets the **dataProxyEnabled** field to **true** in the **form_config.json** file to enable the update-through-proxy feature.
...@@ -29,7 +33,7 @@ Processing flow of the widget update proxy (indicated by the red arrows in the f ...@@ -29,7 +33,7 @@ Processing flow of the widget update proxy (indicated by the red arrows in the f
1. The data provider uses the **key** + **subscriberId** combination as the data ID to store data to the database. 1. The data provider uses the **key** + **subscriberId** combination as the data ID to store data to the database.
2. The data management service detects the change in the database and publishes the new data to all currently registered subscription instances. 2. The data management service detects the change in the database and publishes the new data to all currently registered subscription instances.
3. The Widget Manager parses data from the subscription instance and sends the data to the widget rendering service. 3. The Widget Manager parses data from the subscription instance and sends the data to the widget rendering service.
4. The widget rendering service runs the widget page code **widgets.abc**, renders based on the new data, and sends the rendered data to the widget component (../reference/arkui-ts/ts-basic-components-formcomponent.md) corresponding to the widget host. 4. The widget rendering service runs the widget page code **widgets.abc**, which implements rendering based on the new data and sends the rendered data to the [FormComponent](../reference/arkui-ts/ts-basic-components-formcomponent.md) corresponding to the widget host.
There are two types of shared data provided by the data provider: There are two types of shared data provided by the data provider:
......
# Configuring a Widget to Update Periodically # Configuring a Widget to Update Periodically
Before configuring a widget to update periodically, enable the periodic update feature by setting the **updateEnabled** field to **true** in the **form_config.json** file.
The widget framework provides the following modes of updating widgets periodically: The widget framework provides the following modes of updating widgets periodically:
...@@ -9,6 +7,8 @@ The widget framework provides the following modes of updating widgets periodical ...@@ -9,6 +7,8 @@ The widget framework provides the following modes of updating widgets periodical
> **NOTE** > **NOTE**
> >
> Before configuring a widget to update periodically, enable the periodic update feature by setting the **updateEnabled** field to **true** in the **form_config.json** file.
>
> **updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used. > **updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.
```json ```json
......
...@@ -13,13 +13,13 @@ ...@@ -13,13 +13,13 @@
- Widget Manager: a resident agent that manages widgets in the system. It provides the [formProvider](../reference/apis/js-apis-app-form-formProvider.md) and [formHost](../reference/apis/js-apis-app-form-formHost.md) APIs as well as the APIs for widget management, usage, and periodic updates. - Widget Manager: a resident agent that manages widgets in the system. It provides the [formProvider](../reference/apis/js-apis-app-form-formProvider.md) and [formHost](../reference/apis/js-apis-app-form-formHost.md) APIs as well as the APIs for widget management, usage, and periodic updates.
- Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [widget components](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host on a one-to-one basis. The widget rendering service runs the widget page code **widgets.abc** for rendering, and sends the rendered data to the corresponding widget component on the widget host. - Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [FormComponent](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host on a one-to-one basis. The widget rendering service runs the widget page code **widgets.abc** for rendering, and sends the rendered data to the corresponding [FormComponent](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host.
**Figure 2** Working principles of the ArkTS widget rendering service **Figure 2** Working principles of the ArkTS widget rendering service
![WidgetRender](figures/WidgetRender.png) ![WidgetRender](figures/WidgetRender.png)
Unlike JS widgets, ArkTS widgets support logic code execution. The widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of a widget provider run in the same virtual machine operating environment, and rendering instances of different widget providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different widget providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-ui-page) object. Use one **globalThis** object for widgets from the same widget provider, and different **globalThis** objects for widgets from different widget providers. Unlike JS widgets, ArkTS widgets support logic code execution. The widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of a widget provider run in the same ArkTS virtual machine operating environment, and rendering instances of different widget providers run in different ArkTS virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different widget providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-for-data-synchronization) object. Use one **globalThis** object for widgets from the same widget provider, and different **globalThis** objects for widgets from different widget providers.
## Advantages of ArkTS Widgets ## Advantages of ArkTS Widgets
......
...@@ -12,7 +12,7 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common ...@@ -12,7 +12,7 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
| createSubscriber(subscribeInfo: [CommonEventSubscribeInfo](../reference/apis/js-apis-commonEventManager.md#commoneventsubscribeinfo), callback: AsyncCallback&lt;[CommonEventData](../reference/apis/js-apis-commonEventManager.md#commoneventdata)&gt;): void | Creates a subscriber. This API uses an asynchronous callback to return the result.| | createSubscriber(subscribeInfo: [CommonEventSubscribeInfo](../reference/apis/js-apis-commonEventManager.md#commoneventsubscribeinfo), callback: AsyncCallback&lt;[CommonEventSubscriber](../reference/apis/js-apis-inner-commonEvent-commonEventSubscriber.md#usage)&gt;): void | Creates a subscriber. This API uses an asynchronous callback to return the result.|
| createSubscriber(subscribeInfo: CommonEventSubscribeInfo): Promise&lt;CommonEventSubscriber&gt; | Creates a subscriber. This API uses a promise to return the result.| | createSubscriber(subscribeInfo: CommonEventSubscribeInfo): Promise&lt;CommonEventSubscriber&gt; | Creates a subscriber. This API uses a promise to return the result.|
| subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback): void | Subscribes to common events.| | subscribe(subscriber: CommonEventSubscriber, callback: AsyncCallback): void | Subscribes to common events.|
......
...@@ -56,7 +56,7 @@ The **FormExtensionAbility** class has the following APIs. For details, see [For ...@@ -56,7 +56,7 @@ The **FormExtensionAbility** class has the following APIs. For details, see [For
| onFormEvent(formId: string, message: string): void | Called to instruct the widget provider to process a widget event.| | onFormEvent(formId: string, message: string): void | Called to instruct the widget provider to process a widget event.|
| onRemoveForm(formId: string): void | Called to notify the widget provider that a widget is being destroyed.| | onRemoveForm(formId: string): void | Called to notify the widget provider that a widget is being destroyed.|
| onConfigurationUpdate(config: Configuration): void | Called when the configuration of the environment where the widget is running is being updated.| | onConfigurationUpdate(config: Configuration): void | Called when the configuration of the environment where the widget is running is being updated.|
| onShareForm?(formId: string): { [key: string]: any } | Called to notify the widget provider that the widget host is sharing the widget data.| | onShareForm?(formId: string): { [key: string]: Object } | Called to notify the widget provider that the widget host is sharing the widget data.|
The **FormProvider** class has the following APIs. For details, see [FormProvider](../reference/apis/js-apis-app-form-formProvider.md). The **FormProvider** class has the following APIs. For details, see [FormProvider](../reference/apis/js-apis-app-form-formProvider.md).
...@@ -95,7 +95,7 @@ The widget provider development based on the [stage model](stage-model-developme ...@@ -95,7 +95,7 @@ The widget provider development based on the [stage model](stage-model-developme
To create a widget in the stage model, you need to implement the lifecycle callbacks of FormExtensionAbility. Generate a widget template and then perform the following: To create a widget in the stage model, you need to implement the lifecycle callbacks of FormExtensionAbility. Generate a widget template and then perform the following:
1. Import related modules to **EntryFormAbility.ts**. 1. Import related modules to **EntryFormAbility.ets**.
```ts ```ts
...@@ -106,7 +106,7 @@ To create a widget in the stage model, you need to implement the lifecycle callb ...@@ -106,7 +106,7 @@ To create a widget in the stage model, you need to implement the lifecycle callb
import dataPreferences from '@ohos.data.preferences'; import dataPreferences from '@ohos.data.preferences';
``` ```
2. Implement the FormExtension lifecycle callbacks in **EntryFormAbility.ts**. 2. Implement the FormExtension lifecycle callbacks in **EntryFormAbility.ets**.
```ts ```ts
...@@ -176,7 +176,7 @@ To create a widget in the stage model, you need to implement the lifecycle callb ...@@ -176,7 +176,7 @@ To create a widget in the stage model, you need to implement the lifecycle callb
"extensionAbilities": [ "extensionAbilities": [
{ {
"name": "EntryFormAbility", "name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ts", "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"label": "$string:EntryFormAbility_label", "label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc", "description": "$string:EntryFormAbility_desc",
"type": "form", "type": "form",
...@@ -547,6 +547,29 @@ The following are examples: ...@@ -547,6 +547,29 @@ The following are examples:
} }
``` ```
Note:
**JSON Value** in **data** supports multi-level nested data. When updating data, ensure that complete data is carried.
Assume that a widget is displaying the course information of Mr. Zhang on July 18, as shown in the following code snippet.
```ts
"data": {
"Day": "07.18",
"teacher": {
"name": "Mr.Zhang",
"course": "Math"
}
}
```
To update the widget content to the course information of Mr. Li on July 18, you must pass the complete data as follows, instead of only a single date item such as **name** or **course**:
```ts
"teacher": {
"name": "Mr.Li",
"course": "English"
}
```
- Receive the router event in UIAbility and obtain parameters. - Receive the router event in UIAbility and obtain parameters.
......
# HTTP Data Request # HTTP Data Request
## When to Use ## Overview
An application can initiate a data request over HTTP. Common HTTP methods include **GET**, **POST**, **OPTIONS**, **HEAD**, **PUT**, **DELETE**, **TRACE**, and **CONNECT**. An application can initiate a data request over HTTP. Common HTTP methods include **GET**, **POST**, **OPTIONS**, **HEAD**, **PUT**, **DELETE**, **TRACE**, and **CONNECT**.
...@@ -18,7 +18,7 @@ The following table provides only a simple description of the related APIs. For ...@@ -18,7 +18,7 @@ The following table provides only a simple description of the related APIs. For
| ----------------------------------------- | ----------------------------------- | | ----------------------------------------- | ----------------------------------- |
| createHttp() | Creates an HTTP request. | | createHttp() | Creates an HTTP request. |
| request() | Initiates an HTTP request to a given URL. | | request() | Initiates an HTTP request to a given URL. |
| request2()<sup>10+</sup> | Initiates an HTTP network request based on the URL and returns a streaming response.| | requestInStream()<sup>10+</sup> | Initiates an HTTP network request to a given URL and returns a streaming response.|
| destroy() | Destroys an HTTP request. | | destroy() | Destroys an HTTP request. |
| on(type: 'headersReceive') | Registers an observer for HTTP Response Header events. | | on(type: 'headersReceive') | Registers an observer for HTTP Response Header events. |
| off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.| | off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.|
...@@ -27,8 +27,8 @@ The following table provides only a simple description of the related APIs. For ...@@ -27,8 +27,8 @@ The following table provides only a simple description of the related APIs. For
| off\('dataReceive'\)<sup>10+</sup> | Unregisters the observer for events indicating receiving of HTTP streaming responses. | | off\('dataReceive'\)<sup>10+</sup> | Unregisters the observer for events indicating receiving of HTTP streaming responses. |
| on\('dataEnd'\)<sup>10+</sup> | Registers an observer for events indicating completion of receiving HTTP streaming responses. | | on\('dataEnd'\)<sup>10+</sup> | Registers an observer for events indicating completion of receiving HTTP streaming responses. |
| off\('dataEnd'\)<sup>10+</sup> | Unregisters the observer for events indicating completion of receiving HTTP streaming responses.| | off\('dataEnd'\)<sup>10+</sup> | Unregisters the observer for events indicating completion of receiving HTTP streaming responses.|
| on\('dataProgress'\)<sup>10+</sup> | Registers an observer for events indicating progress of receiving HTTP streaming responses. | | on\('dataReceiveProgress'\)<sup>10+</sup> | Registers an observer for events indicating progress of receiving HTTP streaming responses. |
| off\('dataProgress'\)<sup>10+</sup> | Unregisters the observer for events indicating progress of receiving HTTP streaming responses.| | off\('dataReceiveProgress'\)<sup>10+</sup> | Unregisters the observer for events indicating progress of receiving HTTP streaming responses.|
## How to Develop request APIs ## How to Develop request APIs
...@@ -53,6 +53,7 @@ httpRequest.on('headersReceive', (header) => { ...@@ -53,6 +53,7 @@ httpRequest.on('headersReceive', (header) => {
}); });
httpRequest.request( httpRequest.request(
// Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL.
"EXAMPLE_URL",
{ {
method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET. method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET.
// You can add header fields based on service requirements. // You can add header fields based on service requirements.
...@@ -81,7 +82,7 @@ httpRequest.request( ...@@ -81,7 +82,7 @@ httpRequest.request(
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
httpRequest.destroy(); httpRequest.destroy();
} else { } else {
console.info('error:' + JSON.stringify(err)); console.error('error:' + JSON.stringify(err));
// Unsubscribe from HTTP Response Header events. // Unsubscribe from HTTP Response Header events.
httpRequest.off('headersReceive'); httpRequest.off('headersReceive');
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
...@@ -91,12 +92,12 @@ httpRequest.request( ...@@ -91,12 +92,12 @@ httpRequest.request(
); );
``` ```
## How to Develop request2 APIs ## How to Develop requestInStream APIs
1. Import the **http** namespace from **@ohos.net.http.d.ts**. 1. Import the **http** namespace from **@ohos.net.http.d.ts**.
2. Call **createHttp()** to create an **HttpRequest** object. 2. Call **createHttp()** to create an **HttpRequest** object.
3. Depending on your need, call **on()** of the **HttpRequest** object to subscribe to HTTP response header events as well as events indicating receiving of HTTP streaming responses, progress of receiving HTTP streaming responses, and completion of receiving HTTP streaming responses. 3. Depending on your need, call **on()** of the **HttpRequest** object to subscribe to HTTP response header events as well as events indicating receiving of HTTP streaming responses, progress of receiving HTTP streaming responses, and completion of receiving HTTP streaming responses.
4. Call **request2()** to initiate a network request. You need to pass in the URL and optional parameters of the HTTP request. 4. Call **requestInStream()** to initiate a network request. You need to pass in the URL and optional parameters of the HTTP request.
5. Parse the returned response code as needed. 5. Parse the returned response code as needed.
6. Call **off()** of the **HttpRequest** object to unsubscribe from the related events. 6. Call **off()** of the **HttpRequest** object to unsubscribe from the related events.
7. Call **httpRequest.destroy()** to release resources after the request is processed. 7. Call **httpRequest.destroy()** to release resources after the request is processed.
...@@ -122,11 +123,11 @@ httpRequest.on('dataEnd', () => { ...@@ -122,11 +123,11 @@ httpRequest.on('dataEnd', () => {
console.info('No more data in response, data receive end'); console.info('No more data in response, data receive end');
}); });
// Subscribe to events indicating progress of receiving HTTP streaming responses. // Subscribe to events indicating progress of receiving HTTP streaming responses.
httpRequest.on('dataProgress', (data) => { httpRequest.on('dataReceiveProgress', (data) => {
console.log("dataProgress receiveSize:" + data.receiveSize + ", totalSize:" + data.totalSize); console.log("dataReceiveProgress receiveSize:" + data.receiveSize + ", totalSize:" + data.totalSize);
}); });
httpRequest.request2( httpRequest.requestInStream(
// Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL.
"EXAMPLE_URL", "EXAMPLE_URL",
{ {
...@@ -146,14 +147,14 @@ httpRequest.request2( ...@@ -146,14 +147,14 @@ httpRequest.request2(
readTimeout: 60000, // Optional. The default value is 60000, in ms. If a large amount of data needs to be transmitted, you are advised to set this parameter to a larger value to ensure normal data transmission. readTimeout: 60000, // Optional. The default value is 60000, in ms. If a large amount of data needs to be transmitted, you are advised to set this parameter to a larger value to ensure normal data transmission.
usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system. usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system.
}, (err, data) => { }, (err, data) => {
console.info('error:' + JSON.stringify(err)); console.error('error:' + JSON.stringify(err));
console.info('ResponseCode :' + JSON.stringify(data)); console.info('ResponseCode :' + JSON.stringify(data));
// Unsubscribe from HTTP Response Header events. // Unsubscribe from HTTP Response Header events.
httpRequest.off('headersReceive'); httpRequest.off('headersReceive');
// Unregister the observer for events indicating receiving of HTTP streaming responses. // Unregister the observer for events indicating receiving of HTTP streaming responses.
httpRequest.off('dataReceive'); httpRequest.off('dataReceive');
// Unregister the observer for events indicating progress of receiving HTTP streaming responses. // Unregister the observer for events indicating progress of receiving HTTP streaming responses.
httpRequest.off('dataProgress'); httpRequest.off('dataReceiveProgress');
// Unregister the observer for events indicating completion of receiving HTTP streaming responses. // Unregister the observer for events indicating completion of receiving HTTP streaming responses.
httpRequest.off('dataEnd'); httpRequest.off('dataEnd');
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
...@@ -161,10 +162,3 @@ httpRequest.request2( ...@@ -161,10 +162,3 @@ httpRequest.request2(
} }
); );
``` ```
## Samples
The following sample is provided to help you better understand how to develop the HTTP data request feature:
- [`Http`: Data Request (ArkTS) (API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Connectivity/Http)
- [HTTP Communication (ArkTS) (API9)](https://gitee.com/openharmony/codelabs/tree/master/NetworkManagement/SmartChatEtsOH)
# IPC & RPC Development Guidelines # IPC & RPC Development Guidelines
## When to Use ## Overview
IPC/RPC enables a proxy and a stub that run on different processes to communicate with each other, regardless of whether they run on the same or different devices. IPC/RPC enables a proxy and a stub that run on different processes to communicate with each other, regardless of whether they run on the same or different devices.
......
# Network Connection Management # Network Connection Management
## Introduction ## Overview
The Network Connection Management module provides basic network management capabilities, including management of Wi-Fi/cellular/Ethernet connection priorities, network quality evaluation, subscription to network connection status changes, query of network connection information, and DNS resolution. The Network Connection Management module provides basic network management capabilities, including management of Wi-Fi/cellular/Ethernet connection priorities, network quality evaluation, subscription to network connection status changes, query of network connection information, and DNS resolution.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-connection.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-connection.md).
## Basic Concepts ## Basic Concepts
......
# Ethernet Connection # Ethernet Connection
## Introduction ## Overview
The Ethernet Connection module allows a device to access the Internet through a network cable. After a device is connected to the Ethernet through a network cable, the device can obtain a series of network attributes, such as the dynamically allocated IP address, subnet mask, gateway, and DNS. You can manually configure and obtain the network attributes of the device in static mode. The Ethernet Connection module allows a device to access the Internet through a network cable. After a device is connected to the Ethernet through a network cable, the device can obtain a series of network attributes, such as the dynamically allocated IP address, subnet mask, gateway, and DNS. You can manually configure and obtain the network attributes of the device in static mode.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-ethernet.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-ethernet.md).
## **Constraints** ## **Constraints**
......
# MDNS Management # MDNS Management
## Introduction ## Overview
Multicast DNS (mDNS) provides functions such as adding, removing, discovering, and resolving local services on a LAN. Multicast DNS (mDNS) provides functions such as adding, removing, discovering, and resolving local services on a LAN.
- Local service: a service provider on a LAN, for example, a printer or scanner. - Local service: a service provider on a LAN, for example, a printer or scanner.
......
# Network Sharing # Network Sharing
## Introduction ## Overview
The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume. The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md).
## Basic Concepts ## Basic Concepts
......
# Traffic Management # Traffic Management
## Introduction ## Overview
The traffic management module allows you to query real-time or historical data traffic by the specified network interface card (NIC) or user ID (UID). The traffic management module allows you to query real-time or historical data traffic by the specified network interface card (NIC) or user ID (UID).
...@@ -11,6 +11,7 @@ Its functions include: ...@@ -11,6 +11,7 @@ Its functions include:
- Subscribing to traffic change events by NIC or UID - Subscribing to traffic change events by NIC or UID
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-statistics.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-statistics.md).
The following describes the development procedure specific to each application scenario. The following describes the development procedure specific to each application scenario.
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
A virtual private network (VPN) is a dedicated network established on a public network. On a VPN, the connection between any two nodes does not have an end-to-end physical link required by the traditional private network. Instead, user data is transmitted over a logical link because a VPN is a logical network deployed over the network platform (such as the Internet) provided by the public network service provider. A virtual private network (VPN) is a dedicated network established on a public network. On a VPN, the connection between any two nodes does not have an end-to-end physical link required by the traditional private network. Instead, user data is transmitted over a logical link because a VPN is a logical network deployed over the network platform (such as the Internet) provided by the public network service provider.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-vpn.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-vpn.md).
The following describes the development procedure specific to each application scenario. The following describes the development procedure specific to each application scenario.
......
# Socket Connection # Socket Connection
## Introduction ## Overview
The Socket Connection module allows an application to transmit data over a socket connection through the TCP, UDP, or TLS protocol. The Socket Connection module allows an application to transmit data over a socket connection through the TCP, UDP, or TLS protocol.
......
# Subscribing to State Changes of a Remote Object # Subscribing to State Changes of a Remote Object
## Overview
IPC/RPC allows you to subscribe to the state changes of a remote stub object. When the remote stub object dies, a death notification will be sent to your local proxy object. Such subscription and unsubscription are controlled by APIs. To be specific, you need to implement the **DeathRecipient** interface and the **onRemoteDied** API to clear resources. This callback is invoked when the process accommodating the remote stub object dies, or the device accommodating the remote stub object leaves the network. It is worth noting that these APIs should be called in the following order: The proxy object must first subscribe to death notifications of the stub object. If the stub object is in the normal state, the proxy object can cancel the subscription as required. If the process of the stub object exits or the device hosting the stub object goes offline, subsequent operations customized by the proxy object will be automatically triggered. IPC/RPC allows you to subscribe to the state changes of a remote stub object. When the remote stub object dies, a death notification will be sent to your local proxy object. Such subscription and unsubscription are controlled by APIs. To be specific, you need to implement the **DeathRecipient** interface and the **onRemoteDied** API to clear resources. This callback is invoked when the process accommodating the remote stub object dies, or the device accommodating the remote stub object leaves the network. It is worth noting that these APIs should be called in the following order: The proxy object must first subscribe to death notifications of the stub object. If the stub object is in the normal state, the proxy object can cancel the subscription as required. If the process of the stub object exits or the device hosting the stub object goes offline, subsequent operations customized by the proxy object will be automatically triggered.
## When to Use ## When to Use
......
# WebSocket Connection # WebSocket Connection
## When to Use ## Overview
You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event. You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event.
......
# DFX # DFX
- [Development of Application Event Logging](hiappevent-guidelines.md) - [Development of Application Event Logging](hiappevent-guidelines.md)
- [Development of Performance Tracing](hitracemeter-guidelines.md)
- [Development of Distributed Call Chain Tracing](hitracechain-guidelines.md) - [Development of Distributed Call Chain Tracing](hitracechain-guidelines.md)
- [HiLog Development (Native)](hilog-guidelines.md) - [HiLog Development (Native)](hilog-guidelines.md)
- Performance Tracing - Performance Tracing
......
# Application Freeze (appfreeze) Log Analysis # Application Freeze (appfreeze) Log Analysis
## Introduction ## Overview
Application freeze (appfreeze) means that an application does not respond to user operations (for example, clicking) within a given period of time. OpenHarmony provides a mechanism for detecting appfreeze faults and generates appfreeze logs for fault analysis. Application freeze (appfreeze) means that an application does not respond to user operations (for example, clicking) within a given period of time. OpenHarmony provides a mechanism for detecting appfreeze faults and generates appfreeze logs for fault analysis.
......
# Application Recovery Development # Application Recovery Development
## When to Use ## Overview
During application running, some unexpected behaviors are inevitable. For example, unprocessed exceptions and errors are thrown, and the call or running constraints of the recovery framework are violated. During application running, some unexpected behaviors are inevitable. For example, unprocessed exceptions and errors are thrown, and the call or running constraints of the recovery framework are violated.
...@@ -99,9 +99,12 @@ import AbilityConstant from '@ohos.app.ability.AbilityConstant' ...@@ -99,9 +99,12 @@ import AbilityConstant from '@ohos.app.ability.AbilityConstant'
- Define and register the [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback. For details about its usage, see [errorManager](../reference/apis/js-apis-app-ability-errorManager.md). - Define and register the [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback. For details about its usage, see [errorManager](../reference/apis/js-apis-app-ability-errorManager.md).
```ts ```ts
var registerId = -1; import * as GlobalWant from "../file1"
var callback = { export let abilityWant : Want // file1
onUnhandledException(errMsg) {
let registerId = -1;
let callback: Callback = {
onUnhandledException(errMsg: string): void {
console.log(errMsg); console.log(errMsg);
appRecovery.saveAppState(); appRecovery.saveAppState();
appRecovery.restartApp(); appRecovery.restartApp();
...@@ -112,7 +115,7 @@ import AbilityConstant from '@ohos.app.ability.AbilityConstant' ...@@ -112,7 +115,7 @@ import AbilityConstant from '@ohos.app.ability.AbilityConstant'
// Main window is created, set main page for this ability // Main window is created, set main page for this ability
console.log("[Demo] MainAbility onWindowStageCreate") console.log("[Demo] MainAbility onWindowStageCreate")
globalThis.registerObserver = (() => { G.registerObserver = (() => {
registerId = errorManager.on('error', callback); registerId = errorManager.on('error', callback);
}) })
...@@ -138,13 +141,15 @@ After the callback triggers **appRecovery.saveAppState()**, **onSaveState(state, ...@@ -138,13 +141,15 @@ After the callback triggers **appRecovery.saveAppState()**, **onSaveState(state,
After the callback triggers **appRecovery.restartApp()**, the application is restarted. After the restart, **onCreate(want, launchParam)** of **MainAbility** is called, and the saved data is in **parameters** of **want**. After the callback triggers **appRecovery.restartApp()**, the application is restarted. After the restart, **onCreate(want, launchParam)** of **MainAbility** is called, and the saved data is in **parameters** of **want**.
```ts ```ts
storage: LocalStorage import * as GlobalWant from "../file1"
export let abilityWant : Want // file1
onCreate(want, launchParam) { onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate") console.log("[Demo] MainAbility onCreate")
globalThis.abilityWant = want; GlobalWant.abilityWant = want;
if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) { if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
this.storage = new LocalStorage(); this.storage = new LocalStorage();
let recoveryData = want.parameters["myData"]; let recoveryData: string = want.parameters["myData"];
this.storage.setOrCreate("myData", recoveryData); this.storage.setOrCreate("myData", recoveryData);
this.context.restoreWindowStage(this.storage); this.context.restoreWindowStage(this.storage);
} }
...@@ -154,12 +159,15 @@ onCreate(want, launchParam) { ...@@ -154,12 +159,15 @@ onCreate(want, launchParam) {
- Unregister the **ErrorObserver** callback. - Unregister the **ErrorObserver** callback.
```ts ```ts
import * as GlobalWant from "../file1"
export let abilityWant : Want // file1
onWindowStageDestroy() { onWindowStageDestroy() {
// Main window is destroyed, release UI related resources // Main window is destroyed, release UI related resources
console.log("[Demo] MainAbility onWindowStageDestroy") console.log("[Demo] MainAbility onWindowStageDestroy")
globalThis.unRegisterObserver = (() => { G.unRegisterObserver = (() => {
errorManager.off('error', registerId, (err) => { errorManager.off(type: 'error', registerId: number, (err:Error) => {
console.error("[Demo] err:", err); console.error("[Demo] err:", err);
}); });
}) })
...@@ -171,24 +179,25 @@ onWindowStageDestroy() { ...@@ -171,24 +179,25 @@ onWindowStageDestroy() {
This is triggered by the recovery framework. You do not need to register an **ErrorObserver** callback. You only need to implement **onSaveState** for application state saving and **onCreate** for data restore. This is triggered by the recovery framework. You do not need to register an **ErrorObserver** callback. You only need to implement **onSaveState** for application state saving and **onCreate** for data restore.
```ts ```ts
export default class MainAbility extends Ability { import * as GlobalWant from "../file1"
storage: LocalStorage export let abilityWant : Want // file1
onCreate(want, launchParam) {
console.log("[Demo] MainAbility onCreate")
globalThis.abilityWant = want;
if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
this.storage = new LocalStorage();
let recoveryData = want.parameters["myData"];
this.storage.setOrCreate("myData", recoveryData);
this.context.restoreWindowStage(this.storage);
}
}
onSaveState(state, wantParams) { export default class MainAbility extends Ability {
// Ability has called to save app data onCreate(want: Want, launchParam:AbilityConstant.LaunchParam):void {
console.log("[Demo] MainAbility onSaveState") console.log("[Demo] MainAbility onCreate")
wantParams["myData"] = "my1234567"; GlobalWant.abilityWant = want;
return AbilityConstant.OnSaveResult.ALL_AGREE; if (launchParam.launchReason == AbilityConstant.LaunchReason.APP_RECOVERY) {
this.storage = new LocalStorage();
let recoveryData: string = want.parameters["myData"];
this.storage.setOrCreate<string>("myData", recoveryData);
this.context.restoreWindowStage(this.storage);
} }
}
onSaveState(reason: AbilityConstant.StateType, wantParam: Record<string, Object>): AbilityConstant.OnSaveResult {
// Ability has called to save app data
console.log("[Demo] MainAbility onSaveState")
wantParam["myData"] = "my1234567";
return AbilityConstant.OnSaveResult.ALL_AGREE;
}
} }
``` ```
# Process Crash (cppcrash) Log Analysis # Process Crash (cppcrash) Log Analysis
## Introduction ## Overview
A process crash refers to a C/C++ runtime crash. The FaultLogger module of OpenHarmony provides capabilities such as process crash detection, log collection, log storage, and log reporting, helping you to locate faults more effectively. A process crash refers to a C/C++ runtime crash. The FaultLogger module of OpenHarmony provides capabilities such as process crash detection, log collection, log storage, and log reporting, helping you to locate faults more effectively.
......
# Development of Error Manager # Development of Error Manager
## When to Use ## Overview
If coding specification issues or errors exist in the code of an application, the application may encounter unexpected errors, for example, uncaught exceptions or application lifecycle timeouts, while it is running. In such a case, the application may exit unexpectedly. Error logs, however, are usually stored on users' local storage, making it inconvenient to locate faults. With the APIs provided by the **errorManager** module, your application will be able to report related errors and logs to your service platform for fault locating before it exits. If coding specification issues or errors exist in the code of an application, the application may encounter unexpected errors, for example, uncaught exceptions or application lifecycle timeouts, while it is running. In such a case, the application may exit unexpectedly. Error logs, however, are usually stored on users' local storage, making it inconvenient to locate faults. With the APIs provided by the **errorManager** module, your application will be able to report related errors and logs to your service platform for fault locating before it exits.
......
# Development of Application Event Logging # Development of Application Event Logging
## Introduction ## Overview
A traditional log system aggregates log information generated by all applications running on the entire device, making it difficult to identify key information in the log. Therefore, an effective logging mechanism is needed to evaluate mission-critical information, for example, number of visits, number of daily active users, user operation habits, and key factors that affect application usage. A traditional log system aggregates log information generated by all applications running on the entire device, making it difficult to identify key information in the log. Therefore, an effective logging mechanism is needed to evaluate mission-critical information, for example, number of visits, number of daily active users, user operation habits, and key factors that affect application usage.
......
# HiLog Development (Native) # HiLog Development (Native)
## Introduction ## Overview
HiLog is the log system of OpenHarmony that provides logging for the system framework, services, and applications to record information on user operations and system running status. HiLog is the log system of OpenHarmony that provides logging for the system framework, services, and applications to record information on user operations and system running status.
......
# Development of Distributed Call Chain Tracing # Development of Distributed Call Chain Tracing
## Introduction ## Overview
The hiTraceChain module provides APIs to implement call chain tracing throughout a service process. This can help you quickly obtain the run log for the call chain of a specified service process and locate faults in inter-device, inter-process, or inter-thread communications. The hiTraceChain module provides APIs to implement call chain tracing throughout a service process. This can help you quickly obtain the run log for the call chain of a specified service process and locate faults in inter-device, inter-process, or inter-thread communications.
......
# Development of Performance Tracing (ArkTS) # Development of Performance Tracing (ArkTS)
## Introduction ## Overview
hiTraceMeter provides APIs for system performance tracing. You can call the APIs provided by the hiTraceMeter module in your own service logic to effectively track service processes and check the system performance. hiTraceMeter provides APIs for system performance tracing. You can call the APIs provided by the hiTraceMeter module in your own service logic to effectively track service processes and check the system performance.
...@@ -21,7 +21,7 @@ hiTraceMeter provides APIs for system performance tracing. You can call the APIs ...@@ -21,7 +21,7 @@ hiTraceMeter provides APIs for system performance tracing. You can call the APIs
## Available APIs ## Available APIs
The performance tracing APIs are provided by the **hiTraceMeter** module. For details, see [API Reference](../reference/apis/js-apis-hitracemeter.md). The performance tracing APIs are provided by the **hiTraceMeter** module. For details, see [API Reference]( ../reference/apis/js-apis-hitracemeter.md).
**APIs for performance tracing** **APIs for performance tracing**
...@@ -35,60 +35,16 @@ The performance tracing APIs are provided by the **hiTraceMeter** module. For de ...@@ -35,60 +35,16 @@ The performance tracing APIs are provided by the **hiTraceMeter** module. For de
In this example, distributed call chain tracing begins when the application startup execution page is loaded and stops when the service usage is completed. In this example, distributed call chain tracing begins when the application startup execution page is loaded and stops when the service usage is completed.
1. Create a JS application project. In the displayed **Project** window, choose **entry** > **src** > **main** > **js** > **default** > **pages** > **index**, and double-click **index.js**. Add the code to implement performance tracing upon page loading. The sample code is as follows: 1. Create an ArkTS application project. In the displayed **Project** window, choose **entry** > **src** > **main** > **ets** > **pages** > **index**, and double-click **index.js**. Add the code to implement performance tracing upon page loading. For example, if the name of the trace task is **HITRACE\_TAG\_APP**, the sample code is as follows:
```js
import hiTraceMeter from '@ohos.hiTraceMeter'
export default {
data: {
title: ""
},
onInit() {
this.title = this.$t('strings.world');
// Start trace tasks with the same name concurrently.
hiTraceMeter.startTrace("business", 1);
// Keep the service process running.
console.log(`business running`);
hiTraceMeter.startTrace("business", 2); // Start the second trace task with the same name while the first task is still running. The tasks are running concurrently and therefore their taskId must be different.
// Keep the service process running.
console.log(`business running`);
hiTraceMeter.finishTrace("business", 1);
// Keep the service process running.
console.log(`business running`);
hiTraceMeter.finishTrace("business", 2);
// Start trace tasks with the same name in serial mode.
hiTraceMeter.startTrace("business", 1);
// Keep the service process running.
console.log(`business running`);
hiTraceMeter.finishTrace("business", 1); // End the first trace task.
// Keep the service process running.
console.log(`business running`);
hiTraceMeter.startTrace("business", 1); // Start the second trace task with the same name in serial mode.
// Keep the service process running.
console.log(`business running`);
let traceCount = 3;
hiTraceMeter.traceByValue("myTestCount", traceCount);
traceCount = 4;
hiTraceMeter.traceByValue("myTestCount", traceCount);
hiTraceMeter.finishTrace("business", 1);
}
}
```
2. Create an ArkTs application project. In the displayed **Project** window, choose **entry** > **src** > **main** > **ets** > **pages** > **index**, and double-click **index.js**. Add the code to implement performance tracing upon page loading. For example, if the name of the trace task is **HITRACE\_TAG\_APP**, the sample code is as follows:
```ts ```ts
import hitrace from '@ohos.hiTraceMeter'; import hitrace from '@ohos.hiTraceMeter';
@Entry @Entry
@Component @Component
struct Index { struct Index {
@State message: string = 'Hello World'; @State message: string = 'Hello World';
build() { build() {
Row() { Row() {
Column() { Column() {
...@@ -97,7 +53,7 @@ In this example, distributed call chain tracing begins when the application star ...@@ -97,7 +53,7 @@ In this example, distributed call chain tracing begins when the application star
.fontWeight(FontWeight.Bold) .fontWeight(FontWeight.Bold)
.onClick(() => { .onClick(() => {
this.message = 'Hello ArkUI'; this.message = 'Hello ArkUI';
// Start trace tasks with the same name concurrently. // Start trace tasks with the same name concurrently.
hitrace.startTrace("HITRACE_TAG_APP", 1001); hitrace.startTrace("HITRACE_TAG_APP", 1001);
// Keep the service process running. // Keep the service process running.
...@@ -107,7 +63,7 @@ In this example, distributed call chain tracing begins when the application star ...@@ -107,7 +63,7 @@ In this example, distributed call chain tracing begins when the application star
hitrace.startTrace("HITRACE_TAG_APP", 1002); hitrace.startTrace("HITRACE_TAG_APP", 1002);
// Keep the service process running. // Keep the service process running.
console.log(`HITRACE_TAG_APP running`); console.log(`HITRACE_TAG_APP running`);
hitrace.finishTrace("HITRACE_TAG_APP", 1001); hitrace.finishTrace("HITRACE_TAG_APP", 1001);
hitrace.finishTrace("HITRACE_TAG_APP", 1002); hitrace.finishTrace("HITRACE_TAG_APP", 1002);
...@@ -143,7 +99,7 @@ In this example, distributed call chain tracing begins when the application star ...@@ -143,7 +99,7 @@ In this example, distributed call chain tracing begins when the application star
``` ```
3. Click the run button on the application page. Then, run the following commands in sequence in shell: 3. Click the run button on the application page. Then, run the following commands in sequence in shell:
```shell ```shell
hdc shell hdc shell
hitrace --trace_begin app hitrace --trace_begin app
......
# Development of Performance Tracing (Native) # Development of Performance Tracing (Native)
## Introduction ## Overview
hiTraceMeter provides APIs for system performance tracing. You can call the APIs provided by the hiTraceMeter module in your own service logic to effectively track service processes and check the system performance. hiTraceMeter provides APIs for system performance tracing. You can call the APIs provided by the hiTraceMeter module in your own service logic to effectively track service processes and check the system performance.
> **NOTE** > **NOTE**
......
...@@ -21,42 +21,6 @@ try { ...@@ -21,42 +21,6 @@ try {
} }
``` ```
## How do I hide the status bar to get the immersive effect?
Applicable to: OpenHarmony 3.2 Beta5 (API version 9)
**Solution**
1. Use **onWindowStageCreate** to obtain a **windowClass** object.
```
onWindowStageCreate(windowStage) {
// When the main window is created, set the main page for this ability.
console.log("[Demo] MainAbility onWindowStageCreate")
windowStage.getMainWindow((err, data) => {
if (err.code) {
console.error('Failed to obtain the main window.')
return;
}
// Obtain a windowClass object.
globalThis.windowClass = data;
})
}
```
2. Enable the full-screen mode for the window and hide the status bar.
```
globalThis.windowClass.setFullScreen(isFullScreen, (err, data) => {
if (err.code) {
console.error('Failed to enable the full-screen mode. Cause:' + JSON.stringify(err));
return;
}
console.info('Succeeded in enabling the full-screen mode. Data: ' + JSON.stringify(data));
});
```
## How do I obtain the window width and height? ## How do I obtain the window width and height?
Applicable to: OpenHarmony 3.2 Beta5 (API version 9, stage model) Applicable to: OpenHarmony 3.2 Beta5 (API version 9, stage model)
......
...@@ -33,20 +33,21 @@ Example: Listener for changes of an image. When the image is favorited, the list ...@@ -33,20 +33,21 @@ Example: Listener for changes of an image. When the image is favorited, the list
```ts ```ts
import dataSharePredicates from '@ohos.data.dataSharePredicates'; import dataSharePredicates from '@ohos.data.dataSharePredicates';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
let predicates = new dataSharePredicates.DataSharePredicates(); let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg'); predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg');
let fetchOptions = { let fetchOptions: photoAccessHelper.FetchOptions = {
fetchColumns: [], fetchColumns: [],
predicates: predicates predicates: predicates
}; };
try { try {
let fetchResult = await phAccessHelper.getAssets(fetchOptions); let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOptions);
let fileAsset = await fetchResult.getFirstObject(); let fileAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
console.info('getAssets fileAsset.uri : ' + fileAsset.uri); console.info('getAssets fileAsset.uri : ' + fileAsset.uri);
let onCallback = (changeData) => { let onCallback = (changeData:photoAccessHelper.ChangeData) => {
console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); console.info('onCallback successfully, changData: ' + JSON.stringify(changeData));
} }
phAccessHelper.registerChange(fileAsset.uri, false, onCallback); phAccessHelper.registerChange(fileAsset.uri, false, onCallback);
...@@ -77,21 +78,22 @@ Example: Listener for a user album. When the album is renamed, the listener call ...@@ -77,21 +78,22 @@ Example: Listener for a user album. When the album is renamed, the listener call
```ts ```ts
import dataSharePredicates from '@ohos.data.dataSharePredicates'; import dataSharePredicates from '@ohos.data.dataSharePredicates';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
let predicates = new dataSharePredicates.DataSharePredicates(); let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; let albumName: photoAccessHelper.AlbumKeys = photoAccessHelper.AlbumKey.ALBUM_NAME;
predicates.equalTo(albumName, 'albumName'); predicates.equalTo(albumName, 'albumName');
let fetchOptions = { let fetchOptions: dataSharePredicates.FetchOptions = {
fetchColumns: [], fetchColumns: [],
predicates: predicates predicates: predicates
}; };
try { try {
let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions); let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.Album> = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions);
let album = await fetchResult.getFirstObject(); let album: photoAccessHelper.Album = await fetchResult.getFirstObject();
console.info('getAlbums successfullyfully, albumName: ' + album.albumUri); console.info('getAlbums successfullyfully, albumName: ' + album.albumUri);
let onCallback = (changeData) => { let onCallback = (changeData: photoAccessHelper.ChangeData) => {
console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); console.info('onCallback successfully, changData: ' + JSON.stringify(changeData));
} }
phAccessHelper.registerChange(album.albumUri, false, onCallback); phAccessHelper.registerChange(album.albumUri, false, onCallback);
...@@ -133,21 +135,22 @@ Example: Register a listener for all file assets. When an observed file asset is ...@@ -133,21 +135,22 @@ Example: Register a listener for all file assets. When an observed file asset is
```ts ```ts
import dataSharePredicates from '@ohos.data.dataSharePredicates'; import dataSharePredicates from '@ohos.data.dataSharePredicates';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
let onCallback = (changeData) => { let onCallback = (changeData: photoAccessHelper.ChangeData) => {
console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); console.info('onCallback successfully, changData: ' + JSON.stringify(changeData));
} }
phAccessHelper.registerChange(photoAccessHelper.DefaultChangeUri.DEFAULT_PHOTO_URI, true, onCallback); phAccessHelper.registerChange(photoAccessHelper.DefaultChangeUri.DEFAULT_PHOTO_URI, true, onCallback);
let predicates = new dataSharePredicates.DataSharePredicates(); let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
let fetchOptions = { let fetchOptions: photoAccessHelper.FetchOptions = {
fetchColumns: [], fetchColumns: [],
predicates: predicates predicates: predicates
}; };
try { try {
let fetchResult = await phAccessHelper.getAssets(fetchOptions); let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOptions);
let fileAsset = await fetchResult.getFirstObject(); let fileAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
console.info('getAssets fileAsset.uri : ' + fileAsset.uri); console.info('getAssets fileAsset.uri : ' + fileAsset.uri);
await fileAsset.favorite(true); await fileAsset.favorite(true);
fetchResult.close(); fetchResult.close();
...@@ -175,23 +178,24 @@ Example: Unregister listening for an image. After that, the unregistered listene ...@@ -175,23 +178,24 @@ Example: Unregister listening for an image. After that, the unregistered listene
```ts ```ts
import dataSharePredicates from '@ohos.data.dataSharePredicates'; import dataSharePredicates from '@ohos.data.dataSharePredicates';
import photoAccessHelper from '@ohos.file.photoAccessHelper';
let predicates = new dataSharePredicates.DataSharePredicates(); let predicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg'); predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg');
let fetchOptions = { let fetchOptions: photoAccessHelper.FetchOptions = {
fetchColumns: [], fetchColumns: [],
predicates: predicates predicates: predicates
}; };
try { try {
let fetchResult = await phAccessHelper.getAssets(fetchOptions); let fetchResult: photoAccessHelper.FetchResult<photoAccessHelper.PhotoAsset> = await phAccessHelper.getAssets(fetchOptions);
let fileAsset = await fetchResult.getFirstObject(); let fileAsset: photoAccessHelper.PhotoAsset = await fetchResult.getFirstObject();
console.info('getAssets fileAsset.uri : ' + fileAsset.uri); console.info('getAssets fileAsset.uri : ' + fileAsset.uri);
let onCallback1 = (changeData) => { let onCallback1 = (changeData: photoAccessHelper.ChangeData) => {
console.info('onCallback1, changData: ' + JSON.stringify(changeData)); console.info('onCallback1, changData: ' + JSON.stringify(changeData));
} }
let onCallback2 = (changeData) => { let onCallback2 = (changeData: photoAccessHelper.ChangeData) => {
console.info('onCallback2, changData: ' + JSON.stringify(changeData)); console.info('onCallback2, changData: ' + JSON.stringify(changeData));
} }
phAccessHelper.registerChange(fileAsset.uri, false, onCallback1); phAccessHelper.registerChange(fileAsset.uri, false, onCallback1);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册