提交 0a86f98b 编写于 作者: G ge-yafang

Merge branch 'OpenHarmony-4.0-Beta2' of https://gitee.com/ge-yafang/docs into OpenHarmony-4.0-Beta2

...@@ -200,9 +200,9 @@ zh-cn/application-dev/reference/apis/js-apis-appmanager.md @littlejerry1 @RayShi ...@@ -200,9 +200,9 @@ zh-cn/application-dev/reference/apis/js-apis-appmanager.md @littlejerry1 @RayShi
zh-cn/application-dev/reference/apis/js-apis-arraylist.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-arraylist.md @gongjunsong @ge-yafang @flyingwolf @BlackStone
zh-cn/application-dev/reference/apis/js-apis-audio.md @liuyuehua1 @zengyawen @magekkkk @currydavids zh-cn/application-dev/reference/apis/js-apis-audio.md @liuyuehua1 @zengyawen @magekkkk @currydavids
zh-cn/application-dev/reference/apis/js-apis-backgroundTaskManager.md @chenmingJay @ningningW @nan-xiansen @iceice1001 zh-cn/application-dev/reference/apis/js-apis-backgroundTaskManager.md @chenmingJay @ningningW @nan-xiansen @iceice1001
zh-cn/application-dev/reference/apis/js-apis-battery-info.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-battery-info.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-bluetooth.md @cheng_guohong @RayShih @cheng_guohong @quanli125 zh-cn/application-dev/reference/apis/js-apis-bluetooth.md @cheng_guohong @RayShih @cheng_guohong @quanli125
zh-cn/application-dev/reference/apis/js-apis-brightness.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-brightness.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-buffer.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-buffer.md @gongjunsong @ge-yafang @flyingwolf @BlackStone
zh-cn/application-dev/reference/apis/js-apis-bundle-AbilityInfo.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundle-AbilityInfo.md @shuaytao @RayShih @wangzhen107 @inter515
zh-cn/application-dev/reference/apis/js-apis-bundle-ApplicationInfo.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundle-ApplicationInfo.md @shuaytao @RayShih @wangzhen107 @inter515
...@@ -322,7 +322,7 @@ zh-cn/application-dev/reference/apis/js-apis-pasteboard.md @han-zhengshi @ge-yaf ...@@ -322,7 +322,7 @@ zh-cn/application-dev/reference/apis/js-apis-pasteboard.md @han-zhengshi @ge-yaf
zh-cn/application-dev/reference/apis/js-apis-permissionrequestresult.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen zh-cn/application-dev/reference/apis/js-apis-permissionrequestresult.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen
zh-cn/application-dev/reference/apis/js-apis-plainarray.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-plainarray.md @gongjunsong @ge-yafang @flyingwolf @BlackStone
zh-cn/application-dev/reference/apis/js-apis-pointer.md @yuanxinying @ningningW @cococoler @alien0208 zh-cn/application-dev/reference/apis/js-apis-pointer.md @yuanxinying @ningningW @cococoler @alien0208
zh-cn/application-dev/reference/apis/js-apis-power.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-power.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-privacyManager.md @nianCode @zengyawen @shuqinglin2 @jinhaihw zh-cn/application-dev/reference/apis/js-apis-privacyManager.md @nianCode @zengyawen @shuqinglin2 @jinhaihw
zh-cn/application-dev/reference/apis/js-apis-process.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-process.md @gongjunsong @ge-yafang @flyingwolf @BlackStone
zh-cn/application-dev/reference/apis/js-apis-processrunninginfo.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen zh-cn/application-dev/reference/apis/js-apis-processrunninginfo.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen
...@@ -335,7 +335,7 @@ zh-cn/application-dev/reference/apis/js-apis-request.md @feng-aiwen @ningningW @ ...@@ -335,7 +335,7 @@ zh-cn/application-dev/reference/apis/js-apis-request.md @feng-aiwen @ningningW @
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
zh-cn/application-dev/reference/apis/js-apis-runninglock.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-runninglock.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-screen.md @zhangqiang183 @ge-yafang @zhouyaoying @zxg-gitee @nobuggers zh-cn/application-dev/reference/apis/js-apis-screen.md @zhangqiang183 @ge-yafang @zhouyaoying @zxg-gitee @nobuggers
zh-cn/application-dev/reference/apis/js-apis-screenshot.md @zhangqiang183 @ge-yafang @zhouyaoying @zxg-gitee @nobuggers zh-cn/application-dev/reference/apis/js-apis-screenshot.md @zhangqiang183 @ge-yafang @zhouyaoying @zxg-gitee @nobuggers
zh-cn/application-dev/reference/apis/js-apis-securityLabel.md @panqinxu @zengyawen @bubble_mao @jinhaihw zh-cn/application-dev/reference/apis/js-apis-securityLabel.md @panqinxu @zengyawen @bubble_mao @jinhaihw
...@@ -350,9 +350,9 @@ zh-cn/application-dev/reference/apis/js-apis-stack.md @gongjunsong @ge-yafang @f ...@@ -350,9 +350,9 @@ zh-cn/application-dev/reference/apis/js-apis-stack.md @gongjunsong @ge-yafang @f
zh-cn/application-dev/reference/apis/js-apis-statfs.md @panqinxu @zengyawen @bubble_mao @jinhaihw zh-cn/application-dev/reference/apis/js-apis-statfs.md @panqinxu @zengyawen @bubble_mao @jinhaihw
zh-cn/application-dev/reference/apis/js-apis-storage-statistics.md @panqinxu @zengyawen @bubble_mao @jinhaihw zh-cn/application-dev/reference/apis/js-apis-storage-statistics.md @panqinxu @zengyawen @bubble_mao @jinhaihw
zh-cn/application-dev/reference/apis/js-apis-system-app.md @HelloCrease @niulihua @tomatodevboy zh-cn/application-dev/reference/apis/js-apis-system-app.md @HelloCrease @niulihua @tomatodevboy
zh-cn/application-dev/reference/apis/js-apis-system-battery.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-system-battery.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-system-bluetooth.md @cheng_guohong @RayShih @cheng_guohong @quanli125 zh-cn/application-dev/reference/apis/js-apis-system-bluetooth.md @cheng_guohong @RayShih @cheng_guohong @quanli125
zh-cn/application-dev/reference/apis/js-apis-system-brightness.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-system-brightness.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-system-cipher.md @gaoyong @zengyawen @niejiteng @jumozhanjiang zh-cn/application-dev/reference/apis/js-apis-system-cipher.md @gaoyong @zengyawen @niejiteng @jumozhanjiang
zh-cn/application-dev/reference/apis/js-apis-system-configuration.md @Buda-Liu @ningningW @budda-wang @tomatodevboy zh-cn/application-dev/reference/apis/js-apis-system-configuration.md @Buda-Liu @ningningW @budda-wang @tomatodevboy
zh-cn/application-dev/reference/apis/js-apis-system-date-time.md @feng-aiwen @ningningW @illybyy @murphy1984 zh-cn/application-dev/reference/apis/js-apis-system-date-time.md @feng-aiwen @ningningW @illybyy @murphy1984
...@@ -375,7 +375,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-timer.md @feng-aiwen @ningni ...@@ -375,7 +375,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-timer.md @feng-aiwen @ningni
zh-cn/application-dev/reference/apis/js-apis-system-vibrate.md @hellohyh001 @ningningW @butterls @star-wind-snow-and-rain zh-cn/application-dev/reference/apis/js-apis-system-vibrate.md @hellohyh001 @ningningW @butterls @star-wind-snow-and-rain
zh-cn/application-dev/reference/apis/js-apis-telephony-data.md @zhang-hai-feng @zengyawen @jyh926 @gaoxi785 zh-cn/application-dev/reference/apis/js-apis-telephony-data.md @zhang-hai-feng @zengyawen @jyh926 @gaoxi785
zh-cn/application-dev/reference/apis/js-apis-testRunner.md @inter515 @littlejerry1 @RayShih @inter515 @jiyong zh-cn/application-dev/reference/apis/js-apis-testRunner.md @inter515 @littlejerry1 @RayShih @inter515 @jiyong
zh-cn/application-dev/reference/apis/js-apis-thermal.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-thermal.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-timer.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-timer.md @gongjunsong @ge-yafang @flyingwolf @BlackStone
zh-cn/application-dev/reference/apis/js-apis-touchevent.md @mayunteng_1 @ningningW @cococoler @alien0208 zh-cn/application-dev/reference/apis/js-apis-touchevent.md @mayunteng_1 @ningningW @cococoler @alien0208
zh-cn/application-dev/reference/apis/js-apis-shortKey.md @mayunteng_1 @ningningW @cococoler @alien0208 zh-cn/application-dev/reference/apis/js-apis-shortKey.md @mayunteng_1 @ningningW @cococoler @alien0208
...@@ -426,7 +426,7 @@ zh-cn/application-dev/reference/apis/js-apis-application-configuration.md @littl ...@@ -426,7 +426,7 @@ zh-cn/application-dev/reference/apis/js-apis-application-configuration.md @littl
zh-cn/application-dev/reference/apis/js-apis-application-configurationConstant.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen zh-cn/application-dev/reference/apis/js-apis-application-configurationConstant.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen
zh-cn/application-dev/reference/apis/js-apis-application-quickFixManager.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen zh-cn/application-dev/reference/apis/js-apis-application-quickFixManager.md @littlejerry1 @RayShih @gwang2008 @chengxingzhen
zh-cn/application-dev/reference/apis/js-apis-avsession.md @liuyuehua1 @zengyawen @saga2020 @currydavids zh-cn/application-dev/reference/apis/js-apis-avsession.md @liuyuehua1 @zengyawen @saga2020 @currydavids
zh-cn/application-dev/reference/apis/js-apis-batteryStatistics.md @aqxyjay @zengyawen @aqxyjay @alien0208 zh-cn/application-dev/reference/apis/js-apis-batteryStatistics.md @zengyawen @alien0208
zh-cn/application-dev/reference/apis/js-apis-Bundle-BundleStatusCallback.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-Bundle-BundleStatusCallback.md @shuaytao @RayShih @wangzhen107 @inter515
zh-cn/application-dev/reference/apis/js-apis-bundleManager-abilityInfo.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundleManager-abilityInfo.md @shuaytao @RayShih @wangzhen107 @inter515
zh-cn/application-dev/reference/apis/js-apis-bundleManager-applicationInfo.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundleManager-applicationInfo.md @shuaytao @RayShih @wangzhen107 @inter515
......
# 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))
})
```
# 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
...@@ -48,7 +48,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam ...@@ -48,7 +48,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 +65,7 @@ Typically, the call event is triggered for touching of buttons. Below is an exam ...@@ -65,7 +65,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.
......
...@@ -4,19 +4,26 @@ ...@@ -4,19 +4,26 @@
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 +32,7 @@ On the widget page, the **postCardAction** API can be used to trigger a message ...@@ -25,11 +32,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%')
} }
} }
``` ```
...@@ -46,8 +49,8 @@ On the widget page, the **postCardAction** API can be used to trigger a message ...@@ -46,8 +49,8 @@ On the widget page, the **postCardAction** API can be used to trigger a message
// 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 = {
'title':'Title Update Success.', // Matches the widget layout. 'title':'Title Update.', // It matches the widget layout.
'detail':'Detail Update Success.', // Matches the widget layout. '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) => {
...@@ -63,4 +66,6 @@ On the widget page, the **postCardAction** API can be used to trigger a message ...@@ -63,4 +66,6 @@ On the widget page, the **postCardAction** API can be used to trigger a message
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) |
...@@ -23,8 +23,11 @@ The ArkTS widget provides the **postCardAction()** API for interaction between t ...@@ -23,8 +23,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:
......
...@@ -5,7 +5,7 @@ The **router** capability of the **postCardAction** API can be used in a widget ...@@ -5,7 +5,7 @@ The **router** capability of the **postCardAction** API can be used in a widget
![WidgerCameraCard](figures/WidgerCameraCard.png) ![WidgerCameraCard](figures/WidgerCameraCard.png)
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 +57,7 @@ Generally, a button is used to start a page. ...@@ -57,7 +57,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.
......
# 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.
......
...@@ -11,6 +11,7 @@ Typical MDNS management scenarios include: ...@@ -11,6 +11,7 @@ Typical MDNS management scenarios include:
- Discovering local services and listening to the status changes of local services of the specified type through the **DiscoveryService** object. - Discovering local services and listening to the status changes of local services of the specified type through the **DiscoveryService** object.
> **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 [mDNS Management](../reference/apis/js-apis-net-mdns.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 [mDNS Management](../reference/apis/js-apis-net-mdns.md).
The following describes the development procedure specific to each application scenario. The following describes the development procedure specific to each application scenario.
...@@ -28,9 +29,13 @@ For the complete list of APIs and example code, see [mDNS Management](../referen ...@@ -28,9 +29,13 @@ For the complete list of APIs and example code, see [mDNS Management](../referen
| ohos.net.mdns.DiscoveryService | startSearchingMDNS(): void | Searches for mDNS services on the LAN.| | ohos.net.mdns.DiscoveryService | startSearchingMDNS(): void | Searches for mDNS services on the LAN.|
| ohos.net.mdns.DiscoveryService | stopSearchingMDNS(): void | Stops searching for mDNS services on the LAN.| | ohos.net.mdns.DiscoveryService | stopSearchingMDNS(): void | Stops searching for mDNS services on the LAN.|
| ohos.net.mdns.DiscoveryService | on(type: 'discoveryStart', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Enables listening for **discoveryStart** events.| | ohos.net.mdns.DiscoveryService | on(type: 'discoveryStart', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Enables listening for **discoveryStart** events.|
| ohos.net.mdns.DiscoveryService | off(type: 'discoveryStart', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Disables listening for **discoveryStart** events.|
| ohos.net.mdns.DiscoveryService | on(type: 'discoveryStop', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Enables listening for **discoveryStop** events.| | ohos.net.mdns.DiscoveryService | on(type: 'discoveryStop', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Enables listening for **discoveryStop** events.|
| ohos.net.mdns.DiscoveryService | off(type: 'discoveryStop', callback: Callback<{serviceInfo: LocalServiceInfo, errorCode?: MdnsError}>): void | Disables listening for **discoveryStop** events.|
| ohos.net.mdns.DiscoveryService | on(type: 'serviceFound', callback: Callback\<LocalServiceInfo>): void | Enables listening for **serviceFound** events.| | ohos.net.mdns.DiscoveryService | on(type: 'serviceFound', callback: Callback\<LocalServiceInfo>): void | Enables listening for **serviceFound** events.|
| ohos.net.mdns.DiscoveryService | off(type: 'serviceFound', callback: Callback\<LocalServiceInfo>): void | Disables listening for **serviceFound** events.|
| ohos.net.mdns.DiscoveryService | on(type: 'serviceLost', callback: Callback\<LocalServiceInfo>): void | Enables listening for **serviceLost** events.| | ohos.net.mdns.DiscoveryService | on(type: 'serviceLost', callback: Callback\<LocalServiceInfo>): void | Enables listening for **serviceLost** events.|
| ohos.net.mdns.DiscoveryService | off(type: 'serviceLost', callback: Callback\<LocalServiceInfo>): void | Disables listening for **serviceLost** events.|
## Managing Local Services ## Managing Local Services
...@@ -94,10 +99,11 @@ mdns.removeLocalService(context, localServiceInfo, function (error, data) { ...@@ -94,10 +99,11 @@ mdns.removeLocalService(context, localServiceInfo, function (error, data) {
1. Connect the device to the Wi-Fi network. 1. Connect the device to the Wi-Fi network.
2. Import the **mdns** namespace from **@ohos.net.mdns**. 2. Import the **mdns** namespace from **@ohos.net.mdns**.
3. Creates a **DiscoveryService** object, which is used to discover mDNS services of the specified type. 3. Create a **DiscoveryService** object, which is used to discover mDNS services of the specified type.
4. Subscribe to mDNS service discovery status changes. 4. Subscribe to mDNS service discovery status changes.
5. Enable discovery of mDNS services on the LAN. 5. Enable discovery of mDNS services on the LAN.
6. Stop searching for mDNS services on the LAN. 6. Stop searching for mDNS services on the LAN.
7. Unsubscribe from mDNS service discovery status changes.
```js ```js
// Import the mdns namespace from @ohos.net.mdns. // Import the mdns namespace from @ohos.net.mdns.
...@@ -139,4 +145,18 @@ discoveryService.startSearchingMDNS(); ...@@ -139,4 +145,18 @@ discoveryService.startSearchingMDNS();
// Stop searching for mDNS services on the LAN. // Stop searching for mDNS services on the LAN.
discoveryService.stopSearchingMDNS(); discoveryService.stopSearchingMDNS();
// Unsubscribe from mDNS service discovery status changes.
discoveryService.off('discoveryStart', (data) => {
console.log(JSON.stringify(data));
});
discoveryService.off('discoveryStop', (data) => {
console.log(JSON.stringify(data));
});
discoveryService.off('serviceFound', (data) => {
console.log(JSON.stringify(data));
});
discoveryService.off('serviceLost', (data) => {
console.log(JSON.stringify(data));
});
``` ```
...@@ -32,7 +32,7 @@ The underlying devices manage the data by device. The device KV stores support d ...@@ -32,7 +32,7 @@ The underlying devices manage the data by device. The device KV stores support d
The **DatamgrService** provides the following synchronization types: The **DatamgrService** provides the following synchronization types:
- Manual synchronization: The application calls **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). You can use the [**sync()** with the **query** parameter](../reference/apis/js-apis-distributedKVStore.md#sync-1) to synchronize the data that meets the specified conditions. The manual synchronization is available only for system applications. - Manual synchronization: The application calls **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). You can use the [**sync()** with the **query** parameter](../reference/apis/js-apis-distributedKVStore.md#sync-1) to synchronize the data that meets the specified conditions.
- Automatic synchronization: The distributed database automatically pushes local data to the remote end and pulls remote data to the local end. An automatic synchronization is triggered when a device goes online or an application updates data. - Automatic synchronization: The distributed database automatically pushes local data to the remote end and pulls remote data to the local end. An automatic synchronization is triggered when a device goes online or an application updates data.
...@@ -72,8 +72,6 @@ When data is added, deleted, or modified, a notification is sent to the subscrib ...@@ -72,8 +72,6 @@ When data is added, deleted, or modified, a notification is sent to the subscrib
- Each KV store supports a maximum of eight callbacks for subscription of data change notifications. - Each KV store supports a maximum of eight callbacks for subscription of data change notifications.
- The manual synchronization is available only for system applications.
## Available APIs ## Available APIs
...@@ -247,24 +245,21 @@ The following uses a single KV store as an example to describe how to implement ...@@ -247,24 +245,21 @@ The following uses a single KV store as an example to describe how to implement
> **NOTE** > **NOTE**
> >
> If manual synchronization is used, **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications. > If manual synchronization is used, **deviceIds** can be obtained by [devManager.getAvailableDeviceListSync](../reference/apis/js-apis-distributedDeviceManager.md#getavailabledevicelistsync).
```js ```js
import deviceManager from '@ohos.distributedHardware.deviceManager'; import deviceManager from '@ohos.distributedDeviceManager';
let devManager; let devManager;
try {
// create deviceManager // create deviceManager
deviceManager.createDeviceManager('bundleName', (err, value) => { devManager = deviceManager.createDeviceManager(context.applicationInfo.name);
if (!err) { // deviceIds is obtained by devManager.getAvailableDeviceListSync.
devManager = value;
// deviceIds is obtained by devManager.getTrustedDeviceListSync.
let deviceIds = []; let deviceIds = [];
if (devManager !== null) { if (devManager != null) {
// The ohos.permission.ACCESS_SERVICE_DM permission is required. This permission is available only for system applications. let devices = devManager.getAvailableDeviceListSync();
let devices = devManager.getTrustedDeviceListSync();
for (let i = 0; i < devices.length; i++) { for (let i = 0; i < devices.length; i++) {
deviceIds[i] = devices[i].deviceId; deviceIds[i] = devices[i].networkId;
} }
} }
try { try {
...@@ -273,6 +268,8 @@ The following uses a single KV store as an example to describe how to implement ...@@ -273,6 +268,8 @@ The following uses a single KV store as an example to describe how to implement
} catch (e) { } catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
} }
} catch (err) {
console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message);
} }
});
``` ```
\ No newline at end of file
...@@ -10,9 +10,16 @@ You can synchronize the application data in a local RDB store on a device to oth ...@@ -10,9 +10,16 @@ You can synchronize the application data in a local RDB store on a device to oth
OpenHamony supports synchronization of the relational data of an application across multiple devices. OpenHamony supports synchronization of the relational data of an application across multiple devices.
- Distributed table list<br>After a table is created for an application in an RDB store, you can set it as a distributed table. When querying the RDB store of a remote device, you can obtain the distributed table name of the remote device based on the local table name. - Distributed table list
- Synchronization mode<br>Data can be synchronized between devices in either of the following ways: <br>- Pushing data from a local device to a remote device. <br>- Pulling data from a remote device to a local device. After a table is created for an application in an RDB store, you can set it as a distributed table. When querying the RDB store of a remote device, you can obtain the distributed table name of the remote device based on the local table name.
- Synchronization mode
Data can be synchronized between devices in either of the following ways:
- Pushing data from a local device to a remote device.
- Pulling data from a remote device to a local device.
## Working Principles ## Working Principles
...@@ -44,12 +51,10 @@ When data is added, deleted, or modified, a notification is sent to the subscrib ...@@ -44,12 +51,10 @@ When data is added, deleted, or modified, a notification is sent to the subscrib
- Each RDB store supports a maximum of eight callbacks for subscription of data change notifications. - Each RDB store supports a maximum of eight callbacks for subscription of data change notifications.
- Third-party applications cannot call the distributed APIs that must be specified with the device.
## Available APIs ## Available APIs
The following table lists the APIs for cross-device data synchronization of RDB stores. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). Most of the APIs for cross-device data synchronization of RDB stores are executed asynchronously in callback or promise mode. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
...@@ -73,7 +78,7 @@ The following table lists the APIs for cross-device data synchronization of RDB ...@@ -73,7 +78,7 @@ The following table lists the APIs for cross-device data synchronization of RDB
import relationalStore from '@ohos.data.relationalStore'; import relationalStore from '@ohos.data.relationalStore';
``` ```
2. Request permissions. 2. Apply for the required permission.
1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). 1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file).
2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). 2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization).
...@@ -142,20 +147,19 @@ The following table lists the APIs for cross-device data synchronization of RDB ...@@ -142,20 +147,19 @@ The following table lists the APIs for cross-device data synchronization of RDB
> **NOTE** > **NOTE**
> >
> **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications. > The value of **deviceIds** can be obtained by [deviceManager.getAvailableDeviceListSync](../reference/apis/js-apis-distributedDeviceManager.md#getavailabledevicelistsync).
```js ```js
// Obtain device IDs. // Obtain device IDs.
import deviceManager from '@ohos.distributedHardware.deviceManager'; import deviceManager from '@ohos.distributedDeviceManager';
let dmInstance = null;
let deviceId = null;
deviceManager.createDeviceManager("com.example.appdatamgrverify", (err, manager) => { try {
if (err) { dmInstance = deviceManager.createDeviceManager("com.example.appdatamgrverify");
console.info(`Failed to create device manager. Code:${err.code},message:${err.message}`); let devices = dmInstance.getAvailableDeviceListSync();
return; deviceId = devices[0].networkId;
}
let devices = manager.getTrustedDeviceListSync();
let deviceId = devices[0].deviceId;
// Construct a predicate object for querying the distributed table. // Construct a predicate object for querying the distributed table.
let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
...@@ -169,5 +173,7 @@ The following table lists the APIs for cross-device data synchronization of RDB ...@@ -169,5 +173,7 @@ The following table lists the APIs for cross-device data synchronization of RDB
console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`); console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`);
} }
) )
}) } catch (err) {
console.error("createDeviceManager errCode:" + err.code + ",errMessage:" + err.message);
}
``` ```
\ No newline at end of file
...@@ -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)
......
# Full SDK Compilation # Full SDK Compilation
The full SDK provides a full set of APIs available in OpenHarmony, including system APIs required by system applications. Vendors can leverage this SDK to develop applications. The full SDK provides a full set of APIs available in OpenHarmony, including system APIs required by system applications. Vendors can use this SDK to develop applications.
Since OpenHarmony 3.2 Beta5, the full SDK is not provided with the version. If necessary, you can compile the SDK using the full source code. Since OpenHarmony 3.2 Beta5, the full SDK is not provided with the version. You can obtain the full SDK by compiling the source code.
## Compiling the Full SDK ## Compiling the Full SDK
...@@ -17,13 +17,20 @@ Since OpenHarmony 3.2 Beta5, the full SDK is not provided with the version. If n ...@@ -17,13 +17,20 @@ Since OpenHarmony 3.2 Beta5, the full SDK is not provided with the version. If n
2. Download source code based on the downloaded manifest repository: **repo sync -c -d --force-sync** 2. Download source code based on the downloaded manifest repository: **repo sync -c -d --force-sync**
3. Perform precompilation: **yes y | apt install libxinerama-dev libxcursor-dev libxrandr-dev libxi-dev && rm -rf prebuilts/clang/ohos/darwin-x86_64/clang-480513 && rm -rf prebuilts/clang/ohos/windows-x86_64/clang-480513 && rm -rf prebuilts/clang/ohos/linux-x86_64/clang-480513 && bash build/prebuilts_download.sh -ndk && apt-get update -qqy && apt-get install doxygen -y --force-yes** 3. Perform precompilation:
4. Perform compilation: **./build.sh --product-name ohos-sdk** yes y | apt install libxinerama-dev libxcursor-dev libxrandr-dev libxi-dev && rm -rf prebuilts/clang/ohos/darwin-x86_64/clang-480513 && rm -rf prebuilts/clang/ohos/windows-x86_64/clang-480513 && rm -rf prebuilts/clang/ohos/linux-x86_64/clang-480513 && apt-get update -qqy && apt-get install doxygen -y --force-yes
4. Perform compilation on Windows:
./build.sh --product-name ohos-sdk
5. Perform compilation on macOS:
./build.sh --product-name ohos-sdk --gn-args full_mini_debug=false --keep-ninja-going --gn-args=is_use_check_deps=false
If the compilation is successful, export the files in the **out/sdk/packages/ohos-sdk/** directory. If the compilation is successful, export the files in the **out/sdk/packages/ohos-sdk/** directory.
## Replacing the SDK ## Replacing the SDK
After the full SDK is compiled, switch to it in DevEco Studio. For details, see [Guide to Switching to Full SDK](full-sdk-switch-guide.md). After the full SDK is compiled, switch to it in DevEco Studio. For details, see [Switching to Full SDK](full-sdk-switch-guide.md).
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
When a user needs to download a file from the network to a local directory or save a user file into another directory, use **FilePicker** to save the file. When a user needs to download a file from the network to a local directory or save a user file into another directory, use **FilePicker** to save the file.
The operations for saving images, audio or video clips, and documents are similar. Call **save()** of the corresponding picker instance and pass in **saveOptions**. No permission is required if your application uses **FilePicker** to access files. The operations for saving images, audio or video clips, and documents are similar. Call **save()** of the corresponding picker instance and pass in **saveOptions**. You do not need to apply for any permission for this type of APIs.
The **save()** method saves files in the file manager, not in **Gallery**. The **save()** method saves files in the file manager, not in **Gallery**.
...@@ -58,7 +58,7 @@ For example, select an image from **Gallery** and save it to the file manager. ...@@ -58,7 +58,7 @@ For example, select an image from **Gallery** and save it to the file manager.
The permission on the URI returned by **save()** is read/write. Further operations on the file can be performed based on the URI in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URI returned by **save()** is read/write. Further operations on the file can be performed based on the URI in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let uri:string; let uris = null;
async photoViewPickerSave() { async photoViewPickerSave() {
try { try {
const photoSaveOptions = new picker.PhotoSaveOptions(); // Create a photoSaveOptions instance. const photoSaveOptions = new picker.PhotoSaveOptions(); // Create a photoSaveOptions instance.
...@@ -68,9 +68,8 @@ For example, select an image from **Gallery** and save it to the file manager. ...@@ -68,9 +68,8 @@ For example, select an image from **Gallery** and save it to the file manager.
try { try {
let photoSaveResult = await photoViewPicker.save(photoSaveOptions); let photoSaveResult = await photoViewPicker.save(photoSaveOptions);
if (photoSaveResult != undefined) { if (photoSaveResult != undefined) {
console.info("[picker] photoViewPickerSave photoSaveResult = " + JSON.stringify(photoSaveResult)); uris = photoSaveResult;
this.uri = photoSaveResult[0]; console.info('photoViewPicker.save to file succeed and uris are:' + uris);
console.info('photoViewPicker.save to file succeed and uri is:' + photoSaveResult[0]);
} }
} catch (err) { } catch (err) {
console.error(`[picker] Invoke photoViewPicker.save failed, code is ${err.code}, message is ${err.message}`); console.error(`[picker] Invoke photoViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
...@@ -112,6 +111,7 @@ For example, select an image from **Gallery** and save it to the file manager. ...@@ -112,6 +111,7 @@ For example, select an image from **Gallery** and save it to the file manager.
```ts ```ts
const documentSaveOptions = new picker.DocumentSaveOptions(); // Create a documentSaveOptions instance. const documentSaveOptions = new picker.DocumentSaveOptions(); // Create a documentSaveOptions instance.
documentSaveOptions.newFileNames = ["DocumentViewPicker01.txt"]; // (Optional) Set the name of the document to save. documentSaveOptions.newFileNames = ["DocumentViewPicker01.txt"]; // (Optional) Set the name of the document to save.
documentSaveOptions.fileSuffixChoices = ['.png', '.txt', '.mp4']; // (Optional) Types of the documents to save.
``` ```
3. Create a **documentViewPicker** instance, and call [save()](../reference/apis/js-apis-file-picker.md#save-3) to open the **FilePicker** page to save the document. After the user selects the destination folder, the document is saved and the URI of the document saved is returned. 3. Create a **documentViewPicker** instance, and call [save()](../reference/apis/js-apis-file-picker.md#save-3) to open the **FilePicker** page to save the document. After the user selects the destination folder, the document is saved and the URI of the document saved is returned.
...@@ -119,11 +119,11 @@ For example, select an image from **Gallery** and save it to the file manager. ...@@ -119,11 +119,11 @@ For example, select an image from **Gallery** and save it to the file manager.
The permission on the URI returned by **save()** is read/write. Further operations on the file can be performed based on the URI in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URI returned by **save()** is read/write. Further operations on the file can be performed based on the URI in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let uri = null; let uris = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => { documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => {
uri = documentSaveResult[0]; uris = documentSaveResult;
console.info('documentViewPicker.save to file succeed and uri is:' + uri); console.info('documentViewPicker.save to file succeed and uris are:' + uris);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
}) })
......
# Selecting User Files # Selecting User Files
If your application needs to support share and saving of user files (such as images and videos), you can use OpenHarmony [FilePicker](../reference/apis/js-apis-file-picker.md) to implement selection and saving of user files. No permission is required if your application uses **FilePicker** to access files. If your application needs to support share and saving of user files (such as images and videos), you can use OpenHarmony [FilePicker](../reference/apis/js-apis-file-picker.md) to implement selection and saving of user files. You do not need to apply for any permission for this type of APIs.
The **FilePicker** provides the following interfaces by file type: The **FilePicker** provides the following interfaces by file type:
...@@ -25,9 +25,9 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -25,9 +25,9 @@ The **FilePicker** provides the following interfaces by file type:
const photoSelectOptions = new picker.PhotoSelectOptions(); const photoSelectOptions = new picker.PhotoSelectOptions();
``` ```
3. Set the file type and the maximum number of media files to select.<br> 3. Set the file type and the maximum number of media files to select.
For example, select a maximum of five images. For details about the media file types, see [PhotoViewMIMETypes](../reference/apis/js-apis-file-picker.md#photoviewmimetypes).
For example, select a maximum of five images. For details about the media file types, see [PhotoViewMIMETypes](../reference/apis/js-apis-file-picker.md#photoviewmimetypes).
```ts ```ts
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; // Select images. photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE; // Select images.
photoSelectOptions.maxSelectNumber = 5; // Set the maximum number of images to select. photoSelectOptions.maxSelectNumber = 5; // Set the maximum number of images to select.
...@@ -38,11 +38,11 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -38,11 +38,11 @@ The **FilePicker** provides the following interfaces by file type:
The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let uri = null; let uris = null;
const photoViewPicker = new picker.PhotoViewPicker(); const photoViewPicker = new picker.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => { photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
uri = photoSelectResult.photoUris[0]; uris = photoSelectResult.photoUris;
console.info('photoViewPicker.select to file succeed and uri is:' + uri); console.info('photoViewPicker.select to file succeed and uris are:' + uris);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -77,6 +77,9 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -77,6 +77,9 @@ The **FilePicker** provides the following interfaces by file type:
```ts ```ts
const documentSelectOptions = new picker.DocumentSelectOptions(); const documentSelectOptions = new picker.DocumentSelectOptions();
documentSelectOptions.maxSelectNumber = 5; // (Optional) Maximum number of documents to select.
documentSelectOptions.defaultFilePathUri = "file://docs/storage/Users/currentUser/test"; // (Optional) Path of the file or directory to select.
documentSelectOptions.fileSuffixFilters = ['.png', '.txt', '.mp4']; // (Optional) File name extensions of the documents to select.
``` ```
3. Create a **documentViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-3) to open the **FilePicker** page for the user to select documents. After the documents are selected, a result set containing the file URIs is returned. 3. Create a **documentViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-3) to open the **FilePicker** page for the user to select documents. After the documents are selected, a result set containing the file URIs is returned.
...@@ -85,16 +88,12 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -85,16 +88,12 @@ The **FilePicker** provides the following interfaces by file type:
For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attributes, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md). For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attributes, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md).
> **NOTE**
>
> Currently, **DocumentSelectOptions** is not configurable. By default, all types of user files are selected.
```ts ```ts
let uri = null; let uris = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => { documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
uri = documentSelectResult[0]; uris = documentSelectResult;
console.info('documentViewPicker.select to file succeed and uri is:' + uri); console.info('documentViewPicker.select to file succeed and uris are:' + uris);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -172,7 +171,7 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -172,7 +171,7 @@ The **FilePicker** provides the following interfaces by file type:
let uri = null; let uri = null;
const audioViewPicker = new picker.AudioViewPicker(); const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.select(audioSelectOptions).then(audioSelectResult => { audioViewPicker.select(audioSelectOptions).then(audioSelectResult => {
uri = audioSelectOptions[0]; uri = audioSelectResult[0];
console.info('audioViewPicker.select to file succeed and uri is:' + uri); console.info('audioViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
......
...@@ -484,3 +484,8 @@ The development process of the Neural Network Runtime consists of three phases: ...@@ -484,3 +484,8 @@ The development process of the Neural Network Runtime consists of three phases:
rm /data/local/tmp/*nncache rm /data/local/tmp/*nncache
``` ```
## Samples
The following sample is provided to help you understand how to connect a third-party AI inference framework to the Neural Network Runtime:
- [Development Guide for Connecting TensorFlow Lite to NNRt Delegate](https://gitee.com/openharmony/ai_neural_network_runtime/tree/master/example/deep_learning_framework)
...@@ -81,7 +81,7 @@ The following describes how to use the **\<XComponent>** to call the native APIs ...@@ -81,7 +81,7 @@ The following describes how to use the **\<XComponent>** to call the native APIs
// ... // ...
``` ```
2. Register the N-API module. For details, see [Using Native APIs in Application Projects](https://gitee.com/openharmony/docs/blob/master/en/application-dev/napi/napi-guidelines.md). 2. Register the N-API module. For details, see [Using Native APIs in Application Projects](napi-guidelines.md).
```c++ ```c++
// In the napi_init.cpp file, use the Init method to register the target function to transfer the encapsulated C++ methods for the JS side to call. // In the napi_init.cpp file, use the Init method to register the target function to transfer the encapsulated C++ methods for the JS side to call.
......
...@@ -6,7 +6,7 @@ Following these practices, you can reduce your application's startup time, respo ...@@ -6,7 +6,7 @@ Following these practices, you can reduce your application's startup time, respo
- Improving application startup and response time - Improving application startup and response time
- [Speeding Up Application Cold Start](../performance/improve-application-startup-and-response/improve-application-cold-start-speed.md) - [Speeding Up Application Cold Start](improve-application-startup-and-response/improve-application-cold-start-speed.md)
Application startup latency is a key factor that affects user experience. To speed up the application cold start, you are advised to perform optimization in the following four phases: Application startup latency is a key factor that affects user experience. To speed up the application cold start, you are advised to perform optimization in the following four phases:
...@@ -18,16 +18,16 @@ Following these practices, you can reduce your application's startup time, respo ...@@ -18,16 +18,16 @@ Following these practices, you can reduce your application's startup time, respo
​ 4. Home page loading and drawing ​ 4. Home page loading and drawing
- [Speeding Up Application Response](../performance/improve-application-startup-and-response/improve-application-response.md) - [Speeding Up Application Response](improve-application-startup-and-response/improve-application-response.md)
A premium interaction experience requires quick response to user input. To improve your application's response time, you are advised to prevent the main thread from being blocked by non-UI tasks and reduce the number of component to be refreshed. A premium interaction experience requires quick response to user input. To improve your application's response time, you are advised to prevent the main thread from being blocked by non-UI tasks and reduce the number of components to be refreshed.
- Reducing frame loss - Reducing frame loss
- [Reducing Nesting](../performance/reduce-frame-loss-and-frame-freezing/reduce-view-nesting-levels.md) - [Reducing Nesting](reduce-frame-loss-and-frame-freezing/reduce-view-nesting-levels.md)
The smoothness of rendering the layout to the screen affects the user perceived quality. It is recommended that you minimize nesting in your code to shorten the render time. The smoothness of rendering the layout to the screen affects the user perceived quality. It is recommended that you minimize nesting in your code to shorten the render time.
- [Reducing Frame Loss](../performance/reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md) - [Reducing Frame Loss](reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md)
Whether animations in your application run smoothly is a key factor that affects user experience. You are advised to use the system-provided animation APIs to reduce frame loss. Whether animations in your application run smoothly is a key factor that affects user experience. You are advised to use the system-provided animation APIs to reduce frame loss.
...@@ -4,7 +4,7 @@ Application startup latency is a key factor that affects user experience. When a ...@@ -4,7 +4,7 @@ Application startup latency is a key factor that affects user experience. When a
## Analyzing the Time Required for Application Cold Start ## Analyzing the Time Required for Application Cold Start
The cold start process of OpenHarmony applications can be divided into four phases: application process creation and initialization, application and ability initialization, ability lifecycle, and home page loading and drawing, as shown in the following figure. The cold start process of OpenHarmony applications can be divided into four phases: application process creation and initialization, application and ability initialization, ability lifecycle, and home page loading and drawing, as shown below.
![application-cold-start](../figure/application-cold-start.png) ![application-cold-start](../figure/application-cold-start.png)
...@@ -24,7 +24,7 @@ With regard to the icon of the startup page, the recommended maximum resolution ...@@ -24,7 +24,7 @@ With regard to the icon of the startup page, the recommended maximum resolution
"description": "$string:EntryAbility_desc", "description": "$string:EntryAbility_desc",
"icon": "$media:icon", "icon": "$media:icon",
"label": "$string:EntryAbility_label", "label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startWindowIcon", // Modify the icon of the startup page. It is recommended that the icon be less than or equal to 256 pixels x 256 pixels. "startWindowIcon": "$media:startWindowIcon", // Modify the icon of the startup page. It is recommended that the icon be less than or equal to 256 x 256 pixels.
"startWindowBackground": "$color:start_window_background", "startWindowBackground": "$color:start_window_background",
"visible": true, "visible": true,
"skills": [ "skills": [
...@@ -43,7 +43,7 @@ With regard to the icon of the startup page, the recommended maximum resolution ...@@ -43,7 +43,7 @@ With regard to the icon of the startup page, the recommended maximum resolution
## 2. Shortening Time Required for Application and Ability Initialization ## 2. Shortening Time Required for Application and Ability Initialization
In this phase of application and ability initialization, resources are loaded, VMs are created, application and ability related objects are created and initialized, and dependent modules are loaded. In this phase of application and ability initialization, resources are loaded, virtual machines are created, application and ability related objects are created and initialized, and dependent modules are loaded.
### Minimizing the Number of Imported Modules ### Minimizing the Number of Imported Modules
...@@ -57,7 +57,7 @@ In this phase of ability lifecycle, the ability lifecycle callbacks are executed ...@@ -57,7 +57,7 @@ In this phase of ability lifecycle, the ability lifecycle callbacks are executed
In the application startup process, the system executes the ability lifecycle callbacks. Whenever possible, avoid performing time-consuming operations in these callbacks. You are advised to perform time-consuming operations through asynchronous tasks or execute them in other threads. In the application startup process, the system executes the ability lifecycle callbacks. Whenever possible, avoid performing time-consuming operations in these callbacks. You are advised to perform time-consuming operations through asynchronous tasks or execute them in other threads.
In these lifecycle callbacks, perform only necessary operations. For details, see [UIAbility Lifecycle](https://gitee.com/openharmony/docs/blob/master/en/application-dev/application-models/uiability-lifecycle.md). In these lifecycle callbacks, perform only necessary operations. For details, see [UIAbility Lifecycle](../../application-models/uiability-lifecycle.md).
## 4. Shortening Time Required for Home Page Loading and Drawing ## 4. Shortening Time Required for Home Page Loading and Drawing
......
...@@ -61,7 +61,7 @@ struct ImageExample1 { ...@@ -61,7 +61,7 @@ struct ImageExample1 {
### Using TaskPool for Asynchronous Processing ### Using TaskPool for Asynchronous Processing
Compared with the worker thread, [TaskPool](https://gitee.com/sqsyqqy/docs/blob/master/en/application-dev/reference/apis/js-apis-taskpool.md) provides the task priority setting and automatic thread pool management mechanism. The following is an example: Compared with the worker thread, [TaskPool](../../reference/apis/js-apis-taskpool.md) provides the task priority setting and automatic thread pool management mechanism. The following is an example:
```javascript ```javascript
import taskpool from '@ohos.taskpool'; import taskpool from '@ohos.taskpool';
......
...@@ -90,7 +90,7 @@ struct AttrAnimationExample { ...@@ -90,7 +90,7 @@ struct AttrAnimationExample {
} }
``` ```
For more details, see [Property Animator](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/arkui-ts/ts-animatorproperty.md). For more details, see [Property Animation](../../reference/arkui-ts/ts-animatorproperty.md).
## Using System-Provided Explicit Animation APIs ## Using System-Provided Explicit Animation APIs
...@@ -139,4 +139,4 @@ struct AnimateToExample { ...@@ -139,4 +139,4 @@ struct AnimateToExample {
} }
``` ```
For more details, see [Explicit Animation](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/arkui-ts/ts-explicit-animation.md). For more details, see [Explicit Animation](../../reference/arkui-ts/ts-explicit-animation.md).
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
The decorators described in the previous topics are used to share state variables within a page, that is, within a component tree. If you want to share state data at the application level or across multiple pages, you would need to apply application-level state management. ArkTS provides a wide variety of application state management capabilities: The decorators described in the previous topics are used to share state variables within a page, that is, within a component tree. If you want to share state data at the application level or across multiple pages, you would need to apply application-level state management. ArkTS provides a wide variety of application state management capabilities:
- [LocalStorage](arkts-localstorage.md): API for storing the UI state, usually used for state sharing within a [UIAbility](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/apis/js-apis-app-ability-uiAbility.md) or between pages. - [LocalStorage](arkts-localstorage.md): API for storing the UI state, usually used for state sharing within a [UIAbility](../reference/apis/js-apis-app-ability-uiAbility.md) or between pages.
- [AppStorage](arkts-appstorage.md): special, singleton LocalStorage object within the application, which is created by the UI framework at application startup and provides the central storage for application UI state attributes. - [AppStorage](arkts-appstorage.md): special, singleton LocalStorage object within the application, which is created by the UI framework at application startup and provides the central storage for application UI state attributes.
- [PersistentStorage](arkts-persiststorage.md): API for persisting application attributes. It is usually used together with AppStorage to persist selected AppStorage attributes to the disk so that their values are the same upon application re-start as they were when the application was closed. - [PersistentStorage](arkts-persiststorage.md): API for persisting application attributes. It is usually used together with AppStorage to persist selected AppStorage attributes to the disk so that their values are the same upon application re-start as they were when the application was closed.
- [Environment](arkts-environment.md): a range of environment parameters regarding the device where the application runs. The environment parameters are synchronized to the AppStorage and can be used together with the AppStorage. - [Environment](arkts-environment.md): a range of environment parameters regarding the device where the application runs. The environment parameters are synchronized to AppStorage and can be used together with AppStorage.
...@@ -23,8 +23,7 @@ Selected state attributes of AppStorage can be synced with different data source ...@@ -23,8 +23,7 @@ Selected state attributes of AppStorage can be synced with different data source
As mentioned above, if you want to establish a binding between AppStorage and a custom component, you'll need the \@StorageProp and \@StorageLink decorators. Use \@StorageProp(key) or \@StorageLink(key) to decorate variables in the component, where **key** identifies the attribute in AppStorage. As mentioned above, if you want to establish a binding between AppStorage and a custom component, you'll need the \@StorageProp and \@StorageLink decorators. Use \@StorageProp(key) or \@StorageLink(key) to decorate variables in the component, where **key** identifies the attribute in AppStorage.
When a custom component is initialized, the \@StorageProp(key)/\@StorageLink(key) decorated variable is initialized with the value of the attribute with the given key in AppStorage. Local initialization is mandatory. If an attribute with the given key is missing from AppStorage, it will be added with the stated initializing value. (Whether the attribute with the given key exists in AppStorage depends on the application logic.) When a custom component is initialized, the \@StorageProp(key)/\@StorageLink(key) decorated variable is initialized with the value of the attribute with the given key in AppStorage. Whether the attribute with the given key exists in AppStorage depends on the application logic. This means that the attribute with the given key may be missing from AppStorage. In light of this, local initialization is mandatory for the \@StorageProp(key)/\@StorageLink(key) decorated variable.
By decorating a variable with \@StorageProp(key), a one-way data synchronization is established with the attribute with the given key in AppStorage. A local change can be made, but it will not be synchronized to AppStorage. An update to the attribute with the given key in AppStorage will overwrite local changes. By decorating a variable with \@StorageProp(key), a one-way data synchronization is established with the attribute with the given key in AppStorage. A local change can be made, but it will not be synchronized to AppStorage. An update to the attribute with the given key in AppStorage will overwrite local changes.
......
...@@ -21,14 +21,14 @@ Syntax: ...@@ -21,14 +21,14 @@ Syntax:
```ts ```ts
@Builder myBuilderFunction({ ... }) @Builder MyBuilderFunction({ ... })
``` ```
Usage: Usage:
```ts ```ts
this.myBuilderFunction({ ... }) this.MyBuilderFunction({ ... })
``` ```
- Defining one or more custom builder (\@Builder decorated) functions inside a custom component is allowed. Such a custom builder function can be considered as a private, special type of member functions of that component. - Defining one or more custom builder (\@Builder decorated) functions inside a custom component is allowed. Such a custom builder function can be considered as a private, special type of member functions of that component.
...@@ -66,7 +66,7 @@ There are two types of parameter passing for custom builder functions: [by-value ...@@ -66,7 +66,7 @@ There are two types of parameter passing for custom builder functions: [by-value
- The parameter type must be the same as the declared parameter type. The **undefined** or **null** constants as well as expressions evaluating to these values are not allowed. - The parameter type must be the same as the declared parameter type. The **undefined** or **null** constants as well as expressions evaluating to these values are not allowed.
- All parameters are immutable inside the custom builder function. If mutability and synchronization of the mutation is required, the custom builder should be replaced by a custom component with a [@Link](arkts-link.md) decorated variable. - All parameters are immutable inside the@Builder decorated function.
- The \@Builder function body follows the same [syntax rules](arkts-create-custom-components.md#build-function) as the **build** function. - The \@Builder function body follows the same [syntax rules](arkts-create-custom-components.md#build-function) as the **build** function.
......
...@@ -15,12 +15,11 @@ Environment is a singleton object created by the ArkUI framework at application ...@@ -15,12 +15,11 @@ Environment is a singleton object created by the ArkUI framework at application
- Use **Environment.EnvProp** to save the environment variables of the device to AppStorage. - Use **Environment.EnvProp** to save the environment variables of the device to AppStorage.
```ts ```ts
// Save the language code of the device to AppStorage. The default value is en. // Save languageCode to AppStorage. The default value is en.
// Whenever its value changes in the device environment, it will update its value in AppStorage.
Environment.EnvProp('languageCode', 'en'); Environment.EnvProp('languageCode', 'en');
``` ```
- To keep a component variable updated with changes in the device environment, this variable should be decorated with \@StorageProp. - Decorate the variables with \@StorageProp to link them with components.
```ts ```ts
@StorageProp('languageCode') lang : string = 'en'; @StorageProp('languageCode') lang : string = 'en';
...@@ -29,6 +28,7 @@ Environment is a singleton object created by the ArkUI framework at application ...@@ -29,6 +28,7 @@ Environment is a singleton object created by the ArkUI framework at application
The chain of updates is as follows: Environment > AppStorage > Component. The chain of updates is as follows: Environment > AppStorage > Component.
> **NOTE** > **NOTE**
>
> An \@StorageProp decorated variable can be locally modified, but the change will not be updated to AppStorage. This is because the environment variable parameters are read-only to the application. > An \@StorageProp decorated variable can be locally modified, but the change will not be updated to AppStorage. This is because the environment variable parameters are read-only to the application.
...@@ -69,3 +69,29 @@ if (lang.get() === 'en') { ...@@ -69,3 +69,29 @@ if (lang.get() === 'en') {
console.info('Hello!'); console.info('Hello!');
} }
``` ```
## Restrictions
Environment can be called only when the [UIContext](../reference/apis/js-apis-arkui-UIContext.md#uicontext), which can be obtained through [runScopedTask](../reference/apis/js-apis-arkui-UIContext.md#runscopedtask), is specified. If Environment is called otherwise, no device environment data can be obtained.
```ts
// EntryAbility.ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.loadContent('pages/Index');
let window = windowStage.getMainWindow()
window.then(window => {
let uicontext = window.getUIContext()
uicontext.runScopedTask(() => {
Environment.EnvProp('languageCode', 'en');
})
})
}
}
```
...@@ -7,16 +7,16 @@ ArkTS is the preferred main programming language for application development in ...@@ -7,16 +7,16 @@ ArkTS is the preferred main programming language for application development in
The following syntaxes in TS are restricted in ArkTS: The following syntaxes in TS are restricted in ArkTS:
- Static typing is enforced. Static typing is one of the most important features of ArkTS. If the program is statically typed, i.e. all types are known at the compile time, it’s much easier to understand which data structures are used in the code. At the same time, since all types are known before the program actually runs, code correctness can be verified by the compiler, which eliminates many runtime type checks and improves the performance. - Static typing is enforced. Static typing is one of the most important features of ArkTS. If the program is statically typed, that is, all types are known at the compile time, it's much easier to understand which data structures are used in the code. At the same time, since all types are known before the program actually runs, code correctness can be verified by the compiler, which eliminates many runtime type checks and improves the performance.
- Changing object layout in runtime is prohibited. To achieve maximum performance benefits, ArkTS requires that layout of objects does not change during program execution. - Changing object layout in runtime is prohibited. To achieve maximum performance benefits, ArkTS requires that layout of objects does not change during program execution.
- Semantics of operators is restricted. To achieve better performance and encourage developers to write cleaner code, ArkTS restricts the semantics of operators. Such as, the binary `+` operator supports only for strings and numbers but not for objects. - Semantics of operators is restricted. To achieve better performance and encourage developers to write cleaner code, ArkTS restricts the semantics of operators. For example, the binary `+` operator is supported for strings and numbers, but not for objects.
- structural typing is not supported. Support for structural typing is a major feature which needs lots of consideration and careful implementation in language specification, compiler and runtime. Currently, ArkTS does not supports structural typing. The team will be ready to reconsider based on real-world scenarios and feedback. - Structural typing is not supported. Support for structural typing is a major feature that needs lots of consideration and careful implementation in language specification, compiler and runtime. Currently, ArkTS does not supports structural typing. The team will be ready to reconsider this feature based on real-world scenarios and feedback.
The added features offered by ArkTS for ArkUI framework include the following: The added features offered by ArkTS for the ArkUI framework include the following:
- [Basic syntax](arkts-basic-syntax-overview.md): ArkTS defines declarative UI description, custom components, and dynamic extension of UI elements. All these, together with built-in components, event methods, and attribute methods in ArkUI, jointly underpin UI development. - [Basic syntax](arkts-basic-syntax-overview.md): ArkTS defines declarative UI description, custom components, and dynamic extension of UI elements. All these, together with built-in components, event methods, and attribute methods in ArkUI, jointly underpin UI development.
......
...@@ -36,7 +36,7 @@ LocalStorage provides two decorators based on the synchronization type of the co ...@@ -36,7 +36,7 @@ LocalStorage provides two decorators based on the synchronization type of the co
## Restrictions ## Restrictions
- Once created, the type of a named attribute cannot be changed. Subsequent calls to **Set** must set a value of same type. - Once created, the type of a named attribute cannot be changed. Subsequent calls to **Set** must set a value of same type.
- LocalStorage provides page-level storage. The [GetShared](../reference/arkui-ts/ts-state-management.md#getshared9) API can only obtain the LocalStorage instance transferred through [windowStage.loadContent](../reference/apis/js-apis-window.md#loadcontent9) in the current stage. Otherwise, **undefined** is returned. Example: [Sharing a LocalStorage Instance from UIAbility to One or More Pages](#sharing-a-localstorage-instance-from-uiability-to-one-or-more-pages). - LocalStorage provides page-level storage. The [GetShared](../reference/arkui-ts/ts-state-management.md#getshared10) API can only obtain the LocalStorage instance passed through [windowStage.loadContent](../reference/apis/js-apis-window.md#loadcontent9) in the current stage. If the instance is not available, **undefined** is returned. For the example, see [Example of Sharing a LocalStorage Instance from UIAbility to One or More Pages](#example-of-sharing-a-localstorage-instance-from-uiability-to-one-or-more-pages).
## \@LocalStorageProp ## \@LocalStorageProp
...@@ -300,9 +300,9 @@ struct CompA { ...@@ -300,9 +300,9 @@ struct CompA {
``` ```
### State Variable Synchronization Between Sibling Nodes ### Example of Syncing State Variable Between Sibling Components
This example shows how to use \@LocalStorageLink to create a two-way synchronization for the state between sibling nodes. This example shows how to use \@LocalStorageLink to create a two-way synchronization for the state between sibling components.
Check the changes in the **Parent** custom component. Check the changes in the **Parent** custom component.
...@@ -377,7 +377,7 @@ Changes in the **Child** custom component: ...@@ -377,7 +377,7 @@ Changes in the **Child** custom component:
``` ```
### Sharing a LocalStorage Instance from UIAbility to One or More Pages ### Example of Sharing a LocalStorage Instance from UIAbility to One or More Pages
In the preceding examples, the LocalStorage instance is shared only in an \@Entry decorated component and its owning child component (a page). To enable a LocalStorage instance to be shared across pages, you can create a LocalStorage instance in the owning UIAbility and call windowStage.[loadContent](../reference/apis/js-apis-window.md#loadcontent9). In the preceding examples, the LocalStorage instance is shared only in an \@Entry decorated component and its owning child component (a page). To enable a LocalStorage instance to be shared across pages, you can create a LocalStorage instance in the owning UIAbility and call windowStage.[loadContent](../reference/apis/js-apis-window.md#loadcontent9).
......
...@@ -24,7 +24,7 @@ Persistence of data is a relatively slow operation. Applications should avoid th ...@@ -24,7 +24,7 @@ Persistence of data is a relatively slow operation. Applications should avoid th
The preceding situations may overload the change process of persisted data. As a result, the PersistentStorage implementation may limit the change frequency of persisted attributes. The preceding situations may overload the change process of persisted data. As a result, the PersistentStorage implementation may limit the change frequency of persisted attributes.
PersistentStorage is associated with UIContext and can be called to persist data only when [UIContext](../reference/apis/js-apis-arkui-UIContext.md#uicontext) is specified. The context can be identified in [runScopedTask](../reference/apis/js-apis-arkui-UIContext.md#runscopedtask). PersistentStorage can be called to persist data only when the [UIContext](../reference/apis/js-apis-arkui-UIContext.md#uicontext), which can be obtained through [runScopedTask](../reference/apis/js-apis-arkui-UIContext.md#runscopedtask), is specified.
## Application Scenarios ## Application Scenarios
......
...@@ -3,6 +3,9 @@ ...@@ -3,6 +3,9 @@
**ForEach** enables repeated content based on array-type data. **ForEach** enables repeated content based on array-type data.
> **NOTE**
>
> Since API version 9, this API is supported in ArkTS widgets.
## API Description ## API Description
...@@ -19,8 +22,8 @@ ForEach( ...@@ -19,8 +22,8 @@ ForEach(
| Name | Type | Mandatory | Description | | Name | Type | Mandatory | Description |
| ------------- | ---------------------------------------- | ---- | ---------------------------------------- | | ------------- | ---------------------------------------- | ---- | ---------------------------------------- |
| arr | Array | Yes | An array, which can be empty, in which case no child component is created. The functions that return array-type values are also allowed, for example, **arr.slice (1, 3)**. The set functions cannot change any state variables including the array itself, such as **Array.splice**, **Array.sort**, and **Array.reverse**.| | arr | Array | Yes | An array, which can be empty, in which case no child component is created. The functions that return array-type values are also allowed, for example, **arr.slice (1, 3)**. The set functions cannot change any state variables including the array itself, such as **Array.splice**, **Array.sort**, and **Array.reverse**.|
| itemGenerator | (item:&nbsp;any,&nbsp;index?:&nbsp;number)&nbsp;=&gt;&nbsp;void | Yes | A lambda function used to generate one or more child components for each data item in an array. Each component and its child component list must be contained in parentheses.<br>**NOTE**<br>- The type of the child component must be the one allowed inside the parent container component of **ForEach**. For example, a **\<LitemItem>** child component is allowed only when the parent container component of **ForEach** is **\<List>**.<br>- The child build function is allowed to return an **if** or another **ForEach**. **ForEach** can be placed inside **if**.<br>- The optional **index** parameter should only be specified in the function signature if used in its body.| | itemGenerator | (item: any, index?: number) =&gt; void | Yes | A lambda function used to generate one or more child components for each data item in an array. Each component and its child component list must be contained in parentheses.<br>**NOTE**<br>- The type of the child component must be the one allowed inside the parent container component of **ForEach**. For example, a **\<ListItem>** child component is allowed only when the parent container component of **ForEach** is **\<List>**.<br>- The child build function is allowed to return an **if** or another **ForEach**. **ForEach** can be placed inside **if**.<br>- The optional **index** parameter should only be specified in the function signature if used in its body.|
| keyGenerator | (item:&nbsp;any,&nbsp;index?:&nbsp;number)&nbsp;=&gt;&nbsp;string | No | An anonymous function used to generate a unique and fixed key value for each data item in an array. This key-value generator is optional. However, for performance reasons, it is strongly recommended that the key-value generator be provided, so that the development framework can better identify array changes. For example, if no key-value generator is provided, a reverse of an array will result in rebuilding of all nodes in **ForEach**.<br>**NOTE**<br>- Two items inside the same array must never work out the same ID.<br>- If **index** is not used, an item's ID must not change when the item's position within the array changes. However, if **index** is used, then the ID must change when the item is moved within the array.<br>- When an item is replaced by a new one (with a different value), the ID of the replaced and the ID of the new item must be different.<br>- When **index** is used in the build function, it should also be used in the ID generation function.<br>- The ID generation function is not allowed to mutate any component state.| | keyGenerator | (item: any, index?: number) =&gt; string | No | An anonymous function used to generate a unique and fixed key value for each data item in an array. This key-value generator is optional. However, for performance reasons, it is strongly recommended that the key-value generator be provided, so that the development framework can better identify array changes. For example, if no key-value generator is provided, a reverse of an array will result in rebuilding of all nodes in **ForEach**.<br>**NOTE**<br>- Two items inside the same array must never work out the same ID.<br>- If **index** is not used, an item's ID must not change when the item's position within the array changes. However, if **index** is used, then the ID must change when the item is moved within the array.<br>- When an item is replaced by a new one (with a different value), the ID of the replaced and the ID of the new item must be different.<br>- When **index** is used in the build function, it should also be used in the ID generation function.<br>- The ID generation function is not allowed to mutate any component state.|
## Restrictions ## Restrictions
......
...@@ -45,12 +45,12 @@ If the style of each component needs to be set separately, this will result in a ...@@ -45,12 +45,12 @@ If the style of each component needs to be set separately, this will result in a
```ts ```ts
@Component @Component
struct FancyUse { struct FancyUse {
@State heightVlaue: number = 100 @State heightValue: number = 100
@Styles fancy() { @Styles fancy() {
.height(this.heightVlaue) .height(this.heightValue)
.backgroundColor(Color.Yellow) .backgroundColor(Color.Yellow)
.onClick(() => { .onClick(() => {
this.heightVlaue = 200 this.heightValue = 200
}) })
} }
} }
...@@ -77,14 +77,14 @@ The following example demonstrates the usage of \@Styles inside and outside a co ...@@ -77,14 +77,14 @@ The following example demonstrates the usage of \@Styles inside and outside a co
@Entry @Entry
@Component @Component
struct FancyUse { struct FancyUse {
@State heightVlaue: number = 100 @State heightValue: number = 100
// Define a \@Styles decorated method inside a component declaration. // Define a \@Styles decorated method inside a component declaration.
@Styles fancy() { @Styles fancy() {
.width(200) .width(200)
.height(this.heightVlaue) .height(this.heightValue)
.backgroundColor(Color.Yellow) .backgroundColor(Color.Yellow)
.onClick(() => { .onClick(() => {
this.heightVlaue = 200 this.heightValue = 200
}) })
} }
......
...@@ -11,6 +11,7 @@ HUAWEI DevEco Studio For OpenHarmony (DevEco Studio for short) is a one-stop int ...@@ -11,6 +11,7 @@ HUAWEI DevEco Studio For OpenHarmony (DevEco Studio for short) is a one-stop int
- **Multi-device bidirectional real-time preview**: Bidirectional preview, real-time preview, live preview, component preview, and multi-device preview of UI code to quickly view how your code runs on devices. For details, see [Previewing Your App/Service](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-previewing-app-service-0000001218760596). - **Multi-device bidirectional real-time preview**: Bidirectional preview, real-time preview, live preview, component preview, and multi-device preview of UI code to quickly view how your code runs on devices. For details, see [Previewing Your App/Service](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-previewing-app-service-0000001218760596).
- **High-performance build system**: Hvigor, a compilation and building tool to compile and package applications/services in one-click mode, better supporting ArkTS/JS development. - **High-performance build system**: Hvigor, a compilation and building tool to compile and package applications/services in one-click mode, better supporting ArkTS/JS development.
- **One-stop information acquisition**: A one-stop information acquisition platform that takes into account the developer journey of learning, development, and help seeking, in order to facilitate developer activities. - **One-stop information acquisition**: A one-stop information acquisition platform that takes into account the developer journey of learning, development, and help seeking, in order to facilitate developer activities.
- **Efficient code debugging**: Various debugging capabilities such as TS, JS, and C/C++ code breakpoint setting, single-step execution, and variable viewing, improving the efficiency of analyzing application/service issues. - **Efficient code debugging**: Various debugging capabilities such as ArkTS, JS, and C/C++ code breakpoint setting, single-step execution, and variable viewing, improving the efficiency of analyzing application/service issues.
- **Scenario-based profiling**: A performance profiling tool that provides scenario-based profiling features, such as real-time data monitoring, measurement of the time it takes to execute functions, and memory analysis, to help you quickly locate faulty code.
For more information, see [DevEco Studio User Guide (OpenHarmony)](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421). For more information, see [DevEco Studio User Guide (OpenHarmony)](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421).
...@@ -238,7 +238,7 @@ The **abilities** tag represents the UIAbility configuration of the module, whic ...@@ -238,7 +238,7 @@ The **abilities** tag represents the UIAbility configuration of the module, whic
| description | Description of the UIAbility component. The value is a string with a maximum of 255 bytes or a resource index to the description in multiple languages.| String| Yes (initial value: left empty)| | description | Description of the UIAbility component. The value is a string with a maximum of 255 bytes or a resource index to the description in multiple languages.| String| Yes (initial value: left empty)|
| icon | Icon of the UIAbility component. The value is an icon resource index.| String| Yes (initial value: left empty)<br>If **UIAbility** is set to **MainElement**, this attribute is mandatory.| | icon | Icon of the UIAbility component. The value is an icon resource index.| String| Yes (initial value: left empty)<br>If **UIAbility** is set to **MainElement**, this attribute is mandatory.|
| label | Name of the UIAbility component displayed to users. The value is a string resource index.| String| Yes (initial value: left empty)<br>If **UIAbility** is set to **MainElement**, this attribute is mandatory.| | label | Name of the UIAbility component displayed to users. The value is a string resource index.| String| Yes (initial value: left empty)<br>If **UIAbility** is set to **MainElement**, this attribute is mandatory.|
| permissions | Permissions required for another application to access the UIAbility component.<br>The value is an array of permission names predefined by the system, generally in the reverse domain name notation. It contains a maximum of 255 bytes.| String array| Yes (initial value: left empty)| | permissions | Permissions required for another application to access the UIAbility component.<br>The value is generally in the reverse domain name notation and contains a maximum of 255 bytes. It is an array of predefined permission names.| String array| Yes (initial value: left empty)|
| [metadata](#metadata)| Metadata information of the UIAbility component.| Object array| Yes (initial value: left empty)| | [metadata](#metadata)| Metadata information of the UIAbility component.| Object array| Yes (initial value: left empty)|
| exported | Whether the UIAbility component can be called by other applications.<br>- **true**: The UIAbility component can be called by other applications.<br>- **false**: The UIAbility component cannot be called by other applications.| Boolean| Yes (initial value: **false**)| | exported | Whether the UIAbility component can be called by other applications.<br>- **true**: The UIAbility component can be called by other applications.<br>- **false**: The UIAbility component cannot be called by other applications.| Boolean| Yes (initial value: **false**)|
| continuable | Whether the UIAbility component can be [migrated](../application-models/hop-cross-device-migration.md).<br>- **true**: The UIAbility component can be migrated.<br>- **false**: The UIAbility component cannot be migrated.| Boolean| Yes (initial value: **false**)| | continuable | Whether the UIAbility component can be [migrated](../application-models/hop-cross-device-migration.md).<br>- **true**: The UIAbility component can be migrated.<br>- **false**: The UIAbility component cannot be migrated.| Boolean| Yes (initial value: **false**)|
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
> **NOTE** > **NOTE**
> >
> To use ArkTS, your DevEco Studio must be V3.0.0.900 Beta3 or later.
>
> In this document, DevEco Studio 4.0 Beta2 is used. You can download it [here](../../release-notes/OpenHarmony-v4.0-beta2.md#version-mapping). > In this document, DevEco Studio 4.0 Beta2 is used. You can download it [here](../../release-notes/OpenHarmony-v4.0-beta2.md#version-mapping).
## Creating an ArkTS Project ## Creating an ArkTS Project
...@@ -306,7 +304,13 @@ You can implement page redirection through the [page router](../reference/apis/j ...@@ -306,7 +304,13 @@ You can implement page redirection through the [page router](../reference/apis/j
.height('5%') .height('5%')
// Bind the onClick event to the Next button so that clicking the button redirects the user to the second page. // Bind the onClick event to the Next button so that clicking the button redirects the user to the second page.
.onClick(() => { .onClick(() => {
router.pushUrl({ url: 'pages/Second' }) console.info(`Succeeded in clicking the 'Next' button.`)
// Redirect the user to the second page.
router.pushUrl({ url: 'pages/Second' }).then(() => {
console.info('Succeeded in jumping to the second page.')
}).catch((err) => {
console.error(`Failed to jump to the second page.Code is ${err.code}, message is ${err.message}`)
})
}) })
} }
.width('100%') .width('100%')
...@@ -350,7 +354,14 @@ You can implement page redirection through the [page router](../reference/apis/j ...@@ -350,7 +354,14 @@ You can implement page redirection through the [page router](../reference/apis/j
.height('5%') .height('5%')
// Bind the onClick event to the Back button so that clicking the button redirects the user back to the first page. // Bind the onClick event to the Back button so that clicking the button redirects the user back to the first page.
.onClick(() => { .onClick(() => {
console.info(`Succeeded in clicking the 'Back' button.`)
try {
// Redirect the user back to the first page.
router.back() router.back()
console.info('Succeeded in returning to the first page.')
} catch (err) {
console.error(`Failed to return to the first page.Code is ${err.code}, message is ${err.message}`)
}
}) })
} }
.width('100%') .width('100%')
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册