提交 c676f9c6 编写于 作者: W wangkailong 提交者: Gitee

Merge branch 'OpenHarmony-3.2-Release' of gitee.com:openharmony/docs into OpenHarmony-3.2-Release

Signed-off-by: Nwangkailong <wangkailong6@huawei.com>

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
...@@ -383,7 +383,7 @@ zh-cn/application-dev/reference/apis/js-apis-lightweightmap.md @ge-yafang ...@@ -383,7 +383,7 @@ zh-cn/application-dev/reference/apis/js-apis-lightweightmap.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-lightweightset.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-lightweightset.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-linkedlist.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-linkedlist.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-list.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-list.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-logs.md @HelloCrease zh-cn/application-dev/reference/apis/js-apis-logs.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-media.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-media.md @zengyawen
zh-cn/application-dev/reference/apis/js-apis-medialibrary.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-medialibrary.md @zengyawen
zh-cn/application-dev/reference/apis/js-apis-mediaquery.md @HelloCrease zh-cn/application-dev/reference/apis/js-apis-mediaquery.md @HelloCrease
...@@ -454,7 +454,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-vibrate.md @ningningW ...@@ -454,7 +454,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-vibrate.md @ningningW
zh-cn/application-dev/reference/apis/js-apis-telephony-data.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-telephony-data.md @zengyawen
zh-cn/application-dev/reference/apis/js-apis-testRunner.md @RayShih zh-cn/application-dev/reference/apis/js-apis-testRunner.md @RayShih
zh-cn/application-dev/reference/apis/js-apis-thermal.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-thermal.md @zengyawen
zh-cn/application-dev/reference/apis/js-apis-timer.md @HelloCrease zh-cn/application-dev/reference/apis/js-apis-timer.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-touchevent.md @ningningW zh-cn/application-dev/reference/apis/js-apis-touchevent.md @ningningW
zh-cn/application-dev/reference/apis/js-apis-treemap.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-treemap.md @ge-yafang
zh-cn/application-dev/reference/apis/js-apis-treeset.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-treeset.md @ge-yafang
......
# OpenHarmony Project # OpenHarmony Project
> **NOTE**
>
> You are reading documents of OpenHarmony 3.2 Release. Obtain the [compatible SDK](../release-notes/OpenHarmony-v3.2-release.md#version-mapping) during your application development.
## Introduction ## Introduction
OpenHarmony is an open-source project incubated and operated by the OpenAtom Foundation. The purpose of this project is to build an open-source, distributed operating system (OS) framework for smart devices in all scenarios of a fully-connected world. OpenHarmony is an open-source project incubated and operated by the OpenAtom Foundation. The purpose of this project is to build an open-source, distributed operating system (OS) framework for smart devices in all scenarios of a fully-connected world.
...@@ -189,7 +193,7 @@ For details about how to obtain the source code of OpenHarmony, see [Source Code ...@@ -189,7 +193,7 @@ For details about how to obtain the source code of OpenHarmony, see [Source Code
## How to Participate ## How to Participate
For details about how to join in the OpenHarmony community, see [OpenHarmony Community](https://gitee.com/openharmony/community/blob/master/README-EN.md) For details about how to join in the OpenHarmony community, see [OpenHarmony Community](https://gitee.com/openharmony/community/blob/master/README_EN.md)
For details about how to contribute, see [How to contribute](contribute/how-to-contribute.md). For details about how to contribute, see [How to contribute](contribute/how-to-contribute.md).
......
...@@ -41,18 +41,46 @@ ...@@ -41,18 +41,46 @@
- [Resource Categories and Access](quick-start/resource-categories-and-access.md) - [Resource Categories and Access](quick-start/resource-categories-and-access.md)
- Learning ArkTS - Learning ArkTS
- [Getting Started with ArkTS](quick-start/arkts-get-started.md) - [Getting Started with ArkTS](quick-start/arkts-get-started.md)
- ArkTS Syntax (Declarative UI) - Basic Syntax
- [Basic UI Description](quick-start/arkts-basic-ui-description.md) - [Basic Syntax Overview](quick-start/arkts-basic-syntax-overview.md)
- State Management - [Declarative UI Description](quick-start/arkts-declarative-ui-description.md)
- [Basic Concepts](quick-start/arkts-state-mgmt-concepts.md) - Custom Component
- [State Management with Page-level Variables](quick-start/arkts-state-mgmt-page-level.md) - [Creating a Custom Component](quick-start/arkts-create-custom-components.md)
- [State Management with Application-level Variables](quick-start/arkts-state-mgmt-application-level.md) - [Page and Custom Component Lifecycle](quick-start/arkts-page-custom-components-lifecycle.md)
- [Dynamic UI Element Building](quick-start/arkts-dynamic-ui-elememt-building.md) - [\@Builder: Custom Builder Function](quick-start/arkts-builder.md)
- [Rendering Control](quick-start/arkts-rendering-control.md) - [\@BuilderParam: @Builder Function Reference](quick-start/arkts-builderparam.md)
- [Restrictions and Extensions](quick-start/arkts-restrictions-and-extensions.md) - [\@Styles: Definition of Resusable Styles](quick-start/arkts-style.md)
- [\@Extend: Extension of Built-in Components](quick-start/arkts-extend.md)
- [stateStyles: Polymorphic Style](quick-start/arkts-statestyles.md)
- State Management
- [State Management Overview](quick-start/arkts-state-management-overview.md)
- Component State Management
- [\@State: State Owned by Component](quick-start/arkts-state.md)
- [\@Prop: One-Way Synchronization from Parent to Child Components](quick-start/arkts-prop.md)
- [\@Link: Two-Way Synchronization Between Parent and Child Components](quick-start/arkts-link.md)
- [\@Provide and \@Consume: Two-Way Synchronization with Descendant Components](quick-start/arkts-provide-and-consume.md)
- [\@Observed and \@ObjectLink: Observing Attribute Changes in Nested Class Objects](quick-start/arkts-observed-and-objectlink.md)
- Application State Management
- [Application State Management Overview](quick-start/arkts-application-state-management-overview.md)
- [LocalStorage: UI State Storage](quick-start/arkts-localstorage.md)
- [AppStorage: Application-wide UI State Storage](quick-start/arkts-appstorage.md)
- [PersistentStorage: Application State Persistence](quick-start/arkts-persiststorage.md)
- [Environment: Device Environment Query](quick-start/arkts-environment.md)
- Other State Management Features
- [Overview of Other State Management Features](quick-start/arkts-other-state-mgmt-functions-overview.md)
- [\@Watch: Getting Notified of State Variable Changes](quick-start/arkts-watch.md)
- [$$ Syntax: Two-Way Synchronization of Built-in Components](quick-start/arkts-two-way-sync.md)
- Rendering Control
- [Rendering Control Overview](quick-start/arkts-rendering-control-overview.md)
- [if/else: Conditional Rendering](quick-start/arkts-rendering-control-ifelse.md)
- [ForEach: Rendering of Repeated Content](quick-start/arkts-rendering-control-foreach.md)
- [LazyForEach: Lazy Data Loading](quick-start/arkts-rendering-control-lazyforeach.md)
- Development - Development
- [Application Models](application-models/Readme-EN.md) - [Application Models](application-models/Readme-EN.md)
- [UI Development](ui/Readme-EN.md) - [UI Development](ui/Readme-EN.md)
- [Web](web/Readme-EN.md)
- [Notification](notification/Readme-EN.md) - [Notification](notification/Readme-EN.md)
- [Window Manager](windowmanager/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md)
- [WebGL](webgl/Readme-EN.md) - [WebGL](webgl/Readme-EN.md)
...@@ -86,6 +114,7 @@ ...@@ -86,6 +114,7 @@
- [ArkTS and JS APIs](reference/apis/Readme-EN.md) - [ArkTS and JS APIs](reference/apis/Readme-EN.md)
- [Error Codes](reference/errorcodes/Readme-EN.md) - [Error Codes](reference/errorcodes/Readme-EN.md)
- Native APIs - Native APIs
- [Native APIs](reference/native-apis/Readme-EN.md)
- [Standard Libraries](reference/native-lib/third_party_libc/musl.md) - [Standard Libraries](reference/native-lib/third_party_libc/musl.md)
- [Node_API](reference/native-lib/third_party_napi/napi.md) - [Node_API](reference/native-lib/third_party_napi/napi.md)
- [FAQs](faqs/Readme-EN.md) - [FAQs](faqs/Readme-EN.md)
......
...@@ -116,7 +116,7 @@ Example URIs: ...@@ -116,7 +116,7 @@ Example URIs:
| "name" | Ability name, corresponding to the **Data** class name derived from **Ability**. | | "name" | Ability name, corresponding to the **Data** class name derived from **Ability**. |
| "type" | Ability type, which is **Data** for a Data ability. | | "type" | Ability type, which is **Data** for a Data ability. |
| "uri" | URI used for communication. | | "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.| | "exported" | 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** **config.json configuration example**
...@@ -128,7 +128,7 @@ Example URIs: ...@@ -128,7 +128,7 @@ Example URIs:
"srcLanguage": "ets", "srcLanguage": "ets",
"description": "$string:description_dataability", "description": "$string:description_dataability",
"type": "data", "type": "data",
"visible": true, "exported": true,
"uri": "dataability://ohos.samples.etsdataability.DataAbility" "uri": "dataability://ohos.samples.etsdataability.DataAbility"
}] }]
``` ```
...@@ -154,7 +154,7 @@ The basic dependency packages include: ...@@ -154,7 +154,7 @@ The basic dependency packages include:
import featureAbility from '@ohos.ability.featureAbility' import featureAbility from '@ohos.ability.featureAbility'
import ohos_data_ability from '@ohos.data.dataAbility' import ohos_data_ability from '@ohos.data.dataAbility'
import ohos_data_rdb from '@ohos.data.rdb' import ohos_data_rdb from '@ohos.data.rdb'
var urivar = "dataability:///com.ix.DataAbility" var urivar = "dataability:///com.ix.DataAbility"
var DAHelper = featureAbility.acquireDataAbilityHelper( var DAHelper = featureAbility.acquireDataAbilityHelper(
urivar urivar
......
...@@ -25,7 +25,7 @@ Carry out the following operations to develop the widget provider based on the [ ...@@ -25,7 +25,7 @@ Carry out the following operations to develop the widget provider based on the [
1. Implement lifecycle callbacks by using the **LifecycleForm** APIs. 1. Implement lifecycle callbacks by using the **LifecycleForm** APIs.
2. Create a **FormBindingData** instance. 2. Create a **FormBindingData** instance.
3. Update a widget by using the **FormProvider** APIs. 3. Update a widget by using the **FormProvider** APIs.
4. Develop the widget UI pages. 4. Develop the widget UI page.
## Available APIs ## Available APIs
...@@ -231,7 +231,7 @@ You should override **onDestroy** to implement widget data deletion. ...@@ -231,7 +231,7 @@ You should override **onDestroy** to implement widget data deletion.
} }
``` ```
For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). For details about how to implement persistent data storage, see [Data Persistence by User Preferences](../database/data-persistence-by-preferences.md).
The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary.
...@@ -355,7 +355,7 @@ You can set router and message events for components on a widget. The router eve ...@@ -355,7 +355,7 @@ You can set router and message events for components on a widget. The router eve
1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file. 1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file.
2. Set the router event. 2. Set the router event.
- **action**: **"router"**, which indicates a router event. - **action**: **"router"**, which indicates a router event.
- **abilityName**: target ability name, for example, **com.example.entry.MainAbility**, which is the default UIAbility name in DevEco Studio for the FA model. - **abilityName**: target ability name, for example, **com.example.entry.MainAbility**, which is the default main ability name in DevEco Studio for the FA model.
- **params**: custom parameters of the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the main ability in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field. - **params**: custom parameters of the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the main ability in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field.
3. Set the message event. 3. Set the message event.
- **action**: **"message"**, which indicates a message event. - **action**: **"message"**, which indicates a message event.
......
...@@ -47,7 +47,7 @@ You can specify the launch type by setting **launchType** in the **config.json** ...@@ -47,7 +47,7 @@ You can specify the launch type by setting **launchType** in the **config.json**
| Launch Type | Description |Description | | Launch Type | Description |Description |
| ----------- | ------- |---------------- | | ----------- | ------- |---------------- |
| standard | Multi-instance | A new instance is started each time an ability starts.| | 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.| | 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. By default, **singleton** is used.
......
...@@ -48,96 +48,89 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar ...@@ -48,96 +48,89 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar
} }
``` ```
- Configure the application startup type. - Configure the application startup type.
If **launchType** is set to **standard** 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). 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.
Configure a multi-instance application as follows: Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application.
```javascript ```javascript
{ "requestPermissions": [
"module": { {
"abilities": [ "name": "ohos.permission.DISTRIBUTED_DATASYNC"
{ },
"launchType": "standard" ```
}
]
}
}
```
Configure a singleton application as follows or retain the default settings of **launchType**: 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 ```javascript
{ requestPermissions = async () => {
"module": { let permissions: Array<string> = [
"abilities": [ "ohos.permission.DISTRIBUTED_DATASYNC"
{ ];
"launchType": "singleton" 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")
}
} }
] ```
}
}
```
- 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. 2. Implement the **onContinue()** API.
...@@ -155,7 +148,7 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar ...@@ -155,7 +148,7 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar
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. 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 Example:
```javascript ```javascript
onContinue(wantParam : {[key: string]: any}) { onContinue(wantParam : {[key: string]: any}) {
...@@ -168,8 +161,6 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar ...@@ -168,8 +161,6 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar
} }
``` ```
3. Implement the continuation logic in the **onCreate()** or **onNewWant()** API. 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 **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.
...@@ -178,11 +169,9 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar ...@@ -178,11 +169,9 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar
After data restore is complete, call **restoreWindowStage** to trigger page restoration. 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. You can also use **want.parameters.version** in the **want** parameter to obtain the application version number of the initiator.
Example Example:
```javascript ```javascript
import Ability from '@ohos.application.Ability'; import Ability from '@ohos.application.Ability';
...@@ -211,9 +200,9 @@ For a singleton ability, use **onNewWant()** to achieve the same implementation. ...@@ -211,9 +200,9 @@ For a singleton ability, use **onNewWant()** to achieve the same implementation.
Use distributed objects. 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 [Distributed Data Object Development](../database/database-distributedobject-guidelines.md). 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 the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device. For details, see [Distributed Data Object Development](../database/database-distributedobject-guidelines.md).
- 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**. - 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**.
...@@ -249,8 +238,6 @@ In the ability continuation scenario, the distributed data object is used to syn ...@@ -249,8 +238,6 @@ In the ability continuation scenario, the distributed data object is used to syn
}); });
``` ```
- 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. - 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 ```javascript
...@@ -266,11 +253,11 @@ In the ability continuation scenario, the distributed data object is used to syn ...@@ -266,11 +253,11 @@ In the ability continuation scenario, the distributed data object is used to syn
onCreate(want, launchParam) { onCreate(want, launchParam) {
Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// Obtain the session ID of the distributed data object from the want parameter. // Obtain the session ID of the distributed data object from the want parameter.
this.sessionId = want.parameters.session this.sessionId = want.parameters.session
Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`)
// Before fetching data from the remote device, reset g_object.data to undefined. // Before fetching data from the remote device, reset g_object.data to undefined.
g_object.data = undefined; g_object.data = undefined;
// Set the session ID, so the target will fetch data from the remote device. // Set the session ID, so the target will fetch data from the remote device.
g_object.setSessionId(this.sessionId); g_object.setSessionId(this.sessionId);
...@@ -284,8 +271,6 @@ In the ability continuation scenario, the distributed data object is used to syn ...@@ -284,8 +271,6 @@ In the ability continuation scenario, the distributed data object is used to syn
} }
``` ```
### More Information ### More Information
1. Timeout 1. Timeout
...@@ -295,15 +280,11 @@ In the ability continuation scenario, the distributed data object is used to syn ...@@ -295,15 +280,11 @@ In the ability continuation scenario, the distributed data object is used to syn
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. 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 ### 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). 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. 2. Currently, the application can only implement the continuation capability. The continuation action must be initiated by the system.
### Best Practice ### 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. 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.
...@@ -12,7 +12,7 @@ An ability can be launched in the **standard**, **singleton**, or **specified** ...@@ -12,7 +12,7 @@ An ability can be launched in the **standard**, **singleton**, or **specified**
| Launch Type | Description |Action | | Launch Type | Description |Action |
| ----------- | ------- |---------------- | | ----------- | ------- |---------------- |
| standard | Standard mode | A new instance is started each time an ability starts.| | multiton | Multi-instance mode| A new instance is started each time an ability starts.|
| singleton | Singleton mode | The ability has only one instance in the system. If an instance already exists when an ability is started, that instance is reused.| | singleton | Singleton mode | 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.| | specified | Instance-specific| The internal service of an ability determines whether to create multiple instances during running.|
...@@ -71,7 +71,7 @@ To create Page abilities for an application in the stage model, you must impleme ...@@ -71,7 +71,7 @@ To create Page abilities for an application in the stage model, you must impleme
```js ```js
import Ability from '@ohos.application.Ability' import Ability from '@ohos.application.Ability'
``` ```
4. Implement the lifecycle callbacks of the **UIAbility** class. The default relative path generated by the APIs is **entry\src\main\ets\MainAbility\MainAbility.ts**. 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). 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 ```ts
...@@ -79,29 +79,29 @@ To create Page abilities for an application in the stage model, you must impleme ...@@ -79,29 +79,29 @@ To create Page abilities for an application in the stage model, you must impleme
onCreate(want, launchParam) { onCreate(want, launchParam) {
console.log("MainAbility onCreate") console.log("MainAbility onCreate")
} }
onDestroy() { onDestroy() {
console.log("MainAbility onDestroy") console.log("MainAbility onDestroy")
} }
onWindowStageCreate(windowStage) { onWindowStageCreate(windowStage) {
console.log("MainAbility onWindowStageCreate") console.log("MainAbility onWindowStageCreate")
windowStage.loadContent("pages/index").then(() => { windowStage.loadContent("pages/index").then(() => {
console.log("MainAbility load content succeed") console.log("MainAbility load content succeed")
}).catch((error) => { }).catch((error) => {
console.error("MainAbility load content failed with error: " + JSON.stringify(error)) console.error("MainAbility load content failed with error: " + JSON.stringify(error))
}) })
} }
onWindowStageDestroy() { onWindowStageDestroy() {
console.log("MainAbility onWindowStageDestroy") console.log("MainAbility onWindowStageDestroy")
} }
onForeground() { onForeground() {
console.log("MainAbility onForeground") console.log("MainAbility onForeground")
} }
onBackground() { onBackground() {
console.log("MainAbility onBackground") console.log("MainAbility onBackground")
} }
...@@ -114,6 +114,7 @@ The following example shows how an application obtains the bundle code directory ...@@ -114,6 +114,7 @@ The following example shows how an application obtains the bundle code directory
```ts ```ts
import AbilityStage from "@ohos.application.AbilityStage" import AbilityStage from "@ohos.application.AbilityStage"
export default class MyAbilityStage extends AbilityStage { export default class MyAbilityStage extends AbilityStage {
onCreate() { onCreate() {
console.log("MyAbilityStage onCreate") console.log("MyAbilityStage onCreate")
...@@ -188,7 +189,7 @@ export default class MainAbility extends Ability { ...@@ -188,7 +189,7 @@ export default class MainAbility extends Ability {
``` ```
## Starting an Ability ## Starting an Ability
### Available APIs ### 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-ability-context.md). 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-applicationContext.md).
**Table 3** AbilityContext APIs **Table 3** AbilityContext APIs
|API|Description| |API|Description|
......
...@@ -47,242 +47,263 @@ The table below describes the ability call APIs. For details, see [Ability](../r ...@@ -47,242 +47,263 @@ The table below describes the ability call APIs. For details, see [Ability](../r
## How to Develop ## How to Develop
The procedure for developing the ability call is as follows: The procedure for developing the ability call is as follows:
1. Create a callee ability. 1. Create a callee ability.
2. Access the callee ability. 2. Access the callee ability.
### Creating a 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. 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| 1. **Configure the ability launch type.**
|:------|:------|
|"launchType"|Ability launch type. Set this parameter to **singleton**.| Set **launchType** of the callee ability to **singleton** in the **module.json5** file.
An example of the ability configuration is as follows: |JSON Field|Description|
```json |:------|:------|
"abilities":[{ |"launchType"|Ability launch type. Set this parameter to **singleton**.|
"name": ".CalleeAbility",
"srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", An example of the ability configuration is as follows:
"launchType": "singleton",
"description": "$string:CalleeAbility_desc", ```json
"icon": "$media:icon", "abilities":[{
"label": "$string:CalleeAbility_label", "name": ".CalleeAbility",
"visible": true "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts",
}] "launchType": "singleton",
``` "description": "$string:CalleeAbility_desc",
**2. Import the Ability module.** "icon": "$media:icon",
```ts "label": "$string:CalleeAbility_label",
import Ability from '@ohos.app.ability.UIAbility' "exported": true
``` }]
**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: 2. **Import the ability module.**
```ts
export default class MySequenceable { ```ts
num: number = 0 import Ability from '@ohos.app.ability.UIAbility';
str: string = "" ```
constructor(num, string) { 3. **Define the agreed sequenceable data.**
this.num = num
this.str = string 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
marshalling(messageParcel) { export default class MySequenceable {
messageParcel.writeInt(this.num) num: number = 0
messageParcel.writeString(this.str) str: string = ""
return true
} constructor(num, string) {
this.num = num
unmarshalling(messageParcel) { this.str = string
this.num = messageParcel.readInt() }
this.str = messageParcel.readString()
return true marshalling(messageParcel) {
} messageParcel.writeInt(this.num)
} messageParcel.writeString(this.str)
``` 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: unmarshalling(messageParcel) {
```ts this.num = messageParcel.readInt()
const TAG: string = '[CalleeAbility]' this.str = messageParcel.readString()
const MSG_SEND_METHOD: string = 'CallSendMsg' return true
}
function sendMsgCallback(data) { }
console.log('CalleeSortFunc called') ```
// Obtain the sequenceable data sent by the caller ability. 4. **Implement Callee.on and Callee.off.**
let receivedData = new MySequenceable(0, '')
data.readSequenceable(receivedData) 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:
console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
```ts
// Process the data. const TAG: string = '[CalleeAbility]'
// Return the sequenceable data result to the caller ability. const MSG_SEND_METHOD: string = 'CallSendMsg'
return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
} function sendMsgCallback(data) {
console.log('CalleeSortFunc called')
export default class CalleeAbility extends Ability {
onCreate(want, launchParam) { // Obtain the sequenceable data sent by the caller ability.
try { let receivedData = new MySequenceable(0, '')
this.callee.on(MSG_SEND_METHOD, sendMsgCallback) data.readSequenceable(receivedData)
} catch (error) { console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`)
console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`)
} // Process the data.
} // Return the sequenceable data result to the caller ability.
return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`)
onDestroy() { }
try {
this.callee.off(MSG_SEND_METHOD) export default class CalleeAbility extends Ability {
} catch (error) { onCreate(want, launchParam) {
console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) 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 ### Accessing the Callee Ability
**1. Import the Ability module.** 1. **Import the Ability module.**
```ts
import Ability from '@ohos.app.ability.UIAbility' ```ts
``` import Ability 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: 2. **Obtain the Caller object.**
```ts
// Register the onRelease listener of the caller ability. 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:
private regOnRelease(caller) {
try { ```ts
caller.on("release", (msg) => { // Register the onRelease listener of the caller ability.
console.log(`caller onRelease is called ${msg}`) private regOnRelease(caller) {
}) try {
console.log('caller register OnRelease succeed') caller.on("release", (msg) => {
} catch (error) { console.log(`caller onRelease is called ${msg}`)
console.log(`caller register OnRelease failed with ${error}`) })
} 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', async onButtonGetCaller() {
abilityName: 'CalleeAbility' try {
}) this.caller = await context.startAbilityByCall({
if (this.caller === undefined) { bundleName: 'com.samples.CallApplication',
console.log('get caller failed') abilityName: 'CalleeAbility'
return })
} if (this.caller === undefined) {
console.log('get caller success') console.log('get caller failed')
this.regOnRelease(this.caller) return
} catch (error) { }
console.log(`get caller failed with ${error}`) 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 In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows:
context.startAbilityByCall({ ```ts
deviceId: getRemoteDeviceId(), async onButtonGetRemoteCaller() {
bundleName: 'com.samples.CallApplication', var caller = undefined
abilityName: 'CalleeAbility' var context = this.context
}).then((data) => {
if (data != null) { context.startAbilityByCall({
caller = data deviceId: getRemoteDeviceId(),
console.log('get remote caller success') bundleName: 'com.samples.CallApplication',
// Register the onRelease listener of the caller ability. abilityName: 'CalleeAbility'
caller.on("release", (msg) => { }).then((data) => {
console.log(`remote caller onRelease is called ${msg}`) if (data != null) {
}) caller = data
console.log('remote caller register OnRelease succeed') console.log('get remote caller success')
} // Register the onRelease listener of the caller ability.
}).catch((error) => { caller.on("release", (msg) => {
console.error(`get remote caller failed with ${error}`) console.log(`remote caller onRelease is called ${msg}`)
}) })
} console.log('remote caller register OnRelease succeed')
``` }
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: }).catch((error) => {
```ts console.error(`get remote caller failed with ${error}`)
import deviceManager from '@ohos.distributedHardware.deviceManager'; })
var dmClass; }
function getRemoteDeviceId() { ```
if (typeof dmClass === 'object' && dmClass != null) {
var list = dmClass.getTrustedDeviceListSync() 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:
if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null") ```ts
return import deviceManager from '@ohos.distributedHardware.deviceManager';
} var dmClass;
console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId) function getRemoteDeviceId() {
return list[0].deviceId if (typeof dmClass === 'object' && dmClass != null) {
} else { var list = dmClass.getTrustedDeviceListSync()
console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null") if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') {
} console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null")
} return
``` }
In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows: console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId)
```ts return list[0].deviceId
import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts'; } else {
console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null")
requestPermission() { }
let context = this.context }
let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC'] ```
let atManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionsFromUser(context, permissions).then((data) => { In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows:
console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
}).catch((error) => { ```ts
console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts';
})
} requestPermission() {
``` let context = this.context
**3. Send agreed sequenceable data.** let permissions: Array<string> = ['ohos.permission.DISTRIBUTED_DATASYNC']
let atManager = abilityAccessCtrl.createAtManager();
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: atManager.requestPermissionsFromUser(context, permissions).then((data) => {
```ts console.log("Succeed to request permission from user with data: "+ JSON.stringify(data))
const MSG_SEND_METHOD: string = 'CallSendMsg' }).catch((error) => {
async onButtonCall() { console.log("Failed to request permission from user with error: "+ JSON.stringify(error))
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}`) 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
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: const MSG_SEND_METHOD: string = 'CallSendMsg'
```ts async onButtonCall() {
const MSG_SEND_METHOD: string = 'CallSendMsg' try {
originMsg: string = '' let msg = new MySequenceable(1, 'origin_Msg')
backMsg: string = '' await this.caller.call(MSG_SEND_METHOD, msg)
async onButtonCallWithResult(originMsg, backMsg) { } catch (error) {
try { console.log(`caller call failed with ${error}`)
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, '') 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:
data.readSequenceable(result)
backMsg(result.str) ```ts
console.log(`caller result is [${result.num}, ${result.str}]`) const MSG_SEND_METHOD: string = 'CallSendMsg'
} catch (error) { originMsg: string = ''
console.log(`caller callWithResult failed with ${error}`) backMsg: string = ''
} async onButtonCallWithResult(originMsg, backMsg) {
} try {
``` let msg = new MySequenceable(1, originMsg)
**4. Release the Caller object.** const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg)
console.log('caller callWithResult succeed')
When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows:
```ts let result = new MySequenceable(0, '')
releaseCall() { data.readSequenceable(result)
try { backMsg(result.str)
this.caller.release() console.log(`caller result is [${result.num}, ${result.str}]`)
this.caller = undefined } catch (error) {
console.log('caller release succeed') console.log(`caller callWithResult failed with ${error}`)
} catch (error) { }
console.log(`caller release 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
...@@ -135,7 +135,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks ...@@ -135,7 +135,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks
| Name | Description | Data Type | Default Value Allowed | | Name | Description | Data Type | Default Value Allowed |
| ----------- | ------------------------------------------------------------ | ---------- | -------------------- | | ----------- | ------------------------------------------------------------ | ---------- | -------------------- |
| name | Name of the Extension ability. This field must be specified. | String | No | | name | Name of the Extension ability. This field must be specified. | String | No |
| srcEntrance | Path of the Extension ability lifecycle code. This field must be specified.| String | No | | srcEnty | Path of the Extension ability lifecycle code. This field must be specified.| String | No |
| description | Description of the Extension ability. The value can be a string or a resource index to descriptions in multiple languages.| String | Yes (initial value: left empty)| | description | Description of the Extension ability. The value can be a string or a resource index to descriptions in multiple languages.| String | Yes (initial value: left empty)|
| icon | Index of the Extension ability icon file. | String | Yes (initial value: left empty)| | icon | Index of the Extension ability icon file. | String | Yes (initial value: left empty)|
| label | Descriptive information about the Extension ability presented externally. The value can be a string or a resource index to the description.| String | Yes (initial value: left empty)| | label | Descriptive information about the Extension ability presented externally. The value can be a string or a resource index to the description.| String | Yes (initial value: left empty)|
...@@ -150,7 +150,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks ...@@ -150,7 +150,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks
```json ```json
"extensionAbilities": [{ "extensionAbilities": [{
"name": "FormAbility", "name": "FormAbility",
"srcEntrance": "./ets/FormAbility/FormAbility.ts", "srcEnty": "./ets/FormAbility/FormAbility.ts",
"label": "$string:form_FormAbility_label", "label": "$string:form_FormAbility_label",
"description": "$string:form_FormAbility_desc", "description": "$string:form_FormAbility_desc",
"type": "form", "type": "form",
......
...@@ -33,8 +33,8 @@ OpenHarmony does not support creation of a Service Extension ability for third-p ...@@ -33,8 +33,8 @@ OpenHarmony does not support creation of a Service Extension ability for third-p
"icon": "$media:icon", "icon": "$media:icon",
"description": "service", "description": "service",
"type": "service", "type": "service",
"visible": true, "exported": true,
"srcEntrance": "./ets/ServiceExtAbility/ServiceExtAbility.ts" "srcEnty": "./ets/ServiceExtAbility/ServiceExtAbility.ts"
}] }]
``` ```
...@@ -44,7 +44,7 @@ OpenHarmony does not support creation of a Service Extension ability for third-p ...@@ -44,7 +44,7 @@ OpenHarmony does not support creation of a Service Extension ability for third-p
```js ```js
import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility' import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility'
import rpc from '@ohos.rpc' import rpc from '@ohos.rpc'
class StubTest extends rpc.RemoteObject { class StubTest extends rpc.RemoteObject {
constructor(des) { constructor(des) {
super(des); super(des);
......
...@@ -24,6 +24,8 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O ...@@ -24,6 +24,8 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O
All applications should be developed on top of these frameworks. All applications should be developed on top of these frameworks.
Then, equip yourself for developing the key features, with the following guidelines: Then, equip yourself for developing the key features, with the following guidelines:
- [Web](web/web-component-overview.md)
- [Notification](notification/Readme-EN.md) - [Notification](notification/Readme-EN.md)
- [Window Manager](windowmanager/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md)
- [WebGL](webgl/Readme-EN.md) - [WebGL](webgl/Readme-EN.md)
...@@ -32,7 +34,8 @@ Then, equip yourself for developing the key features, with the following guideli ...@@ -32,7 +34,8 @@ Then, equip yourself for developing the key features, with the following guideli
- [Connectivity](connectivity/Readme-EN.md) - [Connectivity](connectivity/Readme-EN.md)
- [Telephony Service](telephony/Readme-EN.md) - [Telephony Service](telephony/Readme-EN.md)
- [Data Management](database/Readme-EN.md) - [Data Management](database/Readme-EN.md)
- [Background Task Management](task-management/Readme-EN.md) - [File Management](file-management/Readme-EN.md)
- [Task Management](task-management/Readme-EN.md)
- [Device Management](device/Readme-EN.md) - [Device Management](device/Readme-EN.md)
- [Device Usage Statistics](device-usage-statistics/Readme-EN.md) - [Device Usage Statistics](device-usage-statistics/Readme-EN.md)
- [DFX](dfx/Readme-EN.md) - [DFX](dfx/Readme-EN.md)
...@@ -40,7 +43,6 @@ Then, equip yourself for developing the key features, with the following guideli ...@@ -40,7 +43,6 @@ Then, equip yourself for developing the key features, with the following guideli
- [Application Test](application-test/Readme-EN.md) - [Application Test](application-test/Readme-EN.md)
- [IDL Specifications and User Guide](IDL/idl-guidelines.md) - [IDL Specifications and User Guide](IDL/idl-guidelines.md)
- [Using Native APIs in Application Projects](napi/Readme-EN.md) - [Using Native APIs in Application Projects](napi/Readme-EN.md)
- [File Management](file-management/medialibrary-overview.md)
### Tools ### Tools
...@@ -61,11 +63,10 @@ They are organized as follows: ...@@ -61,11 +63,10 @@ They are organized as follows:
- [Component Reference (JavaScript-based Web-like Development Paradigm)](reference/arkui-js/Readme-EN.md) - [Component Reference (JavaScript-based Web-like Development Paradigm)](reference/arkui-js/Readme-EN.md)
- [JS Service Widget UI Components](reference/js-service-widget-ui/Readme-EN.md) - [JS Service Widget UI Components](reference/js-service-widget-ui/Readme-EN.md)
- APIs - APIs
- [JS and TS APIs](reference/apis/Readme-EN.md) - [JS and TS APIs](reference/apis/Readme-EN.md)
- Native APIs - Native APIs
- [Standard Library](reference/native-lib/third_party_libc/musl.md) - [Standard Library](reference/native-lib/third_party_libc/musl.md)
- [Node_API](reference/native-lib/third_party_napi/napi.md) - [Node_API](reference/native-lib/third_party_napi/napi.md)
### Readme ### Readme
......
# Application Development Overview # Application Development Overview
> **NOTE**
>
> You are reading documents of OpenHarmony 3.2 Release. Obtain the [compatible SDK](../release-notes/OpenHarmony-v3.2-release.md#version-mapping) during your application development.
The application development documents provide reference for you to develop applications using the APIs provided by OpenHarmony. They walk you through how to use JavaScript APIs to develop applications on the standard system. The application development documents provide reference for you to develop applications using the APIs provided by OpenHarmony. They walk you through how to use JavaScript APIs to develop applications on the standard system.
The documents are carefully organized as follows: The documents are carefully organized as follows:
### Getting Started ## Getting Started
[Here](quick-start/start-overview.md) you'll learn how to quickly get started with OpenHarmony application development. [Here](quick-start/start-overview.md) you'll learn how to quickly get started with OpenHarmony application development.
...@@ -12,7 +16,7 @@ Browse the documents on the instructions for quickly building your first applica ...@@ -12,7 +16,7 @@ Browse the documents on the instructions for quickly building your first applica
Check out the development fundamentals, which comprise descriptions of the package structure configuration file for OpenHarmony applications and the instructions for use of resource files. Check out the development fundamentals, which comprise descriptions of the package structure configuration file for OpenHarmony applications and the instructions for use of resource files.
### Development ## Development
To facilitate your application development, we provide development guidelines for key features. To facilitate your application development, we provide development guidelines for key features.
...@@ -24,48 +28,45 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O ...@@ -24,48 +28,45 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O
All applications should be developed on top of these frameworks. All applications should be developed on top of these frameworks.
Then, equip yourself for developing the key features, with the following guidelines: Then, equip yourself for developing the key features, with the following guidelines:
- [Web](web/web-component-overview.md)
- [Notification](notification/notification-overview.md) - [Notification](notification/notification-overview.md)
- [Window Manager](windowmanager/window-overview.md) - [Window Manager](windowmanager/window-overview.md)
- [WebGL](webgl/webgl-overview.md) - [WebGL](webgl/webgl-overview.md)
- [Media](media/audio-overview.md) - [Media](media/media-application-overview.md)
- [Security](security/userauth-overview.md) - [Security](security/userauth-overview.md)
- [Connectivity](connectivity/ipc-rpc-overview.md) - [Connectivity](connectivity/ipc-rpc-overview.md)
- [Telephony Service](telephony/telephony-overview.md) - [Telephony Service](telephony/telephony-overview.md)
- [Data Management](database/database-mdds-overview.md) - [Data Management](database/data-mgmt-overview.md)
- [Background Task Management](task-management/background-task-overview.md) - [File Management](file-management/file-management-overview.md)
- [Device Management](device/usb-overview.md) - [Task Management](task-management/background-task-overview.md)
- [Device](device/usb-overview.md)
- [Device Usage Statistics](device-usage-statistics/device-usage-statistics-overview.md) - [Device Usage Statistics](device-usage-statistics/device-usage-statistics-overview.md)
- [DFX](dfx/hiappevent-guidelines.md) - [DFX](dfx/hiappevent-guidelines.md)
- [Internationalization](internationalization/international-overview.md) - [Internationalization](internationalization/international-overview.md)
- [Application Test](application-test/arkxtest-guidelines.md) - [Application Test](application-test/arkxtest-guidelines.md)
- [OpenHarmony IDL Specifications and User Guide](IDL/idl-guidelines.md) - [OpenHarmony IDL Specifications and User Guide](IDL/idl-guidelines.md)
- [Using Native APIs in Application Projects](napi/napi-guidelines.md) - [Using Native APIs in Application Projects](napi/napi-guidelines.md)
- [File Management](file-management/medialibrary-overview.md)
### Tools ## Tools
DevEco Studio is a high-performance integrated development environment (IDE) recommended for developing OpenHarmony applications. DevEco Studio is a high-performance integrated development environment (IDE) recommended for developing OpenHarmony applications.
[Here](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421) you can learn everything about DevEco Studio, including how to use this tool to create a project and sign, debug, and run an application. [Here](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421) you can learn everything about DevEco Studio, including how to use this tool to create a project and sign, debug, and run an application.
### Hands-On Tutorials ## Hands-On Tutorials
To make you better understand how functions work together and jumpstart your application development projects, we provide stripped-down, real-world [samples](https://gitee.com/openharmony/applications_app_samples/blob/OpenHarmony-3.2-Release/README.md) and [codelabs](https://gitee.com/openharmony/codelabs). To make you better understand how functions work together and jumpstart your application development projects, we provide stripped-down, real-world [samples](https://gitee.com/openharmony/applications_app_samples/blob/OpenHarmony-3.2-Release/README.md) and [codelabs](https://gitee.com/openharmony/codelabs).
### API References ## API References
API references encompass all components and APIs available in OpenHarmony, helping you use and integrate APIs more effectively. API references encompass all components and APIs available in OpenHarmony, helping you use and integrate APIs more effectively.
They are organized as follows: They are organized as follows:
- [Component Reference (TypeScript-based Declarative Development Paradigm)](reference/arkui-ts/Readme-EN.md) - [Component Reference (TypeScript-based Declarative Development Paradigm)](reference/arkui-ts/ts-components-summary.md)
- [Component Reference (JavaScript-compatible Web-like Development Paradigm)](reference/arkui-js/js-components-common-attributes.md)
- [Component Reference (JavaScript-based Web-like Development Paradigm)](reference/arkui-js/Readme-EN.md) - [JS Service Widget UI Components](reference/js-service-widget-ui/js-service-widget-file.md)
- [JS and TS APIs](reference/apis/development-intro.md)
- [JS Service Widget UI Components](reference/js-service-widget-ui/Readme-EN.md)
- [JS and TS APIs](reference/apis/js-apis-ability-dataUriUtils.md)
- Native APIs - Native APIs
- [Standard Library](reference/native-lib/third_party_libc/musl.md) - [Standard Library](reference/native-lib/third_party_libc/musl.md)
- [Node_API](reference/native-lib/third_party_napi/napi.md) - [Node_API](reference/native-lib/third_party_napi/napi.md)
...@@ -22,6 +22,31 @@ ...@@ -22,6 +22,31 @@
- [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md) - [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md)
- [InputMethodExtensionAbility](inputmethodextentionability.md) - [InputMethodExtensionAbility](inputmethodextentionability.md)
- [WindowExtensionAbility](windowextensionability.md) - [WindowExtensionAbility](windowextensionability.md)
- Service Widget Development in Stage Model
- [Service Widget Overview](service-widget-overview.md)
- Developing an ArkTS Widget
- [ArkTS Widget Working Principles](arkts-ui-widget-working-principles.md)
- [ArkTS Widget Related Modules](arkts-ui-widget-modules.md)
- ArkTS Widget Development
- [Creating an ArkTS Widget](arkts-ui-widget-creation.md)
- [Configuring Widget Configuration Files](arkts-ui-widget-configuration.md)
- [Widget Lifecycle Management](arkts-ui-widget-lifecycle.md)
- Widget Page Development
- [Widget Page Capability Overview](arkts-ui-widget-page-overview.md)
- [Using Animations in the Widget](arkts-ui-widget-page-animation.md)
- [Applying Custom Drawing in the Widget](arkts-ui-widget-page-custom-drawing.md)
- Widget Event Development
- [Widget Event Capability Overview](arkts-ui-widget-event-overview.md)
- [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md)
- [Updating Widget Content Through UIAbility](arkts-ui-widget-event-uiability.md)
- [Redirecting to a Specified Page Through the Router Event](arkts-ui-widget-event-router.md)
- Widget Data Interaction
- [Widget Data Interaction Overview](arkts-ui-widget-interaction-overview.md)
- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md)
- [Updating Local and Online Images in the Widget](arkts-ui-widget-image-update.md)
- [Updating Widget Content by State](arkts-ui-widget-update-by-status.md)
- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md)
- [Developing a JS Widget](js-ui-widget-development.md)
- [AbilityStage Component Container](abilitystage.md) - [AbilityStage Component Container](abilitystage.md)
- [Context](application-context-stage.md) - [Context](application-context-stage.md)
- Want - Want
......
# Configuring Widget Configuration Files
Widget-related configuration includes **FormExtensionAbility** configuration and widget configuration.
1. Configure FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information.
Example configuration:
```json
{
"module": {
...
"extensionAbilities": [
{
"name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ts",
"label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc",
"type": "form",
"metadata": [
{
"name": "ohos.extension.form",
"resource": "$profile:form_config"
}
]
}
]
}
}
```
2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if resource is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal field structure.
**Table 1** form_config.json file
| Field| Description| Data Type| Default Value Allowed|
| -------- | -------- | -------- | -------- |
| name | Class name of the widget. The value is a string with a maximum of 127 bytes.| String| No|
| description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes.| String| Yes (initial value: left empty)|
| src | Full path of the UI code corresponding to the widget. For an ArkTS widget, the full path must contain the widget file name extension, for example, **./ets/widget/pages/WidgetCard.ets**. For a JS widget, the full path does not need to contain the widget file name extension, for example, **./js/widget/pages/WidgetCard**.| String| No|
| uiSyntax | Type of the widget.<br>- **arkts**: ArkTS widget<br>- **hml**: JS widget| String| Yes (initial value: **hml**)|
| window | Window-related configurations.| Object| Yes|
| isDefault | Whether the widget is a default one. Each UIAbility has only one default widget.<br>- **true**: The widget is the default one.<br>- **false**: The widget is not the default one.| Boolean| No|
| colorMode | Color mode of the widget.<br>- **auto**: auto-adaptive color mode<br>- **dark**: dark color mode<br>- **light**: light color mode| String| Yes (initial value: **auto**)|
| supportDimensions | Grid styles supported by the widget.<br>- **1 * 2**: indicates a grid with one row and two columns.<br>- **2 * 2**: indicates a grid with two rows and two columns.<br>- **2 * 4**: indicates a grid with two rows and four columns.<br>- **4 * 4**: indicates a grid with four rows and four columns.| String array| No|
| defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget.| String| No|
| updateEnabled | Whether the widget can be updated periodically.<br>- **true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.<br>- **false**: The widget cannot be updated periodically.| Boolean| No|
| scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.<br>**NOTE**<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| String| Yes (initial value: The widget cannot be updated periodically.)|
| updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.<br>If the value is **0**, this field does not take effect.<br>If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.<br>**NOTE**<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| Number| Yes (initial value: **0**)|
| formConfigAbility | Link to a specific page of the application. The value is a URI.| String| Yes (initial value: left empty)|
| formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)|
| metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)|
Example configuration:
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,
"scheduledUpdateTime": "10:30",
"updateDuration": 1,
"defaultDimension": "2*2",
"supportDimensions": [
"2*2"
]
}
]
}
```
# Updating Widget Content by Widget Host (for System Applications Only)
Widgets that are updated periodically are subject to the scheduled time or interval settings. To offer more flexible updates, the widget host can provide a button to proactively trigger a widget update. Specifically, the widget host calls the [requestForm](../reference/apis/js-apis-app-form-formHost.md#requestform) API to request a widget update. The system then calls the [onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback in the FormExtensionAbility of the widget provider. In the callback, the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API can be used to update the widget content. For details about the **onUpdateForm** lifecycle callback, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md).
```ts
import formHost from '@ohos.app.form.formHost';
@Entry()
@Component
struct WidgetCard {
formId = ...; // Widget ID
build() {
Button (`Update Widget`)
.type(ButtonType.Capsule)
.width('50%')
.height(50)
.onClick(() => {
console.info('FormAbility update form click');
// formId is the ID of the widget to be updated.
formHost.requestForm(this.formId.toString()).then(() => {
console.info('Succeeded in requestForming.');
});
})
...
}
}
```
# Creating an ArkTS Widget
To create an ArkTS widget in an existing application project, perform the following steps:
1. Create a widget.
![WidgetProjectCreate1](figures/WidgetProjectCreate1.png)
2. Select a widget template based on the actual service scenario.
![WidgetProjectCreate2](figures/WidgetProjectCreate2.png)
3. Set **Language** to **ArkTS** and click **Finish**.
![WidgetProjectCreate3](figures/WidgetProjectCreate3.png)
After an ArkTS widget is created, the following widget-related files are added to the project directory: **EntryFormAbility.ts** (widget lifecycle management file), **WidgetCard.ets** (widget page file), and **form_config.json** (widget configuration file).
![WidgetProjectView](figures/WidgetProjectView.png)
# Updating Widget Content Through FormExtensionAbility
On the widget page, the **postCardAction** API can be used to trigger a message event to the 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.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('title') title: string = 'init';
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button ('Update')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'msgTest': 'messageEvent'
}
});
})
Text(`${this.title}`)
Text(`${this.detail}`)
}
.width('100%')
.height('100%')
}
}
```
- Call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget in the **onFormEvent** callback of the FormExtensionAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`);
let formData = {
'title':'Title Update Success.', // Matches the widget layout.
'detail':'Detail Update Success.', // Matches the widget layout.
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
}
// ...
}
```
The figure below shows the effect.
![WidgetUpdatePage](figures/WidgetUpdatePage.png)
# Widget Event Capability Overview
The ArkTS widget provides the **postCardAction()** API for interaction between the widget internal and the provider application. Currently, this API supports the router, message, and call events and can be called only in the widget.
![WidgetPostCardAction](figures/WidgetPostCardAction.png)
Definition: postCardAction(component: Object, action: Object): void
Parameters:
| Name| Type| Mandatory| Description|
| -------- | -------- | -------- | -------- |
| component | Object | Yes| Instance of the current custom component. Generally, **this** is transferred.|
| action | Object | Yes| Action description. For details, see the following table.|
Description of the action parameter
| **Key** | **Value** | Description|
| -------- | -------- | -------- |
| "action" | string | Action type.<br>- **"router"**: application redirection. If this type of action is triggered, the corresponding UIAbility is displayed. Only the UIAbility of the current application can be displayed.<br>- **"message"**: custom message. If this type of action is triggered, the [onFormEvent()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent) lifecycle callback of the provider FormExtensionAbility is called.<br>- **"call"**: application startup in the background. If this type of action is triggered, the corresponding UIAbility is started but does not run in the foreground. The target application must have the permission to run in the background ([ohos.permission.KEEP_BACKGROUND_RUNNING](../security/permission-list.md#ohospermissionkeep_background_running)).|
| "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.|
| "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.|
Sample code of the **postCardAction()** API:
```typescript
Button ('Jump')
.width('40%')
.height('20%')
.onClick(() => {
postCardAction(this, {
'action': 'router',
'bundleName': 'com.example.myapplication',
'abilityName': 'EntryAbility',
'params': {
'message': 'testForRouter' // Customize the message to be sent.
}
});
})
```
The following are typical widget development scenarios that can be implemented through widget events:
- [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md)
- [Updating Widget Content Through UIAbility](arkts-ui-widget-event-uiability.md)
- [Redirecting to a Specified Page Through the Router Event](arkts-ui-widget-event-router.md)
# Redirecting to a Specified Page Through the Router Event
The **router** capability of the **postCardAction** API can be used in a widget to quickly start the widget provider application. An application can provide different buttons through the widget so that users can jump to different pages at the touch of a button. For example, a camera widget provides the buttons that direct the user to respective pages, such as the page for taking a photo and the page for recording a video.
![WidgerCameraCard](figures/WidgerCameraCard.png)
Generally, a button is used to start a page.
- 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.
```ts
@Entry
@Component
struct WidgetCard {
build() {
Column() {
Button ('Function A')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funA');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'targetPage': 'funA' // Process the information in the EntryAbility.
}
});
})
Button ('Function B')
.margin('20%')
.onClick(() => {
console.info('Jump to EntryAbility funB');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'targetPage': 'funB' // Process the information in the EntryAbility.
}
});
})
}
.width('100%')
.height('100%')
}
}
```
- The UIAbility receives the router event and obtains parameters. It then starts the page specified in the received message.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';
let selectPage = "";
let currentWindowStage = null;
export default class CameraAbility extends UIAbility {
// If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received.
onCreate(want, launchParam) {
// Obtain the targetPage parameter passed in the router event.
console.info("onCreate want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onCreate router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
}
// If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received.
onNewWant(want, launchParam) {
console.info("onNewWant want:" + JSON.stringify(want));
if (want.parameters.params !== undefined) {
let params = JSON.parse(want.parameters.params);
console.info("onNewWant router targetPage:" + params.targetPage);
selectPage = params.targetPage;
}
if (currentWindowStage != null) {
this.onWindowStageCreate(currentWindowStage);
}
}
onWindowStageCreate(windowStage: window.WindowStage) {
let targetPage;
// Start the page specified by targetPage.
switch (selectPage) {
case 'funA':
targetPage = 'pages/FunA';
break;
case 'funB':
targetPage = 'pages/FunB';
break;
default:
targetPage = 'pages/Index';
}
if (currentWindowStage === null) {
currentWindowStage = windowStage;
}
windowStage.loadContent(targetPage, (err, data) => {
if (err && err.code) {
console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
});
}
};
```
# Updating Widget Content Through UIAbility
On the widget page, the **postCardAction** API can be used to trigger a router or call event to start the UIAbility, 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.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('detail') detail: string = 'init';
build() {
Column() {
Button ('Jump')
.margin('20%')
.onClick(() => {
console.info('postCardAction to EntryAbility');
postCardAction(this, {
'action': 'router',
'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed.
'params': {
'detail': 'RouterFromCard'
}
});
})
Text(`${this.detail}`).margin('20%')
}
.width('100%')
.height('100%')
}
}
```
- In the **onCreate()** or **onNewWant()** lifecycle callback of the UIAbility, use the input parameter **want** to obtain the ID (**formID**) and other information of the widget, and then call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget.
```ts
import UIAbility from '@ohos.app.ability.UIAbility';
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import formInfo from '@ohos.app.form.formInfo';
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.
onCreate(want, launchParam) {
console.info('Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message +': onCreate UIAbility.', // Matches the widget layout.
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
// If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received.
onNewWant(want, launchParam) {
console.info('onNewWant Want:' + JSON.stringify(want));
if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let message = JSON.parse(want.parameters.params).detail;
console.info(`UpdateForm formId: ${curFormId}, message: ${message}`);
let formData = {
"detail": message +': onNewWant UIAbility.', // Matches the widget layout.
};
let formMsg = formBindingData.createFormBindingData(formData)
formProvider.updateForm(curFormId, formMsg).then((data) => {
console.info('updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('updateForm failed:' + JSON.stringify(error));
})
}
}
...
}
```
# Updating Local and Online Images in the Widget
Generally, local images or online images downloaded from the network need to be displayed on a widget. To obtain local and online images, use the FormExtensionAbility. The following exemplifies how to show local and online images on a widget.
1. Internet access is required for downloading online images. Therefore, you need to apply for the **ohos.permission.INTERNET** permission. For details, see[Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md).
2. Update local files in the **onAddForm** lifecycle callback of the EntryFormAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
...
// When the widget is added, a local image is opened and transferred to the widget page for display.
onAddForm(want) {
// Assume that the local image head.PNG is in the tmp directory of the current widget.
let tempDir = this.context.getApplicationContext().tempDir;
// Open the local image and obtain the FD after the image is opened.
let file;
try {
file = fs.openSync(tempDir + '/' + 'head.PNG');
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let formData = {
'text': 'Image: Bear',
'imgName': 'imgBear',
'formImages': {
'imgBear': file.fd
},
'loaded': true
}
// Encapsulate the FD in formData and return it to the widget page.
return formBindingData.createFormBindingData(formData);
}
...
}
```
3. Update online files in the onFormEvent lifecycle callback of the EntryFormAbility.
```ts
import formBindingData from '@ohos.app.form.formBindingData';
import formProvider from '@ohos.app.form.formProvider';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import request from '@ohos.request';
import fs from '@ohos.file.fs';
export default class EntryFormAbility extends FormExtensionAbility {
// When the message event is triggered on the widget page, an online image is downloaded and transferred to the widget page for display.
onFormEvent(formId, message) {
let formInfo = formBindingData.createFormBindingData({
'text': 'Updating...'
})
formProvider.updateForm(formId, formInfo)
// Note: The FormExtensionAbility is started when the lifecycle callback is triggered. It can run in the background for only 5 seconds.
// When possible, limit the size of the image to download. If an image cannot be downloaded within 5 seconds, it cannot be updated to the widget page.
let netFile = 'https://xxxx/xxxx.png'; // Specify the URL of the image to download.
let tempDir = this.context.getApplicationContext().tempDir;
let fileName = 'file' + Date.now();
let tmpFile = tempDir + '/' + fileName;
request.downloadFile(this.context, {
url: netFile, filePath: tmpFile, enableMetered: true, enableRoaming: true
}).then((task) => {
task.on('complete', function callback() {
console.info('ArkTSCard download complete:' + tmpFile);
let file;
try {
file = fs.openSync(tmpFile);
} catch (e) {
console.error(`openSync failed: ${JSON.stringify(e)}`);
}
let fileInfo = {};
fileInfo[fileName] = file.fd;
let formData = {
'text': 'Image:' + fileName,
'imgName': fileName,
'formImages': fileInfo,
'loaded': true
};
let formInfo = formBindingData.createFormBindingData(formData)
formProvider.updateForm(formId, formInfo).then((data) => {
console.info('FormAbility updateForm success.' + JSON.stringify(data));
}).catch((error) => {
console.error('FormAbility updateForm failed: ' + JSON.stringify(error));
})
})
task.on('fail', function callBack(err) {
console.info('ArkTSCard download task failed. Cause:' + err);
let formInfo = formBindingData.createFormBindingData({
'text':'Update failed.'
})
formProvider.updateForm(formId, formInfo)
});
}).catch((err) => {
console.error('Failed to request the download. Cause: ' + JSON.stringify(err));
});
}
...
};
```
4. On the widget page, use the **\<Image>** component to display the widget content transferred from the EntryFormAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('text') text: string = 'Loading...';
@LocalStorageProp('loaded') loaded: boolean = false;
@LocalStorageProp('imgName') imgName: string = 'name';
build() {
Column() {
Text(this.text)
.fontSize('12vp')
.textAlign(TextAlign.Center)
.width('100%')
.height('15%')
Row() {
if (this.loaded) {
Image('memory://' + this.imgName)
.width('50%')
.height('50%')
.margin('5%')
} else {
Image('common/start.PNG')
.width('50%')
.height('50%')
.margin('5%')
}
}.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
Button ('Update')
.height('15%')
.onClick(() => {
postCardAction(this, {
'action': 'message',
'params': {
'info': 'refreshImage'
}
});
})
}
.width('100%').height('100%')
.alignItems(HorizontalAlign.Center)
.padding('5%')
}
}
```
> **NOTE**
> - The **\<Image>** component displays images in the remote memory based on the **memory://** identifier in the input parameter (**memory://fileName**). The **fileName** value must be consistent with the key in the object (**'formImages': {key: fd}**) passed by the EntryFormAbility.
>
> - The **\<Image>** component determines whether to update the image based on whether the input parameter is changed. Therefore, the value of **imgName** passed by the EntryFormAbility each time must be different. If the two values of **imgName** passed consecutively are identical, the image is not updated.
# Widget Data Interaction
The ArkTS widget framework provides the **updateForm()** and **requestForm()** APIs to proactively trigger widget updates.
![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png)
| API| System Capability| Constraints|
| -------- | -------- | -------- |
| updateForm | No| 1. Invoked by the provider.<br>2. Allows only the widget provider to update its own widgets. It cannot be used to update widgets by other providers.|
| requestForm | Yes| 1. Invoked by the host.<br>2. Allows only the widget host to update the widgets added to it. It cannot be used to update widgets added to other hosts.|
The following describes the typical use cases of widget updates:
- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md)
- [Updating Local and Online Images](arkts-ui-widget-image-update.md)
- [Updating Widget Content by State](arkts-ui-widget-update-by-status.md)
- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md)
# Widget Lifecycle Management
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**.
```ts
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
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).
```typescript
import formInfo from '@ohos.app.form.formInfo';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import formProvider from '@ohos.app.form.formProvider';
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
console.info('[EntryFormAbility] onAddForm');
// Obtain the unique widget ID formId from the want parameter.
let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY];
// Called when the widget is created. The widget provider should return the widget data binding class.
let obj = {
'title': 'titleOnAddForm',
'detail': 'detailOnAddForm'
};
let formData = formBindingData.createFormBindingData(obj);
return formData;
}
onCastToNormalForm(formId) {
// Called when the form provider is notified that a temporary form is successfully
// converted to a normal form.
// 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}`);
}
onUpdateForm(formId) {
// Override this method to support scheduled updates, periodic updates, or updates requested by the widget host.
console.info('[EntryFormAbility] onUpdateForm');
let obj = {
'title': 'titleOnUpdateForm',
'detail': 'detailOnUpdateForm'
};
let formData = formBindingData.createFormBindingData(obj);
formProvider.updateForm(formId, formData).catch((err) => {
if (err) {
// Print errors.
console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`);
return;
}
});
}
onChangeFormVisibility(newStatus) {
// Called when the form 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.
console.info('[EntryFormAbility] onChangeFormVisibility');
}
onFormEvent(formId, message) {
// Called when a specified message event defined by the form provider is triggered.
// If the widget supports event triggering, override this method and implement the trigger.
console.info('[EntryFormAbility] onFormEvent');
}
onRemoveForm(formId) {
// Called to notify the form provider that a specified form has been destroyed.
// Called when the corresponding widget is deleted. The input parameter is the ID of the deleted card.
console.info('[EntryFormAbility] onRemoveForm');
}
onConfigurationUpdate(config) {
// Called when the system configuration is updated.
console.info('[EntryFormAbility] configurationUpdate:' + JSON.stringify(config));
}
onAcquireFormState(want) {
// Called to return a {@link FormState} object.
// Called when the widget provider receives the status query result of a widget. By default, the initial state of the widget is returned.
return formInfo.FormState.READY;
}
}
```
> **NOTE**
> The FormExtensionAbility cannot reside in the background. Therefore, continuous tasks cannot be processed in the widget lifecycle callbacks. The FormExtensionAbility persists for 5 seconds after the lifecycle callback is completed and will exit if no new lifecycle callback is invoked during this time frame. For the service logic that may take more than 5 seconds to complete, it is recommended that you [start the application](arkts-ui-widget-event-uiability.md). After the processing is complete, use the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) to notify the widget of the update.
# ArkTS Widget Related Modules
**Figure 1** ArkTS widget related modules
![WidgetModules](figures/WidgetModules.png)
- [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md): provides lifecycle callbacks invoked when a widget is created, destroyed, or updated.
- [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md): provides context for FormExtensionAbilities. You can use the APIs of this module to start FormExtensionAbilities.
- [formProvider](../reference/apis/js-apis-app-form-formProvider.md): provides APIs related to the widget provider. You can use the APIs to update a widget, set the next update time for a widget, obtain widget information, and request a widget release.
- [formInfo](../reference/apis/js-apis-app-form-formInfo.md): provides types and enums related to the widget information and state.
- [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.
- [ArkTS widget capabilities](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): lists 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.
- Configure FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md).
- Configure the widget configuration information (**WidgetCard.ets**) in the [form_config.json](arkts-ui-widget-configuration.md) file in **resources/base/profile**.
# Using Animations in the Widget
To make your ArkTS widget more engaging, you can apply animations to it, including [explicit animation](../reference/arkui-ts/ts-explicit-animation.md), [attribute animation](../reference/arkui-ts/ts-animatorproperty.md), and [component transition](../reference/arkui-ts/ts-transition-animation-component.md). Note the following restrictions when using the animations in ArkTS widgets.
**Table 1** Restrictions on animation parameters
| Name| Description| Description|
| -------- | -------- | -------- |
| duration | Animation playback duration| The maximum value is 1 second. If a larger value is set, the animation is still played for 1 second.|
| tempo | Animation playback speed.| Do not set this parameter in the widget. Use the default value 1.|
| delay | Animation delay duration.| Do not set this parameter in the widget. Use the default value 0.|
| iterations | Number of times that the animation is played.| Do not set this parameter in the widget. Use the default value 1.|
The following sample code implements the animation effect of button rotation:
![WidgetAnimation](figures/WidgetAnimation.gif)
```ts
@Entry
@Component
struct AttrAnimationExample {
@State rotateAngle: number = 0;
build() {
Column() {
Button('change rotate angle')
.onClick(() => {
this.rotateAngle = 90;
})
.margin(50)
.rotate({ angle: this.rotateAngle })
.animation({
curve: Curve.EaseOut,
playMode: PlayMode.AlternateReverse
})
}.width('100%').margin({ top: 20 })
}
}
```
# Applying Custom Drawing in the Widget
You can apply custom drawing in your ArkTS widget to create a more vibrant experience. Use the [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) component to create a canvas on the widget, and then use the [CanvasRenderingContext2D](../reference/arkui-ts/ts-canvasrenderingcontext2d.md) object to draw custom graphics on the canvas. The following code shows how to draw a smiling face in the center of the canvas.
```typescript
@Entry
@Component
struct Card {
private canvasWidth: number = 0;
private canvasHeight: number = 0;
// Initialize CanvasRenderingContext2D and RenderingContextSettings.
private settings: RenderingContextSettings = new RenderingContextSettings(true);
private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
build() {
Column() {
Row() {
Canvas(this.context)
.margin('5%')
.width('90%')
.height('90%')
.onReady(() => {
console.info('[ArkTSCard] onReady for canvas draw content');
// Obtain the actual width and height of the canvas in the onReady callback.
this.canvasWidth = this.context.width;
this.canvasHeight = this.context.height;
// Draw the background of the canvas.
this.context.fillStyle = 'rgba(203, 154, 126, 1.00)';
this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
// Draw a red circle in the center of the canvas.
this.context.beginPath();
let radius = this.context.width / 3
let circleX = this.context.width / 2
let circleY = this.context.height / 2
this.context.moveTo(circleX - radius, circleY);
this.context.arc(circleX, circleY, radius, 2 * Math.PI, 0, true);
this.context.closePath();
this.context.fillStyle = 'red';
this.context.fill();
// Draw the left eye of the smiling face.
let leftR = radius / 4
let leftX = circleX - (radius / 2)
let leftY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(leftX, leftY, leftR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// Draw the right eye of the smiling face.
let rightR = radius / 4
let rightX = circleX + (radius / 2)
let rightY = circleY - (radius / 3.5)
this.context.beginPath();
this.context.arc(rightX, rightY, rightR, 0, Math.PI, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
// Draw the mouth of the smiling face.
let mouthR = radius / 2.5
let mouthX = circleX
let mouthY = circleY + (radius / 3)
this.context.beginPath();
this.context.arc(mouthX, mouthY, mouthR, Math.PI, 0, true);
this.context.strokeStyle = '#ffff00'
this.context.lineWidth = 10
this.context.stroke()
})
}
}.height('100%').width('100%')
}
}
```
The figure below shows the effect.
![WidgetCanvasDemo](figures/WidgetCanvasDemo.jpeg)
# Widget Page Capability Overview
You can leverage the ArkUI declarative paradigm to develop ArkTS widget pages. The following widget pages are automatically generated by a DevEco Studio template. You can adjust the pages based on the real-world service scenarios.
![WidgetPreviewPage](figures/WidgetPreviewPage.png)
ArkTS widgets have full capabilities of JS widgets, with added animation and custom drawing capabilities plus partial support for components, events, animations, data management, and state management capabilities of the [declarative paradigm](../reference/arkui-ts/ts-components-summary.md). For details, see [Page Capabilities Supported by ArkTS Widgets](#page-capabilities-supported-by-arkts-widgets).
## Page Capabilities Supported by ArkTS Widgets
For details about the page capabilities supported by ArkTS widgets, see [Learning ArkTS](../quick-start/arkts-create-custom-components.md) and [ArkTS-based Declarative Development Paradigm](../reference/arkui-ts/ts-components-summary.md).
Only the APIs marked with "supported in ArkTS widgets" can be used for ArkTS widgets. Pay special attention to the capability differences with applications.
For example, the following description indicates that the @Component decorator can be used in ArkTS widgets.
![WidgetSupportApi](figures/WidgetSupportApi.png)
# Updating Widget Content by State
Multiple widgets of the same application can be configured to implement different features. For example, two weather widgets can be added to the home screen: one for displaying the weather of London, and the other Beijing. The widget is set to be updated at 07:00 every morning. It needs to detect the configured city, and then updates the city-specific weather information. The following example describes how to dynamically update the widget content based on the state.
- Widget configuration file: Configure the widget to be updated at 07:00 every morning.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true,"scheduledUpdateTime": "07:00",
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Widget page: A widget has different states and needs to be updated by state. When the state changes, **postCardAction** is called to notify the EntryFormAbility.
```ts
let storage = new LocalStorage();
@Entry(storage)
@Component
struct WidgetCard {
@LocalStorageProp('textA') textA: string = 'To be updated...';
@LocalStorageProp('textB') textB: string ='To be updated...';
@State selectA: boolean = false;
@State selectB: boolean = false;
build() {
Column() {
Row() {
Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectA = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectA': JSON.stringify(value)
}
});
})
Text ('State A')
}
Row() {
Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
.select(false)
.onChange((value: boolean) => {
this.selectB = value;
postCardAction(this, {
'action': 'message',
'params': {
'selectB': JSON.stringify(value)
}
});
})
Text ('State B')
}
Row() {// Content that is updated only in state A
Text('状态A: ')
Text(this.textA)
}
Row() { // Content that is updated only in state B
Text ('State B:')
Text(this.textB)
}
}.padding('10%')
}
}
```
- EntryFormAbility: The widget state data is stored in the local database. When the update event callback is triggered, the current widget state is obtained through **formId**, and then content is updated based on the state obtained.
```ts
import formInfo from '@ohos.app.form.formInfo'
import formProvider from '@ohos.app.form.formProvider';
import formBindingData from '@ohos.app.form.formBindingData';
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility';
import dataStorage from '@ohos.data.storage'
export default class EntryFormAbility extends FormExtensionAbility {
onAddForm(want) {
let formId = want.parameters[formInfo.FormParam.IDENTITY_KEY];
let isTempCard: boolean = want.parameters[formInfo.FormParam.TEMPORARY_KEY];
if (isTempCard === false) {// If the widget is a normal one, the widget information is persisted.
console.info('Not temp card, init db for:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.putSync('A' + formId, 'false');
storeDB.putSync('B' + formId, 'false');
storeDB.flushSync();
}
let formData = {};
return formBindingData.createFormBindingData(formData);
}
onRemoveForm(formId) {
console.info('onRemoveForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.deleteSync('A' + formId);
storeDB.deleteSync('B' + formId);
}
// If the widget is a temporary one, it is recommended that the widget information be persisted when the widget is converted to a normal one.
onCastToNormalForm(formId) {
console.info('onCastToNormalForm, formId:' + formId);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
storeDB.putSync('A' + formId, 'false');
storeDB.putSync('B' + formId, 'false');
storeDB.flushSync();
}
onUpdateForm(formId) {
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let stateA = storeDB.getSync('A' + formId, 'false').toString()
let stateB = storeDB.getSync('B' + formId, 'false').toString()
// Update textA in state A.
if (stateA === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textA': 'AAA'
})
formProvider.updateForm(formId, formInfo)
}
// Update textB in state B.
if (stateB === 'true') {
let formInfo = formBindingData.createFormBindingData({
'textB': 'BBB'
})
formProvider.updateForm(formId, formInfo)
}
}
onFormEvent(formId, message) {
// Store the widget state.
console.info('onFormEvent formId:' + formId + 'msg:' + message);
let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore')
let msg = JSON.parse(message)
if (msg.selectA != undefined) {
console.info('onFormEvent selectA info:' + msg.selectA);
storeDB.putSync('A' + formId, msg.selectA);
}
if (msg.selectB != undefined) {
console.info('onFormEvent selectB info:' + msg.selectB);
storeDB.putSync('B' + formId, msg.selectB);
}
storeDB.flushSync();
}
};
```
> **NOTE**
> When the local database is used for widget information persistence, it is recommended that [TEMPORARY_KEY](../reference/apis/js-apis-app-form-formInfo.md#formparam) be used to determine whether the currently added widget is a normal one in the [onAddForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onaddform) lifecycle callback. If the widget is a normal one, the widget information is directly persisted. If the widget is a temporary one, the widget information is persisted when the widget is converted to a normal one ([onCastToNormalForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#oncasttonormalform)). In addition, the persistent widget information needs to be deleted when the widget is destroyed ([onRemoveForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform)), preventing the database size from continuously increasing due to repeated widget addition and deletion.
# 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:
- Set the update interval: The widget will be updated at the specified interval. You can specify the interval by setting the [updateDuration](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update once an hour.
> **NOTE**
>
> **updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // Enable the periodic update feature.
"scheduledUpdateTime": "10:30",
"updateDuration": 2, // Set the interval to update the widget. The value is a natural number, in the unit of 30 minutes.
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Set the scheduled update time: The widget will be updated at the scheduled time every day. You can specify the time by setting the [scheduledUpdateTime](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update at 10:30 a.m. every day.
> **NOTE**
>
> **updateDuration** takes precedence over **scheduledUpdateTime**. For the **scheduledUpdateTime** settings to take effect, set **updateDuration** to **0**.
```json
{
"forms": [
{
"name": "widget",
"description": "This is a service widget.",
"src": "./ets/widget/pages/WidgetCard.ets",
"uiSyntax": "arkts",
"window": {
"designWidth": 720,
"autoDesignWidth": true
},
"colorMode": "auto",
"isDefault": true,
"updateEnabled": true, // Enable the periodic update feature.
"scheduledUpdateTime": "10:30", // Set the scheduled time to update the widget.
"updateDuration": 0,
"defaultDimension": "2*2",
"supportDimensions": ["2*2"]
}
]
}
```
- Set the next update time: The widget will be updated next time at the specified time. You can specify the time by calling the [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime) API. The minimum update interval is 5 minutes. For example, you can configure the widget to update within 5 minutes after the API is called.
```ts
import formProvider from '@ohos.app.form.formProvider';
let formId = '123456789'; // Use the actual widget ID in real-world scenarios.
try {
// Configure the widget to update in 5 minutes.
formProvider.setFormNextRefreshTime(formId, 5, (err, data) => {
if (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
return;
} else {
console.info('Succeeded in setFormNextRefreshTimeing.');
}
});
} catch (err) {
console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`);
}
```
When periodic update is triggered, the system calls the [onUpdateForm()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback of the FormExtensionAbility. In the callback, [updateForm()](../reference/apis/js-apis-app-form-formProvider.md#updateform) can be used to update the widget by the provider. For details about how to use **onUpdateForm()**, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md).
> **NOTE**
> 1. Each widget can be updated at the specified interval for a maximum of 50 times every day, including updates triggered by setting [updateDuration](arkts-ui-widget-configuration.md) or calling [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime). When the limit is reached, the widget cannot be updated in this mode again. The number of update times is reset at 00:00 every day.
>
> 2. The same timer is used for timing updates at the specified interval. Therefore, the first scheduled update of widgets may have a maximum deviation of 30 minutes. For example, the first widget A (updated every half an hour) is added at 03:20. The timer starts and triggers an update every half an hour. The second widget B (updated every half an hour) is added at 03:40. When the timer event is triggered at 03:50, widget A is updated, and widget B will be updated at 04:20 next time.
>
> 3. Updates at the specified interval and updates at the scheduled time are triggered only when the screen is on. When the screen is off, the update action is merely recorded. When the screen is on, the update action is performed.
# ArkTS Widget Working Principles
## Implementation Principles
**Figure 1** ArkTS widget implementation principles
![WidgetPrinciple](figures/WidgetPrinciple.png)
- Widget host: an application that displays the widget content and controls the widget location. Only the system application can function as a widget host.
- Widget provider: an application that provides the widget content to display and controls how widget components are laid out and how they interact with users.
- 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 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.
**Figure 2** Working principles of the ArkTS widget rendering service
![WidgetRender](figures/WidgetRender.png)
Unlike JS widgets, ArkTS widgets support logic code running. To avoid potential ArkTS widget issues from affecting the use of applications, 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 an application provider run in the same virtual machine operating environment, and rendering instances of different application providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different application providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-page) object. Use one **globalThis** object for widgets by the same application provider, and different **globalThis** objects for widgets by different application providers.
## Advantages of ArkTS Widgets
As a quick entry to applications, ArkTS widgets have the following advantages over JS widgets:
- Improved development experience and efficiency, thanks to the unified development paradigm
ArkTS widgets share the same declarative UI development framework as application pages. This means that the page layouts can be directly reused in widgets, improving development experience and efficiency.
**Figure 3** Comparison of widget project structures
![WidgetProject](figures/WidgetProject.png)
- More widget features
- Animation: The ArkTS widget supports the [attribute animation](../reference/arkui-ts/ts-animatorproperty.md) and [explicit animation](../reference/arkui-ts/ts-explicit-animation.md) capabilities, which can be leveraged to deliver a more engaging experience.
- Custom drawing: The ArkTS widget allows you to draw graphics with the [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) component to present information more vividly.
- Logic code execution: The capability to run logic code in widgets means that service logic can be self-closed in widgets, expanding the service application scenarios of widgets.
## Constraints on ArkTS Widgets
Compared with JS widgets, ArkTS widgets provide more capabilities, but they are also more prone to malicious behavior. The ArkTS widget is displayed in the widget host, which is usually the home screen. To ensure user experience and power consumption, the ArkTS widget capability is restricted as follows:
- The .so file cannot be loaded.
- The native programming language cannot be used for development.
- Only [partial](arkts-ui-widget-page-overview.md) components, events, animations, data management, state management, and API capabilities of the declarative paradigm are supported.
- The event processing of the widget is independent of that of the widget host. It is recommended that you do not use the left and right sliding components when the widget host supports left and right swipes to prevent gesture conflicts.
The following features are coming to ArkTS widgets in later versions:
- Breakpoint debugging
- import statements
- Instant preview
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
| -------- | -------- | -------- | | -------- | -------- | -------- |
| [getOrCreateLocalDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7)<br>[getOrCreateLocalDir():Promise&lt;string&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7-1) | There is no corresponding API in the stage model.| Applications developed on the stage model do not have the operation permission in the application root directory. Therefore, no corresponding API is provided.| | [getOrCreateLocalDir(callback:AsyncCallback&lt;string&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7)<br>[getOrCreateLocalDir():Promise&lt;string&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7-1) | There is no corresponding API in the stage model.| Applications developed on the stage model do not have the operation permission in the application root directory. Therefore, no corresponding API is provided.|
| [verifyPermission(permission:string,options:PermissionOptions,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7)<br>[verifyPermission(permission:string,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-1)<br>[verifyPermission(permission:string,options?:PermissionOptions):Promise&lt;number&gt;;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-2) | \@ohos.abilityAccessCtrl.d.ts | [verifyAccessTokenSync(tokenID: number, permissionName: Permissions): GrantStatus;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)<br>[verifyAccessToken(tokenID: number, permissionName: Permissions): Promise&lt;GrantStatus&gt;;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstoken9) | | [verifyPermission(permission:string,options:PermissionOptions,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7)<br>[verifyPermission(permission:string,callback:AsyncCallback&lt;number&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-1)<br>[verifyPermission(permission:string,options?:PermissionOptions):Promise&lt;number&gt;;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-2) | \@ohos.abilityAccessCtrl.d.ts | [verifyAccessTokenSync(tokenID: number, permissionName: Permissions): GrantStatus;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)<br>[verifyAccessToken(tokenID: number, permissionName: Permissions): Promise&lt;GrantStatus&gt;;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstoken9) |
| [requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number,resultCallback:AsyncCallback&lt;PermissionRequestResult&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)<br>[requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number):Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7-1) | application\UIAbilityContext.d.ts | [requestPermissionsFromUser(permissions: Array&lt;string&gt;, requestCallback: AsyncCallback&lt;PermissionRequestResult&gt;) : void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser)<br>[requestPermissionsFromUser(permissions: Array&lt;string&gt;) : Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser-1) | | [requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number,resultCallback:AsyncCallback&lt;PermissionRequestResult&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)<br/>[requestPermissionsFromUser(permissions:Array&lt;string&gt;,requestCode:number):Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7-1) | \@ohos.abilityAccessCtrl.d.ts | [requestPermissionsFromUser(context: Context, permissionList: Array&lt;Permissions&gt;, requestCallback: AsyncCallback&lt;PermissionRequestResult&gt;) : void;](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)<br/>[requestPermissionsFromUser(context: Context, permissionList: Array&lt;Permissions&gt;) : Promise&lt;PermissionRequestResult&gt;;](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9-1) |
| [getApplicationInfo(callback:AsyncCallback&lt;ApplicationInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7)<br>[getApplicationInfo():Promise&lt;ApplicationInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7-1) | application\Context.d.ts | [applicationInfo: ApplicationInfo;](../reference/apis/js-apis-inner-application-context.md#attributes)| | [getApplicationInfo(callback:AsyncCallback&lt;ApplicationInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7)<br>[getApplicationInfo():Promise&lt;ApplicationInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7-1) | application\Context.d.ts | [applicationInfo: ApplicationInfo;](../reference/apis/js-apis-inner-application-context.md#attributes)|
| [getBundleName(callback : AsyncCallback&lt;string&gt;): void;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7)<br>[getBundleName(): Promise&lt;string&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7-1) | application\UIAbilityContext.d.ts | [abilityInfo.bundleName: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)| | [getBundleName(callback : AsyncCallback&lt;string&gt;): void;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7)<br>[getBundleName(): Promise&lt;string&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7-1) | application\UIAbilityContext.d.ts | [abilityInfo.bundleName: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)|
| [getDisplayOrientation(callback : AsyncCallback&lt;bundle.DisplayOrientation&gt;): void;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7)<br>[getDisplayOrientation(): Promise&lt;bundle.DisplayOrientation&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7-1) | \@ohos.screen.d.ts | [readonly orientation: Orientation;](../reference/apis/js-apis-screen.md#orientation) | | [getDisplayOrientation(callback : AsyncCallback&lt;bundle.DisplayOrientation&gt;): void;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7)<br>[getDisplayOrientation(): Promise&lt;bundle.DisplayOrientation&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7-1) | \@ohos.screen.d.ts | [readonly orientation: Orientation;](../reference/apis/js-apis-screen.md#orientation) |
......
...@@ -25,7 +25,8 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab ...@@ -25,7 +25,8 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab
- [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md): ExtensionAbility component of the enterprise_admin type, which provides APIs for processing enterprise management events, such as application installation events on devices and events indicating too many incorrect screen-lock password attempts. - [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md): ExtensionAbility component of the enterprise_admin type, which provides APIs for processing enterprise management events, such as application installation events on devices and events indicating too many incorrect screen-lock password attempts.
> **NOTE**<br> > **NOTE**
>
> 1. Third-party applications cannot implement ServiceExtensionAbility, DataShareExtensionAbility, StaticSubscriberExtensionAbility, or WindowExtensionAbility. > 1. Third-party applications cannot implement ServiceExtensionAbility, DataShareExtensionAbility, StaticSubscriberExtensionAbility, or WindowExtensionAbility.
> >
> 2. To implement transaction processing in the background for a third-party application, use background tasks rather than ServiceExtensionAbility. For details, see [Background Task](../task-management/background-task-overview.md). > 2. To implement transaction processing in the background for a third-party application, use background tasks rather than ServiceExtensionAbility. For details, see [Background Task](../task-management/background-task-overview.md).
...@@ -45,7 +46,7 @@ The following uses [InputMethodExtensionAbility](../reference/apis/js-apis-input ...@@ -45,7 +46,7 @@ The following uses [InputMethodExtensionAbility](../reference/apis/js-apis-input
## Implementing ExtensionAbility of the Specified Type ## Implementing ExtensionAbility of the Specified Type
The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](Widget-development-stage.md). The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](service-widget-overview.md).
You do not need to care when to add or delete a widget. The lifecycle of the FormExtensionAbility instance and the lifecycle of the ExtensionAbility process where the FormExtensionAbility instance is located are scheduled and managed by FormManagerService. You do not need to care when to add or delete a widget. The lifecycle of the FormExtensionAbility instance and the lifecycle of the ExtensionAbility process where the FormExtensionAbility instance is located are scheduled and managed by FormManagerService.
...@@ -63,3 +64,5 @@ You do not need to care when to add or delete a widget. The lifecycle of the For ...@@ -63,3 +64,5 @@ You do not need to care when to add or delete a widget. The lifecycle of the For
> - The two FormExtensionAbility components run in an independent process. > - The two FormExtensionAbility components run in an independent process.
> >
> - The two ImeExtensionAbility components run in an independent process. > - The two ImeExtensionAbility components run in an independent process.
<!--no_check-->
\ No newline at end of file
# Service Widget Overview
A service widget (also called widget) is a set of UI components that display important information or operations specific to an application. It provides users with direct access to a desired application service, without the need to open the application first. A widget usually appears as a part of the UI of another application (which currently can only be a system application, such as the home screen) and provides basic interactive features such as opening a UI page or sending a message.
## Service Widget Architecture
**Figure 1** Service widget architecture
![WidgetArchitecture](figures/WidgetArchitecture.png)
Before you get started, it would be helpful if you have a basic understanding of the following concepts:
- Widget host: an application that displays the widget content and controls the widget location. An example is the home screen in the preceding figure.
- Application icon: an application entry icon, clicking which starts the application process. The icon content does not support interactions.
- Widget: an interactive UI in various sizes. It may provide buttons to implement different functions, such as the button to [update the widget content](arkts-ui-widget-event-formextensionability.md) or [switch to an application](arkts-ui-widget-event-router.md).
- Card provider: an application that provides service widget content to be displayed. It controls the display content, display logic, and component click events triggered on a service widget.
- FormExtensionAbility: widget service logic module, which provides lifecycle callbacks invoked when a widget is created, destroyed, or updated.
- Widget page: widget UI module, which contains display and interaction information such as components, layouts, and events.
Below is the typical procedure of using the widget:
**Figure 2** Typical procedure of using the widget
![WidgetUse](figures/WidgetUse.png)
1. Touch and hold an application icon on the home screen to display the shortcut menu.
2. Touch **Service widget** to access the preview screen.
3. Touch the **Add to home** button. The widget is then added to the home screen.
## Widget UI Development Mode
In the stage model, the UI of a widget can be developed in [ArkTS](arkts-ui-widget-working-principles.md) or [JS](js-ui-widget-development.md).
- A widget developed in the ArkTS-based declarative development paradigm is called ArkTS widget.
- A widget developed in the JS-compatible web-like development paradigm is called JS widget.
ArkTS widgets and JS widgets have different implementation principles and features. The following table lists the differences in capabilities.
| Category| JS widget| ArkTS widget|
| -------- | -------- | -------- |
| Development paradigm| Web-like paradigm| Declarative paradigm|
| Component capability| Supported| Supported|
| Layout capability| Supported| Supported|
| Event capability| Supported| Supported|
| Custom animation| Not supported| Supported|
| Custom drawing| Not supported| Supported|
| Logic code execution (excluding the import capability)| Not supported| Supported|
As can be seen above, ArkTS widgets have more capabilities and use cases than JS widgets. Therefore, ArkTS widgets are always recommended, except for the case where the widget consists of only static pages.
...@@ -232,7 +232,7 @@ The widget configuration file is named **config.json**. Find the **config.json** ...@@ -232,7 +232,7 @@ The widget configuration file is named **config.json**. Find the **config.json**
"type": "service", "type": "service",
"srcLanguage": "ets", "srcLanguage": "ets",
"formsEnabled": true, "formsEnabled": true,
"formConfigAbility": "ability://com.example.entry.EntryAbility", "formConfigAbility": "ability://com.example.entry.MainAbility",
"forms": [{ "forms": [{
"colorMode": "auto", "colorMode": "auto",
"defaultDimension": "2*2", "defaultDimension": "2*2",
...@@ -323,7 +323,7 @@ async function deleteFormInfo(formId: string) { ...@@ -323,7 +323,7 @@ async function deleteFormInfo(formId: string) {
// ... // ...
``` ```
For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). For details about how to implement persistent data storage, see [Application Data Persistence Overview](../database/app-data-persistence-overview.md).
The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary.
...@@ -434,7 +434,7 @@ You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. Thi ...@@ -434,7 +434,7 @@ You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. Thi
"actions": { "actions": {
"routerEvent": { "routerEvent": {
"action": "router", "action": "router",
"abilityName": "com.example.entry.EntryAbility", "abilityName": "com.example.entry.MainAbility",
"params": { "params": {
"message": "add detail" "message": "add detail"
} }
...@@ -452,8 +452,8 @@ You can set router and message events for components on a widget. The router eve ...@@ -452,8 +452,8 @@ You can set router and message events for components on a widget. The router eve
2. Set the router event. 2. Set the router event.
- **action**: **"router"**, which indicates a router event. - **action**: **"router"**, which indicates a router event.
- **abilityName**: name of the ability to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default UIAbility name created by DevEco Studio in the FA model is com.example.entry.EntryAbility. - **abilityName**: name of the ability to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default MainAbility name created by DevEco Studio in the FA model is com.example.entry.MainAbility.
- **params**: custom parameters passed to the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the EntryAbility in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field. - **params**: custom parameters passed to the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the MainAbility in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field.
3. Set the message event. 3. Set the message event.
- **action**: **"message"**, which indicates a message event. - **action**: **"message"**, which indicates a message event.
...@@ -529,7 +529,7 @@ The following is an example: ...@@ -529,7 +529,7 @@ The following is an example:
"actions": { "actions": {
"routerEvent": { "routerEvent": {
"action": "router", "action": "router",
"abilityName": "com.example.entry.EntryAbility", "abilityName": "com.example.entry.MainAbility",
"params": { "params": {
"message": "add detail" "message": "add detail"
} }
......
...@@ -23,7 +23,7 @@ The following table provides only a simple description of the related APIs. For ...@@ -23,7 +23,7 @@ The following table provides only a simple description of the related APIs. For
| off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.| | off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.|
| once\('headersReceive'\)<sup>8+</sup> | Registers a one-time observer for HTTP Response Header events.| | once\('headersReceive'\)<sup>8+</sup> | Registers a one-time observer for HTTP Response Header events.|
## How to Develop ## How to Develop request APIs
1. Import the **http** namespace from **@ohos.net.http.d.ts**. 1. Import the **http** namespace from **@ohos.net.http.d.ts**.
2. Call **createHttp()** to create an **HttpRequest** object. 2. Call **createHttp()** to create an **HttpRequest** object.
...@@ -42,42 +42,42 @@ let httpRequest = http.createHttp(); ...@@ -42,42 +42,42 @@ let httpRequest = http.createHttp();
// This API is used to listen for the HTTP Response Header event, which is returned earlier than the result of the HTTP request. It is up to you whether to listen for HTTP Response Header events. // This API is used to listen for the HTTP Response Header event, which is returned earlier than the result of the HTTP request. It is up to you whether to listen for HTTP Response Header events.
// on('headerReceive', AsyncCallback) is replaced by on('headersReceive', Callback) since API version 8. // on('headerReceive', AsyncCallback) is replaced by on('headersReceive', Callback) since API version 8.
httpRequest.on('headersReceive', (header) => { httpRequest.on('headersReceive', (header) => {
console.info('header: ' + JSON.stringify(header)); console.info('header: ' + JSON.stringify(header));
}); });
httpRequest.request( httpRequest.request(
// Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL.
"EXAMPLE_URL", {
{ method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET.
method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET. // You can add header fields based on service requirements.
// You can add header fields based on service requirements. header: {
header: { 'Content-Type': 'application/json'
'Content-Type': 'application/json' },
}, // This field is used to transfer data when the POST request is used.
// This field is used to transfer data when the POST request is used. extraData: {
extraData: { "data": "data to send",
"data": "data to send", },
}, expectDataType: http.HttpDataType.STRING, // Optional. This field specifies the type of the return data.
expectDataType: http.HttpDataType.STRING, // Optional. This field specifies the type of the return data. usingCache: true, // Optional. The default value is true.
usingCache: true, // Optional. The default value is true. priority: 1, // Optional. The default value is 1.
priority: 1, // Optional. The default value is 1. connectTimeout: 60000 // Optional. The default value is 60000, in ms.
connectTimeout: 60000 // Optional. The default value is 60000, in ms. readTimeout: 60000, // Optional. The default value is 60000, in ms.
readTimeout: 60000, // Optional. The default value is 60000, in ms. usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system.
usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system. }, (err, data) => {
}, (err, data) => { if (!err) {
if (!err) { // data.result carries the HTTP response. Parse the response based on service requirements.
// data.result carries the HTTP response. Parse the response based on service requirements. console.info('Result:' + JSON.stringify(data.result));
console.info('Result:' + JSON.stringify(data.result)); console.info('code:' + JSON.stringify(data.responseCode));
console.info('code:' + JSON.stringify(data.responseCode)); // data.header carries the HTTP response header. Parse the content based on service requirements.
// data.header carries the HTTP response header. Parse the content based on service requirements. console.info('header:' + JSON.stringify(data.header));
console.info('header:' + JSON.stringify(data.header)); console.info('cookies:' + JSON.stringify(data.cookies)); // 8+
console.info('cookies:' + JSON.stringify(data.cookies)); // 8+ } else {
} else { console.info('error:' + JSON.stringify(err));
console.info('error:' + JSON.stringify(err)); // Unsubscribe from HTTP Response Header events.
// Unsubscribe from HTTP Response Header events. httpRequest.off('headersReceive');
httpRequest.off('headersReceive'); // Call the destroy() method to release resources after HttpRequest is complete.
// Call the destroy() method to release resources after HttpRequest is complete. httpRequest.destroy();
httpRequest.destroy();
}
} }
}
); );
``` ```
# Network Sharing # Network Sharing
## Introduction ## Introduction
The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume. The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume.
> **NOTE** > **Note:**
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md).
## Basic Concepts ## Basic Concepts
- Wi-Fi sharing: Shares the network through a Wi-Fi hotspot.
- Bluetooth sharing: Shares the network through Bluetooth. - Wi-Fi sharing: Shares the network through a Wi-Fi hotspot.
- USB tethering: Shares the network using a USB flash drive. - Bluetooth sharing: Shares the network through Bluetooth.
- USB tethering: Shares the network using a USB flash drive.
## **Constraints** ## **Constraints**
- Programming language: C++ and JS
- System: Linux kernel - Programming language: C++ and JS
- The initial APIs of this module are supported since API version 9. Newly added APIs will be marked with a superscript to indicate their earliest API version. - System: Linux kernel
- The initial APIs of this module are supported since API version 9. Newly added APIs will be marked with a superscript to indicate their earliest API version.
## When to Use ## When to Use
Typical network sharing scenarios are as follows: Typical network sharing scenarios are as follows:
- Enabling network sharing
- Disabling network sharing - Enabling Network Sharing
- Obtaining the data traffic of the shared network - Disabling network sharing
- Obtaining the data traffic of the shared network
The following describes the development procedure specific to each application scenario. The following describes the development procedure specific to each application scenario.
## Available APIs ## Available APIs
For the complete list of APIs and example code, see [Network Sharing](../reference/apis/js-apis-net-sharing.md). For the complete list of APIs and example code, see [Network Sharing](../reference/apis/js-apis-net-sharing.md).
| Type| API| Description| | Type| API| Description|
...@@ -54,18 +61,18 @@ For the complete list of APIs and example code, see [Network Sharing](../referen ...@@ -54,18 +61,18 @@ For the complete list of APIs and example code, see [Network Sharing](../referen
```js ```js
// Import the sharing namespace from @ohos.net.sharing. // Import the sharing namespace from @ohos.net.sharing.
import sharing from '@ohos.net.sharing' import sharing from '@ohos.net.sharing'
// Subscribe to network sharing state changes. // Subscribe to network sharing state changes.
sharing.on('sharingStateChange', (error, data) => { sharing.on('sharingStateChange', (error, data) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
console.log(JSON.stringify(data)); console.log(JSON.stringify(data));
}); });
// Call startSharing to start network sharing of the specified type. // Call startSharing to start network sharing of the specified type.
sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
}); });
``` ```
## Disabling network sharing ## Disabling network sharing
...@@ -79,18 +86,18 @@ For the complete list of APIs and example code, see [Network Sharing](../referen ...@@ -79,18 +86,18 @@ For the complete list of APIs and example code, see [Network Sharing](../referen
```js ```js
// Import the sharing namespace from @ohos.net.sharing. // Import the sharing namespace from @ohos.net.sharing.
import sharing from '@ohos.net.sharing' import sharing from '@ohos.net.sharing'
// Subscribe to network sharing state changes. // Subscribe to network sharing state changes.
sharing.on('sharingStateChange', (error, data) => { sharing.on('sharingStateChange', (error, data) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
console.log(JSON.stringify(data)); console.log(JSON.stringify(data));
}); });
// Call stopSharing to stop network sharing of the specified type. // Call stopSharing to stop network sharing of the specified type.
sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
}); });
``` ```
## Obtaining the data traffic of the shared network ## Obtaining the data traffic of the shared network
...@@ -104,27 +111,27 @@ For the complete list of APIs and example code, see [Network Sharing](../referen ...@@ -104,27 +111,27 @@ For the complete list of APIs and example code, see [Network Sharing](../referen
```js ```js
// Import the sharing namespace from @ohos.net.sharing. // Import the sharing namespace from @ohos.net.sharing.
import sharing from '@ohos.net.sharing' import sharing from '@ohos.net.sharing'
// Call startSharing to start network sharing of the specified type. // Call startSharing to start network sharing of the specified type.
sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
}); });
// Call getStatsTotalBytes to obtain the data traffic generated during data sharing. // Call getStatsTotalBytes to obtain the data traffic generated during data sharing.
sharing.getStatsTotalBytes((error, data) => { sharing.getStatsTotalBytes((error, data) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
console.log(JSON.stringify(data)); console.log(JSON.stringify(data));
}); });
// Call stopSharing to stop network sharing of the specified type and clear the data volume of network sharing. // Call stopSharing to stop network sharing of the specified type and clear the data volume of network sharing.
sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
}); });
// Call getStatsTotalBytes again. The data volume of network sharing has been cleared. // Call getStatsTotalBytes again. The data volume of network sharing has been cleared.
sharing.getStatsTotalBytes((error, data) => { sharing.getStatsTotalBytes((error, data) => {
console.log(JSON.stringify(error)); console.log(JSON.stringify(error));
console.log(JSON.stringify(data)); console.log(JSON.stringify(data));
}); });
``` ```
...@@ -13,10 +13,10 @@ The Socket Connection module allows an application to transmit data over a Socke ...@@ -13,10 +13,10 @@ The Socket Connection module allows an application to transmit data over a Socke
## When to Use ## When to Use
Applications transmit data over TCP, UDP, or TLS Socket connections. The main application scenarios are as follows: Applications transmit data over TCP, UDP, or TLSSocket connections. The main application scenarios are as follows:
- Implementing data transmission over TCP/UDP Socket connections - Implementing data transmission over TCP/UDPSocket connections
- Implementing encrypted data transmission over TLS Socket connections - Implementing encrypted data transmission over TLSSocket connections
## Available APIs ## Available APIs
...@@ -40,12 +40,12 @@ Socket connection functions are mainly implemented by the **socket** module. The ...@@ -40,12 +40,12 @@ Socket connection functions are mainly implemented by the **socket** module. The
| off(type:&nbsp;'close') | Unsubscribes from **close** events of the Socket connection.| | off(type:&nbsp;'close') | Unsubscribes from **close** events of the Socket connection.|
| on(type:&nbsp;'error') | Subscribes to **error** events of the Socket connection.| | on(type:&nbsp;'error') | Subscribes to **error** events of the Socket connection.|
| off(type:&nbsp;'error') | Unsubscribes from **error** events of the Socket connection.| | off(type:&nbsp;'error') | Unsubscribes from **error** events of the Socket connection.|
| on(type:&nbsp;'listening') | Subscribes to **listening** events of the UDP Socket connection. | | on(type:&nbsp;'listening') | Subscribes to **listening** events of the UDPSocket connection. |
| off(type:&nbsp;'listening') | Unsubscribes from **listening** events of the UDP Socket connection. | | off(type:&nbsp;'listening') | Unsubscribes from **listening** events of the UDPSocket connection. |
| on(type:&nbsp;'connect') | Subscribes to **connect** events of the TCP Socket connection. | | on(type:&nbsp;'connect') | Subscribes to **connect** events of the TCPSocket connection. |
| off(type:&nbsp;'connect') | Unsubscribes from **connect** events of the TCP Socket connection.| | off(type:&nbsp;'connect') | Unsubscribes from **connect** events of the TCPSocket connection.|
TLS Socket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs. TLSSocket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs.
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
...@@ -56,28 +56,28 @@ TLS Socket connection functions are mainly provided by the **tls_socket** module ...@@ -56,28 +56,28 @@ TLS Socket connection functions are mainly provided by the **tls_socket** module
| getCertificate() | Obtains an object representing the local certificate.| | getCertificate() | Obtains an object representing the local certificate.|
| getCipherSuite() | Obtains a list containing information about the negotiated cipher suite.| | getCipherSuite() | Obtains a list containing information about the negotiated cipher suite.|
| getProtocol() | Obtains a string containing the SSL/TLS protocol version negotiated for the current connection.| | getProtocol() | Obtains a string containing the SSL/TLS protocol version negotiated for the current connection.|
| getRemoteAddress() | Obtains the peer address of the TLS Socket connection.| | getRemoteAddress() | Obtains the peer address of the TLSSocket connection.|
| getRemoteCertificate() | Obtains an object representing a peer certificate.| | getRemoteCertificate() | Obtains an object representing a peer certificate.|
| getSignatureAlgorithms() | Obtains a list containing signature algorithms shared between the server and client, in descending order of priority.| | getSignatureAlgorithms() | Obtains a list containing signature algorithms shared between the server and client, in descending order of priority.|
| getState() | Obtains the TLS Socket connection status.| | getState() | Obtains the TLSSocket connection status.|
| off(type:&nbsp;'close') | Unsubscribes from **close** events of the TLS Socket connection.| | off(type:&nbsp;'close') | Unsubscribes from **close** events of the TLSSocket connection.|
| off(type:&nbsp;'error') | Unsubscribes from **error** events of the TLS Socket connection.| | off(type:&nbsp;'error') | Unsubscribes from **error** events of the TLSSocket connection.|
| off(type:&nbsp;'message') | Unsubscribes from **message** events of the TLS Socket connection.| | off(type:&nbsp;'message') | Unsubscribes from **message** events of the TLSSocket connection.|
| on(type:&nbsp;'close') | Subscribes to **close** events of the TLS Socket connection.| | on(type:&nbsp;'close') | Subscribes to **close** events of the TLSSocket connection.|
| on(type:&nbsp;'error') | Subscribes to **error** events of the TLS Socket connection.| | on(type:&nbsp;'error') | Subscribes to **error** events of the TLSSocket connection.|
| on(type:&nbsp;'message') | Subscribes to **message** events of the TLS Socket connection.| | on(type:&nbsp;'message') | Subscribes to **message** events of the TLSSocket connection.|
| send() | Sends data.| | send() | Sends data.|
| setExtraOptions() | Sets other properties of the TLS Socket connection.| | setExtraOptions() | Sets other properties of the TLSSocket connection.|
## Transmitting Data over TCP/UDP Socket Connections ## Transmitting Data over TCP/UDPSocket Connections
The implementation is similar for UDP Socket and TCP Socket connections. The following uses data transmission over a TCP Socket connection as an example. The implementation is similar for UDPSocket and TCPSocket connections. The following uses data transmission over a TCPSocket connection as an example.
1. Import the required **socket** module. 1. Import the required **socket** module.
2. Create a **TCPSocket** object. 2. Create a **TCPSocket** object.
3. (Optional) Subscribe to TCP Socket connection events. 3. (Optional) Subscribe to TCPSocket connection events.
4. Bind the IP address and port number. The port number can be specified or randomly allocated by the system. 4. Bind the IP address and port number. The port number can be specified or randomly allocated by the system.
...@@ -85,7 +85,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol ...@@ -85,7 +85,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol
6. Send data. 6. Send data.
7. Enable the TCP Socket connection to be automatically closed after use. 7. Enable the TCPSocket connection to be automatically closed after use.
```js ```js
import socket from '@ohos.net.socket' import socket from '@ohos.net.socket'
...@@ -93,7 +93,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol ...@@ -93,7 +93,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol
// Create a TCPSocket object. // Create a TCPSocket object.
let tcp = socket.constructTCPSocketInstance(); let tcp = socket.constructTCPSocketInstance();
// Subscribe to TCP Socket connection events. // Subscribe to TCPSocket connection events.
tcp.on('message', value => { tcp.on('message', value => {
console.log("on message") console.log("on message")
let buffer = value.message let buffer = value.message
...@@ -152,7 +152,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol ...@@ -152,7 +152,7 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol
}); });
}); });
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. // Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events.
setTimeout(() => { setTimeout(() => {
tcp.close((err) => { tcp.close((err) => {
console.log('close socket.') console.log('close socket.')
...@@ -163,11 +163,11 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol ...@@ -163,11 +163,11 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol
}, 30 * 1000); }, 30 * 1000);
``` ```
## Implementing Encrypted Data Transmission over TLS Socket Connections ## Implementing encrypted data transmission over TLSSocket connections
### How to Develop ### How to Develop
TLS Socket connection process on the client: TLSSocket connection process on the client:
1. Import the required **socket** module. 1. Import the required **socket** module.
...@@ -177,145 +177,143 @@ TLS Socket connection process on the client: ...@@ -177,145 +177,143 @@ TLS Socket connection process on the client:
4. Create a **TLSSocket** object. 4. Create a **TLSSocket** object.
5. (Optional) Subscribe to TLS Socket connection events. 5. (Optional) Subscribe to TLSSocket connection events.
6. Send data. 6. Send data.
7. Enable the TLS Socket connection to be automatically closed after use. 7. Enable the TLSSocket connection to be automatically closed after use.
```js ```js
import socket from '@ohos.net.socket' // Create a TLSSocket connection (for two-way authentication).
let tlsTwoWay = socket.constructTLSSocketInstance();
// Create a TLS Socket connection (for two-way authentication).
let tlsTwoWay = socket.constructTLSSocketInstance(); // Subscribe to TLSSocket connection events.
tlsTwoWay.on('message', value => {
// Subscribe to TLS Socket connection events. console.log("on message")
tlsTwoWay.on('message', value => { let buffer = value.message
console.log("on message") let dataView = new DataView(buffer)
let buffer = value.message let str = ""
let dataView = new DataView(buffer) for (let i = 0; i < dataView.byteLength; ++i) {
let str = "" str += String.fromCharCode(dataView.getUint8(i))
for (let i = 0; i < dataView.byteLength; ++i) { }
str += String.fromCharCode(dataView.getUint8(i)) console.log("on connect received:" + str)
} });
console.log("on connect received:" + str) tlsTwoWay.on('connect', () => {
}); console.log("on connect")
tlsTwoWay.on('connect', () => { });
console.log("on connect") tlsTwoWay.on('close', () => {
}); console.log("on close")
tlsTwoWay.on('close', () => { });
console.log("on close")
}); // Bind the local IP address and port number.
tlsTwoWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => {
// Bind the local IP address and port number. if (err) {
tlsTwoWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => { console.log('bind fail');
if (err) { return;
console.log('bind fail'); }
return; console.log('bind success');
} });
console.log('bind success');
}); // Set the communication parameters.
let options = {
// Set the communication parameters. ALPNProtocols: ["spdy/1", "http/1.1"],
let options = {
ALPNProtocols: ["spdy/1", "http/1.1"], // Set up a connection to the specified IP address and port number.
address: {
// Set up a connection to the specified IP address and port number. address: "192.168.xx.xxx",
address: { port: xxxx, // Port
address: "192.168.xx.xxx", family: 1,
port: xxxx, // Port },
family: 1,
}, // Set the parameters used for authentication during communication.
secureOptions: {
// Set the parameters used for authentication during communication. key: "xxxx", // Key
secureOptions: { cert: "xxxx", // Digital certificate
key: "xxxx", // Key ca: ["xxxx"], // CA certificate
cert: "xxxx", // Digital certificate passwd: "xxxx", // Password for generating the key
ca: ["xxxx"], // CA certificate protocols: [socket.Protocol.TLSv12], // Communication protocol
passwd: "xxxx", // Password for generating the key useRemoteCipherPrefer: true, // Whether to preferentially use the peer cipher suite
protocols: [socket.Protocol.TLSv12], // Communication protocol signatureAlgorithms: "rsa_pss_rsae_sha256:ECDSA+SHA256", // Signature algorithm
useRemoteCipherPrefer: true, // Whether to preferentially use the peer cipher suite cipherSuite: "AES256-SHA256", // Cipher suite
signatureAlgorithms: "rsa_pss_rsae_sha256:ECDSA+SHA256", // Signature algorithm },
cipherSuite: "AES256-SHA256", // Cipher suite };
},
}; // Set up a connection.
tlsTwoWay.connect(options, (err, data) => {
// Set up a connection. console.error(err);
tlsTwoWay.connect(options, (err, data) => { console.log(data);
console.error(err); });
console.log(data);
}); // Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events.
tlsTwoWay.close((err) => {
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. if (err) {
tlsTwoWay.close((err) => { console.log("close callback error = " + err);
if (err) { } else {
console.log("close callback error = " + err); console.log("close success");
} else { }
console.log("close success"); tlsTwoWay.off('message');
} tlsTwoWay.off('connect');
tlsTwoWay.off('message'); tlsTwoWay.off('close');
tlsTwoWay.off('connect'); });
tlsTwoWay.off('close');
}); // Create a TLSSocket connection (for one-way authentication).
let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication
// Create a TLS Socket connection (for one-way authentication).
let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication // Subscribe to TLSSocket connection events.
tlsTwoWay.on('message', value => {
// Subscribe to TLS Socket connection events. console.log("on message")
tlsTwoWay.on('message', value => { let buffer = value.message
console.log("on message") let dataView = new DataView(buffer)
let buffer = value.message let str = ""
let dataView = new DataView(buffer) for (let i = 0; i < dataView.byteLength; ++i) {
let str = "" str += String.fromCharCode(dataView.getUint8(i))
for (let i = 0;i < dataView.byteLength; ++i) { }
str += String.fromCharCode(dataView.getUint8(i)) console.log("on connect received:" + str)
} });
console.log("on connect received:" + str) tlsTwoWay.on('connect', () => {
}); console.log("on connect")
tlsTwoWay.on('connect', () => { });
console.log("on connect") tlsTwoWay.on('close', () => {
}); console.log("on close")
tlsTwoWay.on('close', () => { });
console.log("on close")
}); // Bind the local IP address and port number.
tlsOneWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => {
// Bind the local IP address and port number. if (err) {
tlsOneWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => { console.log('bind fail');
if (err) { return;
console.log('bind fail'); }
return; console.log('bind success');
} });
console.log('bind success');
}); // Set the communication parameters.
let oneWayOptions = {
// Set the communication parameters. address: {
let oneWayOptions = { address: "192.168.xxx.xxx",
address: { port: xxxx,
address: "192.168.xxx.xxx", family: 1,
port: xxxx, },
family: 1, secureOptions: {
}, ca: ["xxxx","xxxx"], // CA certificate
secureOptions: { cipherSuite: "AES256-SHA256", // Cipher suite
ca: ["xxxx","xxxx"], // CA certificate },
cipherSuite: "AES256-SHA256", // Cipher suite };
},
}; // Set up a connection.
tlsOneWay.connect(oneWayOptions, (err, data) => {
// Set up a connection. console.error(err);
tlsOneWay.connect(oneWayOptions, (err, data) => { console.log(data);
console.error(err); });
console.log(data);
}); // Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events.
tlsTwoWay.close((err) => {
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. if (err) {
tlsTwoWay.close((err) => { console.log("close callback error = " + err);
if (err) { } else {
console.log("close callback error = " + err); console.log("close success");
} else { }
console.log("close success"); tlsTwoWay.off('message');
} tlsTwoWay.off('connect');
tlsTwoWay.off('message'); tlsTwoWay.off('close');
tlsTwoWay.off('connect'); });
tlsTwoWay.off('close');
});
``` ```
# WebSocket Connection # WebSocket Connection
## When to Use
## Use Cases You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server.
If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API.
You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event.
If an error occurs in any of the preceding processes, the client will receive a callback of the **error** event. If an error occurs in any of the preceding processes, the client will receive a callback of the **error** event.
## Available APIs ## Available APIs
The WebSocket connection function is mainly implemented by the WebSocket module. To use related APIs, you must declare the **ohos.permission.INTERNET** permission. The following table describes the related APIs. The WebSocket connection function is mainly implemented by the WebSocket module. To use related APIs, you must declare the **ohos.permission.INTERNET** permission. The following table describes the related APIs.
| API | Description | | API| Description|
| -------- | -------- | | -------- | -------- |
| createWebSocket() | Creates a WebSocket connection. | | createWebSocket() | Creates a WebSocket connection.|
| connect() | Establishes a WebSocket connection to a given URL. | | connect() | Establishes a WebSocket connection to a given URL.|
| send() | Sends data through the WebSocket connection. | | send() | Sends data through the WebSocket connection.|
| close() | Closes a WebSocket connection. | | close() | Closes a WebSocket connection.|
| on(type: 'open') | Enables listening for **open** events of a WebSocket connection. | | on(type: 'open') | Enables listening for **open** events of a WebSocket connection.|
| off(type: 'open') | Disables listening for **open** events of a WebSocket connection. | | off(type: 'open') | Disables listening for **open** events of a WebSocket connection.|
| on(type: 'message') | Enables listening for **message** events of a WebSocket connection. | | on(type: 'message') | Enables listening for **message** events of a WebSocket connection.|
| off(type: 'message') | Disables listening for **message** events of a WebSocket connection. | | off(type: 'message') | Disables listening for **message** events of a WebSocket connection.|
| on(type: 'close') | Enables listening for **close** events of a WebSocket connection. | | on(type: 'close') | Enables listening for **close** events of a WebSocket connection.|
| off(type: 'close') | Disables listening for **close** events of a WebSocket connection. | | off(type: 'close') | Disables listening for **close** events of a WebSocket connection.|
| on(type: 'error') | Enables listening for **error** events of a WebSocket connection. | | on(type: 'error') | Enables listening for **error** events of a WebSocket connection.|
| off(type: 'error') | Disables listening for **error** events of a WebSocket connection. | | off(type: 'error') | Disables listening for **error** events of a WebSocket connection.|
## How to Develop ## How to Develop
1. Import the required WebSocket module. 1. Import the required webSocket module.
2. Create a **WebSocket** object. 2. Create a **WebSocket** object.
3. (Optional) Subscribe to WebSocket open, message, close, and error events. 3. (Optional) Subscribe to WebSocket **open**, **message**, **close**, and **error** events.
4. Establish a WebSocket connection to a given URL. 4. Establish a WebSocket connection to a given URL.
5. Close the WebSocket connection if it is no longer needed. 5. Close the WebSocket connection if it is no longer needed.
```js ```js
import webSocket from '@ohos.net.webSocket'; import webSocket from '@ohos.net.webSocket';
var defaultIpAddress = "ws://"; var defaultIpAddress = "ws://";
let ws = webSocket.createWebSocket(); let ws = webSocket.createWebSocket();
ws.on('open', (err, value) => { ws.on('open', (err, value) => {
console.log("on open, status:" + JSON.stringify(value)); console.log("on open, status:" + JSON.stringify(value));
// When receiving the on('open') event, the client can use the send() API to communicate with the server. // When receiving the on('open') event, the client can use the send() API to communicate with the server.
ws.send("Hello, server!", (err, value) => { ws.send("Hello, server!", (err, value) => {
if (!err) { if (!err) {
console.log("Message sent successfully"); console.log("Message sent successfully");
} else { } else {
console.log("Failed to send the message. Err:" + JSON.stringify(err)); console.log("Failed to send the message. Err:" + JSON.stringify(err));
} }
}); });
}); });
ws.on('message', (err, value) => { ws.on('message', (err, value) => {
console.log("on message, message:" + value); console.log("on message, message:" + value);
// When receiving the `bye` message (the actual message name may differ) from the server, the client proactively disconnects from the server. // When receiving the `bye` message (the actual message name may differ) from the server, the client proactively disconnects from the server.
if (value === 'bye') { if (value === 'bye') {
ws.close((err, value) => { ws.close((err, value) => {
if (!err) { if (!err) {
console.log("Connection closed successfully"); console.log("Connection closed successfully");
} else { } else {
console.log("Failed to close the connection. Err: " + JSON.stringify(err)); console.log("Failed to close the connection. Err: " + JSON.stringify(err));
} }
}); });
} }
}); });
ws.on('close', (err, value) => { ws.on('close', (err, value) => {
console.log("on close, code is " + value.code + ", reason is " + value.reason); console.log("on close, code is " + value.code + ", reason is " + value.reason);
}); });
ws.on('error', (err) => { ws.on('error', (err) => {
console.log("on error, error:" + JSON.stringify(err)); console.log("on error, error:" + JSON.stringify(err));
}); });
ws.connect(defaultIpAddress, (err, value) => { ws.connect(defaultIpAddress, (err, value) => {
if (!err) { if (!err) {
console.log("Connected successfully"); console.log("Connected successfully");
} else { } else {
console.log("Connection failed. Err:" + JSON.stringify(err)); console.log("Connection failed. Err:" + JSON.stringify(err));
} }
}); });
``` ```
# Data Management # Data Management
- Distributed Data Service - [Data Management Overview](data-mgmt-overview.md)
- [Distributed Data Service Overview](database-mdds-overview.md) - Application Data Persistence
- [Distributed Data Service Development](database-mdds-guidelines.md) - [Overview of Application Data Persistence](app-data-persistence-overview.md)
- Relational Database - [Persisting Preferences Data](data-persistence-by-preferences.md)
- [RDB Overview](database-relational-overview.md) - [Persisting KV Store Data](data-persistence-by-kv-store.md)
- [RDB Development](database-relational-guidelines.md) - [Persisting RDB Store Data](data-persistence-by-rdb-store.md)
- Preferences - Distributed Application Data Synchronization
- [Preferences Overview](database-preference-overview.md) - [Distributed Application Data Synchronization Overview](sync-app-data-across-devices-overview.md)
- [Preferences Development](database-preference-guidelines.md) - [Cross-Device Synchronization of KV Stores](data-sync-of-kv-store.md)
- Distributed Data Object - [Cross-Device Synchronization of RDB Stores](data-sync-of-rdb-store.md)
- [Distributed Data Object Overview](database-distributedobject-overview.md) - [Cross-Device Synchronization of Distributed Data Objects](data-sync-of-distributed-data-object.md)
- [Distributed Data Object Development](database-distributedobject-guidelines.md) - Data Reliability and Security
- Data Share - [Data Reliability and Security Overview](data-reliability-security-overview.md)
- [DataShare Overview](database-datashare-overview.md) - [Database Backup and Restoration](data-backup-and-restore.md)
- [DataShare Development](database-datashare-guidelines.md) - [Database Encryption](data-encryption.md)
- [Access Control by Device and Data Level](access-control-by-device-and-data-level.md)
- Cross-Application Data Sharing (for System Applications Only)
- [Cross-Application Data Sharing Overview](share-device-data-across-apps-overview.md)
- [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
- [Sharing Data in Silent Access](share-data-by-silent-access.md)
# Access Control by Device and Data Level
## Basic Concepts
Distributed data management implements access control based on data security labels and device security levels.
A higher data security label and device security level indicate stricter encryption and access control measures and higher data security.
### Data Security Labels
The data can be rated into four security levels: S1, S2, S3, and S4.
| Risk Level| Security Level| Definition| Example|
| -------- | -------- | -------- | -------- |
| Critical| S4 | Special data types defined by industry laws and regulations, involving the most private individual information or data that may cause significant adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Political opinions, religious and philosophical belief, trade union membership, genetic data, biological information, health and sexual life status, sexual orientation, device authentication, and personal credit card information|
| High| S3 | Data that may cause critical adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Individual real-time precise positioning information and movement trajectory|
| Moderate| S2 | Data that may cause major adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Detailed addresses and nicknames of individuals|
| Low| S1 | Data that may cause minor adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Gender, nationality, and user application records|
### Device Security Levels
Device security levels are classified into SL1 to SL5 based on devices' security capabilities, for example, whether a Trusted Execution Environment (TEE) or a secure storage chip is available. For example, the development boards RK3568 and Hi3516 are SL1 (lower security) devices, and tablets are SL4 (higher security) devices.
During device networking, you can run the **hidumper -s 3511** command to query the device security level. The following example shows how to query the security level of the RK3568 board:
![en-us_image_0000001542496993](figures/en-us_image_0000001542496993.png)
## Access Control Mechanism in Cross-Device Synchronization
In cross-device data synchronization, data access is controlled based on the device security level and data security labels. In principle, data can be synchronized only to the devices whose data security labels are not higher than the device's security level. The access control matrix is as follows:
|Device Security Level|Data Security Labels of the Synchornizable Device|
|---|---|
|SL1|S1|
|SL2|S1 to S2|
|SL3|S1 to S3|
|SL4|S1 to S4|
|SL5|S1 to S4|
For example, the security level of development boards RK3568 and Hi3516 is SL1. The database with data security label S1 can be synchronized with RK3568 and Hi3516, but the databases with database labels S2-S4 cannot.
## When to Use
The access control mechanism ensures secure data storage and synchronization across devices. When creating a database, you need to correctly set the security level for the database.
## Setting the Security Level for a KV Store
When a KV store is created, the **securityLevel** parameter specifies the security level of the KV store. The following example shows how to create a KV store with security level of S1.
For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
encrypt: true,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S1
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Setting the Security Level for an RDB Store
When an RDB store is created, the **securityLevel** parameter specifies the security level of the RDB store. The following example shows how to create an RDB store with security level of S1.
For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1
};
let promise = relationalStore.getRdbStore(this.context, STORE_CONFIG);
promise.then(async (rdbStore) => {
store = rdbStore;
console.info('Succeeded in getting RdbStore.')
}).catch((err) => {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
})
```
# Application Data Persistence Overview
Application data persistence means to save the application data in the memory to a file or database on a device. The data in the memory is usually saved in the forms of data structs or data objects, and the data in storage media can be saved in the forms of text, databases, or binary files.
The OpenHarmony standard system supports typical data storage forms, including user preferences (**Preferences**), key-value databases (**KV-Store**), and relational databases (**RelationalStore**).
You can use proper data storage forms to implement data persistence:
- **Preferences**: used to store application configuration data. Data is stored as text files on a device. When the application is used, it loads all the data from the text file to the memory. **Preferences** allow fast and efficient data access, but are not suitable when a large amount of data needs to be stored.
- **KV-Store**: used to store data in KV pairs, in which the key uniquely identifies the data. A KV store is a kind of non-relational database. It is ideal for storing service data with few data and service relationships. It has been widely used because it poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data synchronization. KV databases feature higher cross-device and cross-version compatibility than relational databases.
- **RelationalStore**: used to store data in rows and columns. It is widely used to process relational data in applications. RelationalStore provides a set of APIs for adding, deleting, modifying, and querying data. You can also define and use SQL statements for complex service scenarios.
# Database Backup and Restoration
## When to Use
You may need to restore a database in any of the following cases:
An important operation being performed by an application is interrupted.
The database is unavailable due to data loss or corruption, or dirty data.
Both KV stores and RDB stores support database backup and restoration. In addition, KV stores allow you to delete database backups to release local storage space.
## Backing Up, Restoring, and Deleting a KV Store
You can use **backup()** to back up a KV store, use **restore()** to restore a KV store, and use **deletebackup()** to delete a KV store backup file. For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
1. Create a KV store.
(1) Create a **kvManager** instance.
(2) Set database parameters.
(3) Create a **kvStore** instance.
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
2. Use **put()** to insert data to the KV store.
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Fail to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
3. Use **backup()** to back up the KV store.
```js
let file = 'BK001';
try {
kvStore.backup(file, (err) => {
if (err) {
console.error(`Fail to backup data.code:${err.code},message:${err.message}`);
} else {
console.info('Succeeded in backupping data.');
}
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
4. Use **delete()** to delete data to simulate unexpected deletion or data tampering.
```js
try {
kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Fail to delete data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in deleting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
5. Use **restore()** to restore the KV store.
```js
let file = 'BK001';
try {
kvStore.restore(file, (err) => {
if (err) {
console.error(`Fail to restore data. Code:${err.code},message:${err.message}`);
} else {
console.info('Succeeded in restoring data.');
}
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
6. Use **deleteBackup()** to delete the backup file to release storage space.
```js
let kvStore;
let files = ['BK001'];
try {
kvStore.deleteBackup(files).then((data) => {
console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`);
}).catch((err) => {
console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`);
})
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Backing Up and Restoring an RDB Store
You can use **backup()** to back up an RDB store, and use **restore()** to restore an RDB store. For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
1. Use **getRdbStore()** to create an RDB store.
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
let context = getContext(this);
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1
};
relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
store = rdbStore;
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
return;
}
store.executeSql("CREATE TABLE IF NOT EXISTS EMPLOYEE (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, salary INTEGER, codes Uint8Array);", null);
console.info('Succeeded in getting RdbStore.');
})
```
2. Use **insert()** to insert data to the RDB store.
```js
const valueBucket = {
'NAME': 'Lisa',
'AGE': 18,
'SALARY': 100.5,
'CODES': new Uint8Array([1, 2, 3, 4, 5])
};
store.insert('EMPLOYEE', valueBucket, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE, (err, rowId) => {
if (err) {
console.error(`Failed to insert data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in inserting data. rowId:${rowId}`);
})
```
3. Use **backup()** to back up the RDB store.
```js
store.backup('dbBackup.db', (err) => {
if (err) {
console.error(`Failed to backup data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in backuping data.`);
})
```
4. Use **delete()** to delete data to simulate unexpected deletion or data tampering.
```js
let predicates = new relationalStore.RdbPredicates('EMPLOYEE');
predicates.equalTo('NAME', 'Lisa');
let promise = store.delete(predicates);
promise.then((rows) => {
console.info(`Delete rows: ${rows}`);
}).catch((err) => {
console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
})
```
5. Use **restore()** to restore the RDB store.
```js
store.restore('dbBackup.db', (err) => {
if (err) {
console.error(`Failed to restore data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in restoring data.`);
})
```
# Database Encryption
## When to Use
OpenHarmony provides the database encryption capability to effectively protect the data stored in a database. Database encryption allows data to be stored and used in ciphertext, ensuring data confidentiality and integrity.
The encrypted database can be accessed only using an API, and the database file cannot be opened in other ways. Whether a database is encrypted is set when the database is created, and the setting cannot be changed.
Both KV stores and RDB stores support database encryption.
## Encrypting a KV Store
When a KV store is created, the **encrypt** parameter in **options** specifies whether to encrypt the KV store. The value **true** means to encrypt the KV store, and the value **false** (default) means the opposite.
For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
```js
import distributedKVStore from '@ohos.data.distributedKVStore';
let kvManager;
let context = getContext(this);
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest',
}
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
let kvStore;
try {
const options = {
createIfMissing: true,
// Whether to encrypt the KV store.
encrypt: true,
backup: false,
autoSync: true,
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION,
securityLevel: distributedKVStore.SecurityLevel.S2
};
kvManager.getKVStore('storeId', options, (err, store) => {
if (err) {
console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
kvStore = store;
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
## Encrypting an RDB Store
When an RDB store is created, the **encrypt** parameter in **options** specifies whether to encrypt the RDB store. The value **true** means to encrypt the RDB store, and the value **false** (default) means the opposite.
For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
```js
import relationalStore from '@ohos.data.relationalStore';
let store;
let context = getContext(this);
const STORE_CONFIG = {
name: 'RdbTest.db',
securityLevel: relationalStore.SecurityLevel.S1,
encrypt: true
};
relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => {
store = rdbStore;
if (err) {
console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting RdbStore.`);
})
```
# Data Management Overview
## Function
Data management provides data storage, management, and synchronization capabilities. For example, you can store the Contacts application data in database for secure management and shared access, and synchronize the contacts information with a smart watch.
- Data storage: provides data persistence capabilities, which can be classified into user preferences, key-value (KV) stores, and relational database (RDB) stores by data characteristics.
- Data management: provides efficient data management capabilities, including permission management, data backup and restoration, and dataShare framework.
- Data synchronization: provides data synchronization across devices. For example, distributed data objects support sharing of memory objects across devices, and distributed databases support database access across devices.
The database stores created by an application are saved to the application sandbox. When the application is uninstalled, the database stores are automatically deleted.
## Working Principles
The data management module includes user preferences (**Preferences**), KV data management (**KV-Store**), RDB data management (**RelationalStore**), distributed data object (**DataObject**), and cross-application data management (**DataShare**). The interface layer provides standard JavaScript APIs for application development. The Frameworks&System service layer implements storage and synchronization of component data, and provides dependencies for SQLite and other subsystems.
**Figure 1** Data management architecture
![dataManagement](figures/dataManagement.jpg)
- **Preferences**: implements persistence of lightweight configuration data and supports subscription of data change notifications. Preferences are used to store application configuration information and user preference settings and do not support distributed synchronization.
- **KV-Store**: implements read, write, encryption, and manual backup of data in KV stores and notification subscription. When an application needs to use the distributed capabilities of KV stores, KV-Store sends a synchronization request to DatamgrService to implement data synchronization across devices.
- **RelationalStore**: implements addition, deletion, modification, query, encryption, manually backup of data in RDB stores, and notification subscription. When an application needs to use the distributed capabilities of an RDB store, RelationalStore sends a synchronization request to DatamgrService to implement data synchronization across devices.
- **DataObject**: independently provides distributed capabilities for the data of object structs. For the object data that is still required after the restart of an application (either the cross-device application or local device application), the **DatamgrService** implements temporary storage of the object data.
- **DataShare**: provides the data provider-consumer mode to implement addition, deletion, modification, and query of cross-application data on a device, and notification subscription. **DataShare** is not bound to any database and can interact with RDB and KV stores. You can also encapsulate your own databases for C/C++ applications.<br> In addition to the provider-consumer mode, **DataShare** provides silent access, which allows direct access to the provider's data via the DatamgrService proxy instead of starting the provider. Currently, only the RDB stores support silent access.
- **DatamgrService**: implements synchronization and cross-application sharing for other components, including cross-device synchronization of **RelationalStore** and **KV-Store**, silent access to provider data of **DataShare**, and temporary storage of **DataObject** synchronization object data.
# Persisting KV Store Data
## When to Use
The key-value (KV) database stores data in the form of KV pairs. You can use KV stores to store data organized in a simple model, for example, product names and prices or employee IDs and daily attendance. The simple data structure allows higher compatibility with different database versions and device types.
## Constraints
- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB.
- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB.
- A maximum of 16 distributed KV stores can be opened simultaneously for an application.
- Blocking operations, for example, modifying UI components, are not allowed in the KV store event callbacks.
## Available APIs
The following table lists the APIs used for KV data persistence. 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 [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md).
| API| Description|
| -------- | -------- |
| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.|
| getKVStore&lt;T&gt;(storeId: string, options: Options, callback: AsyncCallback&lt;T&gt;): void | Creates and obtains a KV store of the specified type.|
| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback&lt;void&gt;): void | Adds a KV pair of the specified type to this KV store.|
| get(key: string, callback: AsyncCallback&lt;Uint8Array\|string\|boolean\|number&gt;): void | Obtains the value of the specified key.|
| delete(key: string, callback: AsyncCallback&lt;void&gt;): void | Deletes a KV pair based on the specified key.|
## How to Develop
1. Create a **KvManager** instance to manage database objects.
Example:
Stage model:
```js
// Import the module.
import distributedKVStore from '@ohos.data.distributedKVStore';
// Stage model
import UIAbility from '@ohos.app.ability.UIAbility';
let kvManager;
export default class EntryAbility extends UIAbility {
onCreate() {
let context = this.context;
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
};
try {
// Create a KVManager instance.
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// Create and obtain the database.
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
}
}
```
FA model:
```js
// Import the module.
import distributedKVStore from '@ohos.data.distributedKVStore';
// FA model
import featureAbility from '@ohos.ability.featureAbility';
let kvManager;
let context = featureAbility.getContext(); // Obtain the context.
const kvManagerConfig = {
context: context,
bundleName: 'com.example.datamanagertest'
};
try {
kvManager = distributedKVStore.createKVManager(kvManagerConfig);
console.info('Succeeded in creating KVManager.');
// Create and obtain the database.
} catch (e) {
console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`);
}
```
2. Create and obtain a KV store.
Example:
```js
try {
const options = {
createIfMissing: true, // Whether to create a KV store when the database file does not exist. By default, a KV store is created.
createIfMissing: true, // Whether to encrypt database files. By default, database files are not encrypted.
backup: false, // Whether to back up database files. By default, the database files are backed up.
autoSync: true, // Whether to automatically synchronize database files. The value **true** means to automatically synchronize database files; the value **false** (default) means the opposite.
kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // Type of the KV store to create. By default, a device KV store is created.
securityLevel: distributedKVStore.SecurityLevel.S2 // Security level of the KV store.
};
// storeId uniquely identifies a KV store.
kvManager.getKVStore('storeId', options, (err, kvStore) => {
if (err) {
console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting KVStore.');
// Perform related data operations.
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
3. Use **put()** to add data to the KV store.
Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
> **NOTE**
>
> The **put()** method adds a KV pair if the specified key does not exists and changes the value if the the specified key already exists.
4. Use **get()** to obtain the value of a key.
Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => {
if (err !== undefined) {
console.error(`Failed to get data. Code:${err.code},message:${err.message}`);
return;
}
console.info(`Succeeded in getting data. data:${data}`);
});
});
} catch (e) {
console.error(`Failed to get data. Code:${e.code},message:${e.message}`);
}
```
5. Use **delete()** to delete the data of the specified key.
Example:
```js
const KEY_TEST_STRING_ELEMENT = 'key_test_string';
const VALUE_TEST_STRING_ELEMENT = 'value_test_string';
try {
kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to put data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => {
if (err !== undefined) {
console.error(`Failed to delete data. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in deleting data.');
});
});
} catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
}
```
# Persisting Preferences Data
## When to Use
The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs, and supports persistence of the KV pairs when required, as well as modification and query of the data. You can use **Preferences** when you want a unique storage for global data. **Preferences** caches data in the memory, which allows fast access when the data is required. **Preferences** is recommended for storing small amount of data, such as personalized settings (font size and whether to enable the night mode) of applications.
## Working Principles
User applications call **Preference** through the JS interface to read and write data files. You can load the data of a **Preferences** persistence file to a **Preferences** instance. Each file uniquely corresponds to an instance. The system stores the instance in memory through a static container until the instance is removed from the memory or the file is deleted. The following figure illustrates how **Preference** works.
The preference persistent file of an application is stored in the application sandbox. You can use **context** to obtain the file path. For details, see [Obtaining the Application Development Path](../application-models/application-context-stage.md#obtaining-the-application-development-path).
**Figure 1** Preferences working mechanism
![preferences](figures/preferences.jpg)
## Constraints
- The key in a KV pair must be a string and cannot be empty or exceed 80 bytes.
- If the value is of the string type, it can be empty or a string not longer than 8192 bytes.
- The memory usage increases with the amount of **Preferences** data. The maximum number of data records recommended is 10,000. Otherwise, high memory overheads will be caused.
## Available APIs
The following table lists the APIs used for preferences data persistence. 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 [User Preferences](../reference/apis/js-apis-data-preferences.md).
| API| Description|
| -------- | -------- |
| getPreferences(context: Context, name: string, callback: AsyncCallback&lt;Preferences&gt;): void | Obtain a **Preferences** instance.|
| put(key: string, value: ValueType, callback: AsyncCallback&lt;void&gt;): void | Writes data to the Preferences instance. You can use **flush()** to persist the **Preferences** instance data.|
| has(key: string, callback: AsyncCallback&lt;boolean&gt;): void | Checks whether the **Preferences** instance contains a KV pair with the given key. The key cannot be empty.|
| get(key: string, defValue: ValueType, callback: AsyncCallback&lt;ValueType&gt;): void | Obtains the value of the specified key. If the value is null or not of the default value type, **defValue** is returned.|
| delete(key: string, callback: AsyncCallback&lt;void&gt;): void | Deletes the KV pair with the given key from the **Preferences** instance.|
| flush(callback: AsyncCallback&lt;void&gt;): void | Flushes the data of this **Preferences** instance to a file for data persistence.|
| on(type: 'change', callback: Callback&lt;{ key : string }&gt;): void | Subscribes to data changes of the specified key. When the value of the specified key is changed and saved by **flush()**, a callback will be invoked to return the new data.|
| off(type: 'change', callback?: Callback&lt;{ key : string }&gt;): void | Unsubscribes from data changes.|
| deletePreferences(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes a **Preferences** instance from memory. If the **Preferences** instance has a persistent file, this API also deletes the persistent file.|
## How to Develop
1. Import the **@ohos.data.preferences** module.
```js
import dataPreferences from '@ohos.data.preferences';
```
2. Obtain a **Preferences** instance. Read data from a file and load the data to a **Preferences** instance for data operations.
Stage model:
```js
import UIAbility from '@ohos.app.ability.UIAbility';
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
try {
dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => {
if (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting preferences.');
// Perform related data operations.
})
} catch (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
}
}
}
```
FA model:
```js
import featureAbility from '@ohos.ability.featureAbility';
// Obtain the context.
let context = featureAbility.getContext();
try {
dataPreferences.getPreferences(context, 'mystore', (err, preferences) => {
if (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
return;
}
console.info('Succeeded in getting preferences.');
// Perform related data operations.
})
} catch (err) {
console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`);
}
```
3. Write data.
Use **put()** to write data to the **Preferences** instance. After data is written, you can use **flush()** to persist the **Preferences** instance data to a file if necessary.
> **NOTE**
>
> If the specified key already exists, the **put()** method changes the value. To prevent a value from being changed by mistake, you can use **has()** to check whether the KV pair exists.
Example:
```js
try {
preferences.has('startup', function (err, val) {
if (err) {
console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
if (val) {
console.info("The key 'startup' is contained.");
} else {
console.info("The key 'startup' does not contain.");
// Add a KV pair.
try {
preferences.put('startup', 'auto', (err) => {
if (err) {
console.error(`Failed to put data. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in putting data.');
})
} catch (err) {
console.error(`Failed to put data. Code: ${err.code},message:${err.message}`);
}
}
})
} catch (err) {
console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`);
}
```
4. Read data.
Use **get()** to obtain the value of the specified key. If the value is null or is not of the default value type, the default data is returned.
Example:
```js
try {
preferences.get('startup', 'default', (err, val) => {
if (err) {
console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
console.info(`Succeeded in getting value of 'startup'. val: ${val}.`);
})
} catch (err) {
console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`);
}
```
5. Delete data.
Use delete() to delete a KV pair.<br>Example:
```js
try {
preferences.delete('startup', (err) => {
if (err) {
console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
return;
}
console.info("Succeeded in deleting the key 'startup'.");
})
} catch (err) {
console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`);
}
```
6. Persist data.
You can use **flush()** to persist the data held in a **Preferences** instance to a file. Example:
```js
try {
preferences.flush((err) => {
if (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in flushing.');
})
} catch (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
}
```
7. Subscribe to data changes.
Specify an observer as the callback to return the data changes for an application. When the value of the subscribed key is changed and saved by **flush()**, the observer callback will be invoked to return the new data. Example:
```js
let observer = function (key) {
console.info('The key' + key + 'changed.');
}
preferences.on('change', observer);
// The data is changed from 'auto' to 'manual'.
preferences.put('startup', 'manual', (err) => {
if (err) {
console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`);
return;
}
console.info("Succeeded in putting the value of 'startup'.");
preferences.flush((err) => {
if (err) {
console.error(`Failed to flush. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in flushing.');
})
})
```
8. Delete a **Preferences** instance from the memory.
Use **deletePreferences()** to delete a **Preferences** instance from the memory. If the **Preferences** instance has a persistent file, the persistent file and its backup and corrupted files will also be deleted.
> **NOTE**
>
> - The deleted **Preferences** instance cannot be used for data operations. Otherwise, data inconsistency will be caused.
>
> - The deleted data and files cannot be restored.
Example:
```js
try {
dataPreferences.deletePreferences(this.context, 'mystore', (err, val) => {
if (err) {
console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
return;
}
console.info('Succeeded in deleting preferences.');
})
} catch (err) {
console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
}
```
\ No newline at end of file
# Data Reliability and Security Overview
## Introduction
During system running, a database fault may occur due to storage damage, insufficient storage space, file system permission, or system power-off. The database fault may cause data loss. For example, the database corruption of Contacts causes the loss of Contacts data. The data management subsystem provides the following solutions and capabilities to ensure data reliability and security:
- Data backup and restoration: Critical data (such as the bank information) can be backed up and restored from the backup to prevent data loss.
- Database encryption: The database that stores sensitive information, such as authentication credentials and financial data, can be encrypted to improve data security.
- Access control by device and data level: The access to data across devices is controlled based on the device security level and data security labels.
In addition, the backup database is stored in the application sandbox. When the storage space is insufficient, you can delete the local database backup to release space.
## Basic Concepts
Before developing functions related to data reliability and security, understand the following concepts.
### Database Backup and Restoration
- Database backup: OpenHarmony provides full backup of database files.
When backing up a database, you only need to invoke the backup API of the database, without closing the database.
- Database restoration: You can restore a database from a database backup file.
### Database Encryption
The entire database file can be encrypted to enhance the database security.
### Data Rating
In distributed scenarios, the access to data is controlled based on the device security level and data security labels.
A higher data security label and device security level indicate stricter encryption and access control measures and higher data security.
## Working Principles
### Database Backup and Restoration Mechanism
The data of a database is backed up to the specified file. Subsequent operations on the database do not affect the backup file. The database is overwritten by the specified backup file only when a restoration is performed.
- KV store backup directory: **/data/service/el1(el2)/public/database/...{appId}/kvdb/backup/...{storeId}**
- RDB store backup directory: **/data/app/el1(el2)/100/database/...{bundlename}/rdb**
### Database Encryption Mechanism
When encrypting a database, you do not need to pass in the key for encryption. The only thing you need to do is set the database encryption status. The system automatically calls the [HUKS APIs](../reference/apis/js-apis-huks.md) to generate a key and encrypt the database.
## Constraints
- The database encryption key is automatically changed once a year.
- A maximum of five backup files can be retained for a KV store.
- Automatic backup of a KV store must be performed when the device is charging and the screen is off.
此差异已折叠。
此差异已折叠。
# Distributed Data Object Overview
The distributed data object management framework provides object-oriented in-memory data management. It provides basic data management capabilities, such as creating, querying, deleting, and modifying distributed data objects, and observing data and status changes of the distributed data objects. This management framework also provides distributed capabilities to implement data object collaboration for the same application between multiple devices that form a Super Device.
## Basic Concepts
- **Distributed in-memory database**
The distributed in-memory database caches data in the memory so that applications can quickly access data. This database, however, does not store data persistently. If the database is closed, the data is not retained.
- **Distributed data object**
A distributed data object is an encapsulation of the JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading data from and writing data to a distributed data object are mapped to the **get** and **put** operations in the corresponding database, respectively.
The distributed data object can be in the following states in its lifecycle:
- **Uninitialized**: The distributed data object is not instantiated or has been destroyed.
- **Local**: The data table is created, but the data cannot be synchronized.
- **Distributed**: The data table is created, and there are at least two online devices with the same session ID. In this case, data can be synchronized across devices. If a device is offline or the session ID is empty, the distributed data object changes to the local state.
## Working Principles
The distributed data objects are encapsulated into JS objects in distributed in-memory databases. This allows the distributed data objects to be operated in the same way as local variables. The system automatically implements cross-device data synchronization.
**Figure 1** Working mechanism
![how-distributedobject-works](figures/how-distributedobject-works.png)
## Constraints
- Data synchronization can be implemented across devices only for the applications with the same **bundleName**.
- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects.
- The maximum size of a distributed data object is 500 KB.
- For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified.
- Only JS APIs are supported.
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册