提交 c9f21f01 编写于 作者: 邹林肯 提交者: zoulinken

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

Signed-off-by: N邹林肯 <735001321@qq.com>

要显示的变更太多。

To preserve performance only 1000 of 1000+ files are displayed.
此差异已折叠。
...@@ -146,7 +146,7 @@ The following table describes the subsystems of OpenHarmony. For details about t ...@@ -146,7 +146,7 @@ The following table describes the subsystems of OpenHarmony. For details about t
## Supported Development Boards ## Supported Development Boards
Currently, the OpenHarmony community supports 22 types of development boards, which are listed in [Development Boards Supported](device-dev/dev-board-on-the-master.md). The following table describes three of them, which are the first three integrated into the OpenHarmony master. You can visit http://ci.openharmony.cn/dailys/dailybuilds to obtain daily builds. Currently, the OpenHarmony community supports 22 types of development boards, which are listed in [Development Boards Supported](device-dev/dev-board-on-the-master.md). The following table describes three of them, which are the first three integrated into the OpenHarmony master. You can visit http://ci.openharmony.cn/workbench/cicd/dailybuild/dailylist to obtain daily builds.
| System Type| Board Model| Chip Model| <div style="width:200pt">Function Description and Use Case</div> | Application Scenario| Code Repository | | System Type| Board Model| Chip Model| <div style="width:200pt">Function Description and Use Case</div> | Application Scenario| Code Repository |
| -------- | -------- | -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- | -------- | -------- |
......
...@@ -62,7 +62,7 @@ sequenceable a.b..C.D ...@@ -62,7 +62,7 @@ sequenceable a.b..C.D
The preceding statement is parsed into the following code in the C++ header file: The preceding statement is parsed into the following code in the C++ header file:
```cpp ```cpp
#include "a/b/d.h" #include "a/b/d.h"
using C::D; using C::D;
``` ```
...@@ -347,11 +347,10 @@ export default { ...@@ -347,11 +347,10 @@ export default {
#### Calling Methods from the Client for IPC #### Calling Methods from the Client for IPC
When the client calls **connectAbility()** to connect to a Service ability, the **onConnect** callback in **onAbilityConnectDone** of the client receives the **IRemoteObject** instance returned by the **onConnect()** method of the Service ability. The client and Service ability are in different applications. Therefore, the directory of the client application must contain a copy of the .idl file (the SDK automatically generates the proxy class). The **onConnect** callback then uses the **IRemoteObject** instance to create the **testProxy** instance of the **IdlTestServiceProxy** class and calls the related IPC method. The sample code is as follows: When the client calls **connectServiceExtensionAbility()** to connect to a Service ability, the **onConnect** callback in **onAbilityConnectDone** of the client receives the **IRemoteObject** instance returned by the **onConnect()** method of the Service ability. The client and Service ability are in different applications. Therefore, the directory of the client application must contain a copy of the .idl file (the SDK automatically generates the proxy class). The **onConnect** callback then uses the **IRemoteObject** instance to create the **testProxy** instance of the **IdlTestServiceProxy** class and calls the related IPC method. The sample code is as follows:
```ts ```ts
import IdlTestServiceProxy from './idl_test_service_proxy' import IdlTestServiceProxy from './idl_test_service_proxy'
import featureAbility from '@ohos.ability.featureAbility';
function callbackTestIntTransaction(result: number, ret: number): void { function callbackTestIntTransaction(result: number, ret: number): void {
if (result == 0 && ret == 124) { if (result == 0 && ret == 124) {
...@@ -396,13 +395,13 @@ var onAbilityConnectDone = { ...@@ -396,13 +395,13 @@ var onAbilityConnectDone = {
} }
}; };
function connectAbility: void { function connectAbility(): void {
let want = { let want = {
bundleName: 'com.example.myapplicationidl', bundleName: 'com.example.myapplicationidl',
abilityName: 'com.example.myapplicationidl.ServiceAbility' abilityName: 'com.example.myapplicationidl.ServiceAbility'
}; };
let connectionId = -1; let connectionId = -1;
connectionId = featureAbility.connectAbility(want, onAbilityConnectDone); connectionId = this.context.connectServiceExtensionAbility(want, onAbilityConnectDone);
} }
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
- [\@BuilderParam Decorator: \@Builder Function Reference](quick-start/arkts-builderparam.md) - [\@BuilderParam Decorator: \@Builder Function Reference](quick-start/arkts-builderparam.md)
- [\@Styles Decorator: Definition of Resusable Styles](quick-start/arkts-style.md) - [\@Styles Decorator: Definition of Resusable Styles](quick-start/arkts-style.md)
- [\@Extend Decorator: Extension of Built-in Components](quick-start/arkts-extend.md) - [\@Extend Decorator: Extension of Built-in Components](quick-start/arkts-extend.md)
- [\@AnimatableExtend Decorator: Definition of Animatable Attributes](quick-start/arkts-animatable-extend.md)
- [stateStyles: Polymorphic Style](quick-start/arkts-statestyles.md) - [stateStyles: Polymorphic Style](quick-start/arkts-statestyles.md)
- State Management - State Management
- [State Management Overview](quick-start/arkts-state-management-overview.md) - [State Management Overview](quick-start/arkts-state-management-overview.md)
...@@ -77,6 +78,7 @@ ...@@ -77,6 +78,7 @@
- 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)
- [ArkTS Common Library](arkts-utils/Readme-EN.md)
- [Web](web/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)
......
# AI # AI
- [Using MindSpore Lite for Model Inference (JS)](mindspore-lite-js-guidelines.md) - [AI Development](ai-overview.md)
- [Using MindSpore Lite JavaScript APIs to Develop AI Applications](mindspore-guidelines-based-js.md)
- [Using MindSpore Lite Native APIs to Develop AI Applications](mindspore-guidelines-based-native.md)
# AI Development
## Overview
OpenHarmony provides native distributed AI capabilities. The AI subsystem consists of the following components:
- MindSpore Lite: an AI inference framework that provides unified APIs for AI inference.
- Neural Network Runtime (NNRt): an intermediate bridge that connects the inference framework and AI hardware.
## MindSpore Lite
MindSpore Lite is a built-in AI inference framework of OpenHarmony. It provides AI model inference capabilities for different hardware devices and end-to-end AI model inference solutions for developers to empower intelligent applications in all scenarios. Currently, MindSpore Lite has been widely used in applications such as image classification, target recognition, facial recognition, and character recognition.
**Figure 1** Development process for MindSpore Lite model inference
![MindSpore workflow](figures/mindspore_workflow.png)
The MindSpore Lite development process consists of two phases:
- Model conversion
MindSpore Lite uses models in `.ms` format for inference. You can use the model conversion tool provided by MindSpore Lite to convert third-party framework models, such as TensorFlow, TensorFlow Lite, Caffe, and ONNX, into `.ms` models. For details, see [Converting Models for Inference](https://www.mindspore.cn/lite/docs/en/r1.8/use/converter_tool.html).
- Model inference
You can call the MindSpore Lite runtime APIs to implement model inference. The procedure is as follows:
1. Create an inference context by setting the inference hardware and number of threads.
2. Load the **.ms** model file.
3. Set the model input data.
4. Perform model inference, and read the output.
MindSpore Lite is built in the OpenHarmony standard system as a system component. You can develop AI applications based on MindSpore Lite in the following ways:
- Method 1: [Using MindSpore Lite JavaScript APIs to develop AI applications](./mindspore-guidelines-based-js.md). You directly call MindSpore Lite JavaScript APIs in the UI code to load the AI model and perform model inference. An advantage of this method is the quick verification of the inference effect.
- Method 2: [Using MindSpore Lite native APIs to develop AI applications](./mindspore-guidelines-based-native.md). You encapsulate the algorithm models and the code for calling MindSpore Lite native APIs into a dynamic library, and then use N-API to encapsulate the dynamic library into JavaScript APIs for the UI to call.
## Neural Network Runtime
Neural Network Runtime (NNRt) functions as a bridge to connect the upper-layer AI inference framework and bottom-layer acceleration chip, implementing cross-chip inference computing of AI models.
MindSpore Lite supports configuration of the NNRt backend, and therefore you can directly configure MindSpore Lite to use the NNRt hardware. The focus of this topic is about how to develop AI applications using MindSpore Lite. For details about how to use NNRt, see [Connecting the Neural Network Runtime to an AI Inference Framework](../napi/neural-network-runtime-guidelines.md).
# Using MindSpore Lite for Model Inference (JS) # Using MindSpore Lite JavaScript APIs to Develop AI Applications
## Scenarios ## Scenarios
MindSpore Lite is an AI engine that implements AI model inference for different hardware devices. It has been used in a wide range of fields, such as image classification, target recognition, facial recognition, and character recognition. You can use the JavaScript APIs provided by MindSpore Lite to directly integrate MindSpore Lite capabilities into the UI code. This way, you can quickly deploy AI algorithms for AI model inference.
This document describes the general development process for implementing MindSpore Lite model inference. For details about how to use native APIs to implement model inference, see [Using MindSpore Lite for Model Inference](../napi/mindspore-lite-guidelines.md).
## Basic Concepts ## Basic Concepts
...@@ -27,16 +25,14 @@ APIs involved in MindSpore Lite model inference are categorized into context API ...@@ -27,16 +25,14 @@ APIs involved in MindSpore Lite model inference are categorized into context API
## How to Develop ## How to Develop
The development process consists of the following main steps: Assume that you have prepared a model in the **.ms** format. The key steps in model inference are model reading, model building, model inference, and memory release. The development procedure is described as follows:
1. Create a context, and set parameters such as the number of runtime threads and device type.
2. Load the model. In this example, the model is read from the file.
3. Load data. Before executing a model, you need to obtain the model input and then fill data in the input tensor.
4. Perform model inference by calling **predict**, and read the output.
1. Prepare the required model. You can download the required model directly or obtain the model by using the model conversion tool. The required data is read from the `bin` file.
- If the downloaded model is in the `.ms` format, you can use it directly for inference. This document uses `mnet.caffemodel.ms` as an example.
- If the downloaded model uses a third-party framework, such as TensorFlow, TensorFlow Lite, Caffe, or ONNX, you can use the [model conversion tool](https://www.mindspore.cn/lite/docs/en/r2.0/use/downloads.html#1-8-1) to convert it to the `.ms` format.
2. Create a context, and set parameters such as the number of runtime threads and device type.
3. Load the model. In this example, the model is read from the file.
4. Load data. Before executing a model, you need to obtain the model input and then fill data in the input tensor.
5. Perform inference and print the output. Call the **predict** API to perform model inference.
```js ```js
@State inputName: string = 'mnet_caffemodel_nhwc.bin'; @State inputName: string = 'mnet_caffemodel_nhwc.bin';
@State T_model_predict: string = 'Test_MSLiteModel_predict' @State T_model_predict: string = 'Test_MSLiteModel_predict'
...@@ -49,7 +45,6 @@ build() { ...@@ -49,7 +45,6 @@ build() {
.fontSize(30) .fontSize(30)
.fontWeight(FontWeight.Bold) .fontWeight(FontWeight.Bold)
.onClick(async () => { .onClick(async () => {
// 1. Prepare for a model.
let syscontext = globalThis.context; let syscontext = globalThis.context;
syscontext.resourceManager.getRawFileContent(this.inputName).then((buffer) => { syscontext.resourceManager.getRawFileContent(this.inputName).then((buffer) => {
this.inputBuffer = buffer; this.inputBuffer = buffer;
...@@ -57,20 +52,24 @@ build() { ...@@ -57,20 +52,24 @@ build() {
}).catch(error => { }).catch(error => {
console.error('Failed to get buffer, error code: ${error.code},message:${error.message}.'); console.error('Failed to get buffer, error code: ${error.code},message:${error.message}.');
}) })
// 2. Create a context.
// 1. Create a context.
let context: mindSporeLite.Context = {}; let context: mindSporeLite.Context = {};
context.target = ['cpu']; context.target = ['cpu'];
context.cpu = {} context.cpu = {}
context.cpu.threadNum = 1; context.cpu.threadNum = 1;
context.cpu.threadAffinityMode = 0; context.cpu.threadAffinityMode = 0;
context.cpu.precisionMode = 'enforce_fp32'; context.cpu.precisionMode = 'enforce_fp32';
// 3. Load the model.
// 2. Load the model.
let modelFile = '/data/storage/el2/base/haps/entry/files/mnet.caffemodel.ms'; let modelFile = '/data/storage/el2/base/haps/entry/files/mnet.caffemodel.ms';
let msLiteModel = await mindSporeLite.loadModelFromFile(modelFile, context); let msLiteModel = await mindSporeLite.loadModelFromFile(modelFile, context);
// 4. Load data.
// 3. Set the input data.
const modelInputs = msLiteModel.getInputs(); const modelInputs = msLiteModel.getInputs();
modelInputs[0].setData(this.inputBuffer.buffer); modelInputs[0].setData(this.inputBuffer.buffer);
// 5. Perform inference and print the output.
// 4. Perform inference and print the output.
console.log('=========MSLITE predict start=====') console.log('=========MSLITE predict start=====')
msLiteModel.predict(modelInputs).then((modelOutputs) => { msLiteModel.predict(modelInputs).then((modelOutputs) => {
let output0 = new Float32Array(modelOutputs[0].getData()); let output0 = new Float32Array(modelOutputs[0].getData());
...@@ -89,21 +88,21 @@ build() { ...@@ -89,21 +88,21 @@ build() {
## Debugging and Verification ## Debugging and Verification
1. Connect to the rk3568 development board on DevEco Studio, click **Run entry**, and compile your own HAP. The following information is displayed: 1. On DevEco Studio, connect to the device, click **Run entry**, and compile your own HAP. The following information is displayed:
```shell ```shell
Launching com.example.myapptfjs Launching com.example.myapptfjs
$ hdc uninstall com.example.myapptfjs $ hdc uninstall com.example.myapptfjs
$ hdc install -r "D:\TVOS\JSAPI\MyAppTfjs\entry\build\default\outputs\default\entry-default-signed.hap" $ hdc install -r "path/to/xxx.hap"
$ hdc shell aa start -a EntryAbility -b com.example.myapptfjs $ hdc shell aa start -a EntryAbility -b com.example.myapptfjs
``` ```
2. Use the hdc tool to connect to the rk3568 development board and push `mnet.caffemodel.ms` to the sandbox directory on the device. `mnet\_caffemodel\_nhwc.bin` is stored in the `rawfile` directory of the local project. 2. Use hdc to connect to the device, and push **mnet.caffemodel.ms** to the sandbox directory on the device. **mnet\_caffemodel\_nhwc.bin** is stored in the **rawfile** directory of the local project.
```shell ```shell
hdc -t 7001005458323933328a00bcdf423800 file send .\mnet.caffemodel.ms /data/app/el2/100/base/com.example.myapptfjs/haps/entry/files/ hdc -t your_device_id file send .\mnet.caffemodel.ms /data/app/el2/100/base/com.example.myapptfjs/haps/entry/files/
``` ```
3. Click **Test\_MSLiteModel\_predict** on the screen of the rk3568 development board to run the test case. The following information is displayed in the HiLog printing result: 3. Click **Test\_MSLiteModel\_predict** on the device screen to run the test case. The following information is displayed in the HiLog printing result:
```shell ```shell
08-27 23:25:50.278 31782-31782/? I C03d00/JSAPP: =========MSLITE predict start===== 08-27 23:25:50.278 31782-31782/? I C03d00/JSAPP: =========MSLITE predict start=====
......
# Using MindSpore Lite Native APIs to Develop AI Applications
## Scenarios
You can use the native APIs provided by MindSpore Lite to deploy AI algorithms and provides APIs for the UI layer to invoke the algorithms for model inference. A typical scenario is the AI SDK development.
## Basic concepts
- [N-API](../reference/native-lib/third_party_napi/napi.md): a set of native APIs used to build JavaScript components. N-APIs can be used to encapsulate libraries developed using C/C++ into JavaScript modules.
## Preparing the Environment
- Install DevEco Studio 3.1.0.500 or later, and update the SDK to API version 10 or later.
## How to Develop
1. Create a native C++ project.
Open DevEco Studio, choose **File** > **New** > **Create Project** to create a native C++ template project. By default, the **entry/src/main/** directory of the created project contains the **cpp/** directory. You can store C/C++ code in this directory and provide JavaScript APIs for the UI layer to call the code.
2. Compile the C++ inference code.
Assume that you have prepared a model in the **.ms** format.
Before using the Native APIs provided by MindSpore Lite for development, you need to reference the corresponding header files.
```c
#include <mindspore/model.h>
#include <mindspore/context.h>
#include <mindspore/status.h>
#include <mindspore/tensor.h>
```
(1). Read model files.
```C++
void *ReadModelFile(NativeResourceManager *nativeResourceManager, const std::string &modelName, size_t *modelSize) {
auto rawFile = OH_ResourceManager_OpenRawFile(nativeResourceManager, modelName.c_str());
if (rawFile == nullptr) {
LOGE("Open model file failed");
return nullptr;
}
long fileSize = OH_ResourceManager_GetRawFileSize(rawFile);
void *modelBuffer = malloc(fileSize);
if (modelBuffer == nullptr) {
LOGE("Get model file size failed");
}
int ret = OH_ResourceManager_ReadRawFile(rawFile, modelBuffer, fileSize);
if (ret == 0) {
LOGI("Read model file failed");
OH_ResourceManager_CloseRawFile(rawFile);
return nullptr;
}
OH_ResourceManager_CloseRawFile(rawFile);
*modelSize = fileSize;
return modelBuffer;
}
```
(2). Create a context, set parameters such as the number of threads and device type, and load the model.
```c++
OH_AI_ModelHandle CreateMSLiteModel(void *modelBuffer, size_t modelSize) {
// Create a context.
auto context = OH_AI_ContextCreate();
if (context == nullptr) {
DestroyModelBuffer(&modelBuffer);
LOGE("Create MSLite context failed.\n");
return nullptr;
}
auto cpu_device_info = OH_AI_DeviceInfoCreate(OH_AI_DEVICETYPE_CPU);
OH_AI_ContextAddDeviceInfo(context, cpu_device_info);
// Load the .ms model file.
auto model = OH_AI_ModelCreate();
if (model == nullptr) {
DestroyModelBuffer(&modelBuffer);
LOGE("Allocate MSLite Model failed.\n");
return nullptr;
}
auto build_ret = OH_AI_ModelBuild(model, modelBuffer, modelSize, OH_AI_MODELTYPE_MINDIR, context);
DestroyModelBuffer(&modelBuffer);
if (build_ret != OH_AI_STATUS_SUCCESS) {
OH_AI_ModelDestroy(&model);
LOGE("Build MSLite model failed.\n");
return nullptr;
}
LOGI("Build MSLite model success.\n");
return model;
}
```
(3). Set the model input data, perform model inference, and obtain the output data.
```js
void RunMSLiteModel(OH_AI_ModelHandle model) {
// Set the model input data.
auto inputs = OH_AI_ModelGetInputs(model);
FillInputTensors(inputs);
auto outputs = OH_AI_ModelGetOutputs(model);
// Perform inference and print the output.
auto predict_ret = OH_AI_ModelPredict(model, inputs, &outputs, nullptr, nullptr);
if (predict_ret != OH_AI_STATUS_SUCCESS) {
OH_AI_ModelDestroy(&model);
LOGE("Predict MSLite model error.\n");
return;
}
LOGI("Run MSLite model success.\n");
LOGI("Get model outputs:\n");
for (size_t i = 0; i < outputs.handle_num; i++) {
auto tensor = outputs.handle_list[i];
LOGI("- Tensor %{public}d name is: %{public}s.\n", static_cast<int>(i), OH_AI_TensorGetName(tensor));
LOGI("- Tensor %{public}d size is: %{public}d.\n", static_cast<int>(i), (int)OH_AI_TensorGetDataSize(tensor));
auto out_data = reinterpret_cast<const float *>(OH_AI_TensorGetData(tensor));
std::cout << "Output data is:";
for (int i = 0; (i < OH_AI_TensorGetElementNum(tensor)) && (i <= kNumPrintOfOutData); i++) {
std::cout << out_data[i] << " ";
}
std::cout << std::endl;
}
OH_AI_ModelDestroy(&model);
}
```
(4). Implement a complete model inference process.
```C++
static napi_value RunDemo(napi_env env, napi_callback_info info)
{
LOGI("Enter runDemo()");
GET_PARAMS(env, info, 2);
napi_value error_ret;
napi_create_int32(env, -1, &error_ret);
const std::string modelName = "ml_headpose.ms";
size_t modelSize;
auto resourcesManager = OH_ResourceManager_InitNativeResourceManager(env, argv[1]);
auto modelBuffer = ReadModelFile(resourcesManager, modelName, &modelSize);
if (modelBuffer == nullptr) {
LOGE("Read model failed");
return error_ret;
}
LOGI("Read model file success");
auto model = CreateMSLiteModel(modelBuffer, modelSize);
if (model == nullptr) {
OH_AI_ModelDestroy(&model);
LOGE("MSLiteFwk Build model failed.\n");
return error_ret;
}
RunMSLiteModel(model);
napi_value success_ret;
napi_create_int32(env, 0, &success_ret);
LOGI("Exit runDemo()");
return success_ret;
}
```
(5). Write the **CMake** script to link the MindSpore Lite dynamic library `libmindspore_lite_ndk.so`.
```cmake
cmake_minimum_required(VERSION 3.4.1)
project(OHOSMSLiteNapi)
set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${NATIVERENDER_ROOT_PATH}
${NATIVERENDER_ROOT_PATH}/include)
add_library(mslite_napi SHARED mslite_napi.cpp)
target_link_libraries(mslite_napi PUBLIC mindspore_lite_ndk) # MindSpore Lite dynamic library to link
target_link_libraries(mslite_napi PUBLIC hilog_ndk.z)
target_link_libraries(mslite_napi PUBLIC rawfile.z)
target_link_libraries(mslite_napi PUBLIC ace_napi.z)
```
3. Use N-APIs to encapsulate C++ dynamic libraries into JavaScript modules.
Create the **libmslite_api/** subdirectory in **entry/src/main/cpp/types/**, and create the **index.d.ts** file in the subdirectory. The file content is as follows:
```js
export const runDemo: (a:String, b:Object) => number;
```
Use the preceding code to define the JavaScript API `runDemo()`.
In addition, add the **oh-package.json5** file to associate the API with the **.so** file to form a complete JavaScript module.
```json
{
"name": "libmslite_napi.so",
"types": "./index.d.ts"
}
```
4. Invoke the encapsulated MindSpore module in the UI code.
In **entry/src/ets/MainAbility/pages/index.ets**, define the **onClick()** event and call the encapsulated **runDemo()** API in the event callback.
```js
import msliteNapi from'libmslite_napi.so' // Import the msliteNapi module.
// Certain code omitted
// Trigger the event when the text on the UI is tapped.
.onClick(() => {
resManager.getResourceManager().then(mgr => {
hilog.info(0x0000, TAG, '*** Start MSLite Demo ***');
let ret = 0;
ret = msliteNapi.runDemo("", mgr); // Call runDemo() to perform AI model inference.
if (ret == -1) {
hilog.info(0x0000, TAG, 'Error when running MSLite Demo!');
}
hilog.info(0x0000, TAG, '*** Finished MSLite Demo ***');
})
})
```
## Debugging and Verification
On DevEco Studio, connect to the device and click **Run entry**. The following log is generated for the application process:
```text
08-08 16:55:33.766 1513-1529/com.mslite.native_demo I A00000/MSLiteNativeDemo: *** Start MSLite Demo ***
08-08 16:55:33.766 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Enter runDemo()
08-08 16:55:33.772 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Read model file success
08-08 16:55:33.799 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Build MSLite model success.
08-08 16:55:33.818 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Run MSLite model success.
08-08 16:55:33.818 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Get model outputs:
08-08 16:55:33.818 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: - Tensor 0 name is: output_node_0.
08-08 16:55:33.818 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: - Tensor 0 size is: 12.
08-08 16:55:33.826 1513-1529/com.mslite.native_demo I A00000/[MSLiteNapi]: Exit runDemo()
08-08 16:55:33.827 1513-1529/com.mslite.native_demo I A00000/MSLiteNativeDemo: *** Finished MSLite Demo ***
```
...@@ -25,7 +25,8 @@ All applications should be developed on top of these frameworks. ...@@ -25,7 +25,8 @@ 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) - [ArkTS Common Library](arkts-utils/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)
......
...@@ -25,13 +25,14 @@ All applications should be developed on top of these frameworks. ...@@ -25,13 +25,14 @@ 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:
- [ArkTS Common Library](arkts-utils/arkts-commonlibrary-overview.md)
- [Web](web/web-component-overview.md) - [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/media-application-overview.md) - [Media](media/media-application-overview.md)
- [Security](security/userauth-overview.md) - [Security](security/userauth-overview.md)
- [AI](ai/mindspore-lite-js-guidelines.md) - [AI](ai/ai-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/data-mgmt-overview.md) - [Data Management](database/data-mgmt-overview.md)
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
- [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md) - [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md)
- [InputMethodExtensionAbility](inputmethodextentionability.md) - [InputMethodExtensionAbility](inputmethodextentionability.md)
- [WindowExtensionAbility (for System Applications Only)](windowextensionability.md) - [WindowExtensionAbility (for System Applications Only)](windowextensionability.md)
- [DriverExtensionAbility](driverextensionability.md)
- Service Widget Development in Stage Model - Service Widget Development in Stage Model
- [Service Widget Overview](service-widget-overview.md) - [Service Widget Overview](service-widget-overview.md)
- Developing an ArkTS Widget - Developing an ArkTS Widget
...@@ -43,6 +44,7 @@ ...@@ -43,6 +44,7 @@
- Widget Data Interaction - Widget Data Interaction
- [Widget Data Interaction Overview](arkts-ui-widget-interaction-overview.md) - [Widget Data Interaction Overview](arkts-ui-widget-interaction-overview.md)
- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md) - [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md)
- [Updating Widget Content Through a Proxy](arkts-ui-widget-update-by-proxy.md)
- [Updating Local and Online Images in the Widget](arkts-ui-widget-image-update.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 State](arkts-ui-widget-update-by-status.md)
- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md) - [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md)
...@@ -59,10 +61,9 @@ ...@@ -59,10 +61,9 @@
- [Component Startup Rules (Stage Model)](component-startup-rules.md) - [Component Startup Rules (Stage Model)](component-startup-rules.md)
- Inter-Device Application Component Interaction (Continuation) - Inter-Device Application Component Interaction (Continuation)
- [Continuation Overview](inter-device-interaction-hop-overview.md) - [Continuation Overview](inter-device-interaction-hop-overview.md)
- [Cross-Device Migration (for System Applications Only)](hop-cross-device-migration.md) - [Cross-Device Migration](hop-cross-device-migration.md)
- [Multi-device Collaboration (for System Applications Only)](hop-multi-device-collaboration.md) - [Multi-device Collaboration (for System Applications Only)](hop-multi-device-collaboration.md)
- [Subscribing to System Environment Variable Changes](subscribe-system-environment-variable-changes.md) - [Subscribing to System Environment Variable Changes](subscribe-system-environment-variable-changes.md)
- [Setting Atomic Services to Support Sharing](atomic-services-support-sharing.md)
- Process Model - Process Model
- [Process Model Overview](process-model-stage.md) - [Process Model Overview](process-model-stage.md)
- Common Events - Common Events
......
...@@ -118,7 +118,12 @@ After developing the custom logic for an accessibility extension service, you mu ...@@ -118,7 +118,12 @@ After developing the custom logic for an accessibility extension service, you mu
``` ```
## Enabling or Disabling a Custom Accessibility Extension Service ## Enabling or Disabling a Custom Accessibility Extension Service
To enable or disable an accessibility extension service, run the following command: You can enable or disable a custom accessibility extension service through the command line interface or the device settings.
**Method 1**: through the command line interface
Run the **hdc shell** command, then the following system command:
- To enable the service: **accessibility enable -a AccessibilityExtAbility -b com.example.demo -c rg** - To enable the service: **accessibility enable -a AccessibilityExtAbility -b com.example.demo -c rg**
- To disable the service: **accessibility disable -a AccessibilityExtAbility -b com.example.demo** - To disable the service: **accessibility disable -a AccessibilityExtAbility -b com.example.demo**
...@@ -126,3 +131,9 @@ In the preceding commands, **AccessibilityExtAbility** indicates the name of the ...@@ -126,3 +131,9 @@ In the preceding commands, **AccessibilityExtAbility** indicates the name of the
If the service is enabled or disabled successfully, the message "enable ability successfully" or "disable ability successfully" is displayed. If the service is enabled or disabled successfully, the message "enable ability successfully" or "disable ability successfully" is displayed.
**Method 2**: through the device settings
- From the device settings screen, access the list of installed extended services under accessibility.
If an extended service is not installed, it is grayed out, and "No service" is displayed.
- Select the target extended service, and toggle on or off the switch to enable or disable it.
- If you opt to enable a service, a security reminder is displayed. Wait until the countdown ends and then select the check box indicating that you are aware of and willing to assume the listed risks.
...@@ -86,13 +86,13 @@ The application file paths obtained by the preceding contexts are different. ...@@ -86,13 +86,13 @@ The application file paths obtained by the preceding contexts are different.
| Name| Path| | Name| Path|
| -------- | -------- | | -------- | -------- |
| bundleCodeDir | \<Path prefix>/el1/bundle/| | bundleCodeDir | \<Path prefix>/el1/bundle|
| cacheDir | \<Path prefix>/\<Encryption level>/base/cache/| | cacheDir | \<Path prefix>/\<Encryption level>/base/cache|
| filesDir | \<Path prefix>/\<Encryption level>/base/files/| | filesDir | \<Path prefix>/\<Encryption level>/base/files|
| preferencesDir | \<Path prefix>/\<Encryption level>/base/preferences/| | preferencesDir | \<Path prefix>/\<Encryption level>/base/preferences|
| tempDir | \<Path prefix>/\<Encryption level>/base/temp/| | tempDir | \<Path prefix>/\<Encryption level>/base/temp|
| databaseDir | \<Path prefix>/\<Encryption level>/database/| | databaseDir | \<Path prefix>/\<Encryption level>/database|
| distributedFilesDir | \<Path prefix>/el2/distributedFiles/| | distributedFilesDir | \<Path prefix>/el2/distributedFiles|
The sample code is as follows: The sample code is as follows:
...@@ -110,6 +110,9 @@ The application file paths obtained by the preceding contexts are different. ...@@ -110,6 +110,9 @@ The application file paths obtained by the preceding contexts are different.
let distributedFilesDir = applicationContext.distributedFilesDir; let distributedFilesDir = applicationContext.distributedFilesDir;
let preferencesDir = applicationContext.preferencesDir; let preferencesDir = applicationContext.preferencesDir;
... ...
// Obtain the application file path.
let filePath = tempDir + 'test.txt';
console.info(`filePath: ${filePath}`);
} }
} }
``` ```
...@@ -118,13 +121,13 @@ The application file paths obtained by the preceding contexts are different. ...@@ -118,13 +121,13 @@ The application file paths obtained by the preceding contexts are different.
| Name| Path| | Name| Path|
| -------- | -------- | | -------- | -------- |
| bundleCodeDir | \<Path prefix>/el1/bundle/| | bundleCodeDir | \<Path prefix>/el1/bundle|
| cacheDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/cache/| | cacheDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/cache|
| filesDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/files/| | filesDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/files|
| preferencesDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/preferences/| | preferencesDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/preferences|
| tempDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/temp/| | tempDir | \<Path prefix>/\<Encryption level>/base/**haps/\<module-name>**/temp|
| databaseDir | \<Path prefix>/\<Encryption level>/database/**\<module-name>**/| | databaseDir | \<Path prefix>/\<Encryption level>/database/**\<module-name>**|
| distributedFilesDir | \<Path prefix>/el2/distributedFiles/**\<module-name>**/| | distributedFilesDir | \<Path prefix>/el2/distributedFiles/**\<module-name>**|
The sample code is as follows: The sample code is as follows:
...@@ -141,6 +144,9 @@ The application file paths obtained by the preceding contexts are different. ...@@ -141,6 +144,9 @@ The application file paths obtained by the preceding contexts are different.
let distributedFilesDir = this.context.distributedFilesDir; let distributedFilesDir = this.context.distributedFilesDir;
let preferencesDir = this.context.preferencesDir; let preferencesDir = this.context.preferencesDir;
... ...
// Obtain the application file path.
let filePath = tempDir + 'test.txt';
console.info(`filePath: ${filePath}`);
} }
} }
``` ```
......
...@@ -16,7 +16,7 @@ Widget-related configuration includes **FormExtensionAbility** configuration and ...@@ -16,7 +16,7 @@ Widget-related configuration includes **FormExtensionAbility** configuration and
"extensionAbilities": [ "extensionAbilities": [
{ {
"name": "EntryFormAbility", "name": "EntryFormAbility",
"srcEntry": "./ets/entryformability/EntryFormAbility.ts", "srcEntry": "./ets/entryformability/EntryFormAbility.ets",
"label": "$string:EntryFormAbility_label", "label": "$string:EntryFormAbility_label",
"description": "$string:EntryFormAbility_desc", "description": "$string:EntryFormAbility_desc",
"type": "form", "type": "form",
...@@ -42,9 +42,9 @@ Widget-related configuration includes **FormExtensionAbility** configuration and ...@@ -42,9 +42,9 @@ Widget-related configuration includes **FormExtensionAbility** configuration and
| 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)| | 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| | 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**)| | uiSyntax | Type of the widget.<br>- **arkts**: ArkTS widget<br>- **hml**: JS widget| String| Yes (initial value: **hml**)|
| window | Window-related configurations.| Object| Yes| | window | Window-related configurations.| Object| YYes (initial value: see Table 2)|
| 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| | 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**)| | colorMode | Color mode of the widget.<br>- **auto**: following the system 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| | 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| | 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**. If both are specified, the value specified by **updateDuration** is used.<br>- **false**: The widget cannot be updated periodically.| Boolean| 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**. If both are specified, the value specified by **updateDuration** is used.<br>- **false**: The widget cannot be updated periodically.| Boolean| No|
...@@ -54,7 +54,14 @@ Widget-related configuration includes **FormExtensionAbility** configuration and ...@@ -54,7 +54,14 @@ Widget-related configuration includes **FormExtensionAbility** configuration and
| formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)| | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)|
| metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)| | metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)|
| dataProxyEnabled | Whether the widget supports the [update-through-proxy](./arkts-ui-widget-update-by-proxy.md) feature.<br>- **true**: The widget supports the update-through-proxy feature.<br>- **false**: The widget does not support the update-through-proxy feature.<br>If this tag is set to **true**, the settings for the scheduled update time will still take effect, but the settings for the update interval and next update time will not.| Boolean| Yes (initial value: **false**)| | dataProxyEnabled | Whether the widget supports the [update-through-proxy](./arkts-ui-widget-update-by-proxy.md) feature.<br>- **true**: The widget supports the update-through-proxy feature.<br>- **false**: The widget does not support the update-through-proxy feature.<br>If this tag is set to **true**, the settings for the scheduled update time will still take effect, but the settings for the update interval and next update time will not.| Boolean| Yes (initial value: **false**)|
| isDynamic | Whether the widget is a dynamic widget. This tag only applies to ArkTS widgets.<br>- **true**: The widget is a dynamic widget.<br>- **false**: The widget is a static widget. In this case, the widget is displayed as a static image after being added.| Boolean| Yes (initial value: **true**)| | isDynamic | Whether the widget is a dynamic widget. This tag applies only to ArkTS widgets.<br>- **true**: The widget is a dynamic widget.<br>- **false**: The widget is a static widget. In this case, the widget is displayed as a static image after being added.| Boolean| Yes (initial value: **true**)|
**Table 2** Internal structure of the window object
| Field| Description| Data Type| Default Value Allowed|
| -------- | -------- | -------- | -------- |
| designWidth | Baseline width for page design. The size of an element is scaled by the actual device width.| Number| Yes (initial value: **720px**)|
| autoDesignWidth | Whether to automatically calculate the baseline width for page design. If it is set to **true**, the **designWidth** attribute will be ignored, and the baseline width will be calculated based on the device width and screen density.| Boolean| Yes (initial value: **false**)|
Example configuration: Example configuration:
......
# Using Animations in the Widget # 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). Just note the following restrictions when using the animations in ArkTS widgets. To make your ArkTS widget more engaging, you can apply animations to it, including [explicit animation](../reference/arkui-ts/ts-explicit-animation.md), [property animation](../reference/arkui-ts/ts-animatorproperty.md), and [component transition](../reference/arkui-ts/ts-transition-animation-component.md). Just note the following restrictions when using the animations in ArkTS widgets.
**Table 1** Restrictions on animation parameters **Table 1** Restrictions on animation parameters
......
...@@ -89,7 +89,7 @@ The update-through-proxy configuration varies by the type of shared data. ...@@ -89,7 +89,7 @@ The update-through-proxy configuration varies by the type of shared data.
} }
``` ```
- In the widget page code file **widgets.abc**, use the variable in LocalStorage to obtain the subscribed data. The variable in LocalStorage is bound to a string and updates the subscribed data in the key:value pair format. The key must be the same as that subscribed to by the widget provider. In this example, the subscribed data is obtained through **'detail'** and displayed in the **\<Text>** component. - In the [widget page code file](arkts-ui-widget-creation.md), use the variable in LocalStorage to obtain the subscribed data. The variable in LocalStorage is bound to a string and updates the subscribed data in the key:value pair format. The key must be the same as that subscribed to by the widget provider. In this example, the subscribed data is obtained through **'detail'** and displayed in the **\<Text>** component.
```ts ```ts
let storage = new LocalStorage(); let storage = new LocalStorage();
@Entry(storage) @Entry(storage)
...@@ -178,7 +178,7 @@ The update-through-proxy configuration varies by the type of shared data. ...@@ -178,7 +178,7 @@ The update-through-proxy configuration varies by the type of shared data.
} }
``` ```
- In the widget page code file (generally the .ets file in the **pages** folder under the widget directory of the project), use the variable in LocalStorage to obtain the subscribed data. The variable in LocalStorage is bound to a string and updates the subscribed data in the key:value pair format. The key must be the same as that subscribed to by the widget provider. In the example, the subscribed data is obtained through **'list'**, and the value of the first element is displayed on the **\<Text>** component. - In the [widget page code file](arkts-ui-widget-creation.md), use the variable in LocalStorage to obtain the subscribed data. The variable in LocalStorage is bound to a string and updates the subscribed data in the key:value pair format. The key must be the same as that subscribed to by the widget provider. In the example, the subscribed data is obtained through **'list'**, and the value of the first element is displayed on the **\<Text>** component.
```ts ```ts
let storage = new LocalStorage(); let storage = new LocalStorage();
@Entry(storage) @Entry(storage)
...@@ -215,4 +215,4 @@ The update-through-proxy configuration varies by the type of shared data. ...@@ -215,4 +215,4 @@ The update-through-proxy configuration varies by the type of shared data.
## Data Provider Development ## Data Provider Development
For details, see [Data Management](../database/data-mgmt-overview.md). For details, see [Data Management](../database/share-data-by-silent-access.md).
...@@ -15,10 +15,11 @@ ...@@ -15,10 +15,11 @@
- Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [widget components](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host on a one-to-one basis. The widget rendering service runs the widget page code **widgets.abc** for rendering, and sends the rendered data to the corresponding widget component on the widget host. - Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [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 **Figure 2** Working principles of the ArkTS widget rendering service
![WidgetRender](figures/WidgetRender.png)
Unlike JS widgets, ArkTS widgets support logic code running. The widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of a widget provider run in the same virtual machine operating environment, and rendering instances of different widget providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different widget providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-page) object. Use one **globalThis** object for widgets from the same widget provider, and different **globalThis** objects for widgets from different widget providers. ![WidgetRender](figures/WidgetRender.png)
Unlike JS widgets, ArkTS widgets support logic code execution. The widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of a widget provider run in the same virtual machine operating environment, and rendering instances of different widget providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different widget providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-ui-page) object. Use one **globalThis** object for widgets from the same widget provider, and different **globalThis** objects for widgets from different widget providers.
## Advantages of ArkTS Widgets ## Advantages of ArkTS Widgets
...@@ -34,7 +35,7 @@ As a quick entry to applications, ArkTS widgets outperform JS widgets in the fol ...@@ -34,7 +35,7 @@ As a quick entry to applications, ArkTS widgets outperform JS widgets in the fol
![WidgetProject](figures/WidgetProject.png) ![WidgetProject](figures/WidgetProject.png)
- More widget features - More widget features
- Animation: ArkTS widgets support 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. - Animation: ArkTS widgets support the [property 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: ArkTS widgets allow you to draw graphics with the [\<Canvas>](../reference/arkui-ts/ts-components-canvas-canvas.md) component to present information more vividly. - Custom drawing: ArkTS widgets allow 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 use cases of widgets. - Logic code execution: The capability to run logic code in widgets means that service logic can be self-closed in widgets, expanding the use cases of widgets.
...@@ -57,6 +58,8 @@ In addition, ArkTS widgets do not support the following features: ...@@ -57,6 +58,8 @@ In addition, ArkTS widgets do not support the following features:
- Instant preview - Instant preview
- Breakpoint debugging. - Breakpoint debugging
- Hot reload - Hot reload
- **setTimeOut**
# Setting Atomic Services to Support Sharing
## How to Develop
1. An application calls [UIAbility.onShare()](../reference/apis/js-apis-app-ability-uiAbility.md#onshare), a lifecycle callback provided by the UIAbility component, to set the data to share. In this lifecycle callback, **ohos.extra.param.key.contentTitle** indicates the title of the content to share in the sharing box, **ohos.extra.param.key.shareAbstract** provides an abstract description of the content, and **ohos.extra.param.key.shareUrl** indicates the online address of the service. You need to set these three items as objects, with the key set to **title**, **abstract**, and **url**, respectively.
```ts
import AbilityConstant from '@ohos.app.ability.AbilityConstant';
class MyUIAbility extends UIAbility {
onShare(wantParams) {
console.log('onShare');
wantParams['ohos.extra.param.key.contentTitle'] = {title: "OA"};
wantParams['ohos.extra.param.key.shareAbstract'] = {abstract: "communication for company employee"};
wantParams['ohos.extra.param.key.shareUrl'] = {url: "oa.example.com"};
}
}
```
2. A system dialog box calls [abilityManager.acquireShareData()](../reference/apis/js-apis-app-ability-abilityManager.md#acquiresharedata) to obtain data shared through atomic service sharing. Specifically, the system finds the UIAbility based on the mission ID and calls the **OnShare()** lifecycle of the UIAbility to obtain the shared data.
```ts
import abilityManager from '@ohos.app.ability.abilityManager';
try {
abilityManager.acquireShareData(1, (err, wantParam) => {
if (err) {
console.error(`acquireShareData fail, err: ${JSON.stringify(err)}`);
} else {
console.log(`acquireShareData success, data: ${JSON.stringify(wantParam)}`);
}
});
} catch (paramError) {
console.error(`error.code: ${JSON.stringify(paramError.code)}, error.message: ${JSON.stringify(paramError.message)}`);
}
```
...@@ -26,7 +26,7 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol ...@@ -26,7 +26,7 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol
- If the **exported** field of the target component is **false**, verify the **ohos.permission.START_INVISIBLE_ABILITY** permission. - If the **exported** field of the target component is **false**, verify the **ohos.permission.START_INVISIBLE_ABILITY** permission.
- For details, see [Component exported Configuration](../quick-start/module-configuration-file.md#abilities). - For details, see [Component exported Configuration](../quick-start/module-configuration-file.md#abilities).
- **Before starting a component of a background application, verify the BACKGROUND permission.** - **Before starting a UIAbility component of a background application, verify the BACKGROUND permission.**
- An application is considered as a foreground application only when the application process gains focus or its UIAbility component is running in the foreground. - An application is considered as a foreground application only when the application process gains focus or its UIAbility component is running in the foreground.
- Verify the **ohos.permission.START_ABILITIES_FROM_BACKGROUND** permission. - Verify the **ohos.permission.START_ABILITIES_FROM_BACKGROUND** permission.
...@@ -45,7 +45,9 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol ...@@ -45,7 +45,9 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol
The rules for starting components on the same device vary in the following scenarios: The rules for starting components on the same device vary in the following scenarios:
- Starting or connecting to the UIAbility, ServiceExtensionAbility, and DataShareExtensionAbility components - Starting the UIAbility component
- Starting the ServiceExtensionAbility and DataShareExtensionAbility components
- Using **startAbilityByCall()** to start the UIAbility component - Using **startAbilityByCall()** to start the UIAbility component
...@@ -56,9 +58,10 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol ...@@ -56,9 +58,10 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol
The rules for starting components on a different device vary in the following scenarios: The rules for starting components on a different device vary in the following scenarios:
- Starting or connecting to the UIAbility, ServiceExtensionAbility, and DataShareExtensionAbility components - Starting the UIAbility component
- Starting the ServiceExtensionAbility and DataShareExtensionAbility components
- Using **startAbilityByCall()** to start the UIAbility component - Using **startAbilityByCall()** to start the UIAbility component
![component-startup-rules](figures/component-startup-inter-stage.png) ![component-startup-rules](figures/component-startup-inter-stage.png)
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
| [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) |
| [setDisplayOrientation(orientation:bundle.DisplayOrientation, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetdisplayorientation7)<br>[setDisplayOrientation(orientation:bundle.DisplayOrientation):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetdisplayorientation7-1) | \@ohos.screen.d.ts | [setOrientation(orientation: Orientation, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-screen.md#setorientation)<br>[setOrientation(orientation: Orientation): Promise&lt;void&gt;;](../reference/apis/js-apis-screen.md#setorientation-1) | | [setDisplayOrientation(orientation:bundle.DisplayOrientation, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetdisplayorientation7)<br>[setDisplayOrientation(orientation:bundle.DisplayOrientation):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetdisplayorientation7-1) | \@ohos.screen.d.ts | [setOrientation(orientation: Orientation, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-screen.md#setorientation)<br>[setOrientation(orientation: Orientation): Promise&lt;void&gt;;](../reference/apis/js-apis-screen.md#setorientation-1) |
| [setShowOnLockScreen(show:boolean, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetshowonlockscreen7)<br>[setShowOnLockScreen(show:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetshowonlockscreen7-1) | \@ohos.window.d.ts | [setShowOnLockScreen(showOnLockScreen: boolean): void;](../reference/apis/js-apis-window.md#setshowonlockscreen9) | | [setShowOnLockScreen(show:boolean, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetshowonlockscreendeprecated)<br>[setShowOnLockScreen(show:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetshowonlockscreendeprecated-1) | \@ohos.window.d.ts | [setShowOnLockScreen(showOnLockScreen: boolean): void;](../reference/apis/js-apis-window.md#setshowonlockscreen9) |
| [setWakeUpScreen(wakeUp:boolean, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetwakeupscreen7)<br>[setWakeUpScreen(wakeUp:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetwakeupscreen7-1) | \@ohos.window.d.ts | [setWakeUpScreen(wakeUp: boolean): void;](../reference/apis/js-apis-window.md#setwakeupscreen9) | | [setWakeUpScreen(wakeUp:boolean, callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextsetwakeupscreen7)<br>[setWakeUpScreen(wakeUp:boolean):Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextsetwakeupscreen7-1) | \@ohos.window.d.ts | [setWakeUpScreen(wakeUp: boolean): void;](../reference/apis/js-apis-window.md#setwakeupscreen9) |
| [getProcessInfo(callback:AsyncCallback&lt;ProcessInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetprocessinfo7)<br>[getProcessInfo():Promise&lt;ProcessInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetprocessinfo7-1) | \@ohos.app.ability.abilityManager.d.ts | [getAbilityRunningInfos(callback: AsyncCallback&lt;Array&lt;AbilityRunningInfo&gt;&gt;): void;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos)<br>[getAbilityRunningInfos(): Promise&lt;Array&lt;AbilityRunningInfo&gt;&gt;;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos-1) | | [getProcessInfo(callback:AsyncCallback&lt;ProcessInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetprocessinfo7)<br>[getProcessInfo():Promise&lt;ProcessInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetprocessinfo7-1) | \@ohos.app.ability.abilityManager.d.ts | [getAbilityRunningInfos(callback: AsyncCallback&lt;Array&lt;AbilityRunningInfo&gt;&gt;): void;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos)<br>[getAbilityRunningInfos(): Promise&lt;Array&lt;AbilityRunningInfo&gt;&gt;;](../reference/apis/js-apis-app-ability-abilityManager.md#getabilityrunninginfos-1) |
| [getElementName(callback:AsyncCallback&lt;ElementName&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetelementname7)<br>[getElementName():Promise&lt;ElementName&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetelementname7-1) | application\UIAbilityContext.d.ts | [abilityInfo.name: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)<br>[abilityInfo.bundleName: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)| | [getElementName(callback:AsyncCallback&lt;ElementName&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetelementname7)<br>[getElementName():Promise&lt;ElementName&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetelementname7-1) | application\UIAbilityContext.d.ts | [abilityInfo.name: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)<br>[abilityInfo.bundleName: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)|
...@@ -26,5 +26,3 @@ ...@@ -26,5 +26,3 @@
| [getAbilityInfo(callback:AsyncCallback&lt;AbilityInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetabilityinfo7)<br>[getAbilityInfo():Promise&lt;AbilityInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetabilityinfo7-1) | application\UIAbilityContext.d.ts | [abilityInfo: AbilityInfo;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)| | [getAbilityInfo(callback:AsyncCallback&lt;AbilityInfo&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextgetabilityinfo7)<br>[getAbilityInfo():Promise&lt;AbilityInfo&gt;;](../reference/apis/js-apis-inner-app-context.md#contextgetabilityinfo7-1) | application\UIAbilityContext.d.ts | [abilityInfo: AbilityInfo;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)|
| [isUpdatingConfigurations(callback:AsyncCallback&lt;boolean&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextisupdatingconfigurations7)<br>[isUpdatingConfigurations():Promise&lt;boolean&gt;;](../reference/apis/js-apis-inner-app-context.md#contextisupdatingconfigurations7-1) | There is no corresponding API in the stage model.| OpenHarmony applications do not restart when the system environment changes. The **onConfigurationUpdated** callback is invoked to notify the applications of the changes. This API provides an empty implementation in the FA model, and the stage model does not provide a corresponding API.| | [isUpdatingConfigurations(callback:AsyncCallback&lt;boolean&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextisupdatingconfigurations7)<br>[isUpdatingConfigurations():Promise&lt;boolean&gt;;](../reference/apis/js-apis-inner-app-context.md#contextisupdatingconfigurations7-1) | There is no corresponding API in the stage model.| OpenHarmony applications do not restart when the system environment changes. The **onConfigurationUpdated** callback is invoked to notify the applications of the changes. This API provides an empty implementation in the FA model, and the stage model does not provide a corresponding API.|
| [printDrawnCompleted(callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextprintdrawncompleted7)<br>[printDrawnCompleted():Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextprintdrawncompleted7-1) | There is no corresponding API in the stage model.| This API provides an empty implementation in the FA model. The stage model does not provide a corresponding API.| | [printDrawnCompleted(callback:AsyncCallback&lt;void&gt;):void;](../reference/apis/js-apis-inner-app-context.md#contextprintdrawncompleted7)<br>[printDrawnCompleted():Promise&lt;void&gt;;](../reference/apis/js-apis-inner-app-context.md#contextprintdrawncompleted7-1) | There is no corresponding API in the stage model.| This API provides an empty implementation in the FA model. The stage model does not provide a corresponding API.|
<!--no_check-->
\ No newline at end of file
...@@ -18,7 +18,7 @@ The table below describes the matching rules of explicit [Want](../reference/api ...@@ -18,7 +18,7 @@ The table below describes the matching rules of explicit [Want](../reference/api
| action | string | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| | action | string | No| No| This field is not used for matching. It is passed to the target application component as a parameter.|
| entities | Array&lt;string&gt; | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| | entities | Array&lt;string&gt; | No| No| This field is not used for matching. It is passed to the target application component as a parameter.|
| flags | number | No| No| This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.| | flags | number | No| No| This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.|
| parameters | {[key:&nbsp;string]:&nbsp;any} | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| | parameters | {[key:&nbsp;string]:&nbsp;Object} | No| No| This field is not used for matching. It is passed to the target application component as a parameter.|
## Matching Rules for Implicit Want ## Matching Rules for Implicit Want
...@@ -35,7 +35,7 @@ The table below describes the matching rules of implicit [Want](../reference/api ...@@ -35,7 +35,7 @@ The table below describes the matching rules of implicit [Want](../reference/api
| action | string | Yes | No | | | action | string | Yes | No | |
| entities | Array&lt;string&gt; | Yes | No | | | entities | Array&lt;string&gt; | Yes | No | |
| flags | number | No | No | This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.| | flags | number | No | No | This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.|
| parameters | {[key:&nbsp;string]:&nbsp;any} | No | No | This field is not used for matching. It is passed to the target application component as a parameter. | | parameters | {[key:&nbsp;string]:&nbsp;Object} | No | No | This field is not used for matching. It is passed to the target application component as a parameter. |
Get familiar with the following about implicit Want: Get familiar with the following about implicit Want:
......
...@@ -53,9 +53,9 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -53,9 +53,9 @@ The table below describes the main APIs used for cross-device migration. For det
| **API**| Description| | **API**| Description|
| -------- | -------- | | -------- | -------- |
| onContinue(wantParam : {[key: string]: any}): OnContinueResult | Called by the initiator to store the data required for migration and indicate whether the migration is accepted.<br>- **AGREE**: The migration is accepted.<br>- **REJECT**: The migration is rejected, for example, when an application is abnormal in **onContinue()**.<br>- **MISMATCH**: The version does not match. The application on the initiator can obtain the version number of the target application from **onContinue()**. If the migration cannot be performed due to version mismatch, this error code is returned.| | onContinue(wantParam : {[key: string]: Object}): OnContinueResult | Called by the initiator to store the data required for migration and indicate whether the migration is accepted.<br>- **AGREE**: The migration is accepted.<br>- **REJECT**: The migration is rejected, for example, when an application is abnormal in **onContinue()**.<br>- **MISMATCH**: The version does not match. The application on the initiator can obtain the version number of the target application from **onContinue()**. If the migration cannot be performed due to version mismatch, this error code is returned.|
| onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the multiton migration scenario. For details, see [UIAbility Component Launch Type](uiability-launch-type.md).| | onCreate(want: Want, param: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page when the target uses cold start or the target is a multiton application and uses hot start. For details, see [UIAbility Component Launch Type](uiability-launch-type.md).|
| onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page in the singleton migration scenario. For details, see [UIAbility Component Launch Type](uiability-launch-type.md).| | onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void; | Called by the target to restore the data and UI page when the target is a singleton application and uses hot start. For details, see [UIAbility Component Launch Type](uiability-launch-type.md).|
...@@ -90,7 +90,7 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -90,7 +90,7 @@ The table below describes the main APIs used for cross-device migration. For det
4. Implement [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) in the UIAbility of the initiator. 4. Implement [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) in the UIAbility of the initiator.
[onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) is called on the initiator. You can save the data in this method to implement application compatibility check and migration decision. [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) is called on the initiator. You can save the data in this method to implement application compatibility check and migration decision.
- Saving migrated data: You can save the data to be migrated in key-value pairs in **wantParam**. - Saving migrated data: You can save the data to be migrated in key-value pairs in **wantParam**.
- Checking application compatibility: You can obtain the version number of the target application from **wantParam** and that of the current application from **wantParam.version** of the **onContinue()** callback. Then you can check the compatibility between the two. - Checking application compatibility: You can obtain the version number of the target application from **wantParam** and that of the current application from **wantParam.version** of the **onContinue()** callback. Then you can check the compatibility between the two.
...@@ -114,7 +114,7 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -114,7 +114,7 @@ The table below describes the main APIs used for cross-device migration. For det
``` ```
5. Implement **onCreate()** and **onNewWant()** in the UIAbility of the target application to implement data restoration. 5. Implement **onCreate()** and **onNewWant()** in the UIAbility of the target application to implement data restoration.
- Implementation example of **onCreate** in the multiton scenario - Implementation example of **onCreate**
- The target device determines whether the startup is **LaunchReason.CONTINUATION** based on **launchReason** in **onCreate()**. - The target device determines whether the startup is **LaunchReason.CONTINUATION** based on **launchReason** in **onCreate()**.
- You can obtain the saved migration data from the **want** parameter. - You can obtain the saved migration data from the **want** parameter.
- After data restoration is complete, call **restoreWindowStage** to trigger page restoration, including page stack information. - After data restoration is complete, call **restoreWindowStage** to trigger page restoration, including page stack information.
...@@ -139,11 +139,29 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -139,11 +139,29 @@ The table below describes the main APIs used for cross-device migration. For det
} }
} }
``` ```
- For a singleton ability, use **onNewWant()** to achieve the same implementation. - For a singleton application, you must also implement **onNewWant()**, in the same way as **onCreate()**.
- Determine the migration scenario in **onNewWant()**, restore data, and trigger page restoration.
```ts
export default class EntryAbility extends UIAbility {
storage : LocalStorage;
onNewWant(want, launchParam) {
console.info(`EntryAbility onNewWant ${AbilityConstant.LaunchReason.CONTINUATION}`)
if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) {
// Obtain the user data from the want parameter.
let workInput = want.parameters.work
console.info(`work input ${workInput}`)
AppStorage.SetOrCreate<string>('ContinueWork', workInput)
this.storage = new LocalStorage();
this.context.restoreWindowStage(this.storage);
}
}
}
```
6. (Optional) Call [setMissionContinueState](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10) to set the mission continuation state. 6. (Optional) Call [setMissionContinueState](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10) to set the mission continuation state.
For an application that supports migration, mission migration is enabled by default, and the system notifies peripheral trusted devices that a mission can be migrated or canceled based on the gain/loss focus state of the mission. If you want the system to send a notification to peripheral devices only when your application is in a specific scenario, set the migration continuation state to **INACTIVE** when the application is started and change it to **ACTIVE** when the application enters that specific scenario. For details about the API, see [setMissionContinueState](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissioncontinuestate10). For an application that supports migration, mission migration is enabled by default, and the system notifies peripheral trusted devices that a mission can be migrated or canceled based on the gain/loss focus state of the mission. If you want the system to send a notification to peripheral devices only when your application is in a specific scenario, set the migration continuation state to **INACTIVE** when the application is started and change it to **ACTIVE** when the application enters that specific scenario.
- Example: An application does not require migration during startup. - Example: An application does not require migration during startup.
...@@ -180,7 +198,7 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -180,7 +198,7 @@ The table below describes the main APIs used for cross-device migration. For det
onContinue(wantParam : {[key: string]: any}) { onContinue(wantParam : {[key: string]: any}) {
console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`) console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
wantParam[wantConstant.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false; wantParam[wantConstant.Params.SUPPORT_CONTINUE_PAGE_STACK_KEY] = false;
return AbilityConstant.OnContinueResult.AGREE; return AbilityConstant.OnContinueResult.AGREE;
} }
...@@ -202,7 +220,7 @@ The table below describes the main APIs used for cross-device migration. For det ...@@ -202,7 +220,7 @@ The table below describes the main APIs used for cross-device migration. For det
onContinue(wantParam : {[key: string]: any}) { onContinue(wantParam : {[key: string]: any}) {
console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`) console.info(`onContinue version = ${wantParam.version}, targetDevice: ${wantParam.targetDevice}`)
wantParam[wantConstant.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false; wantParam[wantConstant.Params.SUPPORT_CONTINUE_SOURCE_EXIT_KEY] = false;
return AbilityConstant.OnContinueResult.AGREE; return AbilityConstant.OnContinueResult.AGREE;
} }
``` ```
...@@ -9,11 +9,11 @@ ...@@ -9,11 +9,11 @@
| onCreate?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate) | | onCreate?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate) |
| onWindowDisplayModeChanged?(isShownInMultiWindow: boolean, newConfig: resourceManager.Configuration): void; | There is no corresponding API in the stage model.| No corresponding API is provided.| | onWindowDisplayModeChanged?(isShownInMultiWindow: boolean, newConfig: resourceManager.Configuration): void; | There is no corresponding API in the stage model.| No corresponding API is provided.|
| onStartContinuation?(): boolean; | There is no corresponding API in the stage model.| In the stage model, an application does not need to detect whether the continuation is successful (detected when the application initiates the continuation request). Therefore, the **onStartContinuation()** callback is deprecated.| | onStartContinuation?(): boolean; | There is no corresponding API in the stage model.| In the stage model, an application does not need to detect whether the continuation is successful (detected when the application initiates the continuation request). Therefore, the **onStartContinuation()** callback is deprecated.|
| onSaveData?(data: Object): boolean; | \@ohos.app.ability.UIAbility.d.ts | [onContinue(wantParam : {[key: string]: any}): AbilityConstant.OnContinueResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) | | onSaveData?(data: Object): boolean; | \@ohos.app.ability.UIAbility.d.ts | [onContinue(wantParam : {[key: string]: Object}): AbilityConstant.OnContinueResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) |
| onCompleteContinuation?(result: number): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) | | onCompleteContinuation?(result: number): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) |
| onRestoreData?(data: Object): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br>[onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant)<br>In multiton or singleton mode, the target ability completes data restoration in the **onCreate()** callback. In the callback, **launchParam.launchReason** is used to determine whether it is a continuation-based launch scenario. If it is, the data saved before continuation can be obtained from the **want** parameter.| | onRestoreData?(data: Object): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br>[onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant)<br>In multiton or singleton mode, the target ability completes data restoration in the **onCreate()** callback. In the callback, **launchParam.launchReason** is used to determine whether it is a continuation-based launch scenario. If it is, the data saved before continuation can be obtained from the **want** parameter.|
| onRemoteTerminated?(): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) | | onRemoteTerminated?(): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) |
| onSaveAbilityState?(outState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onSaveState(reason: AbilityConstant.StateType, wantParam : {[key: string]: any}): AbilityConstant.OnSaveResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonsavestate) | | onSaveAbilityState?(outState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onSaveState(reason: AbilityConstant.StateType, wantParam : {[key: string]: Object}): AbilityConstant.OnSaveResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonsavestate) |
| onRestoreAbilityState?(inState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br>After an application is restarted, the **onCreate()** callback is triggered. In the callback, **launchParam.launchReason** is used to determine whether it is a self-recovery scenario. If it is, the data saved before the restart can be obtained from the **want** parameter.| | onRestoreAbilityState?(inState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)<br>After an application is restarted, the **onCreate()** callback is triggered. In the callback, **launchParam.launchReason** is used to determine whether it is a self-recovery scenario. If it is, the data saved before the restart can be obtained from the **want** parameter.|
| onInactive?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onBackground(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonbackground) | | onInactive?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onBackground(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonbackground) |
| onActive?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onForeground(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonforeground) | | onActive?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onForeground(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonforeground) |
......
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
| onEvent?(formId: string, message: string): void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onFormEvent(formId: string, message: string): void;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent) | | onEvent?(formId: string, message: string): void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onFormEvent(formId: string, message: string): void;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent) |
| onDestroy?(formId: string): void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onRemoveForm(formId: string): void;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform) | | onDestroy?(formId: string): void; | \@ohos.app.form.FormExtensionAbility.d.ts | [onRemoveForm(formId: string): void;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform) |
| onAcquireFormState?(want: Want): formInfo.FormState; | \@ohos.app.form.FormExtensionAbility.d.ts | [onAcquireFormState?(want: Want): formInfo.FormState;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onacquireformstate) | | onAcquireFormState?(want: Want): formInfo.FormState; | \@ohos.app.form.FormExtensionAbility.d.ts | [onAcquireFormState?(want: Want): formInfo.FormState;](../reference/apis/js-apis-app-form-formExtensionAbility.md#onacquireformstate) |
| onShare?(formId: string): {[key: string]: any}; | \@ohos.app.form.FormExtensionAbility.d.ts | [onShareForm?(formId: string): { [key: string]: any };](../reference/apis/js-apis-app-form-formExtensionAbility.md#onshareform) | | onShare?(formId: string): {[key: string]: Object}; | \@ohos.app.form.FormExtensionAbility.d.ts | [onShareForm?(formId: string): { [key: string]: Object };](../reference/apis/js-apis-app-form-formExtensionAbility.md#onshareform) |
# mediaLibrary Switching # mediaLibrary Switching
| API in the FA Model| Corresponding .d.ts File in the Stage Model| Corresponding API in the Stage Model| | API in the FA Model| Corresponding .d.ts File in the Stage Model| Corresponding API in the Stage Model|
| -------- | -------- | -------- | | -------- | -------- | -------- |
| [getMediaLibrary(): MediaLibrary;](../reference/apis/js-apis-medialibrary.md#medialibrarygetmedialibrary) | \@ohos.multimedia.mediaLibrary.d.ts | [getMediaLibrary(context: Context): MediaLibrary;](../reference/apis/js-apis-medialibrary.md#medialibrarygetmedialibrary8) | | [getMediaLibrary():&nbsp;MediaLibrary;](../reference/apis/js-apis-medialibrary.md#medialibrarygetmedialibrary) | \@ohos.file.photoAccessHelper.d.ts | [getPhotoAccessHelper(context: Context): PhotoAccessHelper;](../reference/apis/js-apis-photoAccessHelper.md#photoaccesshelpergetphotoaccesshelper) |
...@@ -8,7 +8,5 @@ ...@@ -8,7 +8,5 @@
| [connectAbility(request: Want, options:ConnectOptions ): number;](../reference/apis/js-apis-ability-particleAbility.md#particleabilityconnectability) | application\ServiceExtensionContext.d.ts | [connectAbility(want: Want, options: ConnectOptions): number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability)<br>[connectServiceExtensionAbility(want: Want, options: ConnectOptions): number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability) | | [connectAbility(request: Want, options:ConnectOptions ): number;](../reference/apis/js-apis-ability-particleAbility.md#particleabilityconnectability) | application\ServiceExtensionContext.d.ts | [connectAbility(want: Want, options: ConnectOptions): number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability)<br>[connectServiceExtensionAbility(want: Want, options: ConnectOptions): number;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextconnectserviceextensionability) |
| [disconnectAbility(connection: number, callback:AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitydisconnectability)<br>[disconnectAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitydisconnectability-1) | application\ServiceExtensionContext.d.ts | [disconnectAbility(connection: number, callback:AsyncCallback&lt;void&gt;): void; ](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br>[disconnectAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1)<br>[disconnectServiceExtensionAbility(connection: number, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br>[disconnectServiceExtensionAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1) | | [disconnectAbility(connection: number, callback:AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitydisconnectability)<br>[disconnectAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitydisconnectability-1) | application\ServiceExtensionContext.d.ts | [disconnectAbility(connection: number, callback:AsyncCallback&lt;void&gt;): void; ](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br>[disconnectAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1)<br>[disconnectServiceExtensionAbility(connection: number, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability)<br>[disconnectServiceExtensionAbility(connection: number): Promise&lt;void&gt;;](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextdisconnectserviceextensionability-1) |
| [acquireDataAbilityHelper(uri: string): DataAbilityHelper;](../reference/apis/js-apis-ability-particleAbility.md#particleabilityacquiredataabilityhelper) | \@ohos.data.dataShare.d.ts<br>[\@ohos.data.fileAccess.d.ts | [createDataShareHelper(context: Context, uri: string, callback: AsyncCallback&lt;DataShareHelper&gt;): void;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper)<br>[createDataShareHelper(context: Context, uri: string): Promise&lt;DataShareHelper&gt;;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper-1)<br>[createFileAccessHelper(context: Context): FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper-1)<br>[createFileAccessHelper(context: Context, wants: Array&lt;Want&gt;): FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper) | | [acquireDataAbilityHelper(uri: string): DataAbilityHelper;](../reference/apis/js-apis-ability-particleAbility.md#particleabilityacquiredataabilityhelper) | \@ohos.data.dataShare.d.ts<br>[\@ohos.data.fileAccess.d.ts | [createDataShareHelper(context: Context, uri: string, callback: AsyncCallback&lt;DataShareHelper&gt;): void;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper)<br>[createDataShareHelper(context: Context, uri: string): Promise&lt;DataShareHelper&gt;;](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper-1)<br>[createFileAccessHelper(context: Context): FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper-1)<br>[createFileAccessHelper(context: Context, wants: Array&lt;Want&gt;): FileAccessHelper;](../reference/apis/js-apis-fileAccess.md#fileaccesscreatefileaccesshelper) |
| [startBackgroundRunning(id: number, request: NotificationRequest, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitystartbackgroundrunning)<br>[startBackgroundRunning(id: number, request: NotificationRequest): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitystartbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent, callback: AsyncCallback): void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunningcallback)<br>[startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunningpromise) | | [startBackgroundRunning(id: number, request: NotificationRequest, callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitystartbackgroundrunning)<br>[startBackgroundRunning(id: number, request: NotificationRequest): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitystartbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent, callback: AsyncCallback): void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunning)<br>[startBackgroundRunning(context: Context, bgMode: BackgroundMode, wantAgent: WantAgent): Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstartbackgroundrunning-1) |
| [cancelBackgroundRunning(callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitycancelbackgroundrunning)<br>[cancelBackgroundRunning(): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitycancelbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [stopBackgroundRunning(context: Context, callback: AsyncCallback): void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunningcallback)<br>[stopBackgroundRunning(context: Context): Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunningpromise) | | [cancelBackgroundRunning(callback: AsyncCallback&lt;void&gt;): void;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitycancelbackgroundrunning)<br>[cancelBackgroundRunning(): Promise&lt;void&gt;;](../reference/apis/js-apis-ability-particleAbility.md#particleabilitycancelbackgroundrunning-1) | \@ohos.resourceschedule.backgroundTaskManager.d.ts | [stopBackgroundRunning(context: Context, callback: AsyncCallback): void;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunning)<br>[stopBackgroundRunning(context: Context): Promise&lt;void&gt;;](../reference/apis/js-apis-resourceschedule-backgroundTaskManager.md#backgroundtaskmanagerstopbackgroundrunning-1) |
<!--no_check-->
\ No newline at end of file
...@@ -8,6 +8,10 @@ This topic describes how the two application components of the stage model start ...@@ -8,6 +8,10 @@ This topic describes how the two application components of the stage model start
A UIAbility starts a PageAbility in the same way as it starts another UIAbility. A UIAbility starts a PageAbility in the same way as it starts another UIAbility.
> **NOTE**
>
> In the FA model, **abilityName** consists of **bundleName** and **AbilityName**. For details, see the code snippet below.
```ts ```ts
import UIAbility from '@ohos.app.ability.UIAbility'; import UIAbility from '@ohos.app.ability.UIAbility';
...@@ -25,7 +29,7 @@ export default class EntryAbility extends UIAbility { ...@@ -25,7 +29,7 @@ export default class EntryAbility extends UIAbility {
}); });
let want = { let want = {
bundleName: "com.ohos.fa", bundleName: "com.ohos.fa",
abilityName: "EntryAbility", abilityName: "com.ohos.fa.EntryAbility",
}; };
this.context.startAbility(want).then(() => { this.context.startAbility(want).then(() => {
console.info('Start Ability successfully.'); console.info('Start Ability successfully.');
......
...@@ -14,7 +14,7 @@ import featureAbility from '@ohos.ability.featureAbility'; ...@@ -14,7 +14,7 @@ import featureAbility from '@ohos.ability.featureAbility';
let parameter = { let parameter = {
"want": { "want": {
bundleName: "com.ohos.stage", bundleName: "com.ohos.stage",
abilityName: "com.ohos.stage.EntryAbility" abilityName: "EntryAbility"
} }
}; };
featureAbility.startAbility(parameter).then((code) => { featureAbility.startAbility(parameter).then((code) => {
......
...@@ -35,8 +35,8 @@ Assume that your application has two UIAbility components: EntryAbility and Func ...@@ -35,8 +35,8 @@ Assume that your application has two UIAbility components: EntryAbility and Func
let want = { let want = {
deviceId: '', // An empty deviceId indicates the local device. deviceId: '', // An empty deviceId indicates the local device.
bundleName: 'com.example.myapplication', bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'func', // moduleName is optional. moduleName: 'func', // moduleName is optional.
abilityName: 'FuncAbility',
parameters: { // Custom information. parameters: { // Custom information.
info: 'From the Index page of EntryAbility', info: 'From the Index page of EntryAbility',
}, },
...@@ -100,8 +100,8 @@ When starting FuncAbility from EntryAbility, you may want the result to be retur ...@@ -100,8 +100,8 @@ When starting FuncAbility from EntryAbility, you may want the result to be retur
let want = { let want = {
deviceId: '', // An empty deviceId indicates the local device. deviceId: '', // An empty deviceId indicates the local device.
bundleName: 'com.example.myapplication', bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'func', // moduleName is optional. moduleName: 'func', // moduleName is optional.
abilityName: 'FuncAbility',
parameters: { // Custom information. parameters: { // Custom information.
info: 'From the Index page of EntryAbility', info: 'From the Index page of EntryAbility',
}, },
...@@ -123,8 +123,8 @@ When starting FuncAbility from EntryAbility, you may want the result to be retur ...@@ -123,8 +123,8 @@ When starting FuncAbility from EntryAbility, you may want the result to be retur
resultCode: RESULT_CODE, resultCode: RESULT_CODE,
want: { want: {
bundleName: 'com.example.myapplication', bundleName: 'com.example.myapplication',
moduleName: 'func', // moduleName is optional.
abilityName: 'FuncAbility', abilityName: 'FuncAbility',
moduleName: 'func',
parameters: { parameters: {
info: 'From the Index page of FuncAbility', info: 'From the Index page of FuncAbility',
}, },
...@@ -219,7 +219,8 @@ The following example describes how to start the UIAbility of another applicatio ...@@ -219,7 +219,8 @@ The following example describes how to start the UIAbility of another applicatio
}) })
``` ```
The following figure shows the effect. When you click **Open PDF**, a dialog box is displayed for you to select the application to use. The following figure shows the effect. When you click **Open PDF**, a dialog box is displayed for you to select the application to use.
![](figures/uiability-intra-device-interaction.png) ![](figures/uiability-intra-device-interaction.png)
3. To stop the **UIAbility** instance when the document application is not in use, call [terminateSelf()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself). 3. To stop the **UIAbility** instance when the document application is not in use, call [terminateSelf()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself).
...@@ -296,9 +297,9 @@ If you want to obtain the return result when using implicit Want to start the UI ...@@ -296,9 +297,9 @@ If you want to obtain the return result when using implicit Want to start the UI
let abilityResult = { let abilityResult = {
resultCode: RESULT_CODE, resultCode: RESULT_CODE,
want: { want: {
bundleName: 'com.example.myapplication', bundleName: 'com.example.funcapplication',
moduleName: 'entry', // moduleName is optional.
abilityName: 'EntryAbility', abilityName: 'EntryAbility',
moduleName: 'entry',
parameters: { parameters: {
payResult: 'OKay', payResult: 'OKay',
}, },
...@@ -366,8 +367,8 @@ let context = ...; // UIAbilityContext ...@@ -366,8 +367,8 @@ let context = ...; // UIAbilityContext
let want = { let want = {
deviceId: '', // An empty deviceId indicates the local device. deviceId: '', // An empty deviceId indicates the local device.
bundleName: 'com.example.myapplication', bundleName: 'com.example.myapplication',
abilityName: 'FuncAbility',
moduleName: 'func', // moduleName is optional. moduleName: 'func', // moduleName is optional.
abilityName: 'FuncAbility',
parameters: { // Custom information. parameters: { // Custom information.
info: 'From the Index page of EntryAbility', info: 'From the Index page of EntryAbility',
}, },
...@@ -383,7 +384,7 @@ context.startAbility(want, options).then(() => { ...@@ -383,7 +384,7 @@ context.startAbility(want, options).then(() => {
}) })
``` ```
The display effect is shown below. The display effect is shown below.
![](figures/start-uiability-floating-window.png) ![](figures/start-uiability-floating-window.png)
...@@ -400,13 +401,13 @@ When the initiator UIAbility starts another UIAbility, it usually needs to redir ...@@ -400,13 +401,13 @@ When the initiator UIAbility starts another UIAbility, it usually needs to redir
```ts ```ts
let context = ...; // UIAbilityContext let context = ...; // UIAbilityContext
let want = { let want = {
deviceId: '', // An empty deviceId indicates the local device. deviceId: '', // An empty deviceId indicates the local device.
bundleName: 'com.example.myapplication', bundleName: 'com.example.funcapplication',
abilityName: 'FuncAbility', moduleName: 'entry', // moduleName is optional.
moduleName: 'func', // moduleName is optional. abilityName: 'EntryAbility',
parameters: { // Custom parameter used to pass the page information. parameters: { // Custom parameter used to pass the page information.
router: 'funcA', router: 'funcA',
}, },
} }
// context is the UIAbilityContext of the initiator UIAbility. // context is the UIAbilityContext of the initiator UIAbility.
context.startAbility(want).then(() => { context.startAbility(want).then(() => {
...@@ -423,24 +424,24 @@ When the target UIAbility is started for the first time, in the **onWindowStageC ...@@ -423,24 +424,24 @@ When the target UIAbility is started for the first time, in the **onWindowStageC
```ts ```ts
import UIAbility from '@ohos.app.ability.UIAbility' import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import Window from '@ohos.window' import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
export default class FuncAbility extends UIAbility { export default class FuncAbility extends UIAbility {
funcAbilityWant; funcAbilityWant: Want;
onCreate(want, launchParam) { onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) {
// Receive the parameters passed by the initiator UIAbility. // Receive the parameters passed by the initiator UIAbility.
this.funcAbilityWant = want; this.funcAbilityWant = want;
} }
onWindowStageCreate(windowStage: Window.WindowStage) { onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created. Set a main page for this UIAbility. // Main window is created. Set a main page for this UIAbility.
let url = 'pages/Index'; let url = 'pages/Index';
if (this.funcAbilityWant?.parameters?.router) { if (this.funcAbilityWant?.parameters?.router && this.funcAbilityWant.parameters.router === 'funcA') {
if (this.funcAbilityWant.parameters.router === 'funA') { url = 'pages/Second';
url = 'pages/Second';
}
} }
windowStage.loadContent(url, (err, data) => { windowStage.loadContent(url, (err, data) => {
... ...
...@@ -449,51 +450,90 @@ export default class FuncAbility extends UIAbility { ...@@ -449,51 +450,90 @@ export default class FuncAbility extends UIAbility {
} }
``` ```
### Starting a Page When the Target UIAbility Is Not Started for the First Time ### Starting a Page When the Target UIAbility Is Not Started for the First Time
You start application A, and its home page is displayed. Then you return to the home screen and start application B. Now you need to start application A again from application B and access a specified page of application A. An example scenario is as follows: When you open the home page of the SMS application and return to the home screen, the SMS application is in the opened state with its home page. Then you open the home page of the Contacts application, access user A's details page, and touch the SMS icon to send an SMS message to user A. The SMS application is started again and the sending page is displayed. If the target UIAbility has been started, the initialization logic is not executed again. Instead, the **onNewWant()** lifecycle callback is directly triggered. To implement redirection, parse the required parameters in **onNewWant()**.
An example scenario is as follows:
1. A user opens the SMS application. The UIAbility instance of the SMS application is started, and the home page of the application is displayed.
2. The user returns to the home screen, and the SMS application switches to the background.
3. The user opens the Contacts application and finds a contact.
4. The user touches the SMS button next to the contact. The UIAbility instance of the SMS application is restarted.
5. Since the UIAbility instance of the SMS application has been started, the **onNewWant()** callback of the UIAbility is triggered, and the initialization logic such as **onCreate()** and **onWindowStageCreate()** is skipped.
```mermaid
sequenceDiagram
Participant U as User
Participant S as SMS app
Participant C as Contacts app
U->>S: Open the SMS app.
S-->>U: The home page of the SMS app is displayed.
U->>S: Return to the home screen.
S->>S: The SMS app enters the background.
U->>C: Open the Contacts app.
C-->>U: The page of the Contact app is displayed.
U->>C: Touch the SMS button next to a contact.
C->>S: Start the SMS app with Want.
S-->>U: The page for sending an SMS message to the contact is displayed.
```
![uiability_not_first_started](figures/uiability_not_first_started.png) The development procedure is as follows:
In summary, when a UIAbility instance of application A has been created and the main page of the UIAbility instance is displayed, you need to start the UIAbility of application A from application B and access a different page. 1. When the UIAbility instance of the SMS application is started for the first time, call [getUIContext()](../reference/apis/js-apis-window.md#getuicontext10) in the **onWindowStageCreate()** lifecycle callback to obtain the [UIContext](../reference/apis/js-apis-arkui-UIContext.md).
1. In the target UIAbility, the **Index** page is loaded by default. The UIAbility instance has been created, and the **onNewWant()** callback rather than **onCreate()** and **onWindowStageCreate()** will be invoked. In the **onNewWant()** callback, parse the **want** parameter and bind it to the global variable **globalThis**.
```ts ```ts
import UIAbility from '@ohos.app.ability.UIAbility' import AbilityConstant from '@ohos.app.ability.AbilityConstant';
import UIAbility from '@ohos.app.ability.UIAbility';
import Want from '@ohos.app.ability.Want';
import window from '@ohos.window';
export default class FuncAbility extends UIAbility { import { Router, UIContext } from '@ohos.arkui.UIContext';
onNewWant(want, launchParam) {
// Receive the parameters passed by the initiator UIAbility. export default class EntryAbility extends UIAbility {
globalThis.funcAbilityWant = want; funcAbilityWant: Want;
uiContext: UIContext;
...
onWindowStageCreate(windowStage: window.WindowStage) {
// Main window is created. Set a main page for this UIAbility.
... ...
let windowClass: window.Window;
windowStage.getMainWindow((err, data) => {
if (err.code) {
console.error(`Failed to obtain the main window. Code is ${err.code}, message is ${err.message}`);
return;
}
windowClass = data;
this.uiContext = windowClass.getUIContext();
})
} }
} }
``` ```
2. In FuncAbility, use the router module to implement redirection to the specified page on the **Index** page. Because the **Index** page of FuncAbility is active, the variable will not be declared again and the **aboutToAppear()** callback will not be triggered. Therefore, the page routing functionality can be implemented in the **onPageShow()** callback of the **Index** page. 2. Parse the **want** parameter passed in the **onNewWant()** callback of the UIAbility of the SMS application, call [getRouter()](../reference/apis/js-apis-arkui-UIContext.md#getrouter) in the **UIContext** class to obtain a [Router](../reference/apis/js-apis-arkui-UIContext.md#router) instance, and specify the target page. When the UIAbility instance of the SMS application is started again, the specified page of the UIAbility instance of the SMS application is displayed.
```ts ```ts
import router from '@ohos.router'; export default class EntryAbility extends UIAbility {
funcAbilityWant: Want;
@Entry uiContext: UIContext;
@Component
struct Index { onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam) {
onPageShow() { if (want?.parameters?.router && want.parameters.router === 'funcA') {
let funcAbilityWant = globalThis.funcAbilityWant; let funcAUrl = 'pages/Second';
let url2 = funcAbilityWant?.parameters?.router; let router: Router = this.uiContext.getRouter();
if (url2 && url2 === 'funcA') { router.pushUrl({
router.replaceUrl({ url: funcAUrl
url: 'pages/Second', }).catch((err) => {
console.error(`Failed to push url. Code is ${err.code}, message is ${err.message}`);
}) })
} }
} }
// Page display. ...
build() {
...
}
} }
``` ```
......
...@@ -157,4 +157,4 @@ The following steps are used as an example. ...@@ -157,4 +157,4 @@ The following steps are used as an example.
1. Open file A. A UIAbility instance, UIAbility instance 1, is started. 1. Open file A. A UIAbility instance, UIAbility instance 1, is started.
2. Close the process of file A in Recents. UIAbility instance 1 is destroyed. Return to the home screen and open file A again. A new UIAbility instance, UIAbility instance 2, is started. 2. Close the process of file A in Recents. UIAbility instance 1 is destroyed. Return to the home screen and open file A again. A new UIAbility instance, UIAbility instance 2, is started.
3. Return to the home screen and open file B. A new UIAbility instance, UIAbility instance 3, is started. 3. Return to the home screen and open file B. A new UIAbility instance, UIAbility instance 3, is started.
4. Return to the home screen and open file A again. UIAbility instance 2 is started. This is because the system automatically matches the key with the UIAbility instance and starts the UIAbility instance that has a matching key. In this example, UIAbility instance 2 has the same key as file A. Therefore, the system pulls back UIAbility instance 2 and focuses it without creating a new instance.hao 4. Return to the home screen and open file A again. UIAbility instance 2 is started. This is because the system automatically matches the key with the UIAbility instance and starts the UIAbility instance that has a matching key. In this example, UIAbility instance 2 has the same key as file A. Therefore, the system pulls back UIAbility instance 2 and focuses it without creating a new instance.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
## Types of Want ## Types of Want
- **Explicit Want**: If **abilityName** and **bundleName** are specified in the **want** parameter when starting an an application component, explicit Want is used. - **Explicit Want**: If **abilityName** and **bundleName** are specified in the **want** parameter when starting an application component, explicit Want is used.
Explicit Want is usually used to start a known target application component in the same application. The target application component is started by specifying **bundleName** of the application where the target application component is located and **abilityName** in the **Want** object. When there is an explicit object to process the request, explicit Want is a simple and effective way to start the target application component. Explicit Want is usually used to start a known target application component in the same application. The target application component is started by specifying **bundleName** of the application where the target application component is located and **abilityName** in the **Want** object. When there is an explicit object to process the request, explicit Want is a simple and effective way to start the target application component.
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
} }
``` ```
- **Implicit Want**: If **abilityName** is not specified in the **want** parameter when starting the an application component, implicit Want is used. - **Implicit Want**: If **abilityName** is not specified in the **want** parameter when starting an application component, implicit Want is used.
Implicit Want can be used when the object used to process the request is unclear and the current application wants to use a capability (defined by the [skills tag](../quick-start/module-configuration-file.md#skills)) provided by another application. The system matches all applications that declare to support the capability. For example, for a link open request, the system matches all applications that support the request and provides the available ones for users to select. Implicit Want can be used when the object used to process the request is unclear and the current application wants to use a capability (defined by the [skills tag](../quick-start/module-configuration-file.md#skills)) provided by another application. The system matches all applications that declare to support the capability. For example, for a link open request, the system matches all applications that support the request and provides the available ones for users to select.
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
> - An application component that meets the conditions is matched. That application component is started. > - An application component that meets the conditions is matched. That application component is started.
> - Multiple application components that meet the conditions are matched. A dialog box is displayed for users to select one of them. > - Multiple application components that meet the conditions are matched. A dialog box is displayed for users to select one of them.
> >
> - If the **want** parameter passed does not contain **abilityName** or **bundleName**, the ServiceExtensionAbility components of all applications cannot be started through implicit Want. > - In the scenario for starting the ServiceExtensionAbility component:
> - If the **want** parameter passed in contains **abilityName**, the ServiceExtensionAbility component cannot be started through implicit Want.
> >
> - If the **want** parameter passed contains **bundleName**, the **startServiceExtensionAbility()** method can be used to implicitly start ServiceExtensionAbility. By default, ServiceExtensionAbility with the highest priority is returned. If all the matching ServiceExtensionAbility components have the same priority, the first ServiceExtensionAbility is returned. > - If the **want** parameter passed in contains **bundleName**, the **startServiceExtensionAbility()** method can be used to implicitly start the ServiceExtensionAbility component. By default, the ServiceExtensionAbility component with the highest priority is returned. If all the matching ServiceExtensionAbility components have the same priority, the first ServiceExtensionAbility component is returned.
...@@ -85,7 +85,7 @@ The **FormBindingData** class has the following APIs. For details, see [FormBind ...@@ -85,7 +85,7 @@ The **FormBindingData** class has the following APIs. For details, see [FormBind
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
| createFormBindingData(obj?: Object \ string): FormBindingData| | Creates a **FormBindingData** object.| | createFormBindingData(obj?:&nbsp;Object&nbsp;\|&nbsp;string):&nbsp;FormBindingData | Creates a **FormBindingData** object. |
## How to Develop ## How to Develop
...@@ -327,9 +327,11 @@ For details about how to implement persistent data storage, see [Application Dat ...@@ -327,9 +327,11 @@ For details about how to implement persistent data storage, see [Application Dat
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.
- Normal widget: a widget persistently used by the widget host - Normal widget: a widget persistently used by the widget host, for example, a widget added to the home screen.
- Temporary widget: a widget temporarily used by the widget host - Temporary widget: a widget temporarily used by the widget host, for example, the widget displayed when you swipe up on a widget application.
Converting a temporary widget to a normal one: After you swipe up on a widget application, a temporary widget is displayed. If you touch the pin button on the widget, it is displayed as a normal widget on the home screen.
Data of a temporary widget will be deleted on the Widget Manager if the widget framework is killed and restarted. The widget provider, however, is not notified of the deletion and still keeps the data. Therefore, the widget provider needs to clear the data of temporary widgets proactively if the data has been kept for a long period of time. If the widget host has converted a temporary widget into a normal one, the widget provider should change the widget data from temporary storage to persistent storage. Otherwise, the widget data may be deleted by mistake. Data of a temporary widget will be deleted on the Widget Manager if the widget framework is killed and restarted. The widget provider, however, is not notified of the deletion and still keeps the data. Therefore, the widget provider needs to clear the data of temporary widgets proactively if the data has been kept for a long period of time. If the widget host has converted a temporary widget into a normal one, the widget provider should change the widget data from temporary storage to persistent storage. Otherwise, the widget data may be deleted by mistake.
......
# ArkTS Common Library
- [Overview of ArkTS Common Library](arkts-commonlibrary-overview.md)
- Concurrency
- [Concurrency Overview](concurrency-overview.md)
- Using Asynchronous Concurrency for Development
- [Asynchronous Concurrency Overview](async-concurrency-overview.md)
- [Single I/O Task Development](single-io-development.md)
- Using Multithread Concurrency for Development
- [Multithread Concurrency Overview](multi-thread-concurrency-overview.md)
- [Comparison Between TaskPool and Worker](taskpool-vs-worker.md)
- [CPU Intensive Task Development](cpu-intensive-task-development.md)
- [I/O Intensive Task Development](io-intensive-task-development.md)
- [Synchronous Task Development](sync-task-development.md)
- Container
- [Container Overview](container-overview.md)
- [Linear Containers](linear-container.md)
- [Nonlinear Containers](nonlinear-container.md)
- XML Generation, Parsing, and Conversion
- [XML Overview](xml-overview.md)
- [XML Generation](xml-generation.md)
- [XML Parsing](xml-parsing.md)
- [XML Conversion](xml-conversion.md)
# Overview of ArkTS Common Library
The ArkTS common library provides common basic capabilities, as illustrated in the figure below.
**Figure 1** Capabilities of the ArkTS common library
![arkts-commonlibrary](figures/arkts-commonlibrary.png)
- Supporting [asynchronous concurrency and multithread concurrency](concurrency-overview.md)
- Supports standard JavaScript asynchronous concurrency capabilities such as Promise and async/await.
- Uses **TaskPool** to provide a multithread running environment for applications. The use of **TaskPool** helps reduce resource consumption and improve system performance. It also frees you from caring about the lifecycle of thread instances.
- Uses **Worker** to support multithread concurrency. The worker thread can communicate with the host thread. You need to proactively create and close a worker thread.
- Providing common capabilities of [adding, deleting, modifying, and querying elements in containers](container-overview.md)
- Constructing and parsing XML files, URLs, and URIs
- Extensible Markup Language (XML) is designed for data transmission and storage. The common library provides APIs for [XML generation, parsing, and conversion](xml-overview.md).
- [URI](../reference/apis/js-apis-uri.md) is a uniform resource identifier that uniquely identifies a resource. [URL](../reference/apis/js-apis-url.md) is a uniform resource locator that provides a path for locating a resource.
- Supporting common [string and binary data processing](../reference/apis/js-apis-util.md) and [logging](../reference/apis/js-apis-logs.md)
- Provides APIs to encode and decode strings.
- Provides APIs to encode and decode Base64-encoded bytes.
- Supports common rational number operations, including comparing rational numbers and obtaining numerators and denominators.
- Provides **Scope** APIs to define the valid range of a field.
- Provides APIs to process binary data in scenarios such as TCP flows or file system operations.
- Supports logging using the console.
- Providing the capability of [obtaining process information and operating processes](../reference/apis/js-apis-process.md)
\ No newline at end of file
# Asynchronous Concurrency Overview
Promise and async/await are standard JavaScript syntax that provides asynchronous concurrency. Asynchronous code ensures that actions initiated now finish later. It allows the execution of only one segment of code at a time and is therefore applicable to the development of a single I/O task, for example, a network request or a file read/write operation.
Promise and async/await allow an application to perform other operations without waiting for the completion of certain actions.
## Promise
Promise is an object used to process asynchronous operations. It converts asynchronous operations into a style similar to synchronous operations for easier code writing and maintenance. Promise provides a state mechanism to manage different phases of asynchronous operations. It also provides methods to register callback functions to handle the success or failure of these operations.
Promise has three states: pending, fulfilled, and rejected. After being created, a Promise object is in the pending state and changes to the fulfilled or rejected state when the asynchronous operation is complete.
The most common usage for Promise is to instantiate a Promise object through a constructor and pass in a function (usually named **executor**) with two parameters. The **executor** function receives two parameters: **resolve** and **reject**, which represent the callback functions that should be called when the asynchronous operation succeeds and fails, respectively. The code snippet below creates a Promise object and simulates an asynchronous operation:
```js
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
const randomNumber = Math.random();
if (randomNumber > 0.5) {
resolve(randomNumber);
} else {
reject(new Error('Random number is too small'));
}
}, 1000);
});
```
In the preceding code, the **setTimeout** function simulates an asynchronous operation that randomly generates a number one second later. If the random number is greater than 0.5, the **resolve** callback function is executed and the generated random number is passed in as a parameter. Otherwise, the **reject** callback function is executed and an error object is passed in as a parameter.
After the Promise object is created, you can use the **then** and **catch** methods to register the callback functions for the fulfilled and rejected states. The **then** method can receive two parameters: one for processing the fulfilled state and the other for processing the rejected state. If only one parameter is passed in, the callback function is executed as long as the state changes. The **catch** method receives a callback function to process the failure result, that is, capture the exception thrown when the Promise state changes to **rejected** or the operation fails. The code snippet below shows the use of **then** and **catch** methods:
```js
promise.then(result => {
console.info(`Random number is ${result}`);
}).catch(error => {
console.error(error.message);
});
```
In the preceding code, the callback function of the **then** method receives the success result of the Promise object as a parameter and outputs it to the console. If the Promise object enters the rejected state, the callback function of the **catch** method receives the error object as a parameter and outputs it to the console.
## Async/Await
Async/Await is a Promise syntax sugar used to process asynchronous operations, making it easier to read and write asynchronous code. The async keyword is used to declare an asynchronous function, and the await keyword is used to wait for Promise parsing (fulfilled or rejected). In this way, the asynchronous operation logic is coded as a synchronous operation.
The **async** function returns a Promise object to represent an asynchronous operation. Inside the **async** function, you can use the await keyword to wait for the parsing of the Promise object and return its parsed value. If an **async** function throws an exception, the Promise object returned by the function is rejected, and the exception information is passed to the **onRejected()** method of the Promise object.
The code snippet below uses async/await to simulate an asynchronous operation that returns a string three seconds later.
```js
async function myAsyncFunction() {
const result = await new Promise((resolve) => {
setTimeout(() => {
resolve('Hello, world!');
}, 3000);
});
console.info(String(result)); // Output: Hello, world!
}
myAsyncFunction();
```
In the preceding code, the await keyword is used to wait for the parsing of the Promise object and store its parsed value in the **result** variable.
Note that the entire operation must be packaged in the **async** function because the code needs to wait for the asynchronous operation to complete. In addition to **await**, you can use the try/catch block to capture exceptions in asynchronous operations.
```js
async function myAsyncFunction() {
try {
const result = await new Promise((resolve) => {
resolve('Hello, world!');
});
} catch (e) {
console.error(`Get exception: ${e}`);
}
}
myAsyncFunction();
```
# Concurrency Overview
Concurrency refers to the capability of processing multiple tasks in the same period. To improve the response speed and frame rate of applications and prevent time-consuming tasks from blocking the main thread, OpenHarmony provides two policies: asynchronous concurrency and multithread concurrency.
- Asynchronous concurrency means that an action in asynchronous code is suspended and will continue later. Only one segment of code is being executed at a time.
- Multithread concurrency allows multiple segments of code to be executed at a time. When the main thread continues to respond to user operations and update the UI, time-consuming operations are performed in the background to avoid application freezing.
Concurrency is used in a variety of scenarios, including a single I/O task, CPU intensive task, I/O intensive task, synchronous task, and the like. You can select a concurrency policy based on your scenario.
ArkTS provides the following mechanisms to support asynchronous concurrency and multithread concurrency:
- Promise and async/await: provide asynchronous concurrency and apply to the development of a single I/O task. For details, see [Asynchronous Concurrency Overview](async-concurrency-overview.md).
- **TaskPool** and **Worker**: provide multithread concurrency and apply to the development of CPU intensive tasks, I/O intensive tasks, and synchronous tasks. For details, see [Multithread Concurrency Overview](multi-thread-concurrency-overview.md).
# Overview of Containers
The container classes provide a set of methods to process elements of various data types stored in containers.
The container classes are implemented in a way similar to static languages. By restricting storage locations and attributes, they remove redundant logic for each type of data while providing the complete functionalities, ensuring efficient data access and improving application performance.
Currently, OpenHarmony provides 14 types of linear and non-linear containers, each of which has its own features and uses cases. For details, see [Linear Containers](linear-container.md) and [Nonlinear Containers](nonlinear-container.md).
# CPU Intensive Task Development
CPU intensive tasks occupy lots of system computing resources for a long period of time, during which other events of the thread are blocked. Example CPU intensive tasks are image processing, video encoding, and data analysis.
OpenHarmony uses multithread concurrency to process CPU intensive tasks. This improves CPU utilization and application response speed.
If a task does not need to occupy a background thread for a long time (3 minutes), you are advised to use **TaskPool**. Otherwise, use **Worker**. The following uses histogram processing and a time-consuming model prediction task in the background as examples.
## Using TaskPool to Process Histograms
1. Implement the logic of image processing.
2. Segment the data, and initiate associated task scheduling through task groups.
Create a [task group](../reference/apis/js-apis-taskpool.md#taskgroup10), call [addTask()](../reference/apis/js-apis-taskpool.md#addtask10) to add tasks, call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute10) to execute the tasks in the task group, and set [a high priority](../reference/apis/js-apis-taskpool.md#priority) for the task group. After all the tasks in the task group are complete, the histogram processing result is returned simultaneously.
3. Summarize and process the result arrays.
```ts
import taskpool from '@ohos.taskpool';
@Concurrent
function imageProcessing(dataSlice: ArrayBuffer) {
// Step 1: Perform specific image processing operations and other time-consuming operations.
return dataSlice;
}
function histogramStatistic(pixelBuffer: ArrayBuffer) {
// Step 2: Perform concurrent scheduling for data in three segments.
let number = pixelBuffer.byteLength / 3;
let buffer1 = pixelBuffer.slice(0, number);
let buffer2 = pixelBuffer.slice(number, number * 2);
let buffer3 = pixelBuffer.slice(number * 2);
let group = new taskpool.TaskGroup();
group.addTask(imageProcessing, buffer1);
group.addTask(imageProcessing, buffer2);
group.addTask(imageProcessing, buffer3);
taskpool.execute(group, taskpool.Priority.HIGH).then((ret: ArrayBuffer[]) => {
// Step 3: Summarize and process the result arrays.
})
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let data: ArrayBuffer;
histogramStatistic(data);
})
}
.width('100%')
}
.height('100%')
}
}
```
## Using Worker for Time-Consuming Data Analysis
The following uses the training of a region-specific house price prediction model as an example. This model can be used to predict house prices in the region based on the house area and number of rooms. The model needs to run for a long time, and the prediction will use the previous running result. Due to these considerations, **Worker** is used for the development.
1. Add the worker creation template provided on DevEco Studio to your project, and name it **MyWorker**.
![newWorker](figures/newWorker.png)
2. In the main thread, call [constructor()](../reference/apis/js-apis-worker.md#constructor9) of **ThreadWorker** to create a **Worker** object. The calling thread is the host thread.
```js
import worker from '@ohos.worker';
const workerInstance = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts');
```
3. In the host thread, call [onmessage()](../reference/apis/js-apis-worker.md#onmessage9) to receive messages from the worker thread, and call [postMessage()](../reference/apis/js-apis-worker.md#postmessage9) to send messages to the worker thread.
For example, the host thread sends training and prediction messages to the worker thread, and receives messages sent back by the worker thread.
```js
// Receive the result of the worker thread.
workerInstance.onmessage = function(e) {
// data carries the information sent by the main thread.
let data = e.data;
console.info('MyWorker.ts onmessage');
// Perform time-consuming operations in the worker thread.
}
workerInstance.onerror = function (d) {
// Receive error information of the worker thread.
}
// Send a training message to the worker thread.
workerInstance.postMessage({ 'type': 0 });
// Send a prediction message to the worker thread.
workerInstance.postMessage({ 'type': 1, 'value': [90, 5] });
```
4. Bind the **Worker** object in the **MyWorker.ts** file. The calling thread is the worker thread.
```js
import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
```
5. In the worker thread, call [onmessage()](../reference/apis/js-apis-worker.md#onmessage9-1) to receive messages sent by the host thread, and call [postMessage()](../reference/apis/js-apis-worker.md#postmessage9-2) to send messages to the host thread.
For example, the prediction model and its training process are defined in the worker thread, and messages are exchanged with the main thread.
```js
import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker';
let workerPort: ThreadWorkerGlobalScope = worker.workerPort;
// Define the training model and result.
let result;
// Define the prediction function.
function predict(x) {
return result[x];
}
// Define the optimizer training process.
function optimize() {
result = {};
}
// onmessage logic of the worker thread.
workerPort.onmessage = function (e: MessageEvents) {
let data = e.data
// Perform operations based on the type of data to transmit.
switch (data.type) {
case 0:
// Perform training.
optimize();
// Send a training success message to the main thread after training is complete.
workerPort.postMessage({ type: 'message', value: 'train success.' });
break;
case 1:
// Execute the prediction.
const output = predict(data.value);
// Send the prediction result to the main thread.
workerPort.postMessage({ type: 'predict', value: output });
break;
default:
workerPort.postMessage({ type: 'message', value: 'send message is invalid' });
break;
}
}
```
6. After the task is completed in the worker thread, destroy the worker thread. The worker thread can be destroyed by itself or the host thread. Then, call [onexit()](../reference/apis/js-apis-worker.md#onexit9) in the host thread to define the processing logic after the worker thread is destroyed.
```js
// After the worker thread is destroyed, execute the onexit() callback.
workerInstance.onexit = function() {
console.info("main thread terminate");
}
```
Method 1: In the host thread, call [terminate()](../reference/apis/js-apis-worker.md#terminate9) to destroy the worker thread and stop the worker thread from receiving messages.
```js
// Destroy the worker thread.
workerInstance.terminate();
```
Method 2: In the worker thread, call [close()](../reference/apis/js-apis-worker.md#close9) to destroy the worker thread and stop the worker thread from receiving messages.
```js
// Destroy the worker thread.
workerPort.close();
```
# I/O Intensive Task Development
Asynchronous concurrency can solve the problem of a single blocking I/O operation. In the case of I/O intensive tasks, the execution of other tasks in the thread is still blocked. To resolve this issue, multithread concurrency is introduced.
The performance focus of I/O intensive tasks is not the CPU processing capability, but the speed and efficiency of I/O operations, since such a task usually requires frequent operations such as disk read/write and network communication. The following uses frequent read/write operations on a system file to simulate concurrency processing of I/O intensive tasks.
1. Define a concurrency function that internally calls I/O capabilities intensively.
```ts
import fs from '@ohos.file.fs';
// Define a concurrency function that internally calls I/O capabilities intensively.
@Concurrent
async function concurrentTest(fileList: string[]) {
// Implement file writing.
async function write(data, filePath) {
let file = await fs.open(filePath, fs.OpenMode.READ_WRITE);
await fs.write(file.fd, data);
fs.close(file);
}
// Write the file cyclically.
for (let i = 0; i < fileList.length; i++) {
write('Hello World!', fileList[i]).then(() => {
console.info(`Succeeded in writing the file. FileList: ${fileList[i]}`);
}).catch((err) => {
console.error(`Failed to write the file. Code is ${err.code}, message is ${err.message}`)
return false;
})
}
return true;
}
```
2. Use **TaskPool** to execute the concurrency function that contains the intensive I/O operations. Specifically, call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute) to execute the tasks and process the scheduling result in a callback. For details about how to obtain **filePath1** and **filePath2** in the example, see [Obtaining Application File Paths](../application-models/application-context-stage.md#obtaining-application-file-paths).
```ts
import taskpool from '@ohos.taskpool';
let filePath1 =...; // Application file path
let filePath2 = ...;
// Use TaskPool to execute the concurrency function that contains the intensive I/O operations.
// In the case of a large array, the distribution of I/O intensive tasks also preempts the main thread. Therefore, multiple threads are required.
taskpool.execute(concurrentTest, [filePath1, filePath2]).then((ret) => {
// Process the scheduling result.
console.info(`The result: ${ret}`);
})
```
# Linear Containers
Linear containers implement a data structure that enables sequential access. The bottom layer of linear containers is implemented through arrays. OpenHarmony provides the following linear containers: **ArrayList**, **Vector**, **List**, **LinkedList**, **Deque**, **Queue**, and **Stack**.
Fully considering the data access speed, linear containers support Create, Read, Update, and Delete (CRUD) through a bytecode instruction at runtime.
## ArrayList
[ArrayList](../reference/apis/js-apis-arraylist.md) is a dynamic array that can be used to construct a global array object. You are advised to use **ArrayList** when elements in a container need to be frequently read.
**ArrayList** uses generics and must be stored in a contiguous memory space. Its initial capacity is 10, and it increases capacity 1.5-fold in each dynamic expansion.
**ArrayList** provides the following CRUD APIs.
| Operation| Description|
| --------- | ------- |
| Adding elements| Use **add(element: T)** to add an element at the end of this container.|
| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).|
| Accessing elements| Use **arr\[index]** to obtain the value at a given position (specified by **index**).|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, arrlist?: ArrayList&lt;T&gt;) =&gt; void, thisArg?: Object): void** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **arr\[index] = xxx** to change the value at a given position (specified by **index**).|
| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.|
| Deleting elements| Use **removeByRange(fromIndex: number, toIndex: number)** to remove all of the elements within a range.|
## Vector
[Vector](../reference/apis/js-apis-vector.md) is a continuous storage structure that can be used to construct a global array object. **Vector** uses generics and must be stored in a contiguous memory space. Its initial capacity is 10, and it has capacity doubled in each dynamic expansion.
Both **Vector** and [ArrayList](../reference/apis/js-apis-arraylist.md) are implemented based on arrays, but **Vector** provides more interfaces for operating the arrays. In addition to operator access, **Vector** provides the getter and setter to provide a more complete verification and error tolerance mechanism.
The APIs provided by **Vector** are deprecated since API version 9. You are advised to use [ArrayList](../reference/apis/js-apis-arraylist.md).
**Vector** provides the following CRUD APIs.
| Operation| Description|
| --------- | ------- |
| Adding elements| Use **add(element: T)** to add an element at the end of this container.|
| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).|
| Accessing elements| Use **vec\[index]** to obtain the value at a given position (specified by **index**).|
| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).|
| Accessing elements| Use **getLastElement()** to obtain the last element in this container.|
| Accessing elements| Use **getIndexOf(element: T)** to obtain the index of the first occurrence of the specified element.|
| Accessing elements| Use **getLastIndexOf(element: T)** to obtain the index of the last occurrence of the specified element.|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, Vector?: Vector&lt;T&gt;) =&gt; void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **vec\[index]=xxx** to change the value at a given position (specified by **index**).|
| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.|
| Modifying elements| Use **setLength(newSize: number)** to set the size of this container.|
| Deleting elements| Use **removeByIndex(index: number)** to remove the value at a given position (specified by **index**).|
| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.|
| Deleting elements| Use **removeByRange(fromIndex: number, toIndex: number)** to remove all of the elements within a range.|
## List
[List](../reference/apis/js-apis-list.md) can be used to construct a singly linked list, which supports access only through the head node to the tail node. **List** uses generics and can be stored in a non-contiguous memory space.
Unlike [LinkedList](../reference/apis/js-apis-linkedlist.md), which is a doubly linked list, **List** is a singly linked list that does not support insertion or removal at both ends.
You are advised to use **List** for frequent insertion and removal operations.
**List** provides the following CRUD APIs.
| Operation| Description|
| --------- | ------ |
| Adding elements| Use **add(element: T)** to add an element at the end of this container.|
| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).|
| Accessing elements| Use **list\[index]** to obtain the value at a given position (specified by **index**).|
| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).|
| Accessing elements| Use **getFirst()** to obtain the first element in this container.|
| Accessing elements| Use **getLast()** to obtain the last element in this container.|
| Accessing elements| Use **getIndexOf(element: T)** to obtain the index of the first occurrence of the specified element.|
| Accessing elements| Use **getLastIndexOf(element: T)** to obtain the index of the last occurrence of the specified element.|
| Accessing elements| Use **forEach(callbackfn: (value: T, index?: number, list?: List&lt;T&gt;)=&gt; void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **list\[index] = xxx** to change the value at a given position (specified by **index**).|
| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.|
| Modifying elements| Use **replaceAllElements(callbackFn:(value: T,index?: number,list?: List&lt;T&gt;)=&gt;T,thisArg?: Object)** to replace all elements in this container with new elements.|
| Deleting elements| Use **removeByIndex(index: number)** to remove the value at a given position (specified by **index**).|
| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.|
## LinkedList
[LinkedList](../reference/apis/js-apis-linkedlist.md) can be used to construct a doubly linked list, which can be traversed at both ends. **LinkedList** uses generics and can be stored in a non-contiguous memory space.
Unlike [List](../reference/apis/js-apis-list.md), which is a singly linked list, **LinkedList** is a doubly linked list that supports insertion and removal at both ends.
**LinkedList** is more efficient in data insertion than [ArrayList](../reference/apis/js-apis-arraylist.md), but less efficient in data access.
You are advised to use **LinkedList** for frequent insertion and removal operations.
**LinkedList** provides the following CRUD APIs.
| Operation| Description|
| ---------- | ------ |
| Adding elements| Use **add(element: T)** to add an element at the end of this container.|
| Adding elements| Use **insert(index: number, element: T)** to insert an element at a given position (specified by **index**).|
| Accessing elements| Use **list\[index]** to obtain the value at a given position (specified by **index**).|
| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).|
| Accessing elements| Use **getFirst()** to obtain the first element in this container.|
| Accessing elements| Use **getLast()** to obtain the last element in this container.|
| Accessing elements| Use **getIndexOf(element: T)** to obtain the index of the first occurrence of the specified element.|
| Accessing elements| Use **getLastIndexOf(element: T)** to obtain the index of the last occurrence of the specified element.|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, list?: LinkedList&lt;T&gt;) =&gt; void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **list\[index]=xxx** to change the value at a given position (specified by **index**).|
| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.|
| Deleting elements| Use **removeByIndex(index: number)** to remove the value at a given position (specified by **index**).|
| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.|
## Deque
[Deque](../reference/apis/js-apis-deque.md) can be used to construct a double-ended queue (deque) that follows the principles of First In First Out (FIFO) and Last In First Out (LIFO). It allows insertion and removal of elements at both the ends.
**Deque** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it has capacity doubled in each dynamic expansion. The bottom layer of **Deque** is implemented by cyclic queues, delivering a high efficiency in enqueuing and dequeuing.
[Queue](../reference/apis/js-apis-queue.md) follows the principle of FIFO only and allows element removal at the front and insertion at the rear.
[Vector](../reference/apis/js-apis-vector.md) supports insertion and deletion of elements in between, as well as at both the ends. When compared with **Vector**, **Deque** is more efficient in inserting and removing header elements, but less efficient in accessing elements.
You are advised to use **Deque** when you need to frequently insert or remove elements at both the ends of a container.
**Deque** provides the following CRUD APIs.
| Operation| Description|
| ---------- | ------ |
| Adding elements| Use **insertFront(element: T)** to insert an element at the front of this container.|
| Adding elements| Use **insertEnd(element: T)** to insert an element at the end of this container.|
| Accessing elements| Use **getFirst()** to obtain the value of the first element in this container, without removing it from the container.|
| Accessing elements| Use **getLast()** to obtain the value of the last element in this container, without removing it from the container.|
| Accessing elements| Use **popFirst()** to obtain the value of the first element in this container and remove it from the container.|
| Accessing elements| Use **popLast()** to obtain the value of the last element in this container and remove it from the container.|
| Accessing elements| Use **forEach(callbackFn:(value: T, index?: number, deque?: Deque&lt;T&gt;) =&gt; void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, deque?: Deque&lt;T&gt;)=&gt; void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **popFirst()** to remove the first element from this container.|
| Deleting elements| Use **popLast()** to remove the last element from this container.|
## Queue
[Queue](../reference/apis/js-apis-queue.md) can be used to construct a queue that follows the FIFO principle.
**Queue** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it has capacity doubled in each dynamic expansion.
The bottom layer of **Queue** is implemented by cyclic queues, delivering a high efficiency in enqueuing and dequeuing.
Unlike [Deque](../reference/apis/js-apis-deque.md), which supports insertion and removal at both the ends, **Queue** supports insertion at one end and removal at the other end.
You are advised to use **Queue** in FIFO scenarios.
**Queue** provides the following CRUD APIs.
| Operation| Description|
| ---------- | ------ |
| Adding elements| Use **add(element: T)** to add an element at the end of this container.|
| Accessing elements| Use **getFirst()** to obtain the value of the first element in this container, without removing it from the container.|
| Accessing elements| Use **pop()** to obtain the value of the first element in this container and remove it from the container.|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, queue?: Queue&lt;T&gt;) =&gt; void,thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, queue?: Queue&lt;T&gt;) =&gt; void,thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **pop()** to remove the first element from this container.|
## Stack
[Stack](../reference/apis/js-apis-stack.md) can be used to construct a stack that follows the Last Out First In (LOFI) principle.
**Stack** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it increases capacity 1.5-fold in each dynamic expansion. The bottom layer of **Stack** is implemented based on arrays. It supports data insertion and removal at one end.
Unlike [Queue](../reference/apis/js-apis-queue.md), which is implemented based on the queue data structure and supports insertion at one end and removal at the other end, **Stack** supports insertion and removal at the same end.
You are advised to use **Stack** in LOFI scenarios.
**Stack** provides the following CRUD APIs.
| Operation| Description|
| ---------- | ------ |
| Adding elements| Use **push(item: T)** to add an element at the top of this container.|
| Accessing elements| Use **peek()** to obtain the value of the top element in this container, without removing it from the container.|
| Accessing elements| Use **pop()** to obtain the value of the top element in this container and remove it from the container.|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, stack?: Stack&lt;T&gt;) =&gt; void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Accessing elements| Use **locate(element: T)** to obtain the index of the first occurrence of the specified element.|
| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, stack?: Stack&lt;T&gt;) =&gt; void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **pop()** to remove the top element from this container.|
## Use of Linear Containers
Refer to the code snippet below to add, access, and modify elements in **ArrayList**, **Vector**, **Deque**, **Stack**, and **List**.
```js
// ArrayList
import ArrayList from '@ohos.util.ArrayList'; // Import the ArrayList module.
let arrayList = new ArrayList();
arrayList.add('a');
arrayList.add(1); // Add an element.
console.info(`result: ${arrayList[0]}`); // Access an element.
arrayList[0] = 'one'; // Modify an element.
console.info(`result: ${arrayList[0]}`);
// Vector
import Vector from '@ohos.util.Vector'; // Import the Vector module.
let vector = new Vector();
vector.add('a');
let b1 = [1, 2, 3];
vector.add(b1);
vector.add(false); // Add an element.
console.info(`result: ${vector[0]}`); // Access an element.
console.info(`result: ${vector.getFirstElement()}`); // Access an element.
// Deque
import Deque from '@ohos.util.Deque'; // Import the Deque module.
let deque = new Deque;
deque.insertFront('a');
deque.insertFront(1); // Add an element.
console.info(`result: ${deque[0]}`); // Access an element.
deque[0] = 'one'; // Modify an element.
console.info(`result: ${deque[0]}`);
// Stack
import Stack from '@ohos.util.Stack'; // Import the Stack module.
let stack = new Stack();
stack.push('a');
stack.push(1); // Add an element.
console.info(`result: ${stack[0]}`); // Access an element.
stack.pop(); // Remove an element.
console.info(`result: ${stack.length}`);
// List
import List from '@ohos.util.List'; // Import the List module.
let list = new List;
list.add('a');
list.add(1);
let b2 = [1, 2, 3];
list.add(b2); // Add an element.
console.info(`result: ${list[0]}`); // Access an element.
console.info(`result: ${list.get(0)}`); // Access an element.
```
# Multithread Concurrency Overview
## Overview
Concurrency models are used to implement concurrent tasks in different scenarios. Common concurrency models are classified into shared memory models and message passing models.
A typical message passing model is actor. It provides a relatively high degree of concurrency while eliminating a series of complex and occasional issues caused by locks. For these reasons, ArkTS chooses the actor model.
Due to the memory isolation feature of the actor model, cross-thread serialization is required.
## Data Transfer Objects
Data objects that can be transferred are classified into the following types: [common objects](#common-objects), [transferable objects](#transferable-objects), [shared objects](#shared-objects), and [native binding objects](#native-binding-objects).
### Common Objects
The structured clone algorithm is used for serialization of common objects. This algorithm recursively transfers an object by clone. It supports more object types than other serialization algorithms.
The following object types are supported: basic types except Symbol, Date, String, RegExp, Array, Map, Set, Object (simple objects only, for example, objects created using **{}** or **new Object**), ArrayBuffer, and typedArray. (Note that only attributes can be transferred for common objects. Prototypes and methods cannot be transferred.)
### Transferable Objects
Transferable objects are serialized through address transfer. It transfers the ownership of an object of the ArrayBuffer type, rather than the content in it. After the ownership is transferred, the object becomes unavailable in the sender and can be used only in the receiver.
```js
// Define a transferable object.
let buffer = new ArrayBuffer(100);
```
### Shared Objects
A shared object is of the **SharedArrayBuffer** type, has a fixed length, and can store any type of data including numbers and strings.
An object of the SharedArrayBuffer type can be transferred between multiple threads. The objects before and after the transfer point to the same memory block, achieving memory sharing.
If multiple operations are simultaneously performed to modify data stored in an object of the SharedArrayBuffer type, you must use atomics to ensure data synchronization. Atomics ensure that the current operation is complete before the next operation starts.
```js
// Define a shared object, which uses atomics to ensure data synchronization.
let sharedBuffer = new SharedArrayBuffer(1024);
```
### Native Binding Objects
Native binding objects are provided by the system. They are bound to underlying system services and enables direct access to these services.
Currently, native bound objects that support serialization include [Context](../application-models/application-context-stage.md) and [RemoteObject](../reference/apis/js-apis-rpc.md#remoteobject).
The **Context** object provides the context information about an application component. It provides a way to access system services and resources so that the application component can interact with the system. For details about how to obtain context information, see [Context (Stage Model)](../application-models/application-context-stage.md).
The **RemoteObject** object implements remote communication. It transfers the reference of an object between processes so that these processes can share the status and methods of the object. The service provider must inherit this class. For details about how to create a **RemoteObject** object, see [RemoteObject](../reference/apis/js-apis-rpc.md#remoteobject).
## TaskPool and Worker
ArkTS provides two multithread concurrency capabilities: **TaskPool** and **Worker**, which differ in their implementation features and use cases. For details, see [Comparison Between TaskPool and Worker](taskpool-vs-worker.md).
# Nonlinear Containers
Nonlinear containers implement a data structure that enables quick search. The bottom layer of nonlinear containers is implemented through hash tables or red-black trees. OpenHarmony provides the following nonlinear containers: **HashMap**, **HashSet**, **TreeMap**, **TreeSet**, **LightWeightMap**, **LightWeightSet**, and **PlainArray**. The types of **key** and **value** in nonlinear containers must meet the ECMA standard.
## HashMap
[HashMap](../reference/apis/js-apis-hashmap.md) is used to store a set of associated key-value (KV) pairs. In a hash map, each key is unique and corresponds to a value.
**HashMap** uses generics. In a hash map, a key is located based on its hash code. The initial capacity of a hash map is 16, and it has capacity doubled in each dynamic expansion. The bottom layer of **HashMap** is implemented based on a hash table. It uses chaining to avoid collisions in hash tables.
**HashMap** is faster in accessing data than [TreeMap](../reference/apis/js-apis-treemap.md), because the former accesses the keys based on the hash codes, whereas the latter stores and accesses the keys in sorted order.
[HashSet](../reference/apis/js-apis-hashset.md) is implemented based on **HashMap**. The input parameter of **HashMap** consists of **key** and **value**. In **HashSet**, only the **value** object is processed.
You are advised to use **HashMap** when you need to quickly access, remove, and insert KV pairs.
**HashMap** provides the following Create, Read, Update, and Delete (CRUD) APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.|
| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.|
| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: HashMap<K, V>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;[K,V]&gt;** for data access.|
| Modifying elements| Use **replace(key: K, newValue: V)** to change the value of the specified key.|
| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: HashMap<K, V>) => void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.|
| Deleting elements| Use **clear()** to clear this container.|
## HashSet
[HashSet](../reference/apis/js-apis-hashset.md) is used to store a set of values, each of which is unique in a hash set.
**HashSet** uses generics. In a hash set, a value is located based on its hash code. The initial capacity of a hash set is 16, and it has capacity doubled in each dynamic expansion. The type of **value** must comply with the ECMA standard. The bottom layer of **HashSet** is implemented based on a hash table. It uses chaining to avoid collisions in hash tables.
**HashSet** is implemented based on [HashMap](../reference/apis/js-apis-hashmap.md). In **HashSet**, only the **value** object is processed.
Unlike [TreeSet](../reference/apis/js-apis-treeset.md), which stores and accesses data in sorted order, **HashSet** stores data in a random order. This means that **HashSet** may use a different order when storing and accessing elements. Both of them allows only unique elements. However, null values are allowed in **HashSet**, but not allowed in **TreeSet**.
You are advised to use **HashSet** when you need a set that has only unique elements or need to deduplicate a set.
**HashSet** provides the following CRUD APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **add(value: T)** to add a value to this container.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: HashSet\<T>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: HashSet\<T>) => void, thisArg?: Object)** to change a value in this container.|
| Deleting elements| Use **remove(value: T)** to remove a value.|
| Deleting elements| Use **clear()** to clear this container.|
## TreeMap
[TreeMap](../reference/apis/js-apis-treemap.md) is used to store a set of associated KV pairs. In a tree map, each key is unique and corresponds to a value.
**TreeMap** uses generics, and the keys in a tree map are ordered. The bottom layer of **TreeMap** is a binary tree, which supports quick search of KV pairs through the children (left child and right child) of the tree. The type of **key** must comply with the ECMA standard. Keys in a tree map are stored in order. The bottom layer of **TreeMap** is implemented based on the red-black tree and supports quick insertion and removal.
[HashMap](../reference/apis/js-apis-hashmap.md) is faster in accessing data than **TreeMap**, because the former accesses the keys based on the hash codes, whereas the latter stores and accesses the keys in sorted order.
You are advised to use **TreeMap** when you need to store KV pairs in sorted order.
**TreeMap** provides the following CRUD APIs.
| Operation| Description|
| ------- | ------ |
| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.|
| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.|
| Accessing elements| Use **getFirstKey()** to obtain the first key in this container.|
| Accessing elements| Use **getLastKey()** to obtain the last key in this container.|
| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: TreeMap<K, V>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator\<[K,V]>** for data access.|
| Modifying elements| Use **replace(key: K, newValue: V)** to change the value of the specified key.|
| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: TreeMap<K, V>) => void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.|
| Deleting elements| Use **clear()** to clear this container.|
## TreeSet
[TreeSet](../reference/apis/js-apis-treeset.md) is used to store a set of values, each of which is unique in a tree set.
**TreeSet** uses generics, and the values in a tree set are ordered. The bottom layer of **TreeSet** is a binary tree, which supports quick search of a value through the children (left child and right child) of the tree. The type of **value** must meet the ECMA standard. Values in a tree set are stored in order. The bottom layer of **TreeSet** is implemented based on the red-black tree and supports quick insertion and removal.
**TreeSet** is implemented based on [TreeMap](../reference/apis/js-apis-treemap.md). In **TreeSet**, only **value** objects are processed. **TreeSet** can be used to store values, each of which must be unique.
[HashSet](../reference/apis/js-apis-hashset.md) stores data in a random order, whereas **TreeSet** stores data in sorted order. Both of them allows only unique elements. However, null values are allowed in **HashSet**, but not allowed in **TreeSet**.
You are advised to use **TreeSet** when you need to store data in sorted order.
**TreeSet** provides the following CRUD APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **add(value: T)** to add a value to this container.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **getFirstValue()** to obtain the first value in this container.|
| Accessing elements| Use **getLastValue()** to obtain the last value in this container.|
| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: TreeSet\<T>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: TreeSet\<T>) => void, thisArg?: Object)** to change a value in this container.|
| Deleting elements| Use **remove(value: T)** to remove a value.|
| Deleting elements| Use **clear()** to clear this container.|
## LightWeightMap
[LightWeightMap](../reference/apis/js-apis-lightweightmap.md) is used to store a set of associated KV pairs. In a lightweight map, each key is unique and corresponds to a value. **LightWeightMap** uses generics and a more lightweight structure. It uses the hash code to uniquely identify a key at the bottom layer. It uses linear probing to avoid collisions. In a lightweight map, a key is located by using the hash code and binary search algorithm. The hash code is stored in an array and mapped to a key and its value in another array. The type of **key** must comply with the ECMA standard.
The default initial capacity of a lightweight map is 8, and it has capacity doubled in each expansion.
Compared with [HashMap](../reference/apis/js-apis-hashmap.md), which can also store KV pairs, **LightWeightMap** occupies less memory.
You are advised to use **LightWeightMap** when you need to store and access **KV pairs**.
**LightWeightMap** provides the following CRUD APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.|
| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.|
| Accessing elements| Use **getIndexOfKey(key: K)** to obtain the index of the specified key.|
| Accessing elements| Use **getIndexOfValue(value: V)** to obtain the index of the first occurrence of the specified value.|
| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **getKeyAt(index: number)** to obtain the key of an element at a given position (specified by **index**).|
| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).|
| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: LightWeightMap<K, V>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;[K,V]&gt;** for data access.|
| Modifying elements| Use **setValueAt(index: number, newValue: V)** to change the value of an element at a given position (specified by **index**).|
| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: LightWeightMap<K, V>) => void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.|
| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).|
| Deleting elements| Use **clear()** to clear this container.|
## LightWeightSet
[LightWeightSet](../reference/apis/js-apis-lightweightset.md) is used to store a set of values, each of which is unique in a lightweight set.
**LightWeightSet** uses generics and a lightweight structure. Its default initial capacity is 8, and it has capacity doubled in each expansion. In a lightweight set, a value is located by using the hash code and binary search algorithm. The hash code is stored in an array and mapped to a value in another array. The type of **value** must comply with the ECMA standard.
**LightWeightSet** uses the hash code to uniquely identify a value at the bottom layer. It uses linear probing to avoid collisions and adopts the binary search algorithm.
Compared with [HashSet](../reference/apis/js-apis-hashset.md), which can also store values, **LightWeightSet** occupies less memory.
You are advised to use **LightWeightSet** when you need a set that has only unique elements or need to deduplicate a set.
**LightWeightSet** provides the following CRUD APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **add(obj: T)** to add a value to this container.|
| Accessing elements| Use **getIndexOf(key: T)** to obtain the index of a key.|
| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.|
| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.|
| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).|
| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: LightWeightSet\<T>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;T&gt;** for data access.|
| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: LightWeightSet\<T>) => void, thisArg?: Object)** to change a value in this container.|
| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.|
| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).|
| Deleting elements| Use **clear()** to clear this container.|
## PlainArray
[PlainArray](../reference/apis/js-apis-plainarray.md) is used to store a set of associated KV pairs. In a plain array, each key is unique, corresponds to a value, and is of the number type. **PlainArray** uses generics and a more lightweight structure. In a plain array, a key is located by using the binary search algorithm and is mapped to a value in another array.
The default initial capacity of a plain array is 16, and it has capacity doubled in each expansion.
Both **PlainArray** and [LightWeightMap](../reference/apis/js-apis-lightweightmap.md) are used to store KV pairs in the lightweight structure. However, the key type of **PlainArray** can only be **number**.
You are advised to use PlainArray when you need to store KV pairs whose keys are of the number type.
**PlainArray** provides the following CRUD APIs.
| Operation| Description|
| -------- | ------ |
| Adding elements| Use **add(key: number,value: T)** to add an element (a KV pair) to this container.|
| Accessing elements| Use **get(key: number)** to obtain the value of the specified key.|
| Accessing elements| Use **getIndexOfKey(key: number)** to obtain the index of the specified key.|
| Accessing elements| Use **getIndexOfValue(value: T)** to obtain the index of the specified value.|
| Accessing elements| Use **getKeyAt(index: number)** to obtain the key of an element at a given position (specified by **index**).|
| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).|
| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, PlainArray?: PlainArray\<T>) => void, thisArg?: Object)** to traverse the elements in this container.|
| Accessing elements| Use **\[Symbol.iterator]():IterableIterator&lt;[number, T]&gt;** for data access.|
| Modifying elements| Use **setValueAt(index:number, value: T)** to change the value of an element at a given position (specified by **index**).|
| Modifying elements| Use **forEach(callbackFn: (value: T, index?: number, PlainArray?: PlainArray\<T>) => void, thisArg?: Object)** to modify an element in this container.|
| Deleting elements| Use **remove(key: number)** to remove an element with the specified key.|
| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).|
| Deleting elements| Use **removeRangeFrom(index: number, size: number)** to remove elements in a specified range.|
| Deleting elements| Use **clear()** to clear this container.|
## Use of Nonlinear Containers
Refer to the code snippet below to add, access, and modify elements in **HashMap**, **TreeMap**, **LightWeightMap**, **Stack**, and **PlainArray**.
```js
// HashMap
import HashMap from '@ohos.util.HashMap'; // Import the HashMap module.
let hashMap = new HashMap();
hashMap.set('a', 123);
hashMap.set (4, 123);// Add an element.
console.info(`result: ${hashMap.hasKey(4)}`); // Check whether an element is contained.
console.info(`result: ${hashMap.get('a')}`); // Access an element.
// TreeMap
import TreeMap from '@ohos.util.TreeMap'; // Import the TreeMap module.
let treeMap = new TreeMap();
treeMap.set('a', 123);
treeMap.set('6', 356); // Add an element.
console.info(`result: ${treeMap.get('a')}`); // Access an element.
console.info(`result: ${treeMap.getFirstKey()}`); // Access the first element.
console.info(`result: ${treeMap.getLastKey()}`); // Access the last element.
// LightWeightMap
import LightWeightMap from '@ohos.util.LightWeightMap'; // Import the LightWeightMap module.
let lightWeightMap = new LightWeightMap();
lightWeightMap.set('x', 123);
lightWeightMap.set('8', 356); // Add an element.
console.info(`result: ${lightWeightMap.get('a')}`); // Access an element.
console.info(`result: ${lightWeightMap.get('x')}`); // Access an element.
console.info(`result: ${lightWeightMap.getIndexOfKey('8')}`); // Access an element.
// PlainArray
import PlainArray from '@ohos.util.PlainArray' // Import the PlainArray module.
let plainArray = new PlainArray();
plainArray.add(1, 'sdd');
plainArray.add(2,'sff'); // Add an element.
console.info(`result: ${plainArray.get(1)}`); // Access an element.
console.info(`result: ${plainArray.getKeyAt(1)}`); // Access an element.
```
# Single I/O Task Development
Asynchronous concurrency provided by Promise and async/await is applicable to the development of a single I/O task. The following uses the asynchronous concurrency capability to write a file as an example.
1. Implement the logic of a single I/O task.
```js
import fs from '@ohos.file.fs';
async function write(data: string, filePath: string) {
let file = await fs.open(filePath, fs.OpenMode.READ_WRITE);
fs.write(file.fd, data).then((writeLen) => {
fs.close(file);
}).catch((err) => {
console.error(`Failed to write data. Code is ${err.code}, message is ${err.message}`);
})
}
```
2. Use the asynchronous capability to invoke the single I/O task. For details about how to obtain **filePath** in the example, see [Obtaining Application File Paths](../application-models/application-context-stage.md#obtaining-application-file-paths).
```js
let filePath = ...; // Application file path
write('Hello World!', filePath).then(() => {
console.info('Succeeded in writing data.');
})
```
# Synchronous Task Development
Synchronous tasks are executed in order among multiple threads. For example, as a synchronization primitive, locks prevent data contention.
To implement synchronous tasks, you must consider the collaboration and synchronization between multiple threads and ensure the correctness of data and the correct execution of programs.
If synchronous tasks are independent of each other, you are advised to use **TaskPool**, since it focuses on single independent tasks. For example, a series of imported static methods or methods implemented in singletons are independent. If synchronous tasks are associated with each other, use **Worker**, for example, methods implemented in class objects (not singleton class objects).
## Using TaskPool to Process Independent Synchronous Tasks
**TaskPool** is recommended for scheduling independent synchronous tasks. Typical synchronous tasks are those using static methods. If a unique handle or class object constructed using a singleton points to multiple tasks and these tasks can be used between different worker threads, you can also use **TaskPool**.
1. Define a concurrency function that internally calls the synchronous methods.
2. Create a task, execute the task through **TaskPool**, and perform operations on the asynchronous result. Create a [task](../reference/apis/js-apis-taskpool.md#task) and call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute-1) to execute the task synchronously.
3. Perform concurrent operations.
Simulate a singleton class that contains synchronous calls.
```ts
// handle.ts code
export default class Handle {
static getInstance() {
// Return a singleton object.
}
static syncGet() {
// Synchronous getter.
return;
}
static syncSet(num: number) {
// Synchronous setter.
return;
}
}
```
Use **TaskPool** to call the related synchronous methods.
```ts
// Index.ets code
import taskpool from '@ohos.taskpool';
import Handle from './Handle'; // Return a static handle.
// Step 1: Define a concurrency function that internally calls the synchronous methods.
@Concurrent
function func(num: number) {
// Call the synchronous wait implemented in a static class object.
Handle.syncSet(num);
// Alternatively, call the synchronous wait implemented in a singleton object.
Handle.getInstance().syncGet();
return true;
}
// Step 2: Create and execute a task.
async function asyncGet() {
// Create a task and pass in the function func.
let task = new taskpool.Task(func, 1);
// Execute the task and obtain the result res.
let res = await taskpool.execute(task);
// Perform operations on the synchronous result.
console.info(String(res));
}
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
// Step 3: Perform concurrent operations.
asyncGet();
})
}
.width('100%')
.height('100%')
}
}
}
```
## Using Worker to Process Associated Synchronous Tasks
Use **Worker** when you want to schedule a series of synchronous tasks using the same handle or depending on the same class object.
1. Create a **Worker** object in the main thread and receive messages from the worker thread.
```js
import worker from '@ohos.worker';
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let w = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts');
w.onmessage = function (d) {
// Receive the result of the worker thread.
}
w.onerror = function (d) {
// Receive error information of the worker thread.
}
// Send a Set message to the worker thread.
w.postMessage({'type': 0, 'data': 'data'})
// Send a Get message to the worker thread.
w.postMessage({'type': 1})
// Destroy the worker thread.
w.terminate()
})
}
.width('100%')
}
.height('100%')
}
}
```
2. Bind the **Worker** object in the worker thread and process the synchronous task logic.
```js
// handle.ts code
export default class Handle {
syncGet() {
return;
}
syncSet(num: number) {
return;
}
}
// Worker.ts code
import worker, { ThreadWorkerGlobalScope, MessageEvents } from '@ohos.worker';
import Handle from './handle.ts' // Return a handle.
var workerPort : ThreadWorkerGlobalScope = worker.workerPort;
// Handle that cannot be transferred. All operations depend on this handle.
var handler = new Handle()
// onmessage() logic of the worker thread.
workerPort.onmessage = function(e : MessageEvents) {
switch (e.data.type) {
case 0:
handler.syncSet(e.data.data);
workerPort.postMessage('success set');
case 1:
handler.syncGet();
workerPort.postMessage('success get');
}
}
```
# Comparison Between TaskPool and Worker
**TaskPool** and **Worker** provide a multithread running environment for applications to process time-consuming computing tasks or resource intensive tasks, preventing these tasks from blocking the main thread. This maximizes system utilization, reduces overall resource consumption, and improves overall system performance.
This topic compares **TaskPool** with **Worker** from two aspects: [implementation features](#implementation-feature-comparison) and [use cases](#use-case-comparison). It also describes their operating mechanisms and precautions.
## Implementation Feature Comparison
**Table 1** Comparison between TaskPool and Worker in implementation features
| Item| TaskPool | Worker |
| -------- | -------- | -------- |
| Memory model| Threads are isolated from each other, and memory is not shared.| Threads are isolated from each other, and memory is not shared.|
| Parameter passing mechanism| The structured clone algorithm is used for serialization and deserialization.<br>ArrayBuffer and SharedArrayBuffer are used for parameter passing and sharing.| The structured clone algorithm is used for serialization and deserialization.<br>ArrayBuffer and SharedArrayBuffer are used for parameter passing and sharing.|
| Parameter passing| Parameters are directly passed in, without being encapsulated.| Only one parameter can be carried in a message object. Therefore, you must encapsulate excess parameters.|
| Method invocation| Methods are directly passed in and called.| Messages are passed in the worker thread and the corresponding methods are called.|
| Return value| A value is returned by default after asynchronous calling.| Messages proactively sent must be parsed and assigned by calling **onmessage()**.|
| Lifecycle| The task pool manages its own lifecycle, without considering the load.| You are required to manage the number and lifecycle of worker threads.|
| Maximum number of task pools| The number is automatically managed, rather than being manually configured.| A maximum of eight worker threads are supported.|
| Maximum task execution duration| 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, the time consumed by I/O tasks such as network download and file read/write)| There is no restriction.|
| Task priority setting| Setting the task priority is supported.| Setting the task priority is not supported.|
| Task cancellation| Tasks that have been initiated can be canceled.| Tasks that have been initiated cannot be canceled.|
## Use Case Comparison
Both **TaskPool** and **Worker** support multithread concurrency. **TaskPool** worker threads are bound to the system scheduling priority and support load balancing (automatic scaling). **Worker** threads are manually created and do not support scheduling priority setting. Therefore, **TaskPool** provides better performance than **Worker** and is recommended in most scenarios.
**TaskPool** is oriented to thread-level independent tasks, and tasks running for more than 3 minutes are automatically reclaimed by the system. **Worker** is oriented to threads and supports thread execution for a long time.
Common use cases are as follows:
- Use **Worker** for a task that runs for more than 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, I/O tasks such as network download and file read/write). For example, use **Worker** for a 1-hour prediction algorithm training job in the background.
- Use **Worker** for a series of associated synchronous tasks. For example, use **Worker** for a series of database operations, since the same handle is required.
- Use **TaskPool** for a task for which the priority needs to be set. For example, in the histogram rendering scenario in Gallery, histogram data calculated in the background is used for UI display on the foreground. This requires high-priority processing. In this case, use **TaskPool**.
- Use **TaskPool** for a task that needs to be canceled frequently. For example, in the large image browsing scenario in Gallery, both images on the left and right sides of the current image are cached. When the user slides to the next image, a cache task on one side needs to be canceled. In this case, use **TaskPool**.
- Use **TaskPool** for a large number of tasks or tasks with scattered scheduling points. For example, a large-scale application with multiple modules has multiple time-consuming tasks, and it is inconvenient to use eight worker threads to manage load. In this case, **TaskPool** is recommended.
## TaskPool Operating Mechanism
**Figure 1** TaskPool operating mechanism
![taskpool](figures/taskpool.png)
With **TaskPool**, you can encapsulate tasks in the main thread and throw the tasks to the task queue. The system selects proper worker threads to distribute and execute the tasks, and then returns the result to the main thread. **TaskPool** provides APIs to execute and cancel tasks, and set the task priority. It also minimizes system resource usage through unified thread management, dynamic scheduling, and load balancing algorithms. By default, the system starts a worker thread and increases the thread quantity as the number of tasks increases. The maximum number of worker threads that can be created depends on the number of physical cores of the device. The formula is max(3, Number of physical cores – 1). If no task is distributed for a long period of time, the system reduces the number of worker threads.
## Worker Operating Mechanism
**Figure 2** Worker operating mechanism
![worker](figures/worker.png)
The thread that creates the worker thread is referred to as the host thread (not necessarily the main thread, since a worker thread can also create a worker subthread). A worker thread is also named an actor thread. Each worker thread has an independent instance from the host thread, including the infrastructure, object, and code segment. The worker thread communicates with the host thread by means of message exchange. They use the serialization technique to exchange commands and data.
## Precautions for TaskPool
- A task function must be decorated with **\@Concurrent** and can be used only in .ets files.
- A task function must be a common function or async function, but not a class member function or anonymous function.
- A task function can use imported variables and input parameter variables only in a project created on the stage model. In a project created on the FA model, it can use input parameter variables only.
- A task function in the **TaskPool** worker thread must finish the execution within 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, the duration of I/O tasks such as network download and file read/write). Otherwise, it forcibly exits.
- Input parameter types in a task function must be those supported by serialization. For details, see [Common Objects](multi-thread-concurrency-overview.md#common-objects).
- Parameters of the ArrayBuffer type are transferred in **TaskPool** by default. You can set the transfer list by calling [setTransferList()](../reference/apis/js-apis-taskpool.md#settransferlist10).
- The context objects in different threads are different. Therefore, **TaskPool** worker threads can use only thread-safe libraries, rather than UI-related non-thread-safe libraries.
- A maximum of 16 MB data can be serialized.
## Precautions for Worker
- The rules for passing in the **Worker.ts** path during the worker creation vary in different API versions. For details, see [Precautions for File Paths](#precautions-for-file-paths).
- After a worker thread is created, you must manually manage its lifecycle. A maximum of eight worker threads can run simultaneously. For details, see [Lifecycle Precautions](#lifecycle-precautions).
- Modules of the [ability type](../quick-start/application-package-structure-stage.md) support **Worker**, but modules of the [library type](../quick-start/application-package-structure-stage.md) do not support **Worker**.
- When creating a worker thread, the **Worker.ts** file of another module cannot be used. This means that a worker cannot be called across modules.
- The context objects in different threads are different. Therefore, **Worker** threads can use only thread-safe libraries, rather than UI-related non-thread-safe libraries.
- A maximum of 16 MB data can be serialized.
### Precautions for File Paths
Before calling an API of the **Worker** module, you must create a **Worker** instance. The constructor function varies in different API versions.
```js
// Use the following function in API version 9 and later versions:
const worker1 = new worker.ThreadWorker(scriptURL);
// Use the following function in API version 8 and earlier versions:
const worker1 = new worker.Worker(scriptURL);
```
The **Worker.ts** file path (specified by **scriptURL**) must be passed in the constructor function. By default, the **workers** directory (upper-level directory of the **Worker.ts** file) is at the same level as the **pages** directory.
**Stage Model**
The following is an example of **scriptURL** in the constructor function:
```js
// Method 1
// In the stage model, the workers directory is at the same level as the pages directory in the entry module.
const worker1 = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts', {name:"first worker in Stage model"});
// In the stage model, the workers directory is a child directory of the pages directory in the entry module.
const worker2 = new worker.ThreadWorker('entry/ets/pages/workers/MyWorker.ts');
// Method 2
// In the stage model, the workers directory is at the same level as the pages directory in the entry module, and bundlename is com.example.workerdemo.
const worker3 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/workers/worker');
// In the stage model, the workers directory is a child directory of the pages directory in the entry module, and bundlename is com.example.workerdemo.
const worker4 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/pages/workers/worker');
```
- Based on the directory structure of the stage model project, the field meanings in method 1 are as follows:
- **entry**: value of the **name** attribute under **module** in the **module.json5** file.
- **ets**: directory for storing the ArkTS source code. It is fixed.
- **workers/MyWorker.ts**: path of the worker source file in the **ets** directory.
- Based on the directory structure of the stage model project, the field meanings in method 2 are as follows:
- **\@bundle**: fixed label.
- **bundlename**: bundle name of the current application.
- **entryname**: value of the **name** attribute under **module** in the **module.json5** file.
- **ets**: directory for storing the ArkTS source code. It is fixed.
- **workerdir/workerfile**: path of the worker source file in the **ets** directory.
**FA Model**
The following is an example of **scriptURL** in the constructor function:
```js
// In the FA model, the workers directory is at the same level as the pages directory in the entry module.
const worker1 = new worker.ThreadWorker('workers/worker.js', {name:'first worker in FA model'});
// In the FA model, the workers directory is at the same level as the parent directory of the pages directory in the entry module.
const worker2 = new worker.ThreadWorker('../workers/worker.js');
```
### Lifecycle Precautions
- Creating and terminating worker threads consume performance. Therefore, you are advised to manage available workers and reuse them. The worker threads keep running even when they are idle. Therefore, when a worker thread is not required, call [terminate()](../reference/apis/js-apis-worker.md#terminate9) interface or [parentPort.close()](../reference/apis/js-apis-worker.md#close9) to destroy it. If a worker thread is destroyed or being destroyed, an error is thrown when it is called.
- A maximum of eight worker threads can co-exist.
- In API version 8 and earlier versions, when the number of worker threads exceeds the upper limit, the error "Too many workers, the number of workers exceeds the maximum." is thrown.
- Since API version 9, when the number of worker threads exceeds the upper limit, the error "Worker initialization failure, the number of workers exceeds the maximum." is thrown.
# XML Conversion
Converting XML text into JavaScript objects makes it easier to process and manipulate data. In addition, JavaScript objects are more suitable than XML text for JavaScript applications.
The common library provides the **ConvertXML** class to convert XML text into JavaScript objects. The input is XML strings and conversion options, and the output is a JavaScript object. For details about the conversion options, see the API reference [@ohos.convertxml (XML-to-JavaScript Conversion)](../reference/apis/js-apis-convertxml.md).
## Precautions
To ensure successful XML parsing and conversion, the input XML data must comply with the standard format.
## How to Develop
The following steps walk you through on how to convert an XML file into a JavaScript object to obtain the tag values.
1. Import the **convertxml** module.
```js
import convertxml from '@ohos.convertxml';
```
2. Pass in an XML file to be converted and set conversion options.
```js
let xml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<note importance="high" logged="true">' +
' <title>Happy</title>' +
' <todo>Work</todo>' +
' <todo>Play</todo>' +
'</note>';
let options = {
// trim: false, indicating that spaces before and after the text are not deleted after conversion.
// declarationKey: "_declaration", indicating that _declaration is used to identify the file declaration after conversion.
// instructionKey: "_instruction", indicating that _instruction is used to identify instructions after conversion.
// attributesKey: "_attributes", indicating that _attributes is used to identify attributes after conversion.
// textKey: "_text", indicating that _text is used to identify tag values after conversion.
// cdataKey: "_cdata", indicating that _cdata is used to identify unparsed data after conversion.
// docTypeKey: "_doctype", indicating that _doctype is used to identify documents after conversion.
// commentKey: "_comment", indicating that _comment is used to identify comments after conversion.
// parentKey: "_parent", indicating that _parent is used to identify parent classes after conversion.
// typeKey: "_type", indicating that _type is used to identify types after conversion.
// nameKey: "_name", indicating that _name is used to identify tag names after conversion.
// elementsKey: "_elements", indicating that _elements is used to identify elements after conversion.
trim: false,
declarationKey: "_declaration",
instructionKey: "_instruction",
attributesKey: "_attributes",
textKey: "_text",
cdataKey: "_cdata",
docTypeKey: "_doctype",
commentKey: "_comment",
parentKey: "_parent",
typeKey: "_type",
nameKey: "_name",
elementsKey: "_elements"
}
```
3. Call the conversion function and print the result.
```js
let conv = new convertxml.ConvertXML();
let result = conv.convertToJSObject(xml, options);
let strRes = JSON.stringify(result); // Convert the JavaScript object into a JSON string for explicit output.
console.info(strRes);
// Alternatively, directly process the JavaScript object to obtain the tag values.
let title = result['_elements'][0]['_elements'][0]['_elements'][0]['_text']; // Parse the value of the <title> tag.
let todo = result['_elements'][0]['_elements'][1]['_elements'][0]['_text']; // Parse the value of the <todo> tag.
let todo2 = result['_elements'][0]['_elements'][2]['_elements'][0]['_text']; // Parse the value of the <todo> tag.
console.info(title); // Happy
console.info(todo); // Work
console.info(todo2); // Play
```
The output is as follows:
```js
strRes:
{"_declaration":{"_attributes":{"version":"1.0","encoding":"utf-8"}},"_elements":[{"_type":"element","_name":"note",
"_attributes":{"importance":"high","logged":"true"},"_elements":[{"_type":"element","_name":"title",
"_elements":[{"_type":"text","_text":"Happy"}]},{"_type":"element","_name":"todo",
"_elements":[{"_type":"text","_text":"Work"}]},{"_type":"element","_name":"todo",
"_elements":[{"_type":"text","_text":"Play"}]}]}]}
title:Happy
todo:Work
todo2:Play
```
# XML Generation
XML can be used as a data exchange format, which is supported by a wealth of systems and applications. For example, web services can transfer structured data in XML format.
XML can also be used as a message passing format for communication between nodes in a distributed system.
## Precautions
- XML tags must appear in pairs: one start tag and one end tag.
- XML tags are case sensitive. The start tag and end tag must use the same case.
## How to Develop
The **xml** module provides the **XmlSerializer** class to generate XML files. The input is an object of the ArrayBuffer or DataView type with a fixed length, which is used to store the output XML data.
You can call different to write different types of content. For example, call **startElement(name: string)** to write the start tag and **setText(text: string)** to write a tag value.
For details about the APIs of the **XML** module, see [@ohos.xml (XML Parsing and Generation)](../reference/apis/js-apis-xml.md).
The following steps walk you through on how to generate an XML file.
1. Import the modules.
```js
import xml from '@ohos.xml';
import util from '@ohos.util';
```
2. Create a buffer and create an **XmlSerializer** object, either based on an object of the ArrayBuffer or DataView type.
```js
// 1. Create an XmlSerializer object based on an object of the ArrayBuffer type.
let arrayBuffer = new ArrayBuffer(2048); // Create a 2048-byte object of the ArrayBuffer type.
let thatSer = new xml.XmlSerializer (arrayBuffer); // Create an XmlSerializer object based on the object of the ArrayBuffer type.
// 2. Create an XmlSerializer object based on an object of the DataView type.
let arrayBuffer = new ArrayBuffer(2048); // Create a 2048-byte object of the ArrayBuffer type.
let dataView = new DataView(arrayBuffer); // Use an object of the DataView type to operate the object of the ArrayBuffer type.
let thatSer = new xml.XmlSerializer (dataView); // Create an XmlSerializer object based on the object of the DataView type.
```
3. Call the functions to generate an XML file.
```js
thatSer.setDeclaration(); // Write the XML file declaration.
thatSer.startElement('bookstore'); // Write the start flag.
thatSer.startElement('book'); // Write the start tag of a nested element.
thatSer.setAttributes('category', 'COOKING'); // Write the attributes and attribute values.
thatSer.startElement('title');
thatSer.setAttributes('lang', 'en');
thatSer.setText('Everyday'); // Write the tag value.
thatSer.endElement(); // Write the end flag.
thatSer.startElement('author');
thatSer.setText('Giada');
thatSer.endElement();
thatSer.startElement('year');
thatSer.setText('2005');
thatSer.endElement();
thatSer.endElement();
thatSer.endElement();
```
4. Use **Uint8Array** to operate the object of the ArrayBuffer type, and use **TextDecoder** to decode the Uint8Array.
```js
let view = new Uint8Array(arrayBuffer); // Use Uint8Array to read data from the object of the ArrayBuffer type.
let textDecoder = util.TextDecoder.create(); // Call the TextDecoder class of the util module.
let res = textDecoder.decodeWithStream (view); // Decode the view.
console.info(res);
```
The output is as follows:
```js
<?xml version=\"1.0\" encoding=\"utf-8\"?><bookstore>\r\n <book category=\"COOKING\">\r\n <title lang=\"en\">Everyday</title>\r\n <author>Giada</author>\r\n <year>2005</year>\r\n </book>\r\n</bookstore>
```
# XML Overview
Extensible Markup Language (XML) is a markup language used to describe data. It aims to provide a common way to transmit and store data, especially data frequently used in web applications. XML does not predefine tags. As a result, it is more flexible and widely used.
An XML file consists of elements, attributes, and content.
- An element refers to a tag pair that contains text, attributes, or other elements.
- Attributes provide additional information about an element.
- Content is the data or sub-element contained in an element.
XML supports the use of XML Schema Definition (XSD) or Document Type Definition (DTD) for defining the document structure. This allows you to customize rules to verify whether an XML document is in the expected format.
XML also supports features such as namespaces, entity references, comments, and processing instructions, making it easy to adapt to diverse data requirements.
The common library provides XML-related basic capabilities, including [XML generation](xml-generation.md), [XML parsing](xml-parsing.md), and [XML conversion](xml-conversion.md).
# XML Parsing
Data transferred in XML format must be parsed in actual use. Generally, three types of elements need to be parsed, as described in [Parsing XML Tags and Tag Values](#parsing-xml-tags-and-tag-values), [Parsing XML Attributes and Attribute Values](#parsing-xml-attributes-and-attribute-values), and [Parsing XML Event Types and Element Depths](#parsing-xml-event-types-and-element-depths).
The **xml** module provides the **XmlPullParser** class to parse XML files. The input is an object of the ArrayBuffer or DataView type containing XML text, and the output is the parsed information.
**Table 1** XML parsing options
| Name| Type| Mandatory| Description|
| -------- | -------- | -------- | -------- |
| supportDoctype | boolean | No| Whether to ignore the document type. The default value is **false**, indicating that the document type is parsed.|
| ignoreNameSpace | boolean | No| Whether to ignore the namespace. The default value is **false**, indicating that the namespace is parsed.|
| tagValueCallbackFunction | (name: string, value: string) =&gt; boolean | No| Callback used to return **tagValue**, which consists of a tag and its value. The default value is **null**, indicating that XML tags and tag values are not parsed.|
| attributeValueCallbackFunction | (name: string, value: string) =&gt; boolean | No| Callback used to return **attributeValue**, which consists of an attribute and its value. The default value is **null**, indicating that XML attributes and attribute values are not parsed.|
| tokenValueCallbackFunction | (eventType: EventType, value: ParseInfo) =&gt; boolean | No| Callback used to return **tokenValue**, which consists of the event type and the attributes of **parseInfo**. The default value is **null**, indicating that the event type and the attributes of **parseInfo** are not parsed.|
## Precautions
- To ensure successful XML parsing and conversion, the input XML data must comply with the standard format.
- Currently, parsing a given node is not supported.
## Parsing XML Tags and Tag Values
1. Import the modules.
```js
import xml from '@ohos.xml';
import util from '@ohos.util'; // Use the API provided by the util module to encode the file.
```
2. Create an **XmlPullParser** object.
The **XmlPullParser** object can be created based on an object of the ArrayBuffer or DataView type.
```js
let strXml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<note importance="high" logged="true">' +
'<title>Play</title>' +
'<lens>Work</lens>' +
'</note>';
let textEncoder = new util.TextEncoder();
let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters.
// 1. Create an XmlPullParser object based on an object of the ArrayBuffer type.
let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8');
// 2. Create an XmlPullParser object based on an object of the DataView type.
let dataView = new DataView(arrBuffer.buffer);
let that = new xml.XmlPullParser(dataView, 'UTF-8');
```
3. Customize a callback function. In this example, the tag and tag value are directly printed.
```js
let str = '';
function func(name, value){
str = name + value;
console.info(str);
return true; // The value true means to continue parsing, and false means to stop parsing.
}
```
4. Set parsing options and call the **parse()** function.
```js
let options = {supportDoctype:true, ignoreNameSpace:true, tagValueCallbackFunction:func};
that.parse(options);
```
The output is as follows:
```js
note
title
Play
title
lens
Work
lens
note
```
## Parsing XML Attributes and Attribute Values
1. Import the modules.
```js
import xml from '@ohos.xml';
import util from '@ohos.util'; // Use the API provided by the util module to encode the file.
```
2. Create an **XmlPullParser** object.
```js
let strXml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<note importance="high" logged="true">' +
' <title>Play</title>' +
' <title>Happy</title>' +
' <lens>Work</lens>' +
'</note>';
let textEncoder = new util.TextEncoder();
let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters.
let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8');
```
3. Customize a callback function. In this example, the attribute and attribute value are directly printed.
```js
let str = '';
function func(name, value){
str += name + ' ' + value + ' ';
return true; // The value true means to continue parsing, and false means to stop parsing.
}
```
4. Set parsing options and call the **parse()** function.
```js
let options = {supportDoctype:true, ignoreNameSpace:true, attributeValueCallbackFunction:func};
that.parse(options);
console.info(str); // Print all attributes and their values at a time.
```
The output is as follows:
```js
importance high logged true // Attributes and attribute values of the note node
```
## Parsing XML Event Types and Element Depths
1. Import the modules.
```js
import xml from '@ohos.xml';
import util from '@ohos.util'; // Use the API provided by the util module to encode the file.
```
2. Create an **XmlPullParser** object.
```js
let strXml =
'<?xml version="1.0" encoding="utf-8"?>' +
'<note importance="high" logged="true">' +
'<title>Play</title>' +
'</note>';
let textEncoder = new util.TextEncoder();
let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters.
let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8');
```
3. Customize a callback function. In this example, the event type and element depth are directly printed.
```js
let str = '';
function func(name, value){
str = name +' ' + value.getDepth(); // getDepth is called to obtain the element depth.
console.info(str)
return true; // The value true means to continue parsing, and false means to stop parsing.
}
```
4. Set parsing options and call the **parse()** function.
```js
let options = {supportDoctype:true, ignoreNameSpace:true, tokenValueCallbackFunction:func};
that.parse(options);
```
The output is as follows:
```js
0 0 // 0: <?xml version="1.0" encoding="utf-8"?>. The event type value of START_DOCUMENT is 0. 0: The depth is 0.
2 1 // 2: <note importance="high" logged="true">. The event type value of START_TAG is 2. 1: The depth is 1.
2 2 // 2: <title>. The event type value of START_TAG is 2. 2: The depth is 2.
4 2 // 4: Play. The event type value of TEXT is 4. 2: The depth is 2.
3 2 // 3: </title>. The event type value of END_TAG is 3. 2: The depth is 2.
3 1 // 3: </note>. The event type value of END_TAG is 3. 1: The depth is 1 (corresponding to <note>).
1 0 // 1: The event type value of END_DOCUMENT is 1. 0: The depth is 0.
```
## Example Scenario
The following uses invoking all parsing options as an example to describe how to parse XML tags, attributes, and event types.
```js
import xml from '@ohos.xml';
import util from '@ohos.util';
let strXml =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<book category="COOKING">' +
'<title lang="en">Everyday</title>' +
'<author>Giada</author>' +
'</book>';
let textEncoder = new util.TextEncoder();
let arrBuffer = textEncoder.encodeInto(strXml);
let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8');
let str = '';
function tagFunc(name, value) {
str = name + value;
console.info('tag-' + str);
return true;
}
function attFunc(name, value) {
str = name + ' ' + value;
console.info('attri-' + str);
return true;
}
function tokenFunc(name, value) {
str = name + ' ' + value.getDepth();
console.info('token-' + str);
return true;
}
let options = {
supportDocType: true,
ignoreNameSpace: true,
tagValueCallbackFunction: tagFunc,
attributeValueCallbackFunction: attFunc,
tokenValueCallbackFunction: tokenFunc
};
that.parse(options);
```
The output is as follows:
```js
tag-
token-0 0
tag-book
attri-category COOKING
token-2 1
tag-title
attri-lang en
token-2 2
tag-Everyday
token-4 2
tag-title
token-3 2
tag-author
token-2 2
tag-Giada
token-4 2
tag-author
token-3 2
tag-book
token-3 1
tag-
token-1 0
```
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
- [Ethernet Connection](net-ethernet.md) - [Ethernet Connection](net-ethernet.md)
- [Network Connection Management](net-connection-manager.md) - [Network Connection Management](net-connection-manager.md)
- [mDNS Management](net-mdns.md) - [mDNS Management](net-mdns.md)
- [Traffic Management](net-statistics.md)
- [VPN Management](net-vpn.md)
- IPC & RPC - IPC & RPC
- [IPC & RPC Overview](ipc-rpc-overview.md) - [IPC & RPC Overview](ipc-rpc-overview.md)
- [IPC & RPC Development](ipc-rpc-development-guideline.md) - [IPC & RPC Development](ipc-rpc-development-guideline.md)
......
# HTTP Data Request # HTTP Data Request
## When to Use ## Overview
An application can initiate a data request over HTTP. Common HTTP methods include **GET**, **POST**, **OPTIONS**, **HEAD**, **PUT**, **DELETE**, **TRACE**, and **CONNECT**. An application can initiate a data request over HTTP. Common HTTP methods include **GET**, **POST**, **OPTIONS**, **HEAD**, **PUT**, **DELETE**, **TRACE**, and **CONNECT**.
...@@ -18,7 +18,7 @@ The following table provides only a simple description of the related APIs. For ...@@ -18,7 +18,7 @@ The following table provides only a simple description of the related APIs. For
| ----------------------------------------- | ----------------------------------- | | ----------------------------------------- | ----------------------------------- |
| createHttp() | Creates an HTTP request. | | createHttp() | Creates an HTTP request. |
| request() | Initiates an HTTP request to a given URL. | | request() | Initiates an HTTP request to a given URL. |
| request2()<sup>10+</sup> | Initiates an HTTP network request based on the URL and returns a streaming response.| | requestInStream()<sup>10+</sup> | Initiates an HTTP network request to a given URL and returns a streaming response.|
| destroy() | Destroys an HTTP request. | | destroy() | Destroys an HTTP request. |
| on(type: 'headersReceive') | Registers an observer for HTTP Response Header events. | | on(type: 'headersReceive') | Registers an observer for HTTP Response Header events. |
| off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.| | off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.|
...@@ -27,8 +27,8 @@ The following table provides only a simple description of the related APIs. For ...@@ -27,8 +27,8 @@ The following table provides only a simple description of the related APIs. For
| off\('dataReceive'\)<sup>10+</sup> | Unregisters the observer for events indicating receiving of HTTP streaming responses. | | off\('dataReceive'\)<sup>10+</sup> | Unregisters the observer for events indicating receiving of HTTP streaming responses. |
| on\('dataEnd'\)<sup>10+</sup> | Registers an observer for events indicating completion of receiving HTTP streaming responses. | | on\('dataEnd'\)<sup>10+</sup> | Registers an observer for events indicating completion of receiving HTTP streaming responses. |
| off\('dataEnd'\)<sup>10+</sup> | Unregisters the observer for events indicating completion of receiving HTTP streaming responses.| | off\('dataEnd'\)<sup>10+</sup> | Unregisters the observer for events indicating completion of receiving HTTP streaming responses.|
| on\('dataProgress'\)<sup>10+</sup> | Registers an observer for events indicating progress of receiving HTTP streaming responses. | | on\('dataReceiveProgress'\)<sup>10+</sup> | Registers an observer for events indicating progress of receiving HTTP streaming responses. |
| off\('dataProgress'\)<sup>10+</sup> | Unregisters the observer for events indicating progress of receiving HTTP streaming responses.| | off\('dataReceiveProgress'\)<sup>10+</sup> | Unregisters the observer for events indicating progress of receiving HTTP streaming responses.|
## How to Develop request APIs ## How to Develop request APIs
...@@ -53,6 +53,7 @@ httpRequest.on('headersReceive', (header) => { ...@@ -53,6 +53,7 @@ httpRequest.on('headersReceive', (header) => {
}); });
httpRequest.request( httpRequest.request(
// Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL.
"EXAMPLE_URL",
{ {
method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET. method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET.
// You can add header fields based on service requirements. // You can add header fields based on service requirements.
...@@ -81,7 +82,7 @@ httpRequest.request( ...@@ -81,7 +82,7 @@ httpRequest.request(
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
httpRequest.destroy(); httpRequest.destroy();
} else { } else {
console.info('error:' + JSON.stringify(err)); console.error('error:' + JSON.stringify(err));
// Unsubscribe from HTTP Response Header events. // Unsubscribe from HTTP Response Header events.
httpRequest.off('headersReceive'); httpRequest.off('headersReceive');
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
...@@ -91,12 +92,12 @@ httpRequest.request( ...@@ -91,12 +92,12 @@ httpRequest.request(
); );
``` ```
## How to Develop request2 APIs ## How to Develop requestInStream APIs
1. Import the **http** namespace from **@ohos.net.http.d.ts**. 1. Import the **http** namespace from **@ohos.net.http.d.ts**.
2. Call **createHttp()** to create an **HttpRequest** object. 2. Call **createHttp()** to create an **HttpRequest** object.
3. Depending on your need, call **on()** of the **HttpRequest** object to subscribe to HTTP response header events as well as events indicating receiving of HTTP streaming responses, progress of receiving HTTP streaming responses, and completion of receiving HTTP streaming responses. 3. Depending on your need, call **on()** of the **HttpRequest** object to subscribe to HTTP response header events as well as events indicating receiving of HTTP streaming responses, progress of receiving HTTP streaming responses, and completion of receiving HTTP streaming responses.
4. Call **request2()** to initiate a network request. You need to pass in the URL and optional parameters of the HTTP request. 4. Call **requestInStream()** to initiate a network request. You need to pass in the URL and optional parameters of the HTTP request.
5. Parse the returned response code as needed. 5. Parse the returned response code as needed.
6. Call **off()** of the **HttpRequest** object to unsubscribe from the related events. 6. Call **off()** of the **HttpRequest** object to unsubscribe from the related events.
7. Call **httpRequest.destroy()** to release resources after the request is processed. 7. Call **httpRequest.destroy()** to release resources after the request is processed.
...@@ -122,11 +123,11 @@ httpRequest.on('dataEnd', () => { ...@@ -122,11 +123,11 @@ httpRequest.on('dataEnd', () => {
console.info('No more data in response, data receive end'); console.info('No more data in response, data receive end');
}); });
// Subscribe to events indicating progress of receiving HTTP streaming responses. // Subscribe to events indicating progress of receiving HTTP streaming responses.
httpRequest.on('dataProgress', (data) => { httpRequest.on('dataReceiveProgress', (data) => {
console.log("dataProgress receiveSize:" + data.receiveSize + ", totalSize:" + data.totalSize); console.log("dataReceiveProgress receiveSize:" + data.receiveSize + ", totalSize:" + data.totalSize);
}); });
httpRequest.request2( httpRequest.requestInStream(
// Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL.
"EXAMPLE_URL", "EXAMPLE_URL",
{ {
...@@ -146,14 +147,14 @@ httpRequest.request2( ...@@ -146,14 +147,14 @@ httpRequest.request2(
readTimeout: 60000, // Optional. The default value is 60000, in ms. If a large amount of data needs to be transmitted, you are advised to set this parameter to a larger value to ensure normal data transmission. readTimeout: 60000, // Optional. The default value is 60000, in ms. If a large amount of data needs to be transmitted, you are advised to set this parameter to a larger value to ensure normal data transmission.
usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system. usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system.
}, (err, data) => { }, (err, data) => {
console.info('error:' + JSON.stringify(err)); console.error('error:' + JSON.stringify(err));
console.info('ResponseCode :' + JSON.stringify(data)); console.info('ResponseCode :' + JSON.stringify(data));
// Unsubscribe from HTTP Response Header events. // Unsubscribe from HTTP Response Header events.
httpRequest.off('headersReceive'); httpRequest.off('headersReceive');
// Unregister the observer for events indicating receiving of HTTP streaming responses. // Unregister the observer for events indicating receiving of HTTP streaming responses.
httpRequest.off('dataReceive'); httpRequest.off('dataReceive');
// Unregister the observer for events indicating progress of receiving HTTP streaming responses. // Unregister the observer for events indicating progress of receiving HTTP streaming responses.
httpRequest.off('dataProgress'); httpRequest.off('dataReceiveProgress');
// Unregister the observer for events indicating completion of receiving HTTP streaming responses. // Unregister the observer for events indicating completion of receiving HTTP streaming responses.
httpRequest.off('dataEnd'); httpRequest.off('dataEnd');
// Call the destroy() method to release resources after HttpRequest is complete. // Call the destroy() method to release resources after HttpRequest is complete.
...@@ -161,10 +162,3 @@ httpRequest.request2( ...@@ -161,10 +162,3 @@ httpRequest.request2(
} }
); );
``` ```
## Samples
The following sample is provided to help you better understand how to develop the HTTP data request feature:
- [`Http`: Data Request (ArkTS) (API9)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Connectivity/Http)
- [HTTP Communication (ArkTS) (API9)](https://gitee.com/openharmony/codelabs/tree/master/NetworkManagement/SmartChatEtsOH)
# IPC & RPC Development Guidelines # IPC & RPC Development Guidelines
## When to Use ## Overview
IPC/RPC enables a proxy and a stub that run on different processes to communicate with each other, regardless of whether they run on the same or different devices. IPC/RPC enables a proxy and a stub that run on different processes to communicate with each other, regardless of whether they run on the same or different devices.
......
# Network Connection Management # Network Connection Management
## Introduction ## Overview
The Network Connection Management module provides basic network management capabilities, including management of Wi-Fi/cellular/Ethernet connection priorities, network quality evaluation, subscription to network connection status changes, query of network connection information, and DNS resolution. The Network Connection Management module provides basic network management capabilities, including management of Wi-Fi/cellular/Ethernet connection priorities, network quality evaluation, subscription to network connection status changes, query of network connection information, and DNS resolution.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-connection.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-connection.md).
## Basic Concepts ## Basic Concepts
...@@ -107,7 +108,7 @@ conn.on('netAvailable', (data => { ...@@ -107,7 +108,7 @@ conn.on('netAvailable', (data => {
// Listen to network status change events. If the network is unavailable, an on_netUnavailable event is returned. // Listen to network status change events. If the network is unavailable, an on_netUnavailable event is returned.
conn.on('netUnavailable', (data => { conn.on('netUnavailable', (data => {
console.log("net is unavailable, netId is " + data.netId); console.log("net is unavailable, data is " + JSON.stringify(data));
})); }));
// Register an observer for network status changes. // Register an observer for network status changes.
......
# Ethernet Connection # Ethernet Connection
## Introduction ## Overview
The Ethernet Connection module allows a device to access the Internet through a network cable. After a device is connected to the Ethernet through a network cable, the device can obtain a series of network attributes, such as the dynamically allocated IP address, subnet mask, gateway, and DNS. You can manually configure and obtain the network attributes of the device in static mode. The Ethernet Connection module allows a device to access the Internet through a network cable. After a device is connected to the Ethernet through a network cable, the device can obtain a series of network attributes, such as the dynamically allocated IP address, subnet mask, gateway, and DNS. You can manually configure and obtain the network attributes of the device in static mode.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-ethernet.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-ethernet.md).
## **Constraints** ## **Constraints**
......
# MDNS Management # MDNS Management
## Introduction ## Overview
Multicast DNS (mDNS) provides functions such as adding, removing, discovering, and resolving local services on a LAN. Multicast DNS (mDNS) provides functions such as adding, removing, discovering, and resolving local services on a LAN.
- Local service: a service provider on a LAN, for example, a printer or scanner. - Local service: a service provider on a LAN, for example, a printer or scanner.
......
# Network Sharing # Network Sharing
## Introduction ## Overview
The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume. The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume.
> **NOTE** > **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md). > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md).
## Basic Concepts ## Basic Concepts
......
# Traffic Management
## Overview
The traffic management module allows you to query real-time or historical data traffic by the specified network interface card (NIC) or user ID (UID).
Its functions include:
- Obtaining real-time traffic data by NIC or UID
- Obtaining historical traffic data by NIC or UID
- Subscribing to traffic change events by NIC or UID
> **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-statistics.md).
The following describes the development procedure specific to each application scenario.
## Available APIs
For the complete list of APIs and example code, see [Traffic Management](../reference/apis/js-apis-net-statistics.md).
| Type| API| Description|
| ---- | ---- | ---- |
| ohos.net.statistics | getIfaceRxBytes(nic: string, callback: AsyncCallback\<number>): void; |Obtains the real-time downlink data traffic of the specified NIC. |
| ohos.net.statistics | getIfaceTxBytes(nic: string, callback: AsyncCallback\<number>): void; |Obtains the real-time uplink data traffic of the specified NIC. |
| ohos.net.statistics | getCellularRxBytes(callback: AsyncCallback\<number>): void; |Obtains the real-time downlink data traffic of the cellular network.|
| ohos.net.statistics | getCellularTxBytes(callback: AsyncCallback\<number>): void; |Obtains the real-time uplink data traffic of the cellular network.|
| ohos.net.statistics | getAllRxBytes(callback: AsyncCallback\<number>): void; |Obtains the real-time downlink data traffic of the all NICs. |
| ohos.net.statistics | getAllTxBytes(callback: AsyncCallback\<number>): void; |Obtains the real-time uplink data traffic of the all NICs. |
| ohos.net.statistics | getUidRxBytes(uid: number, callback: AsyncCallback\<number>): void; |Obtains the real-time downlink data traffic of the specified application. |
| ohos.net.statistics | getUidTxBytes(uid: number, callback: AsyncCallback\<number>): void; |Obtains the real-time uplink data traffic of the specified application. |
| ohos.net.statistics | getTrafficStatsByIface(ifaceInfo: IfaceInfo, callback: AsyncCallback\<NetStatsInfo>): void; |Obtains the historical data traffic of the specified NIC. |
| ohos.net.statistics | getTrafficStatsByUid(uidInfo: UidInfo, callback: AsyncCallback\<NetStatsInfo>): void; |Obtains the historical data traffic of the specified application. |
| ohos.net.statistics | on(type: 'netStatsChange', callback: Callback\<{ iface: string, uid?: number }>): void |Subscribes to traffic change events.|
| ohos.net.statistics | off(type: 'netStatsChange', callback?: Callback\<{ iface: string, uid?: number }>): void; |Unsubscribes from traffic change events.|
## Obtaining Real-Time Traffic Data by NIC or UID
1. Obtain the real-time data traffic of the specified NIC.
2. Obtain the real-time data traffic of the cellular network.
3. Obtain the real-time data traffic of all NICs.
4. Obtain the real-time data traffic of the specified application.
```js
// Import the statistics namespace from @ohos.net.statistics.
import statistics from '@ohos.net.statistics'
// Obtain the real-time downlink data traffic of the specified NIC.
statistics.getIfaceRxBytes("wlan0", (error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time uplink data traffic of the specified NIC.
statistics.getIfaceTxBytes("wlan0", (error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time downlink data traffic of the cellular network.
statistics.getCellularRxBytes((error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time uplink data traffic of the cellular network.
statistics.getCellularTxBytes((error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time downlink data traffic of the all NICs.
statistics.getAllRxBytes((error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time uplink data traffic of the all NICs.
statistics.getAllTxBytes((error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time downlink data traffic of the specified application.
let uid = 20010038;
statistics.getUidRxBytes(uid, (error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
// Obtain the real-time uplink data traffic of the specified application.
let uid = 20010038;
statistics.getUidTxBytes(uid, (error, stats) => {
console.log(JSON.stringify(error))
console.log(JSON.stringify(stats))
})
```
## Obtaining Historical Traffic Data by NIC or UID
1. Obtain the historical data traffic of the specified NIC.
2. Obtain the historical data traffic of the specified application.
```js
let ifaceInfo = {
iface: "wlan0",
startTime: 1685948465,
endTime: 16859485670
}
// Obtain the historical data traffic of the specified NIC.
statistics.getTrafficStatsByIface(ifaceInfo), (error, statsInfo) => {
console.log(JSON.stringify(error))
console.log("getTrafficStatsByIface bytes of received = " + JSON.stringify(statsInfo.rxBytes));
console.log("getTrafficStatsByIface bytes of sent = " + JSON.stringify(statsInfo.txBytes));
console.log("getTrafficStatsByIface packets of received = " + JSON.stringify(statsInfo.rxPackets));
console.log("getTrafficStatsByIface packets of sent = " + JSON.stringify(statsInfo.txPackets));
});
let uidInfo = {
ifaceInfo: {
iface: "wlan0",
startTime: 1685948465,
endTime: 16859485670
},
uid: 20010037
}
// Obtain the historical data traffic of the specified application.
statistics.getTrafficStatsByUid(uidInfo), (error, statsInfo) => {
console.log(JSON.stringify(error))
console.log("getTrafficStatsByUid bytes of received = " + JSON.stringify(statsInfo.rxBytes));
console.log("getTrafficStatsByUid bytes of sent = " + JSON.stringify(statsInfo.txBytes));
console.log("getTrafficStatsByUid packets of received = " + JSON.stringify(statsInfo.rxPackets));
console.log("getTrafficStatsByUid packets of sent = " + JSON.stringify(statsInfo.txPackets));
});
```
## Subscribing to Traffic Change Events
1. Subscribe to traffic change events.
2. Unsubscribe from traffic change events.
```js
let callback = data => {
console.log("on netStatsChange, data:" + JSON.stringify(data));
}
// Subscribe to traffic change events.
statistics.on('netStatsChange', callback);
// Unsubscribe from traffic change events. You can pass the callback of the **on** function if you want to unsubscribe from a certain type of event. If you do not pass the callback, you will unsubscribe from all events.
statistics.off('netStatsChange', callback);
statistics.off('netStatsChange');
```
# VPN Management
## Overview
A virtual private network (VPN) is a dedicated network established on a public network. On a VPN, the connection between any two nodes does not have an end-to-end physical link required by the traditional private network. Instead, user data is transmitted over a logical link because a VPN is a logical network deployed over the network platform (such as the Internet) provided by the public network service provider.
> **NOTE**
>
> To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [Traffic Management](../reference/apis/js-apis-net-vpn.md).
The following describes the development procedure specific to each application scenario.
## Available APIs
For the complete list of APIs and example code, see [VPN Management](../reference/apis/js-apis-net-vpn.md).
| Type| API| Description|
| ---- | ---- | ---- |
| ohos.net.vpn | setUp(config: VpnConfig, callback: AsyncCallback\<number\>): void | Establishes a VPN. This API uses an asynchronous callback to return the result.|
| ohos.net.vpn | protect(socketFd: number, callback: AsyncCallback\<void\>): void | Enables VPN tunnel protection. This API uses an asynchronous callback to return the result.|
| ohos.net.vpn | destroy(callback: AsyncCallback\<void\>): void | Destroys a VPN. This API uses an asynchronous callback to return the result.|
## Starting a VPN
1. Establish a VPN tunnel. The following uses the UDP tunnel as an example.
2. Enable protection for the UDP tunnel.
3. Establish a VPN.
4. Process data of the virtual network interface card (vNIC), such as reading or writing data.
5. Destroy the VPN.
This example shows how to develop an application using native C++ code. For details, see [Simple Native C++ Example (ArkTS) (API9)] (https://gitee.com/openharmony/codelabs/tree/master/NativeAPI/NativeTemplateDemo).
The sample application consists of two parts: JS code and C++ code.
## JS Code
The JS code is used to implement the service logic, such as creating a tunnel, establishing a VPN, enabling VPN protection, and destroying a VPN.
```js
import hilog from '@ohos.hilog';
import vpn from '@ohos.net.vpn';
import UIAbility from '@ohos.app.ability.UIAbility';
import vpn_client from "libvpn_client.so"
class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
globalThis.context = this.context;
}
}
let TunnelFd = -1
let VpnConnection = vpn.createVpnConnection(globalThis.context)
@Entry
@Component
struct Index {
@State message: string = 'Test VPN'
//1. Establish a VPN tunnel. The following uses the UDP tunnel as an example.
CreateTunnel() {
TunnelFd = vpn_client.udpConnect("192.168.43.208", 8888)
}
// 2. Enable protection for the UDP tunnel.
Protect() {
VpnConnection.protect(TunnelFd).then(function () {
console.info("vpn Protect Success.")
}).catch(function (err) {
console.info("vpn Protect Failed " + JSON.stringify(err))
})
}
SetupVpn() {
let config = {
addresses: [{
address: {
address: "10.0.0.5",
family: 1
},
prefixLength: 24,
}],
routes: [],
mtu: 1400,
dnsAddresses: [
"114.114.114.114"
],
acceptedApplications: [],
refusedApplications: []
}
try {
// 3. Create a VPN.
VpnConnection.setUp(config, (error, data) => {
console.info("tunfd: " + JSON.stringify(data));
// 4. Process data of the virtual vNIC, such as reading or writing data.
vpn_client.startVpn(data, TunnelFd)
})
} catch (error) {
console.info("vpn setUp fail " + JSON.stringify(error));
}
}
// 5. Destroy the VPN.
Destroy() {
vpn_client.stopVpn(TunnelFd)
VpnConnection.destroy().then(function () {
console.info("vpn Destroy Success.")
}).catch(function (err) {
console.info("vpn Destroy Failed " + JSON.stringify(err))
})
}
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
console.info("vpn Client")
})
Button('CreateTunnel').onClick(() => {
this.CreateTunnel()
}).fontSize(50)
Button('Protect').onClick(() => {
this.Protect()
}).fontSize(50)
Button('SetupVpn').onClick(() => {
this.SetupVpn()
}).fontSize(50)
Button('Destroy').onClick(() => {
this.Destroy()
}).fontSize(50)
}
.width('100%')
}
.height('100%')
}
}
```
## C++ Code
The C++ code is used for underlying service implementation, such as UDP tunnel client implementation and vNIC data read and write.
```c++
#include "napi/native_api.h"
#include "hilog/log.h"
#include <cstring>
#include <thread>
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <thread>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFFER_SIZE 2048
#define VPN_LOG_TAG "NetMgrVpn"
#define VPN_LOG_DOMAIN 0x15b0
#define MAKE_FILE_NAME (strrchr(__FILE__, '/') + 1)
#define NETMANAGER_VPN_LOGE(fmt, ...) \
OH_LOG_Print(LOG_APP, LOG_ERROR, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \
__LINE__, ##__VA_ARGS__)
#define NETMANAGER_VPN_LOGI(fmt, ...) \
OH_LOG_Print(LOG_APP, LOG_INFO, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \
__LINE__, ##__VA_ARGS__)
#define NETMANAGER_VPN_LOGD(fmt, ...) \
OH_LOG_Print(LOG_APP, LOG_DEBUG, VPN_LOG_DOMAIN, VPN_LOG_TAG, "vpn [%{public}s %{public}d] " fmt, MAKE_FILE_NAME, \
__LINE__, ##__VA_ARGS__)
struct FdInfo {
int32_t tunFd = 0;
int32_t tunnelFd = 0;
struct sockaddr_in serverAddr;
};
static FdInfo fdInfo;
static bool threadRunF = false;
static std::thread threadt1;
static std::thread threadt2;
// Obtain the IP address of the UDP server.
static constexpr const int MAX_STRING_LENGTH = 1024;
std::string GetStringFromValueUtf8(napi_env env, napi_value value) {
std::string result;
char str[MAX_STRING_LENGTH] = {0};
size_t length = 0;
napi_get_value_string_utf8(env, value, str, MAX_STRING_LENGTH, &length);
if (length > 0) {
return result.append(str, length);
}
return result;
}
void HandleReadTunfd(FdInfo fdInfo) {
uint8_t buffer[BUFFER_SIZE] = {0};
while (threadRunF) {
int ret = read(fdInfo.tunFd, buffer, sizeof(buffer));
if (ret <= 0) {
if (errno != 11) {
NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunfd: %{public}d", errno, fdInfo.tunFd);
}
continue;
}
// Read data from the vNIC and send the data to the UDP server through the UDP tunnel.
NETMANAGER_VPN_LOGD("buffer: %{public}s, len: %{public}d", buffer, ret);
ret = sendto(fdInfo.tunnelFd, buffer, ret, 0, (struct sockaddr *)&fdInfo.serverAddr, sizeof(fdInfo.serverAddr));
if (ret <= 0) {
NETMANAGER_VPN_LOGE("send to server[%{public}s:%{public}d] failed, ret: %{public}d, error: %{public}s",
inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), ret,
strerror(errno));
continue;
}
}
}
void HandleTcpReceived(FdInfo fdInfo) {
int addrlen = sizeof(struct sockaddr_in);
uint8_t buffer[BUFFER_SIZE] = {0};
while (threadRunF) {
int length = recvfrom(fdInfo.tunnelFd, buffer, sizeof(buffer), 0, (struct sockaddr *)&fdInfo.serverAddr,
(socklen_t *)&addrlen);
if (length < 0) {
if (errno != 11) {
NETMANAGER_VPN_LOGE("read tun device error: %{public}d, tunnelfd: %{public}d", errno, fdInfo.tunnelFd);
}
continue;
}
// Receive data from the UDP server and write the data to the vNIC.
NETMANAGER_VPN_LOGD("from [%{public}s:%{public}d] data: %{public}s, len: %{public}d",
inet_ntoa(fdInfo.serverAddr.sin_addr), ntohs(fdInfo.serverAddr.sin_port), buffer, length);
int ret = write(fdInfo.tunFd, buffer, length);
if (ret <= 0) {
NETMANAGER_VPN_LOGE("error Write To Tunfd, errno: %{public}d", errno);
}
}
}
static napi_value UdpConnect(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
int32_t port = 0;
napi_get_value_int32(env, args[1], &port);
std::string ipAddr = GetStringFromValueUtf8(env, args[0]);
NETMANAGER_VPN_LOGI("ip: %{public}s port: %{public}d", ipAddr.c_str(), port);
// Establish a UDP tunnel.
int32_t sockFd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockFd == -1) {
NETMANAGER_VPN_LOGE("socket() error");
return 0;
}
struct timeval timeout = {1, 0};
setsockopt(sockFd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
memset(&fdInfo.serverAddr, 0, sizeof(fdInfo.serverAddr));
fdInfo.serverAddr.sin_family = AF_INET;
fdInfo.serverAddr.sin_addr.s_addr = inet_addr(ipAddr.c_str()); // server's IP addr
fdInfo.serverAddr.sin_port = htons(port); // port
NETMANAGER_VPN_LOGI("Connection successful");
napi_value tunnelFd;
napi_create_int32(env, sockFd, &tunnelFd);
return tunnelFd;
}
static napi_value StartVpn(napi_env env, napi_callback_info info) {
size_t argc = 2;
napi_value args[2] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
napi_get_value_int32(env, args[0], &fdInfo.tunFd);
napi_get_value_int32(env, args[1], &fdInfo.tunnelFd);
if (threadRunF) {
threadRunF = false;
threadt1.join();
threadt2.join();
}
// Start two threads. One is used to read data from the vNIC, and the other is used to receive data from the server.
threadRunF = true;
std::thread tt1(HandleReadTunfd, fdInfo);
std::thread tt2(HandleTcpReceived, fdInfo);
threadt1 = std::move(tt1);
threadt2 = std::move(tt2);
NETMANAGER_VPN_LOGI("StartVpn successful");
napi_value retValue;
napi_create_int32(env, 0, &retValue);
return retValue;
}
static napi_value StopVpn(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value args[1] = {nullptr};
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
int32_t tunnelFd;
napi_get_value_int32(env, args[0], &tunnelFd);
if (tunnelFd) {
close(tunnelFd);
tunnelFd = 0;
}
// Stop the two threads.
if (threadRunF) {
threadRunF = false;
threadt1.join();
threadt2.join();
}
NETMANAGER_VPN_LOGI("StopVpn successful");
napi_value retValue;
napi_create_int32(env, 0, &retValue);
return retValue;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"udpConnect", nullptr, UdpConnect, nullptr, nullptr, nullptr, napi_default, nullptr},
{"startVpn", nullptr, StartVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
{"stopVpn", nullptr, StopVpn, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
return exports;
}
EXTERN_C_END
static napi_module demoModule = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "entry",
.nm_priv = ((void *)0),
.reserved = {0},
};
extern "C" __attribute__((constructor)) void RegisterEntryModule(void) {
napi_module_register(&demoModule);
}
```
# Socket Connection # Socket Connection
## Introduction ## Overview
The Socket Connection module allows an application to transmit data over a Socket connection through the TCP, UDP, or TLS protocol. The Socket Connection module allows an application to transmit data over a socket connection through the TCP, UDP, or TLS protocol.
## Basic Concepts ## Basic Concepts
...@@ -13,10 +13,11 @@ The Socket Connection module allows an application to transmit data over a Socke ...@@ -13,10 +13,11 @@ 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 TLS socket connections. The main application scenarios are as follows:
- Implementing data transmission over TCP/UDP Socket connections - Implementing data transmission over TCP socket or UDP socket connections
- Implementing encrypted data transmission over TLS Socket connections - Implementing data transmission over TCP socket server connections
- Implementing encrypted data transmission over TLS socket connections
## Available APIs ## Available APIs
...@@ -28,72 +29,75 @@ Socket connection functions are mainly implemented by the **socket** module. The ...@@ -28,72 +29,75 @@ Socket connection functions are mainly implemented by the **socket** module. The
| -------- | -------- | | -------- | -------- |
| constructUDPSocketInstance() | Creates a **UDPSocket** object.| | constructUDPSocketInstance() | Creates a **UDPSocket** object.|
| constructTCPSocketInstance() | Creates a **TCPSocket** object.| | constructTCPSocketInstance() | Creates a **TCPSocket** object.|
| constructTCPSocketServerInstance() | Creates a **TCPSocketServer** object.|
| listen() | Listens for and accepts TCP socket connections established over the socket. (This API is applicable only to TCP.)|
| bind() | Binds the IP address and port number.| | bind() | Binds the IP address and port number.|
| send() | Sends data.| | send() | Sends data.|
| close() | Closes a Socket connection.| | close() | Closes a socket connection.|
| getState() | Obtains the Socket connection status.| | getState() | Obtains the socket connection status.|
| connect() | Connects to the specified IP address and port. This function is supported only for TCP.| | connect() | Connects to the specified IP address and port. This function is supported only for TCP.|
| getRemoteAddress() | Obtains the peer address of the Socket connection. This function is supported only for TCP. The **connect** API must have been called before you use this API.| | getRemoteAddress() | Obtains the peer address of the socket connection. This function is supported only for TCP. The **connect** API must have been called before you use this API.|
| on(type:&nbsp;'message') | Subscribes to **message** events of the Socket connection.| | setExtraOptions() | Sets other properties of the socket connection.|
| off(type:&nbsp;'message') | Unsubscribes from **message** events of the Socket connection.| | on(type:&nbsp;'message') | Subscribes to **message** events of the socket connection.|
| on(type:&nbsp;'close') | Subscribes to **close** events of the Socket connection.| | off(type:&nbsp;'message') | Unsubscribes from **message** events of the socket connection.|
| off(type:&nbsp;'close') | Unsubscribes from **close** events of the Socket connection.| | on(type:&nbsp;'close') | Subscribes to **close** events of the socket connection.|
| on(type:&nbsp;'error') | Subscribes to **error** events of the Socket connection.| | off(type:&nbsp;'close') | Unsubscribes from **close** events of the socket connection.|
| off(type:&nbsp;'error') | Unsubscribes from **error** events of the Socket connection.| | on(type:&nbsp;'error') | Subscribes to **error** events of the socket connection.|
| on(type:&nbsp;'listening') | Subscribes to **listening** events of the UDP Socket connection. | | off(type:&nbsp;'error') | Unsubscribes from **error** events of the socket connection.|
| off(type:&nbsp;'listening') | Unsubscribes from **listening** events of the UDP Socket connection. | | on(type:&nbsp;'listening') | Subscribes to **listening** events of the UDP socket connection. |
| on(type:&nbsp;'connect') | Subscribes to **connect** events of the TCP Socket connection. | | off(type:&nbsp;'listening') | Unsubscribes from **listening** events of the UDP socket connection. |
| off(type:&nbsp;'connect') | Unsubscribes from **connect** events of the TCP Socket connection.| | on(type:&nbsp;'connect') | Subscribes to **connect** events of the TCP socket connection. |
| off(type:&nbsp;'connect') | Unsubscribes from **connect** events of the TCP socket connection.|
TLS Socket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs.
TLS socket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs.
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
| constructTLSSocketInstance() | Creates a **TLSSocket** object.| | constructTLSSocketInstance() | Creates a **TLSSocket** object.|
| bind() | Binds the IP address and port number.| | bind() | Binds the IP address and port number.|
| close(type:&nbsp;'error') | Closes a Socket connection.| | close(type:&nbsp;'error') | Closes a socket connection.|
| connect() | Sets up a connection to the specified IP address and port number.| | connect() | Sets up a connection to the specified IP address and port number.|
| 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 TLS socket 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 TLS socket 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 TLS socket connection.|
| off(type:&nbsp;'error') | Unsubscribes from **error** events of the TLS Socket connection.| | off(type:&nbsp;'error') | Unsubscribes from **error** events of the TLS socket connection.|
| off(type:&nbsp;'message') | Unsubscribes from **message** events of the TLS Socket connection.| | off(type:&nbsp;'message') | Unsubscribes from **message** events of the TLS socket connection.|
| on(type:&nbsp;'close') | Subscribes to **close** events of the TLS Socket connection.| | on(type:&nbsp;'close') | Subscribes to **close** events of the TLS socket connection.|
| on(type:&nbsp;'error') | Subscribes to **error** events of the TLS Socket connection.| | on(type:&nbsp;'error') | Subscribes to **error** events of the TLS socket connection.|
| on(type:&nbsp;'message') | Subscribes to **message** events of the TLS Socket connection.| | on(type:&nbsp;'message') | Subscribes to **message** events of the TLS socket connection.|
| send() | Sends data.| | send() | Sends data.|
| setExtraOptions() | Sets other properties of the TLS Socket connection.| | setExtraOptions() | Sets other properties of the TLS socket connection.|
## Transmitting Data over TCP/UDP Socket Connections ## Transmitting Data over TCP Socket or UDP Socket 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 UDP socket and TCP socket connections. The following uses data transmission over a TCP socket connection as an example.
1. Import the required **socket** module. 1. Import the required **socket** module.
2. Create a **TCPSocket** object. 2. Create a TCP socket connection. A **TCPSocket** object is returned.
3. (Optional) Subscribe to TCP Socket connection events. 3. (Optional) Subscribe to TCP socket 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.
5. Set up a connection to the specified IP address and port number. 5. Set up a connection to the specified IP address and port number.
6. Send data. 6. Send data over the connection.
7. Enable the TCP Socket connection to be automatically closed after use. 7. Enable the TCP socket connection to be automatically closed after use.
```js ```js
import socket from '@ohos.net.socket' import socket from '@ohos.net.socket'
// Create a TCPSocket object. // Create a TCP socket connection. A TCPSocket object is returned.
let tcp = socket.constructTCPSocketInstance(); let tcp = socket.constructTCPSocketInstance();
// Subscribe to TCP Socket connection events. // Subscribe to events of the TCPSocket object.
tcp.on('message', value => { tcp.on('message', value => {
console.log("on message") console.log("on message")
let buffer = value.message let buffer = value.message
...@@ -139,7 +143,7 @@ tcp.bind(bindAddress, err => { ...@@ -139,7 +143,7 @@ tcp.bind(bindAddress, err => {
} }
console.log('connect success'); console.log('connect success');
// Send data. // Send data over the connection.
tcp.send({ tcp.send({
data: 'Hello, server!' data: 'Hello, server!'
}, err => { }, err => {
...@@ -152,7 +156,7 @@ tcp.bind(bindAddress, err => { ...@@ -152,7 +156,7 @@ tcp.bind(bindAddress, err => {
}); });
}); });
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. // Enable the socket connection to be automatically closed after use. Then, unsubscribe from events of the connection.
setTimeout(() => { setTimeout(() => {
tcp.close((err) => { tcp.close((err) => {
console.log('close socket.') console.log('close socket.')
...@@ -163,11 +167,92 @@ setTimeout(() => { ...@@ -163,11 +167,92 @@ setTimeout(() => {
}, 30 * 1000); }, 30 * 1000);
``` ```
## Implementing Data Transmission over TCP Socket Server Connections
### How to Develop
The TCP socket server connection process is described as follows:
1. Import the required **socket** module.
2. Create a TCP socket server connection. A **TCPSocketServer** object is returned.
3. Bind the local IP address and port, and listen for and accept TCP socket connections established over the socket.
4. Subscribe to **connect** events of the **TCPSocketServer** object to listen for client connection status changes.
5. Set up a connection between the client and the server. A **TCPSocketConnection** object is returned.
6. Subscribe to events of the **TCPSocketConnection** object, and send data to the client through the **TCPSocketConnection** object.
7. Close the connection between the client and the server.
8. Unsubscribe from events of the **TCPSocketConnection** and **TCPSocketServer** objects.
```js
import socket from '@ohos.net.socket'
// Create a TCP socket server connection. A TCPSocketServer object is returned.
let tcpServer = socket.constructTCPSocketServerInstance();
// Bind the local IP address and port number for listening.
tcpServer.listen({ address: "192.168.xx.xxx", port: xxxx, family: 1 }, err => {
if (err) {
console.log("listen fail");
return;
}
console.log("listen success");
})
// Subscribe to connect events of the TCPSocketServer object.
tcpServer.on('connect', function(client) {
// Subscribe to events of the TCPSocketConnection object.
client.on('close', () => {
console.log("on close success");
});
client.on('message', function(value) {
let buffer = value.message;
let dataView = new DataView(buffer);
let str = "";
for (let i = 0; i < dataView.byteLength; ++i) {
str += String.fromCharCode(dataView.getUint8(i));
}
console.log("received message--:" + str);
console.log("received address--:" + value.remoteInfo.address);
console.log("received family--:" + value.remoteInfo.family);
console.log("received port--:" + value.remoteInfo.port);
console.log("received size--:" + value.remoteInfo.size);
});
// Send data to the client.
client.send({data: 'Hello, client!'}, err => {
if (err) {
console.log('send fail');
return;
}
console.log('send success');
});
// Close the connection between the client and the server.
client.close(err => {
if (err) {
console.log('close fail');
return;
}
console.log('close success');
});
// Unsubscribe from events of the TCPSocketConnection object.
setTimeout(() => {
client.off('message');
client.off('close');
}, 10 * 1000);
});
// Unsubscribe from events of the TCPSocketServer object.
setTimeout(() => {
tcpServer.off('connect');
}, 30 * 1000);
```
## Implementing Encrypted Data Transmission over TLS Socket Connections ## Implementing Encrypted Data Transmission over TLS Socket Connections
### How to Develop ### How to Develop
TLS Socket connection process on the client: The TLSsocket connection process on the client is described as follows:
1. Import the required **socket** module. 1. Import the required **socket** module.
...@@ -175,21 +260,21 @@ TLS Socket connection process on the client: ...@@ -175,21 +260,21 @@ TLS Socket connection process on the client:
3. For two-way authentication, upload the client CA certificate and digital certificate. For one-way authentication, upload the client CA certificate. 3. For two-way authentication, upload the client CA certificate and digital certificate. For one-way authentication, upload the client CA certificate.
4. Create a **TLSSocket** object. 4. Create a TLS socket connection. A **TLSsocket** object is returned.
5. (Optional) Subscribe to TLS Socket connection events. 5. (Optional) Subscribe to TLS socket connection events.
6. Send data. 6. Send data over the connection.
7. Enable the TLS Socket connection to be automatically closed after use. 7. Enable the TLS socket connection to be automatically closed after use.
```js ```js
import socket from '@ohos.net.socket' import socket from '@ohos.net.socket'
// Create a TLS Socket connection (for two-way authentication). // Create a TLS socket connection (for two-way authentication). A TLSSocket object is returned.
let tlsTwoWay = socket.constructTLSSocketInstance(); let tlsTwoWay = socket.constructTLSSocketInstance();
// Subscribe to TLS Socket connection events. // Subscribe to TLS socket connection events.
tlsTwoWay.on('message', value => { tlsTwoWay.on('message', value => {
console.log("on message") console.log("on message")
let buffer = value.message let buffer = value.message
...@@ -246,7 +331,7 @@ tlsTwoWay.connect(options, (err, data) => { ...@@ -246,7 +331,7 @@ tlsTwoWay.connect(options, (err, data) => {
console.log(data); console.log(data);
}); });
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. // Enable the socket connection to be automatically closed after use. Then, unsubscribe from events of the connection.
tlsTwoWay.close((err) => { tlsTwoWay.close((err) => {
if (err) { if (err) {
console.log("close callback error = " + err); console.log("close callback error = " + err);
...@@ -258,10 +343,10 @@ tlsTwoWay.close((err) => { ...@@ -258,10 +343,10 @@ tlsTwoWay.close((err) => {
tlsTwoWay.off('close'); tlsTwoWay.off('close');
}); });
// Create a TLS Socket connection (for one-way authentication). // Create a TLS socket connection (for one-way authentication). A TLSsocket object is returned.
let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication
// Subscribe to TLS Socket connection events. // Subscribe to TLS socket connection events.
tlsTwoWay.on('message', value => { tlsTwoWay.on('message', value => {
console.log("on message") console.log("on message")
let buffer = value.message let buffer = value.message
...@@ -307,7 +392,7 @@ tlsOneWay.connect(oneWayOptions, (err, data) => { ...@@ -307,7 +392,7 @@ tlsOneWay.connect(oneWayOptions, (err, data) => {
console.log(data); console.log(data);
}); });
// Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. // Enable the socket connection to be automatically closed after use. Then, unsubscribe from events of the connection.
tlsTwoWay.close((err) => { tlsTwoWay.close((err) => {
if (err) { if (err) {
console.log("close callback error = " + err); console.log("close callback error = " + err);
...@@ -319,11 +404,3 @@ tlsTwoWay.close((err) => { ...@@ -319,11 +404,3 @@ tlsTwoWay.close((err) => {
tlsTwoWay.off('close'); tlsTwoWay.off('close');
}); });
``` ```
## Samples
The following samples are provided to help you better understand how to develop Socket connection features:
- [`Socket`: Socket Connection (ArkTS) (API9)] (https://gitee.com/openharmony/applications_app_samples/tree/master/code/BasicFeature/Connectivity/Socket)
- [UDP Socket (ArkTS) (API9)](https://gitee.com/openharmony/codelabs/tree/master/NetworkManagement/UdpDemoOH)
- [TCP Socket (ArkTS) (API9)](https://gitee.com/openharmony/codelabs/tree/master/NetworkManagement/TcpSocketDemo)
# Subscribing to State Changes of a Remote Object # Subscribing to State Changes of a Remote Object
## Overview
IPC/RPC allows you to subscribe to the state changes of a remote stub object. When the remote stub object dies, a death notification will be sent to your local proxy object. Such subscription and unsubscription are controlled by APIs. To be specific, you need to implement the **DeathRecipient** interface and the **onRemoteDied** API to clear resources. This callback is invoked when the process accommodating the remote stub object dies, or the device accommodating the remote stub object leaves the network. It is worth noting that these APIs should be called in the following order: The proxy object must first subscribe to death notifications of the stub object. If the stub object is in the normal state, the proxy object can cancel the subscription as required. If the process of the stub object exits or the device hosting the stub object goes offline, subsequent operations customized by the proxy object will be automatically triggered. IPC/RPC allows you to subscribe to the state changes of a remote stub object. When the remote stub object dies, a death notification will be sent to your local proxy object. Such subscription and unsubscription are controlled by APIs. To be specific, you need to implement the **DeathRecipient** interface and the **onRemoteDied** API to clear resources. This callback is invoked when the process accommodating the remote stub object dies, or the device accommodating the remote stub object leaves the network. It is worth noting that these APIs should be called in the following order: The proxy object must first subscribe to death notifications of the stub object. If the stub object is in the normal state, the proxy object can cancel the subscription as required. If the process of the stub object exits or the device hosting the stub object goes offline, subsequent operations customized by the proxy object will be automatically triggered.
## When to Use ## When to Use
......
# WebSocket Connection # WebSocket Connection
## When to Use ## Overview
You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event. You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event.
......
...@@ -16,7 +16,11 @@ ...@@ -16,7 +16,11 @@
- [Database Backup and Restoration](data-backup-and-restore.md) - [Database Backup and Restoration](data-backup-and-restore.md)
- [Database Encryption](data-encryption.md) - [Database Encryption](data-encryption.md)
- [Access Control by Device and Data Level](access-control-by-device-and-data-level.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
- [Cross-Application Data Sharing Overview](share-device-data-across-apps-overview.md) - [Data Sharing Overview](data-share-overview.md)
- [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md) - [Unified Data Definition](unified-data-definition.md)
- [Sharing Data in Silent Access](share-data-by-silent-access.md) - One-to-Many Data Sharing (for System Applications Only)
- [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
- [Silent Access via the DatamgrService](share-data-by-silent-access.md)
- Many-to-Many Data Sharing
- [Sharing Data Using Unified Data Channels](unified-data-channels.md)
\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
## Function ## 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 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 storage: provides data persistence capabilities, which can be classified into user preferences, key-value (KV) stores, and relational database (RDB) stores by data characteristics.
...@@ -16,9 +16,9 @@ The database stores created by an application are saved to the application sandb ...@@ -16,9 +16,9 @@ The database stores created by an application are saved to the application sandb
## Working Principles ## 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. The data management module includes preferences, KV data management (KV-Store), relational data management (RelatoinalStore), distributed data object (DataObject), cross-application data management (DataShare), and unified data management framework (UDMF). 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 **Figure 1** Data management architecture
![dataManagement](figures/dataManagement.jpg) ![dataManagement](figures/dataManagement.jpg)
...@@ -33,4 +33,7 @@ The data management module includes user preferences (**Preferences**), KV data ...@@ -33,4 +33,7 @@ The data management module includes user preferences (**Preferences**), KV 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. - **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.
- **UDMF**: defines the data language and standards for cross-application and cross-device data interaction, improving data interaction efficiency. The UDMF provides secure and standard data transmission channels and supports different levels of data access permissions and lifecycle management policies. It helps implement efficient data sharing across applications and devices.
- **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. - **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.
...@@ -68,7 +68,7 @@ The following table lists the APIs used for persisting user preference data. For ...@@ -68,7 +68,7 @@ The following table lists the APIs used for persisting user preference data. For
return; return;
} }
console.info('Succeeded in getting preferences.'); console.info('Succeeded in getting preferences.');
// Perform related data operations. // Before performing related data operations, obtain a Preferences instance.
}) })
} catch (err) { } catch (err) {
console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`); console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`);
...@@ -93,7 +93,7 @@ The following table lists the APIs used for persisting user preference data. For ...@@ -93,7 +93,7 @@ The following table lists the APIs used for persisting user preference data. For
return; return;
} }
console.info('Succeeded in getting preferences.'); console.info('Succeeded in getting preferences.');
// Perform related data operations. // Before performing related data operations, obtain a Preferences instance.
}) })
} catch (err) { } catch (err) {
console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`); console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`);
...@@ -220,4 +220,4 @@ The following table lists the APIs used for persisting user preference data. For ...@@ -220,4 +220,4 @@ The following table lists the APIs used for persisting user preference data. For
} catch (err) { } catch (err) {
console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`); console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`);
} }
``` ```
\ No newline at end of file
...@@ -18,7 +18,7 @@ A relational database (RDB) store is used to store data in complex relational mo ...@@ -18,7 +18,7 @@ A relational database (RDB) store is used to store data in complex relational mo
**RelationalStore** provides APIs for applications to perform data operations. With SQLite as the underlying persistent storage engine, **RelationalStore** provides SQLite database features, including transactions, indexes, views, triggers, foreign keys, parameterized queries, prepared SQL statements, and more. **RelationalStore** provides APIs for applications to perform data operations. With SQLite as the underlying persistent storage engine, **RelationalStore** provides SQLite database features, including transactions, indexes, views, triggers, foreign keys, parameterized queries, prepared SQL statements, and more.
**Figure 1** Working mechanism **Figure 1** Working mechanism
![relationStore_local](figures/relationStore_local.jpg) ![relationStore_local](figures/relationStore_local.jpg)
...@@ -37,23 +37,23 @@ A relational database (RDB) store is used to store data in complex relational mo ...@@ -37,23 +37,23 @@ A relational database (RDB) store is used to store data in complex relational mo
The following table lists the APIs used for RDB 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 [RDB Store](../reference/apis/js-apis-data-relationalStore.md). The following table lists the APIs used for RDB 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 [RDB Store](../reference/apis/js-apis-data-relationalStore.md).
| API| Description| | API| Description|
| -------- | -------- | | -------- | -------- |
| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback&lt;RdbStore&gt;): void | Obtains a **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.| | getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback&lt;RdbStore&gt;): void | Obtains a **RdbStore** instance to implement RDB store operations. You can set **RdbStore** parameters based on actual requirements and use **RdbStore** APIs to perform data operations.|
| executeSql(sql: string, bindArgs: Array&lt;ValueType&gt;, callback: AsyncCallback&lt;void&gt;):void | Executes an SQL statement that contains specified arguments but returns no value.| | executeSql(sql: string, bindArgs: Array&lt;ValueType&gt;, callback: AsyncCallback&lt;void&gt;):void | Executes an SQL statement that contains specified arguments but returns no value.|
| insert(table: string, values: ValuesBucket, callback: AsyncCallback&lt;number&gt;):void | Inserts a row of data into a table.| | insert(table: string, values: ValuesBucket, callback: AsyncCallback&lt;number&gt;):void | Inserts a row of data into a table.|
| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.| | update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.|
| delete(predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.| | delete(predicates: RdbPredicates, callback: AsyncCallback&lt;number&gt;):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.|
| query(predicates: RdbPredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;ResultSet&gt;):void | Queries data in the RDB store based on specified conditions.| | query(predicates: RdbPredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;ResultSet&gt;):void | Queries data in the RDB store based on specified conditions.|
| deleteRdbStore(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes an RDB store.| | deleteRdbStore(context: Context, name: string, callback: AsyncCallback&lt;void&gt;): void | Deletes an RDB store.|
## How to Develop ## How to Develop
1. Obtain an **RdbStore** instance.<br> Example: 1. Obtain an **RdbStore** instance.<br>Example:
Stage model: Stage model:
```js ```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module. import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import UIAbility from '@ohos.app.ability.UIAbility'; import UIAbility from '@ohos.app.ability.UIAbility';
...@@ -65,7 +65,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -65,7 +65,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
securityLevel: relationalStore.SecurityLevel.S1 // Database security level. securityLevel: relationalStore.SecurityLevel.S1 // Database security level.
}; };
// The current RDB store version is 3, and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES). // The RDB store version is 3, and the table structure is EMPLOYEE (NAME, AGE, SALARY, CODES).
const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // SQL statement for creating a data table. const SQL_CREATE_TABLE ='CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)'; // SQL statement for creating a data table.
relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => { relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => {
...@@ -97,7 +97,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -97,7 +97,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
store.version = 3; store.version = 3;
} }
// Perform operations such as adding, deleting, modifying, and querying data in the RDB store. // Before performing data operations on the database, obtain an RdbStore instance.
}); });
} }
...@@ -106,7 +106,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -106,7 +106,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
FA model: FA model:
```js ```js
import relationalStore from '@ohos.data.relationalStore'; // Import the module. import relationalStore from '@ohos.data.relationalStore'; // Import the module.
import featureAbility from '@ohos.ability.featureAbility'; import featureAbility from '@ohos.ability.featureAbility';
...@@ -151,7 +151,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -151,7 +151,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
store.version = 3; store.version = 3;
} }
// Perform operations such as adding, deleting, modifying, and querying data in the RDB store. // Before performing data operations on the database, obtain an RdbStore instance.
}); });
``` ```
...@@ -160,10 +160,12 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -160,10 +160,12 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
> >
> - The RDB store created by an application varies with the context. Multiple RDB stores are created for the same database name with different application contexts. For example, each UIAbility has its own context. > - The RDB store created by an application varies with the context. Multiple RDB stores are created for the same database name with different application contexts. For example, each UIAbility has its own context.
> >
> - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. If you want to move the files of an RDB store to another place for view, you must also move the temporary files with finename extensions **-wal** or **-shm** in the same directory. Once an application is uninstalled, the database files and temporary files generated by the application on the device are also removed. > - When an application calls **getRdbStore()** to obtain an RDB store instance for the first time, the corresponding database file is generated in the application sandbox. When the RDB store is used, temporary files ended with **-wal** and **-shm** may be generated in the same directory as the database file. If you want to move the database files to other places, you must also move these temporary files. After the application is uninstalled, the database files and temporary files generated on the device are also removed.
2. Use **insert()** to insert data to the RDB store. Example: 2. Use **insert()** to insert data to the RDB store.
Example:
```js ```js
const valueBucket = { const valueBucket = {
'NAME': 'Lisa', 'NAME': 'Lisa',
...@@ -177,13 +179,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -177,13 +179,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
return; return;
} }
console.info(`Succeeded in inserting data. rowId:${rowId}`); console.info(`Succeeded in inserting data. rowId:${rowId}`);
}) })
``` ```
> **NOTE** > **NOTE**
> >
> **RelationalStore** does not provide explicit flush operations for data persistence. Data inserted by **insert()** is stored in files persistently. > **RelationalStore** does not provide explicit flush operations for data persistence. Data inserted by **insert()** is stored in files persistently.
3. Modify or delete data based on the specified **Predicates** instance. 3. Modify or delete data based on the specified **Predicates** instance.
Use **update()** to modify data and **delete()** to delete data. Use **update()** to modify data and **delete()** to delete data.
...@@ -258,9 +260,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -258,9 +260,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
Example: Example:
> **NOTE**
>
> After the deletion, you are advised to set the database object to null.
Stage model: Stage model:
```js ```js
import UIAbility from '@ohos.app.ability.UIAbility'; import UIAbility from '@ohos.app.ability.UIAbility';
...@@ -271,6 +277,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -271,6 +277,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return; return;
} }
store = null;
console.info('Succeeded in deleting RdbStore.'); console.info('Succeeded in deleting RdbStore.');
}); });
} }
...@@ -279,7 +286,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -279,7 +286,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
FA model: FA model:
```js ```js
import featureAbility from '@ohos.ability.featureAbility'; import featureAbility from '@ohos.ability.featureAbility';
...@@ -291,6 +298,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -291,6 +298,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`);
return; return;
} }
store = null;
console.info('Succeeded in deleting RdbStore.'); console.info('Succeeded in deleting RdbStore.');
}); });
``` ```
\ No newline at end of file
# Cross-Application Data Sharing
## Introduction
OpenHarmony provides APIs for an application to manage its own data and share data with other applications.
Data needs to be shared in a wealth of scenarios. For example, the Contacts, short message service (SMS), and Gallery data always needs to be shared with other applications. However, certain data, such as accounts and passwords, cannot be shared. Some data, such as SMS messages, can be queried but not modified by other applications. Therefore, a secure and efficient cross-application data sharing mechanism for different data sharing scenarios and data privacy protection is very important.
Currently, OpenHarmony supports one-to-many and many-to-many cross-application data sharing, based on the number of the data provider applications involved.
## Basic Concepts
Before you start, understand the following concepts:
- **Data provider**: an application that provides data and implements related services. It is also called the producer or server.
- **Data consumer**: an application that accesses the data or services provided by the data provider. It is also called the client.
- **ValuesBucket**: a set of data to be inserted. It can be one or more data records in key-value (KV) pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array.
- **ResultSet**: a set of query results. It provides flexible modes for users to obtain various data.
- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database.
## Unified Data Definition
When data needs to be shared among multiple applications, a large amount of data needs to be converted for data interaction because the data definition and format vary with applications. To reduce application/service data interaction costs, OpenHarmony uses the unified data definition as the unified data language to build cross-application data interaction standards.
The unified data definition defines common data types. Applications can use the APIs provided by the Unified Data Management Framework (UDMF) to create and use these data types. For details, see [Unified Data Definition](unified-data-definition.md).
## One-to-Many Cross-Application Data Sharing
You can use **DataShare** to implement one-to-many data sharing across applications. Two implementation modes are provided, depending on whether the data provider is started in the cross-application data sharing.
### Implementation
The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs because the **DataShare** access mode does not vary with the data provision mode. This greatly reduces the learning time and development difficulty.
**DataShare** implements cross-application data sharing in either of the following ways:
- [Using DataShareExtensionAbility](share-data-by-datashareextensionability.md)
You need to implement an ExtensionAbility with callbacks in the HAP. When the data consumer calls an API, the ExtensionAbility of the data provider will be automatically started to invoke the registered callback.
You can use **DataShareExtensionAbility** when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases.
- [Using Silent Access via the DatamgrService](share-data-by-silent-access.md)
You need to configure database access rules in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider.
You can use this mode when the cross-application data access involves only database operations (data addition, deletion, modification, and query) or data hosted to the DatamgrService.
If your application is signed with a system signature, you can use both methods. When data is created for the first time, use **DataShareExtensionAbility**. When data is accessed and modified later, use the **DatamgrService** to share data. That is, the data provider is started only when the data is accessed for the first time.
### Restrictions
- **DataShare** is subject to the limitations on the database used by the data provider. For example, the supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use.
- The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by IPC.
- Currently, **dataShare** supports development based on the stage model only.
## Many-to-Many Cross-Application Data Sharing
In one-to-many cross-application data sharing, there is only one data provider. In many-to-many cross-application data sharing, you need to consider data definition, data exchange, and permission management. The UDMF provides a new data sharing and interaction mode to implement many-to-many cross-application data sharing.
### Implementation
[Sharing Data via Unified Data Channels](unified-data-channels.md)
Applications can call the APIs provided by the UDMF to write data that complies with the unified data definition to different data sharing channels of the UDMF. The data in these channels can be read by other applications. The data written into the UDMF is managed based on the permissions of the application, permissions of the data channels, and the permission management logic of the UDMF. Lifecycle management is also performed on the data written into the channels in the same way. In this way, the data scattered in each application is aggregated via different channels of the UDMF, improving the development efficiency and data experience of users.
...@@ -171,7 +171,7 @@ The following uses a single KV store as an example to describe how to implement ...@@ -171,7 +171,7 @@ The following uses a single KV store as an example to describe how to implement
return; return;
} }
console.info('Succeeded in getting KVStore.'); console.info('Succeeded in getting KVStore.');
// Perform related data operations. // Before performing related data operations, obtain a KV store instance.
}); });
} catch (e) { } catch (e) {
console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`);
...@@ -275,4 +275,4 @@ The following uses a single KV store as an example to describe how to implement ...@@ -275,4 +275,4 @@ The following uses a single KV store as an example to describe how to implement
} }
} }
}); });
``` ```
\ No newline at end of file
...@@ -16,8 +16,7 @@ There are two roles in **DataShare**: ...@@ -16,8 +16,7 @@ There are two roles in **DataShare**:
- Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper). - Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper).
**Figure 1** Data sharing mechanism **Figure 1** Data sharing mechanism
![dataShare](figures/dataShare.jpg) ![dataShare](figures/dataShare.jpg)
- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications. - The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications.
...@@ -32,7 +31,7 @@ There are two roles in **DataShare**: ...@@ -32,7 +31,7 @@ There are two roles in **DataShare**:
## How to Develop ## How to Develop
### Data Provider Application Development (Only for System Applications) ### Data Provider Application (Only for System Applications)
[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required. [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required.
...@@ -146,7 +145,7 @@ override the service implementation as required. For example, if the data provid ...@@ -146,7 +145,7 @@ override the service implementation as required. For example, if the data provid
"icon": "$media:icon", "icon": "$media:icon",
"description": "$string:description_datashareextability", "description": "$string:description_datashareextability",
"type": "dataShare", "type": "dataShare",
"uri": "datashare://com.samples.datasharetest.DataShare", "uri": "datashareproxy://com.samples.datasharetest.DataShare",
"exported": true, "exported": true,
"metadata": [{"name": "ohos.extension.dataShare", "resource": "$profile:data_share_config"}] "metadata": [{"name": "ohos.extension.dataShare", "resource": "$profile:data_share_config"}]
} }
...@@ -155,11 +154,11 @@ override the service implementation as required. For example, if the data provid ...@@ -155,11 +154,11 @@ override the service implementation as required. For example, if the data provid
**Table 2** Fields in the data_share_config.json file **Table 2** Fields in the data_share_config.json file
| Field| Description | Mandatory| | Field | Description | Mandatory |
| ------------ | ------------------------------------------------------------ | --- | | ------------- | ---------------------------------------- | ---- |
| tableConfig | Label configuration.| Yes| | tableConfig | Label configuration. | Yes |
| uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:<br> 1. *****: indicates all databases and tables.<br> 2. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}**: specifies a database.<br> 3. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}/{*tableName*}**: specifies a table.<br>If URIs of different formats are configured, only the URI with higher priority takes effect. | Yes| | uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:<br> - *****: indicates all databases and tables.<br> - **datashareproxy://{bundleName}/{moduleName}/{storeName}**: specifies a database.<br>- **datashareproxy://{bundleName}/{moduleName}/{storeName}/{tableName}**: specifies a table. | Yes |
| crossUserMode | Whether data is shared by multiple users. The value **1** means to share data between multiple users, and the value **2** means the opposite. | Yes| | crossUserMode | Whether data is shared by multiple users.<br>The value **1** means to share data between multiple users, and the value **2** means the opposite. | Yes |
**data_share_config.json Example** **data_share_config.json Example**
...@@ -170,18 +169,18 @@ override the service implementation as required. For example, if the data provid ...@@ -170,18 +169,18 @@ override the service implementation as required. For example, if the data provid
"crossUserMode": 1 "crossUserMode": 1
}, },
{ {
"uri": "datashare:///com.acts.datasharetest/entry/DB00", "uri": "datashareproxy://com.acts.datasharetest/entry/DB00",
"crossUserMode": 1 "crossUserMode": 1
}, },
{ {
"uri": "datashare:///com.acts.datasharetest/entry/DB00/TBL00", "uri": "datashareproxy://com.acts.datasharetest/entry/DB00/TBL00",
"crossUserMode": 2 "crossUserMode": 2
} }
] ]
``` ```
### Data Consumer Application Development ### Data Consumer Application
1. Import the dependencies. 1. Import the dependencies.
...@@ -195,7 +194,7 @@ override the service implementation as required. For example, if the data provid ...@@ -195,7 +194,7 @@ override the service implementation as required. For example, if the data provid
```js ```js
// Different from the URI defined in the module.json5 file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/). // Different from the URI defined in the module.json5 file, the URI passed in the parameter has an extra slash (/), because there is a DeviceID parameter between the second and the third slash (/).
let dseUri = ('datashare:///com.samples.datasharetest.DataShare'); let dseUri = ('datashareproxy://com.samples.datasharetest.DataShare');
``` ```
3. Create a **DataShareHelper** instance. 3. Create a **DataShareHelper** instance.
...@@ -239,3 +238,4 @@ override the service implementation as required. For example, if the data provid ...@@ -239,3 +238,4 @@ override the service implementation as required. For example, if the data provid
console.info(`dsHelper delete result:${data}`); console.info(`dsHelper delete result:${data}`);
}); });
``` ```
# Data Sharing Through Silent Access # Silent Access via the DatamgrService
## When to Use ## When to Use
In a typical cross-application data access scenario, an application may be started multiple times. In a typical cross-application data access scenario, the data provider may be started multiple times.
To reduce the number of application startup times and improve the access speed, OpenHarmony provides the silent access feature, which allows direct access to the database without starting the data provider. To reduce the number of startup times of the data provider and improve the access speed, OpenHarmony provides the silent access feature, which allows access to the database without starting the data provider.
Silent access supports only basic database access. If service processing is required, implement service processing in the data consumer. In silent data access, the DatamgrService accesses and modifies data without starting the data provider.
If the service processing is complex, use **DataShareExtensionAbility** to start the data provider. The DatamgrService supports basic database access or data hosting only. If service processing is required, the service processing needs to be encapsulated into APIs for the data consumer to call.
If the service processing is too complex to be processed by the data consumer, use **DataShareExtensionAbility** to start the data provider.
## Working Principles ## Working Principles
The DatamgrService can serve as a proxy to access the following types of data:
- Persistent data
Persistent data belongs to the database of the data provider. It is stored in the sandbox of the data provider and can be shared in declaration mode by the data provider. Persistent data is configured as data tables for access.
- Process data
The process data managed by the **DatamgrService** is stored in the DatamgrService sandbox in JSON or byte format. This type of data is automatically deleted 10 days after no subscription.
| Type | Storage Location | Data Format | Validity Period | Application Scenario |
| ----- | --------- | ----------- | ------------ | --------------------------------- |
| Persistent data| Sandbox of the data provider | Tables in the database | Permanent storage | RDB data applications, such as schedules and conferences. |
| Process data | DatamgrService sandbox| JSON or byte| Automatically deleted 10 days after no subscription| Applications featuring simple and time-sensitive data, such as step count, weather, and heart rate.|
**Figure 1** Silent access **Figure 1** Silent access
![silent_dataShare](figures/silent_dataShare.jpg) ![silent_dataShare](figures/silent_dataShare.jpg)
...@@ -21,26 +42,271 @@ If the service processing is complex, use **DataShareExtensionAbility** to start ...@@ -21,26 +42,271 @@ If the service processing is complex, use **DataShareExtensionAbility** to start
- In silent access, **DatamgrService** obtains the access rules configured by the data provider through directory mapping, performs preprocessing based on rules, and accesses the database. - In silent access, **DatamgrService** obtains the access rules configured by the data provider through directory mapping, performs preprocessing based on rules, and accesses the database.
- To use silent access, the URIs must be in the following format: - To use silent access, the URIs must be in the following format:
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true
datashareproxy://{bundleName}/{dataPath}
The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data.
**dataPath** identifies the data. It can be customized and must be unique in the same data provider application.
"Proxy=true" means to access data without starting the data provider. If **Proxy** is not set to **true**, the data provider is started. ## Constraints
The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data. - Currently, only the RDB stores support silent data access.
- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing.
- The proxy is not allowed to create a database for persistent data. To create a database, you must start the data provider.
- If the data provider is an application with a normal signature, the data read/write permission must be system_basic or higher.
## Constraints ## Available APIs
- Currently, only RDB stores support silent access. The following table lists the APIs for silent data access. Most of the APIs are executed asynchronously in callback or promise mode. In the following table, callback-based APIs are used as an example. For more information about the APIs, see [Data Sharing](../reference/apis/js-apis-data-dataShare.md).
- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing. ### Common API
| API | Description |
| ---------------------------------------- | -------------------- |
| createDataShareHelper(context: Context, uri: string, options: DataShareHelperOptions, callback: AsyncCallback&lt;DataShareHelper&gt;): void | Creates a **DataShareHelper** instance.|
### APIs for Persistent Data
| API | Description |
| ---------------------------------------- | -------------------- |
| insert(uri: string, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Inserts a row of data into a table. |
| delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback&lt;number&gt;): void | Deletes one or more data records from the database. |
| query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array&lt;string&gt;, callback: AsyncCallback&lt;DataShareResultSet&gt;): void | Queries data in the database. |
| update(uri: string, predicates: dataSharePredicates.DataSharePredicates, value: ValuesBucket, callback: AsyncCallback&lt;number&gt;): void | Updates data in the database. |
| addTemplate(uri: string, subscriberId: string, template: Template): void | Adds a data template with the specified subscriber. |
| on(type: 'rdbDataChange', uris: Array&lt;string&gt;, templateId: TemplateId, callback: AsyncCallback&lt;RdbDataChangeNode&gt;): Array&lt;OperationResult | Subscribes to the changes of the data corresponding to the specified URI and template.|
### APIs for Process Data
| API | Description |
| ---------------------------------------- | ------------------ |
| publish(data: Array&lt;PublishedItem&gt;, bundleName: string, version: number, callback: AsyncCallback&lt;Array&lt;OperationResult&gt;&gt;): void | Publish data to the **DatamgrService**.|
| on(type: 'publishedDataChange', uris: Array&lt;string&gt;, subscriberId: string, callback: AsyncCallback&lt;PublishedDataChangeNode&gt;): Array&lt;OperationResult&gt; | Subscribes to changes of the published data. |
## Implementation of the Persistence Data
The following describes how to share an RDB store.
### Data Provider Application
1. In the **module.json5** file, set the ID, read/write permissions, and basic information of the table to be shared under **proxyDatas**.
**Table 1** Fields of proxyDatas in module.json5
| Field | Description | Mandatory |
| ----------------------- | ---------------------------------------- | ---- |
| uri | URI of the data, which is the unique identifier for cross-application data access. | Yes |
| requiredReadPermission | Permission required for reading data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md). | No |
| requiredWritePermission | Permission required for modifying data from the data proxy. If this parameter is not set, other applications are not allowed to modify the data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md). | No |
| metadata | Data source information, including the **name** and **resource** fields.<br> The **name** field identifies the configuration, which has a fixed value of **dataProperties**.<br> The value of **resource** is **$profile:{fileName}**, indicating that the name of the configuration file is **{fileName}.json**.| Yes |
**module.json5 example**
```json
"proxyDatas":[
{
"uri": "datashareproxy://com.acts.ohos.data.datasharetest/test",
"requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
"requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING",
"metadata": {
"name": "dataProperties",
"resource": "$profile:my_config"
}
}
]
```
**Table 2** Fields in my_config.json
| Field | Description | Mandatory |
| ----- | ---------------------------------------- | ---- |
| path | Data source path, in the **Database_name/Table_name** format. Currently, only RDB stores are supported. | Yes |
| type | Database type. Currently, only **rdb** is supported. | Yes |
| scope | Scope of the database.<br>- **module** indicates that the database is located in this module.<br>- **application** indicates that the database is located in this application.| No |
**my_config.json example**
```json
{
"path": "DB00/TBL00",
"type": "rdb",
"scope": "application"
}
```
### Data Consumer Application
1. Import dependencies.
```js
import dataShare from '@ohos.data.dataShare';
import dataSharePredicates from '@ohos.data.dataSharePredicates';
```
2. Define the URI string for communicating with the data provider.
```js
let dseUri = ('datashareproxy://com.acts.ohos.data.datasharetest/test');
```
3. Create a **DataShareHelper** instance.
```js
let dsHelper;
let abilityContext;
export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, "", {
isProxy: true
}, (err, data) => {
dsHelper = data;
});
}
}
```
4. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
```js
// Construct a piece of data.
let valuesBucket = {
'name': 'ZhangSan', 'age': 21, 'isStudent': false, 'Binary': new Uint8Array([1, 2, 3])
};
let updateBucket = {
'name': 'LiSi', 'age': 18, 'isStudent': true, 'Binary': new Uint8Array([1, 2, 3])
};
let predicates = new dataSharePredicates.DataSharePredicates();
let valArray = ['*'];
// Insert a piece of data.
dsHelper.insert(dseUri, valuesBucket, (err, data) => {
console.info(`dsHelper insert result:${data}`);
});
// Update data.
dsHelper.update(dseUri, predicates, updateBucket, (err, data) => {
console.info(`dsHelper update result:${data}`);
});
// Query data.
dsHelper.query(dseUri, predicates, valArray, (err, data) => {
console.info(`dsHelper query result:${data}`);
});
// Delete data.
dsHelper.delete(dseUri, predicates, (err, data) => {
console.info(`dsHelper delete result:${data}`);
});
```
5. Subscribe to the specified data.
```js
function onCallback(err, node: dataShare.RdbDataChangeNode) {
console.info("uri " + JSON.stringify(node.uri));
console.info("templateId " + JSON.stringify(node.templateId));
console.info("data length " + node.data.length);
for (let i = 0; i < node.data.length; i++) {
console.info("data " + node.data[i]);
}
}
let template = {
predicates: {
"p1": "select * from TBL00",
"p2": "select name from TBL00",
},
scheduler: ""
}
dsProxyHelper.addTemplate(dseUri, "111", template);
let templateId: dataShare.TemplateId = {
subscriberId: "111",
bundleNameOfOwner: "com.acts.ohos.data.datasharetestclient"
}
// When the DatamgrService modifies data, onCallback is invoked to return the data queried based on the rules in the template.
let result: Array<dataShare.OperationResult> = dsProxyHelper.on("rdbDataChange", [dseUri], templateId, onCallback);
```
## Implementation of the Process Data
The following describes how to host process data.
### (Optional) Data Provider Application
In the **module.json5** file of the data provider, set the process data ID, read/write permissions, and basic information under **proxyDatas**.
> **NOTE**
>
> - This step is optional.
> - If **proxyDatas** is not configured, the hosted data cannot be accessed by other applications.
> - If **proxyDatas** is not configured, you do not need to use the full data path. For example, you can use **weather** instead of **datashareproxy://com.acts.ohos.data.datasharetest/weather** when publishing, subscribing to, and querying data.
**Table 3** Fields of proxyDatas in module.json5
| Field | Description | Mandatory |
| ----------------------- | ----------------------------- | ---- |
| uri | URI of the data, which is the unique identifier for cross-application data access. | Yes |
| requiredReadPermission | Permission required for reading data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md).| No |
| requiredWritePermission | Permission required for modifying data from the data proxy. If this parameter is not set, other applications are not allowed to access data. For details about the supported permissions, see [Application Permission List](../security/permission-list.md).| No |
**module.json5 example**
```json
"proxyDatas": [
{
"uri": "datashareproxy://com.acts.ohos.data.datasharetest/weather",
"requiredReadPermission": "ohos.permission.GET_BUNDLE_INFO",
"requiredWritePermission": "ohos.permission.KEEP_BACKGROUND_RUNNING"
}
]
```
### Data Consumer Application
1. Import dependencies.
```js
import dataShare from '@ohos.data.dataShare';
```
2. Create a **DataShareHelper** instance.
```js
let dsHelper;
let abilityContext;
- A proxy cannot be used to create a database. If a database needs to be created, the data provider must be started. export default class EntryAbility extends UIAbility {
onWindowStageCreate(windowStage) {
abilityContext = this.context;
dataShare.createDataShareHelper(abilityContext, "", {isProxy : true}, (err, data) => {
dsHelper = data;
});
}
}
```
3. Use the APIs provided by **DataShareHelper** to access the services provided by the provider, for example, adding, deleting, modifying, and querying data.
## How to Develop ```js
// Construct two pieces of data. The first data is not configured with proxyDatas and cannot be accessed by other applications.
let data : Array<dataShare.PublishedItem> = [
{key:"city", subscriberId:"11", data:"xian"},
{key:"datashareproxy://com.acts.ohos.data.datasharetest/weather", subscriberId:"11", data:JSON.stringify("Qing")}];
// Publish data.
let result: Array<dataShare.OperationResult> = await dsProxyHelper.publish(data, "com.acts.ohos.data.datasharetestclient");
```
The URI must be in the following format: 4. Subscribe to the specified data.
datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true ```js
function onPublishCallback(err, node:dataShare.PublishedDataChangeNode) {
console.info("onPublishCallback");
}
let uris:Array<string> = ["city", "datashareproxy://com.acts.ohos.data.datasharetest/weather"];
let result: Array<dataShare.OperationResult> = dsProxyHelper.on("publishedDataChange", uris, "11", onPublishCallback);
```
For details about the development procedure and implementation, see [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md).
...@@ -7,7 +7,7 @@ The distributed application data synchronization allows the data of an applicati ...@@ -7,7 +7,7 @@ The distributed application data synchronization allows the data of an applicati
For example, when data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. You can use this feature in the distributed Gallery, Notepad, Contacts, and File Manager. For example, when data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. You can use this feature in the distributed Gallery, Notepad, Contacts, and File Manager.
For details about how to subscribe to database change notifications between different applications, see [Sharing Application Data with Other Applications](share-device-data-across-apps-overview.md). For details about how to subscribe to database change notifications between different applications, see [Cross-Application Data Sharing](data-share-overview.md).
The data storage modes vary depending on the lifecycle of data to be synchronized: The data storage modes vary depending on the lifecycle of data to be synchronized:
...@@ -24,7 +24,7 @@ In a distributed scenario, cross-device collaboration demands consistent data be ...@@ -24,7 +24,7 @@ In a distributed scenario, cross-device collaboration demands consistent data be
The data consistency can be classified into the following types: The data consistency can be classified into the following types:
- Strong consistency: When data is inserted, deleted, or modified on a device, other devices in the same network can obtain the updates eventually, but may not immediately. - Strong consistency: When data is inserted, deleted, or modified on a device, other devices in the same network will obtain the latest data immediately. Once data is modified, the devices can read the updated data eventually, but may not read the updated data immediately.
- Weak consistency: When data is added, deleted, or modified on a device, other devices in the same network may or may not obtain the updates. The data on these devices may be inconsistent after a certain period of time. - Weak consistency: When data is added, deleted, or modified on a device, other devices in the same network may or may not obtain the updates. The data on these devices may be inconsistent after a certain period of time.
......
此差异已折叠。
此差异已折叠。
...@@ -76,9 +76,9 @@ This JSON file contains two attributes: **MetaData** and **Channels**. ...@@ -76,9 +76,9 @@ This JSON file contains two attributes: **MetaData** and **Channels**.
- **Create**: time when the file was created. This parameter is optional. - **Create**: time when the file was created. This parameter is optional.
- **Description**: additional information such as the vibration effect and creation information. This parameter is optional. - **Description**: additional information such as the vibration effect and creation information. This parameter is optional.
- **Channels** provides information about the vibration channel. It is a JSON array that holds information about each channel. It contains two attributes: **Parameters** and **Pattern**. - **Channels** provides information about the vibration channel. It is a JSON array that holds information about each channel. It contains two attributes: **Parameters** and **Pattern**.
- **Parameters** provides parameters related to the channel. Under it, **Index** indicates the channel ID. The value is fixed at **1** for a single channel. This parameter is mandatory. - **Parameters** provides parameters related to the channel. Under it, **Index** indicates the channel ID. The value is fixed at **1** for a single channel. This parameter is mandatory.
- **Pattern** indicates the vibration sequence. It is a JSON array. Under it, **Event** indicates a vibration event, which can be either of the following types: - **Pattern** indicates the vibration sequence. It is a JSON array. Under it, **Event** indicates a vibration event, which can be either of the following types:
- **transient**: short vibration - **transient**: short vibration
- **continuous**: long vibration - **continuous**: long vibration
The table below describes the parameters under **Event**. The table below describes the parameters under **Event**.
...@@ -89,7 +89,7 @@ The table below describes the parameters under **Event**. ...@@ -89,7 +89,7 @@ The table below describes the parameters under **Event**.
| StartTime | Start time of the vibration. This parameter is mandatory.| [0, 1800 000], in ms, without overlapping| | StartTime | Start time of the vibration. This parameter is mandatory.| [0, 1800 000], in ms, without overlapping|
| Duration | Duration of the vibration. This parameter is valid only when **Type** is **continuous**.| (10, 1600), in ms| | Duration | Duration of the vibration. This parameter is valid only when **Type** is **continuous**.| (10, 1600), in ms|
| Intensity | Intensity of the vibration. This parameter is mandatory.| [0, 100], a relative value that does not represent the actual vibration strength.| | Intensity | Intensity of the vibration. This parameter is mandatory.| [0, 100], a relative value that does not represent the actual vibration strength.|
| Frequency | Frequency of the vibration. This parameter is mandatory.| [0, 100], a relative value that does not represent the actual vibration frequency| | Frequency | Frequency of the vibration. This parameter is mandatory.| [0, 100], a relative value that does not represent the actual vibration frequency.|
The following requirements must be met: The following requirements must be met:
...@@ -221,45 +221,42 @@ The following requirements must be met: ...@@ -221,45 +221,42 @@ The following requirements must be met:
```ts ```ts
import vibrator from '@ohos.vibrator'; import vibrator from '@ohos.vibrator';
const FILE_NAME = "xxx.json";
// Obtain the file descriptor of the vibration configuration file. // Obtain the file descriptor of the vibration configuration file.
let fileDescriptor = undefined; async function getRawfileFd(fileName) {
getContext().resourceManager.getRawFd(FILE_NAME).then(value => { let rawFd = await globalThis.getContext().resourceManager.getRawFd(fileName);
fileDescriptor = { fd: value.fd, offset: value.offset, length: value.length }; return rawFd;
console.info('Succeed in getting resource file descriptor'); }
}).catch(error => {
console.error(`Failed to get resource file descriptor. Code: ${error.code}, message: ${error.message}`); // Close the file descriptor of the vibration configuration file.
}); async function closeRawfileFd(fileName) {
// To use startVibration and stopVibration, you must configure the ohos.permission.VIBRATE permission. await globalThis.getContext().resourceManager.closeRawFd(fileName)
try { }
// Start custom vibration.
vibrator.startVibration({ // Play the custom vibration. To use startVibration and stopVibration, you must configure the ohos.permission.VIBRATE permission.
type: "file", async function playCustomHaptic(fileName) {
hapticFd: { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length } try {
}, { let rawFd = await getRawfileFd(fileName);
usage: "alarm" vibrator.startVibration({
}).then(() => { type: "file",
console.info('Succeed in starting vibration'); hapticFd: { fd: rawFd.fd, offset: rawFd.offset, length: rawFd.length }
}, (error) => { }, {
console.error(`Failed to start vibration. Code: ${error.code}, message: ${error.message}`); usage: "alarm"
}); }).then(() => {
// Stop vibration in all modes. console.info('Succeed in starting vibration');
vibrator.stopVibration(function (error) { }, (error) => {
if (error) { console.error(`Failed to start vibration. Code: ${error.code}, message: ${error.message}`);
console.error(`Failed to stop vibration. Code: ${error.code}, message: ${error.message}`); });
return; vibrator.stopVibration(function (error) {
} if (error) {
console.info('Succeed in stopping vibration'); console.error(`Failed to stop vibration. Code: ${error.code}, message: ${error.message}`);
}) return;
} catch (error) { }
console.error(`An unexpected error occurred. Code: ${error.code}, message: ${error.message}`); console.info('Succeed in stopping vibration');
})
await closeRawfileFd(fileName);
} catch (error) {
console.error(`An unexpected error occurred. Code: ${error.code}, message: ${error.message}`);
}
} }
// Close the vibration file.
getContext().resourceManager.closeRawFd(FILE_NAME).then(() => {
console.info('Succeed in closing resource file descriptor');
}).catch(error => {
console.error(`Failed to close resource file descriptor. Code: ${error.code}, message: ${error.message}`);
});
``` ```
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册