提交 7e24f1ae 编写于 作者: G Gloria

cherry-pick docs from master

Signed-off-by: wusongqing<wusongqing@huawei.com>
上级 f041beee
# 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 ***
```
...@@ -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
...@@ -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**
# Traffic Management
## Introduction
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');
```
...@@ -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.
...@@ -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,15 +37,15 @@ A relational database (RDB) store is used to store data in complex relational mo ...@@ -37,15 +37,15 @@ 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
...@@ -53,7 +53,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -53,7 +53,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
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) => {
...@@ -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';
...@@ -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.
...@@ -254,13 +256,15 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -254,13 +256,15 @@ The following table lists the APIs used for RDB data persistence. Most of the AP
5. Delete the RDB store. 5. Delete the RDB store.
Use **deleteRdbStore()** to delete the RDB store and related database files. Use **deleteRdbStore()** to delete the RDB store and related database files.
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 +275,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -271,6 +275,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 +284,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -279,7 +284,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 +296,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP ...@@ -291,6 +296,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.');
}); });
``` ```
# 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.
...@@ -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).
# Cross-Application Data Sharing Overview
## Function
The application data on a device, such as the Contacts, short message service (SMS), and Gallery data, always needs to be shared with other applications. However, certain data, such as the accounts and passwords, cannot be shared. Certain data, such as SMS messages, can be accessed but not modified by other applications. The **DataShare** module provides a secure and easy-to-use mechanism for sharing data of an application with other applications on the same device.
## Basic Concepts
Before developing cross-application data sharing on a device, understand the following concepts:
- Data provider: an application that provides data and implements related services. It is also called the data 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 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 obtaining various data.
- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database.
## 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 to access the data, because the **DataShare** access mode does not vary with the data provisioning mode. This greatly reduces the learning time and development difficulty.
The cross-application data sharing can be implemented in either of the following ways:
- **DataShareExtensionAbility**
You can implement an ExtensionAbility with a callback 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.
This method is recommended when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases.
- Silent access
You can 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.
This method is recommended when the cross-application data access involves only the operations for adding, deleting, modifying, and querying data in databases.
## Constraints
- **DataShare** is subject to the limitations on the database used by the data provider. 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 inter-process communication (IPC).
- Currently, **dataShare** supports development based on the stage model only.
...@@ -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.
......
# Sharing Data via Unified Data Channels
## When to Use
In many-to-many data sharing across applications, a data channel needs to be provided to access data of different applications and share the data with other applications.
The Unified Data Management Framework (UDMF) provides unified data channels and standard data access interfaces for different service scenarios of many-to-many cross-application data sharing.
## Definition and Implementation of Unified Data Channels
The unified data channel provides cross-application data access for various service scenarios. It can temporarily store the unified data objects to be shared by an application, and manage the access permissions and lifecycle of the data according to certain policies.
The unified data channel is implemented by the system ability provided by the UDMF. When an application (data provider) needs to share data, it calls the **insert()** method provided by the UDMF to write the data to the UDMF data channel, and calls UDMF **update()** or **delete()** to update or delete the data. After passing the permission verification, the target application (data consumer) calls the UDMF **read()** to access the data. After the data is read, the UDMF performs lifecycle management of the data.
The unified data object (**UnifiedData**) is uniquely identified by a URI in the UDMF data channel. The URI is in the **udmf://*intention*/*bundleName*/*groupId*** format, where:
+ **udmf**: protocol used to provide the data channel.
+ *intention*: an enum of the data channel types supported by the UDMF.
+ *bundleName*: bundle name of the data source application.
+ *groupId*: group ID used for batch data management.
Currently, the UDMF provides the public data channel for cross-application data sharing.
**Public data channel**: allows applications to write and read data. The corresponding **intention** is **DATA_HUB**.
## Available APIs
The following table lists the UDMF APIs. All of them 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 [UDMF](../reference/apis/js-apis-data-udmf.md).
| API | Description |
|-----------------------------------------------------------------------------------------|---------------------------------------------|
| insertData(options: Options, data: UnifiedData, callback: AsyncCallback\<string>): void | Inserts data to the UDMF public data channel. A unique data identifier is returned.|
| updateData(options: Options, data: UnifiedData, callback: AsyncCallback\<void>): void | Updates the data in the UDMF public data channel. |
| queryData(options: Options, callback: AsyncCallback\<Array\<UnifiedData>>): void | Queries data in the UDMF public data channel. |
| deleteData(options: Options, callback: AsyncCallback\<Array\<UnifiedData>>): void | Deletes data from the UDMF public data channel. The deleted data set is returned.|
## How to Develop
The following example describes how to implement many-to-many data sharing. The data provider writes data to the UMDF public data channel, and updates and deletes the data. The data consumer obtains the data shared by the data provider.
### Data Provider
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Create a **UnifiedData** object and insert it into the UDMF public data channel.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'hello world!';
let unifiedData = new UDMF.UnifiedData(plainText);
// Specify the type of the data channel to which the data is to be inserted.
let options = {
intention: UDMF.Intention.DATA_HUB
}
try {
UDMF.insertData(options, unifiedData, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in inserting data. key = ${data}`);
} else {
console.error(`Failed to insert data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Insert data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
3. Update the **UnifiedData** object inserted.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'How are you!';
let unifiedData = new UDMF.UnifiedData(plainText);
// Specify the URI of the UnifiedData object to update.
let options = {
key: 'udmf://DataHub/com.ohos.test/0123456789'
};
try {
UDMF.updateData(options, unifiedData, (err) => {
if (err === undefined) {
console.info('Succeeded in updating data.');
} else {
console.error(`Failed to update data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Update data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
4. Delete the **UnifiedData** object from the UDMF public data channel.
```ts
// Specify the type of the data channel whose data is to be deleted.
let options = {
intention: UDMF.Intention.DATA_HUB
};
try {
UDMF.deleteData(options, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in deleting data. size = ${data.length}`);
for (let i = 0; i < data.length; i++) {
let records = data[i].getRecords();
for (let j = 0; j < records.length; j++) {
if (records[j].getType() === UDMF.UnifiedDataType.PLAIN_TEXT) {
let text = <UDMF.PlainText>(records[j]);
console.info(`${i + 1}.${text.textContent}`);
}
}
}
} else {
console.error(`Failed to delete data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Delete data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
### Data Consumer
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Query the **UnifiedData** object in the UDMF public data channel.
```ts
// Specify the type of the data channel whose data is to be queried.
let options = {
intention: UDMF.Intention.DATA_HUB
};
try {
UDMF.queryData(options, (err, data) => {
if (err === undefined) {
console.info(`Succeeded in querying data. size = ${data.length}`);
for (let i = 0; i < data.length; i++) {
let records = data[i].getRecords();
for (let j = 0; j < records.length; j++) {
if (records[j].getType() === UDMF.UnifiedDataType.PLAIN_TEXT) {
let text = <UDMF.PlainText>(records[j]);
console.info(`${i + 1}.${text.textContent}`);
}
}
}
} else {
console.error(`Failed to query data. code is ${err.code},message is ${err.message} `);
}
});
} catch(e) {
console.error(`Query data throws an exception. code is ${e.code},message is ${e.message} `);
}
```
# Unified Data Definition
## When to Use
To streamline cross-application data interaction of OpenHarmony and minimize the application/service data interaction costs, the Unified Data Management Framework (UDMF) provides standard data definitions to define common data types. Applications can use the APIs provided by the UDMF to create and use these data types.
## Unified Data Types
The UDMF provides the following unified data types:
**Basic data types**<br>Basic data types include File and Text, which can be used for cross-application and cross-platform data interaction. Figure 1 and Figure 2 illustrate the basic data types.
**Figure 1** UDMF File
![UDMF_FILE](figures/udmf_type_File.png)
Figure 2 UDMF Text
![UDMF_TEXT](figures/udmf_type_Text.png)
**System Defined Types (SDTs)**<br>The SDTs are specific to the platform or operating system, such as Form (UI card information), AppItem (app description information), and PixelMap (thumbnail). This type of data can be used for cross-application data interaction in a system or platform. Figure 3 illustrates the SDT data.
**Figure 3** UDMF SDT data
![UDMF_SDT](figures/udmf_type_SDT.png)
**App Defined Type (ADT)**<br>The SDT data is application-specific. This type of data can be used for across-platform data interaction for an application. As shown in Figure 4, the MyFile file format can be defined for use in an application ecosystem.
**Figure 4** UDMF ADT data
![UDMF_ADT](figures/udmf_type_ADT.png)
## Restrictions
- The size of each data record in the UDMF cannot exceed 2 MB.
- The UDMF supports data group management. The size of each group cannot exceed 4 MB.
## Available APIs
The UDMF provides the unified data object **UnifiedData** to encapsulate a group of data records **UnifiedRecord**. **UnifiedRecord** is an abstract definition of data content supported by the UDMF, for example, a text record or an image record. The data content type in a data record corresponds to **UnifiedDataType**.
The following table describes common UDMF APIs. For more information, see [UDMF](../reference/apis/js-apis-data-udmf.md).
| Class | API | Description |
|---------------|-------------------|-----------------------------------------------------------------------------------------------|
| UnifiedRecord | getType(): string | Obtains the data type of this data record.|
| UnifiedData | constructor(record: UnifiedRecord) | A constructor used to create a **UnifiedData** object with a data record. |
| UnifiedData | addRecord(record: UnifiedRecord): void | Adds a data record to this **UnifiedRecord** object. |
| UnifiedData | getRecords(): Array\<UnifiedRecord> | Obtains all data records from this **UnifiedData** object. The data obtained is of the **UnifiedRecord** type. You need to obtain the data type by using **getType** and convert the data type to a child class before using it.|
## How to Develop
The following describes how to create a **UnifiedData** object containing two data records: image and plain text.
1. Import the **@ohos.data.UDMF** module.
```ts
import UDMF from '@ohos.data.UDMF';
```
2. Create an image data record and initialize the **UnifiedData** object with the image data record.
(1) Create an image data record.
```ts
let image = new UDMF.Image();
```
(2) Modify object attributes.
```ts
// The Image object contains the imageUri attribute.
image.imageUri = '...';
```
(3) Access the object attributes.
```ts
console.info(`imageUri = ${image.imageUri}`);
```
(4) Create a **UnifiedData** instance.
```ts
let unifiedData = new UDMF.UnifiedData(image);
```
3. Create a plain text data record and add it to the **UnifiedData** instance created.
```ts
let plainText = new UDMF.PlainText();
plainText.textContent = 'this is textContent of plainText';
plainText.abstract = 'abstract of plainText';
plainText.details = {
plainKey1: 'plainValue1',
plainKey2: 'plainValue2',
};
unifiedData.addRecord(plainText);
```
4. Obtain all data records in this **UnifiedData** instance.
```ts
let records = unifiedData.getRecords();
```
5. Traverse each record, determine the data type of the record, and convert the record into a child class object to obtain the original data record.
```ts
for (let i = 0; i < records.length; i ++) {
// Read the type of the data record.
let type = records[i].getType();
switch (type) {
case UDMF.UnifiedDataType.IMAGE:
// Convert the data to obtain the original image data record.
let image = <UDMF.Image>(records[i]);
break;
case UDMF.UnifiedDataType.PLAIN_TEXT:
// Convert the data to obtain the original text record.
let plainText = <UDMF.PlainText>(records[i]);
break;
default:
break;
}
}
```
...@@ -760,147 +760,6 @@ Text in the **\<Text>** component is centered by default. You do not need to set ...@@ -760,147 +760,6 @@ Text in the **\<Text>** component is centered by default. You do not need to set
[Text](../reference/arkui-ts/ts-basic-components-text.md#example-1) [Text](../reference/arkui-ts/ts-basic-components-text.md#example-1)
## How do I set the controlButton attribute for the \<SideBarContainer> component?
Applicable to: OpenHarmony 3.2 Beta5 (API version 9)
**Solution**
The sample code is as follows:
```
@Entry
@Component
struct SideBarContainerExample {
normalIcon : Resource = $r("app.media.icon")
selectedIcon: Resource = $r("app.media.icon")
@State arr: number[] = [1, 2, 3]
@State current: number = 1
build() {
SideBarContainer(SideBarContainerType.Embed)
{
Column() {
ForEach(this.arr, (item, index) => {
Column({ space: 5 }) {
Image(this.current === item ? this.selectedIcon : this.normalIcon).width(64).height(64)
Text("Index0" + item)
.fontSize(25)
.fontColor(this.current === item ? '#0A59F7' : '#999')
.fontFamily('source-sans-pro,cursive,sans-serif')
}
.onClick(() => {
this.current = item
})
}, item => item)
}.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.backgroundColor('#19000000')
Column() {
Text('SideBarContainer content text1').fontSize(25)
Text('SideBarContainer content text2').fontSize(25)
}
.margin({ top: 50, left: 20, right: 30 })
}
.sideBarWidth(150)
.minSideBarWidth(50)
.controlButton({left:32,
top:32,
width:32,
height:32,
icons:{shown: $r("app.media.icon"),
hidden: $r("app.media.icon"),
switching: $r("app.media.icon")}})
.maxSideBarWidth(300)
.onChange((value: boolean) => {
console.info('status:' + value)
})
}
}
```
## How do I implement the dragging feature for the \<Grid> component?
Applicable to: OpenHarmony 3.2 Beta5 (API version 9)
**Solution**
1. Set the **editMode\(true\)** attribute of the **\<Grid>** component to specify whether the component enters the editing mode. In the editing mode, you can drag grid items.
2. Set the image displayed during dragging in the [onItemDragStart](../reference/arkui-ts/ts-container-grid.md#events) callback.
3. Obtain the drag start position and drag insertion position from the [onItemDrop](../reference/arkui-ts/ts-container-grid.md#events) callback, and complete the array position exchange logic in the [onDrag](../reference/arkui-ts/ts-universal-events-drag-drop.md#events) callback. The sample code is as follows:
```
@Entry
@Component
struct GridExample {
@State numbers: String[] = []
scroller: Scroller = new Scroller()
@State text: string = 'drag'
@Builder pixelMapBuilder () { // Drag style
Column() {
Text(this.text)
.fontSize(16)
.backgroundColor(0xF9CF93)
.width(80)
.height(80)
.textAlign(TextAlign.Center)
}
}
aboutToAppear() {
for (let i = 1;i <= 15; i++) {
this.numbers.push(i + '')
}
}
changeIndex(index1: number, index2: number) {// Exchange the array item position.
[this.numbers[index1], this.numbers[index2]] = [this.numbers[index2], this.numbers[index1]];
}
build() {
Column({ space: 5 }) {
Grid(this.scroller) {
ForEach(this.numbers, (day: string) => {
GridItem() {
Text(day)
.fontSize(16)
.backgroundColor(0xF9CF93)
.width(80)
.height(80)
.textAlign(TextAlign.Center)
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Up) {
this.text = day
}
})
}
})
}
.columnsTemplate('1fr 1fr 1fr')
.columnsGap(10)
.rowsGap(10)
.onScrollIndex((first: number) => {
console.info(first.toString())
})
.width('90%')
.backgroundColor(0xFAEEE0)
.height(300)
.editMode(true) // Set whether the grid enters the editing mode. In the editing mode, you can drag grid items.
.onItemDragStart((event: ItemDragInfo, itemIndex: number) => { // Triggered when a grid item starts to be dragged.
return this.pixelMapBuilder() // Set the image displayed during dragging.
})
.onItemDrop((event: ItemDragInfo, itemIndex: number, insertIndex: number, isSuccess: boolean) => { // Triggered when the dragged item is dropped on the drop target of the grid.
console.info('beixiang' + itemIndex + '', insertIndex + '') // itemIndex indicates the initial position of the dragged item; insertIndex indicates the index of the position to which the dragged item will be dropped.
this.changeIndex(itemIndex, insertIndex)
})
}.width('100%').margin({ top: 5 })
}
}
```
## Which API is used for URL encoding? ## Which API is used for URL encoding?
......
# ArkUI Component Development (ArkTS) # ArkUI Component Development (ArkTS)
## Can custom dialog boxes be defined or used in .ts files? ## Can custom dialog boxes be defined and used in .ts files?
Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) Applicable to: OpenHarmony 3.2 Beta 5 (API version 9)
Unfortunately, no. ArkTS syntax is required for defining and initializing custom dialog boxes. Therefore, they can be defined and used only in .ets files. Unfortunately not. Custom dialog boxes require ArkTS syntax for definition and initialization. Therefore, they can be defined and used only in .ets files.
**Reference** **Reference**
...@@ -245,8 +245,8 @@ When a custom dialog box contains a child component whose area size can be chang ...@@ -245,8 +245,8 @@ When a custom dialog box contains a child component whose area size can be chang
**Solution** **Solution**
- Method 1: Use the default style of the custom dialog box. In this case, the dialog box automatically adapts its width to the grid system and its height to the child components; the maximum height is 90% of the container height. - Method 1: Set the custom dialog box to the default style. In this style, the dialog box automatically adapts its width to the grid system and its height to the child components; the maximum height is 90% of the container height.
- Method 2: Use a custom style of the custom dialog box. In this case, the dialog box automatically adapts its width and height to the child components. - Method 2: Set the custom dialog box to a custom style. In this style, the dialog box automatically adapts its width and height to the child components.
**Reference** **Reference**
...@@ -685,3 +685,64 @@ You can use **focusControl.requestFocus** to control the focus of the text input ...@@ -685,3 +685,64 @@ You can use **focusControl.requestFocus** to control the focus of the text input
**Reference** **Reference**
[Focus Control](../reference/arkui-ts/ts-universal-attributes-focus.md) [Focus Control](../reference/arkui-ts/ts-universal-attributes-focus.md)
## How do I set the controlButton attribute for the \<SideBarContainer> component?
Applicable to: OpenHarmony 3.2 Beta5 (API version 9)
**Solution**
Refer to the following sample code:
```
@Entry
@Component
struct SideBarContainerExample {
normalIcon : Resource = $r("app.media.icon")
selectedIcon: Resource = $r("app.media.icon")
@State arr: number[] = [1, 2, 3]
@State current: number = 1
build() {
SideBarContainer(SideBarContainerType.Embed)
{
Column() {
ForEach(this.arr, (item, index) => {
Column({ space: 5 }) {
Image(this.current === item ? this.selectedIcon : this.normalIcon).width(64).height(64)
Text("Index0" + item)
.fontSize(25)
.fontColor(this.current === item ? '#0A59F7' : '#999')
.fontFamily('source-sans-pro,cursive,sans-serif')
}
.onClick(() => {
this.current = item
})
}, item => item)
}.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)
.backgroundColor('#19000000')
Column() {
Text('SideBarContainer content text1').fontSize(25)
Text('SideBarContainer content text2').fontSize(25)
}
.margin({ top: 50, left: 20, right: 30 })
}
.sideBarWidth(150)
.minSideBarWidth(50)
.controlButton({left:32,
top:32,
width:32,
height:32,
icons:{shown: $r("app.media.icon"),
hidden: $r("app.media.icon"),
switching: $r("app.media.icon")}})
.maxSideBarWidth(300)
.onChange((value: boolean) => {
console.info('status:' + value)
})
}
}
```
...@@ -4,6 +4,8 @@ When a user needs to download a file from the network to a local directory or sa ...@@ -4,6 +4,8 @@ When a user needs to download a file from the network to a local directory or sa
The operations for saving images, audio or video clips, and documents are similar. Call **save()** of the corresponding picker instance and pass in **saveOptions**. The operations for saving images, audio or video clips, and documents are similar. Call **save()** of the corresponding picker instance and pass in **saveOptions**.
The **save()** interface saves the file in the file manager, not in the Gallery.
## Saving Images or Video Files ## Saving Images or Video Files
...@@ -23,14 +25,14 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -23,14 +25,14 @@ The operations for saving images, audio or video clips, and documents are simila
3. Create a **photoViewPicker** instance and call [save()](../reference/apis/js-apis-file-picker.md#save) to open the **FilePicker** page to save the files. After the user selects the target folder, the file saving operation is complete. After the files are saved successfully, the URIs of the files saved are returned. 3. Create a **photoViewPicker** instance and call [save()](../reference/apis/js-apis-file-picker.md#save) to open the **FilePicker** page to save the files. After the user selects the target folder, the file saving operation is complete. After the files are saved successfully, the URIs of the files saved are returned.
<br>The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let URI = null; let uri = null;
const photoViewPicker = new picker.PhotoViewPicker(); const photoViewPicker = new picker.PhotoViewPicker();
photoViewPicker.save(photoSaveOptions).then((photoSaveResult) => { photoViewPicker.save(photoSaveOptions).then((photoSaveResult) => {
URI = photoSaveResult[0]; uri = photoSaveResult[0];
console.info('photoViewPicker.save to file succeed and URI is:' + URI); console.info('photoViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke photoViewPicker.save failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke photoViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -39,7 +41,7 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -39,7 +41,7 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**. 4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
...@@ -72,11 +74,11 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -72,11 +74,11 @@ The operations for saving images, audio or video clips, and documents are simila
The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let URI = null; let uri = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => { documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => {
URI = documentSaveResult[0]; uri = documentSaveResult[0];
console.info('documentViewPicker.save to file succeed and URI is:' + URI); console.info('documentViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke documentViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -85,7 +87,7 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -85,7 +87,7 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**. 4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
...@@ -118,11 +120,11 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -118,11 +120,11 @@ The operations for saving images, audio or video clips, and documents are simila
The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **save()** is read/write. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let URI = null; let uri = null;
const audioViewPicker = new picker.AudioViewPicker(); const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.save(audioSaveOptions).then((audioSelectResult) => { audioViewPicker.save(audioSaveOptions).then((audioSelectResult) => {
URI = audioSelectResult[0]; uri = audioSelectResult[0];
console.info('audioViewPicker.save to file succeed and URI is:' + URI); console.info('audioViewPicker.save to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke audioViewPicker.save failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke audioViewPicker.save failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -131,11 +133,11 @@ The operations for saving images, audio or video clips, and documents are simila ...@@ -131,11 +133,11 @@ The operations for saving images, audio or video clips, and documents are simila
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**. 4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_WRITE**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); let file = fs.openSync(uri, fs.OpenMode.READ_WRITE);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
5. Use [fs.writeSync](../reference/apis/js-apis-file-fs.md#writesync) to edit the file based on the FD, and then close the FD. 5. Use [fs.writeSync()](../reference/apis/js-apis-file-fs.md#writesync) to edit the file based on the FD, and then close the FD.
```ts ```ts
let writeLen = fs.writeSync(file.fd, 'hello, world'); let writeLen = fs.writeSync(file.fd, 'hello, world');
......
...@@ -35,14 +35,14 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -35,14 +35,14 @@ The **FilePicker** provides the following interfaces by file type:
4. Create a **photoPicker** instance and call [select()](../reference/apis/js-apis-file-picker.md#select) to open the **FilePicker** page for the user to select files. After the files are selected, [PhotoSelectResult](../reference/apis/js-apis-file-picker.md#photoselectresult) is returned. 4. Create a **photoPicker** instance and call [select()](../reference/apis/js-apis-file-picker.md#select) to open the **FilePicker** page for the user to select files. After the files are selected, [PhotoSelectResult](../reference/apis/js-apis-file-picker.md#photoselectresult) is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the **PhotoSelectResult**. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
```ts ```ts
let URI = null; let uri = null;
const photoViewPicker = new picker.PhotoViewPicker(); const photoViewPicker = new picker.PhotoViewPicker();
photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => { photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => {
URI = photoSelectResult.photoUris[0]; uri = photoSelectResult.photoUris[0];
console.info('photoViewPicker.select to file succeed and URI is:' + URI); console.info('photoViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke photoViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -51,7 +51,7 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -51,7 +51,7 @@ The **FilePicker** provides the following interfaces by file type:
5. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**. 5. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
...@@ -81,20 +81,20 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -81,20 +81,20 @@ The **FilePicker** provides the following interfaces by file type:
3. Create a **documentViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-3) to open the **FilePicker** page for the user to select documents. After the documents are selected, a result set containing the file URIs is returned. 3. Create a **documentViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-3) to open the **FilePicker** page for the user to select documents. After the documents are selected, a result set containing the file URIs is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
<br>For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attribute information, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md). For example, you can use [file management APIs](../reference/apis/js-apis-file-fs.md) to obtain file attribute information, such as the file size, access time, and last modification time, based on the URI. If you need to obtain the file name, use [startAbilityForResult](../../application-dev/application-models/uiability-intra-device-interaction.md).
> **NOTE** > **NOTE**
> >
> Currently, **DocumentSelectOptions** is not configurable. By default, all types of user files are selected. > Currently, **DocumentSelectOptions** is not configurable. By default, all types of user files are selected.
```ts ```ts
let URI = null; let uri = null;
const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance.
documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => { documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
URI = documentSelectResult[0]; uri = documentSelectResult[0];
console.info('documentViewPicker.select to file succeed and URI is:' + URI); console.info('documentViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -129,7 +129,7 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -129,7 +129,7 @@ The **FilePicker** provides the following interfaces by file type:
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**. 4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
...@@ -160,20 +160,20 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -160,20 +160,20 @@ The **FilePicker** provides the following interfaces by file type:
3. Create an **audioViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-6) to open the **FilePicker** page for the user to select audio files. After the files are selected, a result set containing the URIs of the audio files selected is returned. 3. Create an **audioViewPicker** instance, and call [**select()**](../reference/apis/js-apis-file-picker.md#select-6) to open the **FilePicker** page for the user to select audio files. After the files are selected, a result set containing the URIs of the audio files selected is returned.
<br>The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening. The permission on the URIs returned by **select()** is read-only. Further file operations can be performed based on the URIs in the result set. Note that the URI cannot be directly used in the **picker** callback to open a file. You need to define a global variable to save the URI and use a button to trigger file opening.
<br>For example, use the [file management interface](../reference/apis/js-apis-file-fs.md) to obtain the file handle (FD) of the audio clip based on the URI, and then develop the audio playback function based on the media service. For details, see [Audio Playback Development](../media/audio-playback-overview.md). For example, use the [file management interface](../reference/apis/js-apis-file-fs.md) to obtain the file handle (FD) of the audio clip based on the URI, and then develop the audio playback function based on the media service. For details, see [Audio Playback Development](../media/audio-playback-overview.md).
> **NOTE** > **NOTE**
> >
> Currently, **AudioSelectOptions** is not configurable. By default, all types of user files are selected. > Currently, **AudioSelectOptions** is not configurable. By default, all types of user files are selected.
```ts ```ts
let URI = null; let uri = null;
const audioViewPicker = new picker.AudioViewPicker(); const audioViewPicker = new picker.AudioViewPicker();
audioViewPicker.select(audioSelectOptions).then(audioSelectResult => { audioViewPicker.select(audioSelectOptions).then(audioSelectResult => {
URI = audioSelectOptions[0]; uri = audioSelectOptions[0];
console.info('audioViewPicker.select to file succeed and URI is:' + URI); console.info('audioViewPicker.select to file succeed and uri is:' + uri);
}).catch((err) => { }).catch((err) => {
console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`); console.error(`Invoke audioViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
}) })
...@@ -182,7 +182,7 @@ The **FilePicker** provides the following interfaces by file type: ...@@ -182,7 +182,7 @@ The **FilePicker** provides the following interfaces by file type:
4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**. 4. Use a button to trigger invocation of other functions. Use [fs.openSync()](../reference/apis/js-apis-file-fs.md#fsopensync) to open the file based on the URI and obtain the FD. Note that the **mode** parameter of **fs.openSync()** must be **fs.OpenMode.READ_ONLY**.
```ts ```ts
let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); let file = fs.openSync(uri, fs.OpenMode.READ_ONLY);
console.info('file fd: ' + file.fd); console.info('file fd: ' + file.fd);
``` ```
......
# Best Practices for Application Performance
This topic outlines some best practices for improving your application performance to live up to user expectations for quick startup, timely response, and no frame freezing.
Following these practices, you can reduce your application's startup time, response time, and frame loss.
- Improving application startup and response time
- [Speeding Up Application Cold Start](../performance/improve-application-startup-and-response/improve-application-cold-start-speed.md)
Application startup latency is a key factor that affects user experience. To speed up the application cold start, you are advised to perform optimization in the following four phases:
​ 1. Application process creation and initialization
​ 2. Application and ability initialization
​ 3. Ability lifecycle
​ 4. Home page loading and drawing
- [Speeding Up Application Response](../performance/improve-application-startup-and-response/improve-application-response.md)
A premium interaction experience requires quick response to user input. To improve your application's response time, you are advised to prevent the main thread from being blocked by non-UI tasks and reduce the number of component to be refreshed.
- Reducing frame loss
- [Reducing Nesting](../performance/reduce-frame-loss-and-frame-freezing/reduce-view-nesting-levels.md)
The smoothness of rendering the layout to the screen affects the user perceived quality. It is recommended that you minimize nesting in your code to shorten the render time.
- [Reducing Frame Loss](../performance/reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md)
Whether animations in your application run smoothly is a key factor that affects user experience. You are advised to use the system-provided animation APIs to reduce frame loss.
# Speeding Up Application Cold Start
Application startup latency is a key factor that affects user experience. When an application is started, the background does not have a process of the application, and therefore the system creates a new process and allocates it to the application. This startup mode is called cold start.
## Analyzing the Time Required for Application Cold Start
The cold start process of OpenHarmony applications can be divided into four phases: application process creation and initialization, application and ability initialization, ability lifecycle, and home page loading and drawing, as shown in the following figure.
![application-cold-start](../figure/application-cold-start.png)
## 1. Shortening Time Required for Application Process Creation And Initialization
In the phase of application process creation and initialization, the system creates and initializes an application process, including decoding the icon of the startup page (specified by **startWindowIcon**).
### Using startWindowIcon of Appropriate Resolution
With regard to the icon of the startup page, the recommended maximum resolution is 256 x 256 pixels. Larger resolutions may result in slow startup.
```json
"abilities": [
{
"name": "EntryAbility",
"srcEntrance": "./ets/entryability/EntryAbility.ts",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:startWindowIcon", // Modify the icon of the startup page. It is recommended that the icon be less than or equal to 256 pixels x 256 pixels.
"startWindowBackground": "$color:start_window_background",
"visible": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
]
```
## 2. Shortening Time Required for Application and Ability Initialization
In this phase of application and ability initialization, resources are loaded, VMs are created, application and ability related objects are created and initialized, and dependent modules are loaded.
### Minimizing the Number of Imported Modules
Before the application code is executed, the application must find and load all imported modules. Each additional third-party framework or module to be loaded by the application increases the startup time. The time required depends on the number and size of loaded third-party frameworks or modules. To speed up startup, use system-provided modules when possible and load the modules as required.
## 3. Shortening Time Required for Ability Lifecycle
In this phase of ability lifecycle, the ability lifecycle callbacks are executed.
### Avoiding Time-Consuming Operations in Ability Lifecycle Callbacks
In the application startup process, the system executes the ability lifecycle callbacks. Whenever possible, avoid performing time-consuming operations in these callbacks. You are advised to perform time-consuming operations through asynchronous tasks or execute them in other threads.
In these lifecycle callbacks, perform only necessary operations. For details, see [UIAbility Lifecycle](https://gitee.com/openharmony/docs/blob/master/en/application-dev/application-models/uiability-lifecycle.md).
## 4. Shortening Time Required for Home Page Loading and Drawing
In this phase of home page loading and drawing, the home page content is loaded, the layout is measured, and components are refreshed and drawn.
### Avoid time-consuming operations in the custom component lifecycle callbacks.
When the lifecycle of a custom component changes, the corresponding callback is called.
The **aboutToAppear** function is executed after the custom component instance is created and before the page is drawn. The following code asynchronously processes the time-consuming computing task in **aboutToAppear** to avoid executing the operation in this function and blocking the page drawing.
```javascript
@Entry
@Component
struct Index {
@State private text: string = undefined;
private count: number = undefined;
aboutToAppear() {
this.computeTaskAsync(); // Asynchronous task
this.text = "hello world";
}
build() {
Column({space: 10}) {
Text(this.text).fontSize(50)
}
.width('100%')
.height('100%')
.padding(10)
}
computeTask() {
this.count = 0;
while (this.count < 10000000) {
this.count++;
}
this.text = 'task complete';
}
// Asynchronous processing of the computing task
private computeTaskAsync() {
new Promise((resolved, rejected) => {
setTimeout(() => {// setTimeout is used to implement asynchronous processing.
this.computeTask();
}, 1000)
})
}
}
```
# Speeding Up Application Response
This topic provides the following tips for improving your application's response to user input.
- Prevent the main thread from being blocked by non-UI tasks.
- Reduce the number of components to be refreshed.
## Preventing Main Thread from Being Blocked by Non-UI Tasks
When the application responds to user input, its main thread should execute only UI tasks (such as preparation of data to be displayed and update of visible components). It is recommended that non-UI, time-consuming tasks (such as long-time content loading) be executed through asynchronous tasks or allocated to other threads.
### Using Asynchronous Component Loading
The **\<Image>** component has the asynchronous loading feature enabled by default. When an application loads a batch of local images to be displayed on the page, blank placeholder icons are displayed first, and then replaced by the images when these images have finished loading in other threads. In this way, image loading does not block page display. The following code is recommended only when the image loading takes a short time.
```javascript
@Entry
@Component
struct ImageExample1 {
build() {
Column() {
Row() {
Image('resources/base/media/sss001.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
Image('resources/base/media/sss002.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
Image('resources/base/media/sss003.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
Image('resources/base/media/sss004.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%')
}
// Several <Row> containers are omitted here. Each container contains the preceding <Image> components.
}
}
}
```
Recommendation: If it takes a short time to load an image, the benefits of asynchronous loading will be greatly undermined. In this case, change the value of the syncLoad attribute.
```javascript
@Entry
@Component
struct ImageExample1 {
build() {
Column() {
Row() {
Image('resources/base/media/sss001.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
Image('resources/base/media/sss002.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
Image('resources/base/media/sss003.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
Image('resources/base/media/sss004.jpg')
.border({ width: 1 }).borderStyle(BorderStyle.Dashed).aspectRatio(1).width('25%').height('12.5%').syncLoad(true)
}
// Several <Row> containers are omitted here. Each container contains the preceding <Image> components.
}
}
}
```
### Using TaskPool for Asynchronous Processing
Compared with the worker thread, [TaskPool](https://gitee.com/sqsyqqy/docs/blob/master/en/application-dev/reference/apis/js-apis-taskpool.md) provides the task priority setting and automatic thread pool management mechanism. The following is an example:
```javascript
import taskpool from '@ohos.taskpool';
@Concurrent
function computeTask(arr: string[]): string[] {
// Simulate a compute-intensive task.
let count = 0;
while (count < 100000000) {
count++;
}
return arr.reverse();
}
@Entry
@Component
struct AspectRatioExample {
@State children: string[] = ['1', '2', '3', '4', '5', '6'];
aboutToAppear() {
this.computeTaskInTaskPool();
}
async computeTaskInTaskPool() {
const param = this.children.slice();
let task = new taskpool.Task(computeTask, param);
// @ts-ignore
this.children = await taskpool.execute(task);
}
build() {
// Component layout
}
}
```
### Creating Asynchronous Tasks
The following code shows how to declare a long-running non-UI task as an asynchronous task through **Promise**. This allows the main thread to first focus on providing user feedback and completing the initial render, and then execute the asynchronous task when it is idle. After the asynchronous task is complete, related components are redrawn to refresh the page.
```javascript
@Entry
@Component
struct AspectRatioExample {
@State private children: string[] = ['1', '2', '3', '4', '5', '6'];
private count: number = undefined;
aboutToAppear() {
this.computeTaskAsync(); // Invoke the asynchronous compute function.
}
// Simulate a compute-intensive task.
computeTask() {
this.count = 0;
while (this.count < 100000000) {
this.count++;
}
this.children = this.children.reverse();
}
computeTaskAsync() {
new Promise((resolved, rejected) => {
setTimeout(() => {// setTimeout is used to implement asynchronous processing.
this.computeTask();
}, 1000)
})
}
build() {
// Component layout
}
}
```
## Reducing the Number of Components to Be Refreshed
When an application refreshes a page, the number of components to be refreshed must be reduced as much as possible. If this number is too large, the main thread will take a long time to perform measurement and layout. In addition, the **aboutToAppear()** and **aboutToDisappear()** APIs will be called multiple times during the creation and destruction of custom components, increasing the load of the main thread.
### Limiting the Refresh Scope with Containers
Negative example: If a component in a container is included in the **if** condition, changes in the **if** condition result will trigger the creation and destruction of the component. If the container layout is affected in this case, all components in the container are refreshed. As a result, the UI refresh of the main thread takes a long time.
In the following example, the **Text('New Page')** component is controlled by the state variable **isVisible**. When **isVisible** is set to **true**, the component is created. When **isVisible** is set to **false**, the component is destroyed. This means that, when the value of **isVisible** changes, all components in the **\<Stack>** container are refreshed.
```javascript
@Entry
@Component
struct StackExample {
@State isVisible : boolean = false;
build() {
Column() {
Stack({alignContent: Alignment.Top}) {
Text().width('100%').height('70%').backgroundColor(0xd2cab3)
.align(Alignment.Center).textAlign(TextAlign.Center);
// 100 identical <Text> components are omitted here.
if (this.isVisible) {
Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
.align(Alignment.Center).textAlign(TextAlign.Center);
}
}
Button("press").onClick(() => {
this.isVisible = !(this.isVisible);
})
}
}
}
```
Recommendation: For the component controlled by the state variable, add a container to the **if** statement to reduce the refresh scope.
```javascript
@Entry
@Component
struct StackExample {
@State isVisible : boolean = false;
build() {
Column() {
Stack({alignContent: Alignment.Top}) {
Text().width('100%').height('70%').backgroundColor(0xd2cab3)
.align(Alignment.Center).textAlign(TextAlign.Center);
// 100 identical <Text> components are omitted here.
Stack() {
if (this.isVisible) {
Text('New Page').height("100%").height("70%").backgroundColor(0xd2cab3)
.align(Alignment.Center).textAlign(TextAlign.Center);
}
}.width('100%').height('70%')
}
Button("press").onClick(() => {
this.isVisible = !(this.isVisible);
})
}
}
}
```
### Implementing On-Demand Loading of List Items
Negative example: Each of the 10000 elements in **this.arr** is initialized and loaded. As a result, the execution of the main thread takes a long time.
```javascript
@Entry
@Component
struct MyComponent {
@State arr: number[] = Array.from(Array(10000), (v,k) =>k);
build() {
List() {
ForEach(this.arr, (item: number) => {
ListItem() {
Text(`item value: ${item}`)
}
}, (item: number) => item.toString())
}
}
}
```
Recommendation: In similar cases, replace **ForEach** with **LazyForEach** so that only visible elements are loaded.
```javascript
class BasicDataSource implements IDataSource {
private listeners: DataChangeListener[] = []
public totalCount(): number {
return 0
}
public getData(index: number): any {
return undefined
}
registerDataChangeListener(listener: DataChangeListener): void {
if (this.listeners.indexOf(listener) < 0) {
console.info('add listener')
this.listeners.push(listener)
}
}
unregisterDataChangeListener(listener: DataChangeListener): void {
const pos = this.listeners.indexOf(listener);
if (pos >= 0) {
console.info('remove listener')
this.listeners.splice(pos, 1)
}
}
notifyDataReload(): void {
this.listeners.forEach(listener => {
listener.onDataReloaded()
})
}
notifyDataAdd(index: number): void {
this.listeners.forEach(listener => {
listener.onDataAdd(index)
})
}
notifyDataChange(index: number): void {
this.listeners.forEach(listener => {
listener.onDataChange(index)
})
}
notifyDataDelete(index: number): void {
this.listeners.forEach(listener => {
listener.onDataDelete(index)
})
}
notifyDataMove(from: number, to: number): void {
this.listeners.forEach(listener => {
listener.onDataMove(from, to)
})
}
}
class MyDataSource extends BasicDataSource {
private dataArray: string[] = Array.from(Array(10000), (v, k) => k.toString());
public totalCount(): number {
return this.dataArray.length
}
public getData(index: number): any {
return this.dataArray[index]
}
public addData(index: number, data: string): void {
this.dataArray.splice(index, 0, data)
this.notifyDataAdd(index)
}
public pushData(data: string): void {
this.dataArray.push(data)
this.notifyDataAdd(this.dataArray.length - 1)
}
}
@Entry
@Component
struct MyComponent {
private data: MyDataSource = new MyDataSource()
build() {
List() {
LazyForEach(this.data, (item: string) => {
ListItem() {
Text(item).fontSize(20).margin({ left: 10 })
}
}, item => item)
}
}
}
```
# Reducing Frame Loss
Frame loss in the animation arena is a phenomenon where the frame rate of an animation drops when it is running or being created.
When playing an animation, the system needs to calculate the animation curve and draw the component layout within a refresh period. You are advised to use the system-provided animation APIs. With these APIs, setting the curve type, end point position, and duration is enough for meeting common animation needs, thereby reducing the load of the UI main thread.
Negative example: The application uses a custom animation, which involves animation curve calculation. This calculation process may cause high load of the UI thread and frame loss.
```javascript
@Entry
@Component
struct AttrAnimationExample {
@State widthSize: number = 200
@State heightSize: number = 100
@State flag: boolean = true
computeSize() {
let duration = 2000
let period = 16
let widthSizeEnd = undefined
let heightSizeEnd = undefined
if (this.flag) {
widthSizeEnd = 100
heightSizeEnd = 50
} else {
widthSizeEnd = 200
heightSizeEnd = 100
}
let doTimes = duration / period
let deltaHeight = (heightSizeEnd - this.heightSize) / doTimes
let deltaWeight = (widthSizeEnd - this.widthSize) / doTimes
for (let i = 1; i <= doTimes; i++) {
let t = period * (i);
setTimeout(() => {
this.heightSize = this.heightSize + deltaHeight
this.widthSize = this.widthSize + deltaWeight
}, t)
}
this.flag = !this.flag
}
build() {
Column() {
Button('click me')
.onClick(() => {
let delay = 500
setTimeout(() => { this.computeSize() }, delay)
})
.width(this.widthSize).height(this.heightSize).backgroundColor(0x317aff)
}.width('100%').margin({ top: 5 })
}
}
```
## Using System-Provided Attribute Animation APIs
The following uses the system-provided attribute animation APIs to implement the preceding animation features:
```javascript
@Entry
@Component
struct AttrAnimationExample {
@State widthSize: number = 200
@State heightSize: number = 100
@State flag: boolean = true
build() {
Column() {
Button('click me')
.onClick((event: ClickEvent) => {
if (this.flag) {
this.widthSize = 100
this.heightSize = 50
} else {
this.widthSize = 200
this.heightSize = 100
}
this.flag = !this.flag
})
.width(this.widthSize).height(this.heightSize).backgroundColor(0x317aff)
.animation({
duration: 2000, // Animation duration.
curve: Curve.Linear, // Animation curve.
delay: 500, // Animation delay.
iterations: 1, // Number of playback times.
playMode: PlayMode.Normal // Animation playback mode.
}) // Animation configuration for the width and height attributes of the <Button> component.
}.width('100%').margin({ top: 5 })
}
}
```
For more details, see [Property Animator](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/arkui-ts/ts-animatorproperty.md).
## Using System-Provided Explicit Animation APIs
The following uses the system-provided explicit animation APIs to implement the preceding animation features:
```javascript
@Entry
@Component
struct AnimateToExample {
@State widthSize: number = 200;
@State heightSize: number = 100;
@State flag: boolean = true;
build() {
Column() {
Button('click me')
.onClick((event: ClickEvent) => {
if (this.flag) {
animateTo({
duration: 2000, // Animation duration.
curve: Curve.Linear, // Animation curve.
delay: 500, // Animation delay.
iterations: 1, // Number of playback times.
playMode: PlayMode.Normal // Animation playback mode.
}, () => {
this.widthSize = 100;
this.heightSize = 50;
})
} else {
animateTo({
duration: 2000, // Animation duration.
curve: Curve.Linear, // Animation curve.
delay: 500, // Animation delay.
iterations: 1, // Number of playback times.
playMode: PlayMode.Normal // Animation playback mode.
}, () => {
this.widthSize = 200;
this.heightSize = 100;
})
}
this.flag = !this.flag;
})
.width(this.widthSize).height(this.heightSize).backgroundColor(0x317aff)
}.width('100%').margin({ top: 5 })
}
}
```
For more details, see [Explicit Animation](https://gitee.com/openharmony/docs/blob/master/en/application-dev/reference/arkui-ts/ts-explicit-animation.md).
# Reducing Nesting
The view hierarchy can significantly affect application performance. For example, at the 120 Hz refresh rate, a device screen refreshes frames every 8.3 ms. If there are many levels of nested views, the frames may fail to be refreshed within 8.3 ms. As a result, frame loss and frame freezing occur, detracting from user experience. Therefore, it is recommended that you minimize nesting in your code to shorten the refresh time.
## Removing Redundant Levels of Nesting
In the following negative example, the **\<Grid>** component is used to implement a grid, but it is nested inside three levels of **\<Stack>** containers. As a result, the refresh and rendering process takes a long time to complete.
```javascript
@Entry
@Component
struct AspectRatioExample {
@State children: Number[] = Array.from(Array(900), (v, k) => k);
build() {
Scroll() {
Grid() {
ForEach(this.children, (item) => {
GridItem() {
Stack() {
Stack() {
Stack() {
Text(item.toString())
}
}
}
}
}, item => item)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(0)
.rowsGap(0)
.size({ width: "100%", height: "100%" })
}
}
}
```
Recommendation: Reduce the redundant nesting of **\<Stack>** containers. In this way, the number of components that each grid item needs to pass is three less, shortening the refresh and rendering time.
```javascript
// xxx.ets
@Entry
@Component
struct AspectRatioExample {
@State children: Number[] = Array.from(Array(900), (v, k) => k);
build() {
Scroll() {
Grid() {
ForEach(this.children, (item) => {
GridItem() {
Text(item.toString())
}
}, item => item)
}
.columnsTemplate('1fr 1fr 1fr 1fr')
.columnsGap(0)
.rowsGap(0)
.size({ width: "100%", height: "100%" })
}
}
}
```
...@@ -152,7 +152,7 @@ struct Parent { ...@@ -152,7 +152,7 @@ struct Parent {
``` ```
### Example of Component Initialization Through Trailing Closure ### Component Initialization Through Trailing Closure
In a custom component, the \@BuilderParam decorated attribute can be initialized using a trailing closure. During initialization, the component name is followed by a pair of braces ({}) to form a trailing closure. In a custom component, the \@BuilderParam decorated attribute can be initialized using a trailing closure. During initialization, the component name is followed by a pair of braces ({}) to form a trailing closure.
......
...@@ -37,6 +37,8 @@ What the internal state is depends on the component. For example, for the [bindP ...@@ -37,6 +37,8 @@ What the internal state is depends on the component. For example, for the [bindP
| [Toggle](../reference/arkui-ts/ts-basic-components-toggle.md) | isOn | 10 | | [Toggle](../reference/arkui-ts/ts-basic-components-toggle.md) | isOn | 10 |
| [AlphabetIndexer](../reference/arkui-ts/ts-container-alphabet-indexer.md) | selected | 10 | | [AlphabetIndexer](../reference/arkui-ts/ts-container-alphabet-indexer.md) | selected | 10 |
| [Select](../reference/arkui-ts/ts-basic-components-select.md) | selected, value| 10 | | [Select](../reference/arkui-ts/ts-basic-components-select.md) | selected, value| 10 |
| [BindSheet](../reference/arkui-ts/ts-universal-attributes-sheet-transition.md) | isShow | 10 |
| [BindContentCover](../reference/arkui-ts/ts-universal-attributes-modal-transition.md) | isShow | 10 |
- When the variable bound to $$ changes, the UI is re-rendered synchronously. - When the variable bound to $$ changes, the UI is re-rendered synchronously.
......
...@@ -227,9 +227,9 @@ ...@@ -227,9 +227,9 @@
- [@ohos.distributedMissionManager (Distributed Mission Management)](js-apis-distributedMissionManager.md) - [@ohos.distributedMissionManager (Distributed Mission Management)](js-apis-distributedMissionManager.md)
- [@ohos.reminderAgentManager (Reminder Agent Management)](js-apis-reminderAgentManager.md) - [@ohos.reminderAgentManager (Reminder Agent Management)](js-apis-reminderAgentManager.md)
- [@ohos.resourceschedule.backgroundTaskManager (Background Task Management)](js-apis-resourceschedule-backgroundTaskManager.md) - [@ohos.resourceschedule.backgroundTaskManager (Background Task Management)](js-apis-resourceschedule-backgroundTaskManager.md)
- [@ohos.resourceschedule.workScheduler (Work Scheduler)](js-apis-resourceschedule-workScheduler.md) - [@ohos.resourceschedule.workScheduler (Deferred Task Scheduling)](js-apis-resourceschedule-workScheduler.md)
- [@ohos.resourceschedule.usageStatistics (Device Usage Statistics)](js-apis-resourceschedule-deviceUsageStatistics.md) - [@ohos.resourceschedule.usageStatistics (Device Usage Statistics)](js-apis-resourceschedule-deviceUsageStatistics.md)
- [@ohos.WorkSchedulerExtensionAbility (Work Scheduler Callbacks)](js-apis-WorkSchedulerExtensionAbility.md) - [@ohos.WorkSchedulerExtensionAbility (Deferred Task Scheduling Callbacks)](js-apis-WorkSchedulerExtensionAbility.md)
- application - application
- [WorkSchedulerExtensionContext](js-apis-inner-application-WorkSchedulerExtensionContext.md) - [WorkSchedulerExtensionContext](js-apis-inner-application-WorkSchedulerExtensionContext.md)
......
# @ohos.WorkSchedulerExtensionAbility (Work Scheduler Callbacks) # @ohos.WorkSchedulerExtensionAbility (Deferred Task Scheduling Callbacks)
The **WorkSchedulerExtensionAbility** module provides callbacks for Work Scheduler tasks. The **WorkSchedulerExtensionAbility** module provides callbacks for deferred task scheduling.
When developing an application, you can override the APIs of this module and add your own task logic to the APIs. When developing an application, you can override the APIs of this module and add your own task logic to the APIs.
...@@ -28,7 +28,7 @@ import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility' ...@@ -28,7 +28,7 @@ import WorkSchedulerExtensionAbility from '@ohos.WorkSchedulerExtensionAbility'
onWorkStart(work: workScheduler.WorkInfo): void onWorkStart(work: workScheduler.WorkInfo): void
Triggered when the Work Scheduler task starts. Called when the system starts scheduling the deferred task.
**System capability**: SystemCapability.ResourceSchedule.WorkScheduler **System capability**: SystemCapability.ResourceSchedule.WorkScheduler
...@@ -52,7 +52,7 @@ Triggered when the Work Scheduler task starts. ...@@ -52,7 +52,7 @@ Triggered when the Work Scheduler task starts.
onWorkStop(work: workScheduler.WorkInfo): void onWorkStop(work: workScheduler.WorkInfo): void
Triggered when the Work Scheduler task stops. Called when the system stops scheduling the deferred task.
**System capability**: SystemCapability.ResourceSchedule.WorkScheduler **System capability**: SystemCapability.ResourceSchedule.WorkScheduler
......
# @ohos.componentUtils (componentUtils)
The **componentUtils** module provides API for obtaining the coordinates and size of the drawing area of a component.
> **NOTE**
>
> The APIs of this module are supported since API version 10. Updates will be marked with a superscript to indicate their earliest API version.
>
> The functionality of this module depends on UI context. This means that the APIs of this module cannot be used where the UI context is unclear. For details, see [UIContext](./js-apis-arkui-UIContext.md#uicontext).
>
> Since API version 10, you can use the [getComponentUtils](./js-apis-arkui-UIContext.md#getcomponentutils) API in **UIContext** to obtain the **ComponentUtils** object associated with the current UI context. For this API to work correctly, call it after the notification indicating completion of component layout is received through [@ohos.arkui.inspector (layout callback)](js-apis-arkui-inspector.md).
## Modules to Import
```js
import componentUtils from '@ohos.componentUtils'
```
## componentUtils.getRectangleById
getRectangleById(id: string): ComponentInfo
Obtains a **ComponentInfo** object based on the component ID.
**System capability**: SystemCapability.ArkUI.ArkUI.Full
**Parameters**
| Name| Type | Mandatory| Description |
| ------ | ------ | ---- | ---------- |
| id | string | Yes | Component ID.|
**Return value**
| Type | Description |
| ------ | ---------- |
| [ComponentInfo](#componentinfo) | **ComponentInfo** object, which provides the size, position, translation, scaling, rotation, and affine matrix information of the component.|
**Example**
```js
let modePosition = componentUtils.getRectangleById("onClick");
```
## ComponentInfo
| Name | Type | Description |
| ---------------|------------ | -----------------------------|
| size | [Size](#size) | Component size. |
| localOffset | [Offset](#offset) | Offset of the component relative to the parent component. |
| windowOffset | [Offset](#offset) | Offset of the component relative to the window. |
| screenOffset | [Offset](#offset) | Offset of the component relative to the screen. |
| translate | [TranslateResult](#translateresult) | Translation of the component. |
| scale | [ScaleResult](#scaleresult) | Scaling of the component. |
| rotate | [RotateResult](#rotateresult) | Rotation of the component. |
| transform | [Matrix4Result](#matrix4result) | Affine matrix of the component, which is a 4x4 matrix object created based on the input parameter. |
### Size
| Name | Type| Description |
| -------- | ---- | ----------------------------------|
| width | number | Component width. |
| height | number | Component height. |
### Offset
| Name | Type| Description |
| --------| ---- | -----------------------------------|
| x | number | X coordinate. |
| y | number | Y coordinate. |
### TranslateResult
| Name | Type| Description |
| --------| ---- | -----------------------------------|
| x | number | Translation distance along the x-axis. |
| y | number | Translation distance along the y-axis. |
| z | number | Translation distance along the z-axis. |
### ScaleResult
| Name | Type| Description |
| --------| ---- | -----------------------------------|
| x | number | Scale factor along the x-axis. |
| y | number | Scale factor along the y-axis. |
| z | number | Scale factor along the z-axis. |
| centerX | number | X coordinate of the center point. |
| centerY | number | Y coordinate of the center point. |
### RotateResult
| Name | Type| Description |
| --------| ---- | -----------------------------------|
| x | number | X coordinate of the rotation vector. |
| y | number | Y coordinate of the rotation vector. |
| z | number | Z coordinate of the rotation vector. |
| angle | number | Rotation angle. |
| centerX | number | X coordinate of the center point. |
| centerY | number | Y coordinate of the center point. |
### Matrix4Result
| Name | Type| Description |
| --------| ---- | -----------------------------------|
| number | number | Scale factor along the x-axis. Defaults to **1** for the identity matrix. |
| number | number | The second value, which is affected by the rotation of the x, y, and z axes.|
| number | number | The third value, which is affected by the rotation of the x, y, and z axes.|
| number | number | Meaningless value. |
| number | number | The fifth value, which is affected by the rotation of the x, y, and z axes.|
| number | number | Scale factor along the y-axis. Defaults to **1** for the identity matrix. |
| number | number | The seventh value, which is affected by the rotation of the x, y, and z axes.|
| number | number | Meaningless value. |
| number | number | The ninth value, which is affected by the rotation of the x, y, and z axes.|
| number | number | The tenth value, which is affected by the rotation of the x, y, and z axes.|
| number | number | Scale factor along the z-axis. Defaults to **1** for the identity matrix. |
| number | number | Meaningless value. |
| number | number | Translation distance along the x-axis. Defaults to **0** for the identity matrix. |
| number | number | Translation distance along the y-axis. Defaults to **0** for the identity matrix.|
| number | number | Translation distance along the z-axis. Defaults to **0** for the identity matrix.|
| number | number | Valid in homogeneous coordinates, presenting the perspective projection effect. |
**Example**
```js
import matrix4 from '@ohos.matrix4';
import componentUtils from '@ohos.componentUtils';
@Entry
@Component
struct Utils{
private getComponentRect(key) {
console.info("Mode Key: " + key);
let modePosition = componentUtils.getRectangleById(key);
let localOffsetWidth = modePosition.size.width;
let localOffsetHeight = modePosition.size.height;
let localOffsetX = modePosition.localOffset.x;
let localOffsetY = modePosition.localOffset.y;
let windowOffsetX = modePosition.windowOffset.x;
let windowOffsetY = modePosition.windowOffset.y;
let screenOffsetX = modePosition.screenOffset.x;
let screenOffsetY = modePosition.screenOffset.y;
let translateX = modePosition.translate.x;
let translateY = modePosition.translate.y;
let translateZ = modePosition.translate.z;
let scaleX = modePosition.scale.x;
let scaleY = modePosition.scale.y;
let scaleZ = modePosition.scale.z;
let scaleCenterX = modePosition.scale.centerX;
let scaleCenterY = modePosition.scale.centerY;
let rotateX = modePosition.rotate.x;
let rotateY = modePosition.rotate.y;
let rotateZ = modePosition.rotate.z;
let rotateCenterX = modePosition.rotate.centerX;
let rotateCenterY = modePosition.rotate.centerY;
let rotateAngle = modePosition.rotate.angle;
let Matrix4_1 = modePosition.transform[0];
let Matrix4_2 = modePosition.transform[1];
let Matrix4_3 = modePosition.transform[2];
let Matrix4_4 = modePosition.transform[3];
let Matrix4_5 = modePosition.transform[4];
let Matrix4_6 = modePosition.transform[5];
let Matrix4_7 = modePosition.transform[6];
let Matrix4_8 = modePosition.transform[7];
let Matrix4_9 = modePosition.transform[8];
let Matrix4_10 = modePosition.transform[9];
let Matrix4_11 = modePosition.transform[10];
let Matrix4_12 = modePosition.transform[11];
let Matrix4_13 = modePosition.transform[12];
let Matrix4_14 = modePosition.transform[13];
let Matrix4_15 = modePosition.transform[14];
let Matrix4_16 = modePosition.transform[15];
console.info("[getRectangleById] current component obj is: " + modePosition );
}
@State x: number = 120;
@State y: number = 10;
@State z: number = 100;
private matrix1 = matrix4.identity().translate({ x: this.x, y: this.y, z: this.z });
build() {
Column() {
Image($r("app.media.icon"))
.transform(this.matrix1)
.translate({ x: 100, y: 10, z: 50})
.scale({ x: 2, y: 0.5, z: 1 })
.rotate({
x: 1,
y: 1,
z: 1,
centerX: '50%',
centerY: '50%',
angle: 300
})
.width("40%")
.height(100)
.key("image_01")
Button() {
Text('getRectangleById').fontSize(40).fontWeight(FontWeight.Bold);
}.margin({ top: 20 })
.onClick(() => {
this.getComponentRect("image_01");
}).id('onClick');
}
}
}
```
...@@ -180,7 +180,7 @@ Represents the text data. It is a child class of [UnifiedRecord](#unifiedrecord) ...@@ -180,7 +180,7 @@ Represents the text data. It is a child class of [UnifiedRecord](#unifiedrecord)
| Name | Type | Readable| Writable| Description | | Name | Type | Readable| Writable| Description |
| ------- | ------------------------- | ---- | ---- |-----------------------------------------------------------------------------------------------------------------------------------------------------| | ------- | ------------------------- | ---- | ---- |-----------------------------------------------------------------------------------------------------------------------------------------------------|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe the text content. For example, a data object with the following content can be created to describe a text file:<br>{<br>"title":"Title",<br>"content":"Content"<br>}<br>This parameter is optional. The default value is an empty dictionary object.| | details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe the text content. For example, a data object with the following content can be created to describe a text file:<br>{<br>"title":"Title",<br>"content":"Content"<br>}<br> This parameter is optional. The default value is an empty dictionary object.|
**Example** **Example**
...@@ -258,7 +258,7 @@ Represents the file data. It is a child class of [UnifiedRecord](#unifiedrecord) ...@@ -258,7 +258,7 @@ Represents the file data. It is a child class of [UnifiedRecord](#unifiedrecord)
| Name | Type | Readable| Writable| Description | | Name | Type | Readable| Writable| Description |
|---------|---------------------------| ---- | ---- |------------------------------------------------------------------------------------------------------------------------------------------------------| |---------|---------------------------| ---- | ---- |------------------------------------------------------------------------------------------------------------------------------------------------------|
| details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe file information. For example, a data object with the following content can be created to describe a file:<br>{<br>"name":"File name",<br>"type":"File type"<br>}<br>This parameter is optional. The default value is an empty dictionary object.| | details | { [key: string]: string } | Yes | Yes | A dictionary type object, where both the key and value are of the string type and are used to describe file information. For example, a data object with the following content can be created to describe a file:<br>{<br>"name":"File name",<br>"type":"File type"<br>}<br> This parameter is optional. The default value is an empty dictionary object.|
| uri | string | Yes | Yes | URI of the file data. | | uri | string | Yes | Yes | URI of the file data. |
**Example** **Example**
...@@ -484,13 +484,13 @@ let unifiedData = new UDMF.UnifiedData(record); ...@@ -484,13 +484,13 @@ let unifiedData = new UDMF.UnifiedData(record);
## Intention ## Intention
Enumerates the types of the system abilities connected to the UDMF. It identifies the purpose of the data written by the user to the UDMF and the system abilities connected to the UDMF to implement data transmission between applications. Enumerates the data channel types supported by the UDMF. It is used to identify different service scenarios, to which the UDMF data channels apply.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
| Name | Value | Description | | Name | Value | Description |
|----------|-----------|---------| |----------|-----------|---------|
| DATA_HUB | 'DataHub' | Public data hub.| | DATA_HUB | 'DataHub' | Public data channel.|
## Options ## Options
...@@ -499,9 +499,9 @@ Defines the data operation performed by the UDMF. It includes two optional param ...@@ -499,9 +499,9 @@ Defines the data operation performed by the UDMF. It includes two optional param
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
| Name | Type | Readable| Writable| Mandatory| Description | | Name | Type | Readable| Writable| Mandatory| Description |
|-----------|-------------------------|----|----|----|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |-----------|-------------------------|----|----|----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| intention | [Intention](#intention) | Yes | Yes | No | Service tag related to the data operation. | | intention | [Intention](#intention) | Yes | Yes | No | Type of the data channel related to the data operation. |
| key | string | Yes | Yes | No | Unique identifier of a data object in the UDMF, which can be obtained from the return value of [insertData](#udmfinsertdata).<br>The key consists of **udmf:/**, **intention**, **bundleName**, and **groupId** with a (/) in between, for example, **udmf://DataHub/com.ohos.test/0123456789**.<br>**udmf:/** is fixed, **DataHub** is an enum of **intention**, **com.ohos.test** is the bundle name, and **0123456789** is a group ID randomly generated.| | key | string | Yes | Yes | No | Unique identifier of a data object in the UDMF, which can be obtained from the return value of [insertData](#udmfinsertdata).<br>The key consists of **udmf:/**, **intention**, **bundleName**, and **groupId** with a (/) in between, for example, **udmf://DataHub/com.ohos.test/0123456789**.<br>**udmf:/** is fixed, **DataHub** is an enum of **intention**, **com.ohos.test** is the bundle name, and **0123456789** is a group ID randomly generated.|
...@@ -510,7 +510,7 @@ Defines the data operation performed by the UDMF. It includes two optional param ...@@ -510,7 +510,7 @@ Defines the data operation performed by the UDMF. It includes two optional param
insertData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;string&gt;): void insertData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;string&gt;): void
Inserts data to the UDMF. This API uses an asynchronous callback to return the result. Inserts data to the UDMF public data channel. This API uses an asynchronous callback to return the unique identifier of the data inserted.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -552,7 +552,7 @@ try { ...@@ -552,7 +552,7 @@ try {
insertData(options: Options, data: UnifiedData): Promise&lt;string&gt; insertData(options: Options, data: UnifiedData): Promise&lt;string&gt;
Inserts data to the UDMF. This API uses a promise to return the result. Inserts data to the UDMF public data channel. This API uses a promise to return the unique identifier of the data inserted.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -596,7 +596,7 @@ try { ...@@ -596,7 +596,7 @@ try {
updateData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;void&gt;): void updateData(options: Options, data: UnifiedData, callback: AsyncCallback&lt;void&gt;): void
Updates data in the UDMF. This API uses an asynchronous callback to return the result. Updates the data in the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -626,7 +626,7 @@ try { ...@@ -626,7 +626,7 @@ try {
if (err === undefined) { if (err === undefined) {
console.info('Succeeded in updating data.'); console.info('Succeeded in updating data.');
} else { } else {
console.error('Failed to update data. code is ${err.code},message is ${err.message} `); console.error(`Failed to update data. code is ${err.code},message is ${err.message} `);
} }
}); });
} catch(e) { } catch(e) {
...@@ -638,7 +638,7 @@ try { ...@@ -638,7 +638,7 @@ try {
updateData(options: Options, data: UnifiedData): Promise&lt;void&gt; updateData(options: Options, data: UnifiedData): Promise&lt;void&gt;
Updates data in the UDMF. This API uses a promise to return the result. Updates the data in the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -683,7 +683,7 @@ try { ...@@ -683,7 +683,7 @@ try {
queryData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void queryData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void
Queries data in the UDMF. This API uses an asynchronous callback to return the result. Queries data in the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -729,7 +729,7 @@ try { ...@@ -729,7 +729,7 @@ try {
queryData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt; queryData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt;
Queries data in the UDMF. This API uses a promise to return the result. Queries data in the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -778,7 +778,7 @@ try { ...@@ -778,7 +778,7 @@ try {
deleteData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void deleteData(options: Options, callback: AsyncCallback&lt;Array&lt;UnifiedData&gt;&gt;): void
Deletes data from the UDMF. This API uses an asynchronous callback to return the result. Deletes data from the UDMF public data channel. This API uses an asynchronous callback to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
...@@ -824,7 +824,7 @@ try { ...@@ -824,7 +824,7 @@ try {
deleteData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt; deleteData(options: Options): Promise&lt;Array&lt;UnifiedData&gt;&gt;
Deletes data in the UDMF. This API uses a promise to return the result. Deletes data from the UDMF public data channel. This API uses a promise to return the result.
**System capability**: SystemCapability.DistributedDataManager.UDMF.Core **System capability**: SystemCapability.DistributedDataManager.UDMF.Core
......
# WorkSchedulerExtensionContext # WorkSchedulerExtensionContext
The **WorkSchedulerExtensionContext** module, inherited from [ExtensionContext](js-apis-inner-application-extensionContext.md), is the context environment of the WorkSchedulerExtensionAbility. The **WorkSchedulerExtensionContext** module, inherited from [ExtensionContext](js-apis-inner-application-extensionContext.md), provides a context environment for the WorkSchedulerExtensionAbility.
This module provides APIs for accessing the resources of a WorkSchedulerExtensionAbility. This module provides APIs for accessing the resources of a WorkSchedulerExtensionAbility.
......
此差异已折叠。
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
The **statistics** module provides APIs to query real-time or historical data traffic by the specified network interface card (NIC) or user ID (UID). The **statistics** module provides APIs to query real-time or historical data traffic by the specified network interface card (NIC) or user ID (UID).
> **NOTE** > **NOTE**
>
> The initial APIs of this module are supported since API version 10. Newly added APIs will be marked with a superscript to indicate their earliest API version. > The initial APIs of this module are supported since API version 10. Newly added APIs will be marked with a superscript to indicate their earliest API version.
## Modules to Import ## Modules to Import
...@@ -589,7 +588,7 @@ For details about the error codes, see [Traffic Management Error Codes](../error ...@@ -589,7 +588,7 @@ For details about the error codes, see [Traffic Management Error Codes](../error
on(type: 'netStatsChange', callback: Callback\<{ iface: string, uid?: number }>): void on(type: 'netStatsChange', callback: Callback\<{ iface: string, uid?: number }>): void
Subscribes to data traffic change events. Subscribes to traffic change events.
**System API**: This is a system API. **System API**: This is a system API.
...@@ -602,7 +601,7 @@ Subscribes to data traffic change events. ...@@ -602,7 +601,7 @@ Subscribes to data traffic change events.
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| -------- | --------------------------------------- | ---- | ---------- | | -------- | --------------------------------------- | ---- | ---------- |
| type | string | Yes | Event type. This field has a fixed value of **netStatsChange**.| | type | string | Yes | Event type. This field has a fixed value of **netStatsChange**.|
| callback | Callback\<{ iface: string, uid?: number }\> | Yes | Callback invoked when the data traffic changes.<br>**iface**: NIC name.<br>**uid**: application UID.| | callback | Callback\<{ iface: string, uid?: number }\> | Yes | Callback invoked when the traffic changes.<br>**iface**: NIC name.<br>**uid**: application UID.|
**Error codes** **Error codes**
...@@ -628,7 +627,7 @@ For details about the error codes, see [Traffic Management Error Codes](../error ...@@ -628,7 +627,7 @@ For details about the error codes, see [Traffic Management Error Codes](../error
off(type: 'netStatsChange', callback?: Callback\<{ iface: string, uid?: number }>): void; off(type: 'netStatsChange', callback?: Callback\<{ iface: string, uid?: number }>): void;
Unsubscribes from data traffic change events. Unsubscribes from traffic change events.
**System API**: This is a system API. **System API**: This is a system API.
...@@ -641,7 +640,7 @@ Unsubscribes from data traffic change events. ...@@ -641,7 +640,7 @@ Unsubscribes from data traffic change events.
| Name | Type | Mandatory| Description | | Name | Type | Mandatory| Description |
| -------- | --------------------------------------- | ---- | ---------- | | -------- | --------------------------------------- | ---- | ---------- |
| type | string | Yes | Event type. This field has a fixed value of **netStatsChange**.| | type | string | Yes | Event type. This field has a fixed value of **netStatsChange**.|
| callback | Callback\<{ iface: string, uid?: number }\> | No | Callback invoked when the data traffic changes.<br>**iface**: NIC name.<br>uid: application UID.| | callback | Callback\<{ iface: string, uid?: number }\> | No | Callback invoked when the traffic changes.<br>**iface**: NIC name.<br>uid: application UID.|
**Error codes** **Error codes**
......
...@@ -67,7 +67,7 @@ struct RichTextExample { ...@@ -67,7 +67,7 @@ struct RichTextExample {
'<h2 style="text-align: center;">h2 heading</h2>' + '<h2 style="text-align: center;">h2 heading</h2>' +
'<h3 style="text-align: center;">h3 heading</h3>' + '<h3 style="text-align: center;">h3 heading</h3>' +
'<p style="text-align: center;">Regular paragraph</p><hr/>' + '<p style="text-align: center;">Regular paragraph</p><hr/>' +
'<div style="width: 500px;height: 500px;border: 1px solid;margin: 0auto;">' + '<div style="width: 500px;height: 500px;border: 1px solid;margin: 0 auto;">' +
'<p style="font-size: 35px;text-align: center;font-weight: bold; color: rgb(24,78,228)">Font size: 35px; line height: 45px</p>' + '<p style="font-size: 35px;text-align: center;font-weight: bold; color: rgb(24,78,228)">Font size: 35px; line height: 45px</p>' +
'<p style="background-color: #e5e5e5;line-height: 45px;font-size: 35px;text-indent: 2em;">' + '<p style="background-color: #e5e5e5;line-height: 45px;font-size: 35px;text-indent: 2em;">' +
'<p>This is text. This is text. This is text. This is text. This is text. This is text. This is text. This is text. This is text.</p>'; '<p>This is text. This is text. This is text. This is text. This is text. This is text. This is text. This is text. This is text.</p>';
......
...@@ -30,15 +30,15 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the ...@@ -30,15 +30,15 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| Name | Type | Description | | Name | Type | Description |
| ----------------------- | ------------------------------------------------ | ---------------------------------------------- | | ----------------------- | ------------------------------------------------ | ---------------------------------------------- |
| searchButton<sup>10+</sup> | value: string,<br>option?: [SearchButtonOptions](#searchbuttonoptions10) | Text on the search button located next to the search text box. By default, there is no search button. | | searchButton<sup>10+</sup> | value: string,<br>option?: [SearchButtonOptions](#searchbuttonoptions10) | Text on the search button located next to the search text box. By default, there is no search button. |
| placeholderColor | [ResourceColor](ts-types.md#resourcecolor) | Placeholder text color. | | placeholderColor | [ResourceColor](ts-types.md#resourcecolor) | Placeholder text color.<br>Default value: **'#99182431'** |
| placeholderFont | [Font](ts-types.md#font) | Placeholder text style, including the font size, font width, font family, and font style. Currently, only the default font family is supported. | | placeholderFont | [Font](ts-types.md#font) | Placeholder text style, including the font size, font width, font family, and font style. Currently, only the default font family is supported. |
| textFont | [Font](ts-types.md#font) | Style of the text entered in the search box, including the font size, font width, font family, and font style. Currently, only the default font family is supported. | | textFont | [Font](ts-types.md#font) | Style of the text entered in the search box, including the font size, font width, font family, and font style. Currently, only the default font family is supported. |
| textAlign | [TextAlign](ts-appendix-enums.md#textalign) | Text alignment mode in the search text box.<br>Default value: **TextAlign.Start** | | textAlign | [TextAlign](ts-appendix-enums.md#textalign) | Text alignment mode in the search text box.<br>Default value: **TextAlign.Start** |
| copyOption<sup>9+</sup> | [CopyOptions](ts-appendix-enums.md#copyoptions9) | Whether copy and paste is allowed. | | copyOption<sup>9+</sup> | [CopyOptions](ts-appendix-enums.md#copyoptions9) | Whether copy and paste is allowed.<br>Default value: **CopyOptions.LocalDevice**<br>If this attribute is set to **CopyOptions.None**, the text can be pasted, but copy or cut is not allowed. |
| searchIcon<sup>10+</sup> | [IconOptions](#iconoptions10) | Style of the search icon on the left. | | searchIcon<sup>10+</sup> | [IconOptions](#iconoptions10) | Style of the search icon on the left. |
| cancelButton<sup>10+</sup> | {<br>style? : [CancelButtonStyle](#cancelbuttonstyle10)<br>icon?: [IconOptions](#iconoptions10) <br>} | Style of the Cancel button on the right. | | cancelButton<sup>10+</sup> | {<br>style? : [CancelButtonStyle](#cancelbuttonstyle10)<br>icon?: [IconOptions](#iconoptions10) <br>} | Style of the Cancel button on the right.<br>Default value:<br>{<br>style: CancelButtonStyle.INPUT<br>} |
| fontColor<sup>10+</sup> | [ResourceColor](ts-types.md#resourcecolor) | Font color of the input text. | | fontColor<sup>10+</sup> | [ResourceColor](ts-types.md#resourcecolor) | Font color of the input text.<br>Default value: **'#FF182431'**<br>**NOTE**<br>[Universal text attributes](ts-universal-attributes-text-style.md) **fontSize**, **fontStyle**, **fontWeight**, and **fontFamily** are set in the **textFont** attribute.|
| caretStyle<sup>10+</sup> | [CaretStyle](#caretstyle10) | Caret style. | | caretStyle<sup>10+</sup> | [CaretStyle](#caretstyle10) | Caret style.<br>Default value:<br>{<br>width: 1.5vp<br>color: '#007DFF'<br>} |
| enableKeyboardOnFocus<sup>10+</sup> | boolean | Whether to enable the input method when the component obtains focus.<br>Default value: **true** | | enableKeyboardOnFocus<sup>10+</sup> | boolean | Whether to enable the input method when the component obtains focus.<br>Default value: **true** |
| selectionMenuHidden<sup>10+</sup> | boolean | Whether to display the text selection menu when the text box is long-pressed or right-clicked.<br>Default value: **false**| | selectionMenuHidden<sup>10+</sup> | boolean | Whether to display the text selection menu when the text box is long-pressed or right-clicked.<br>Default value: **false**|
## IconOptions<sup>10+</sup> ## IconOptions<sup>10+</sup>
......
...@@ -31,7 +31,7 @@ Since API version 9, this API is supported in ArkTS widgets. ...@@ -31,7 +31,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| Name | Description | | Name | Description |
| -------- | ---------------- | | -------- | ---------------- |
| Checkbox | Check box type.<br>**NOTE**<br>The default value of the universal attribute [margin](ts-universal-attributes-size.md) is as follows:<br>{<br> top: 12 px,<br> right: 12 px,<br> bottom: 12 px,<br> left: 12 px<br> } | | Checkbox | Check box type.<br>**NOTE**<br>The default value of the universal attribute [margin](ts-universal-attributes-size.md) is as follows:<br>{<br> top: 14 px,<br> right: 14 px,<br> bottom: 14 px,<br> left: 14 px<br> } |
| Button | Button type. The set string, if any, will be displayed inside the button. | | Button | Button type. The set string, if any, will be displayed inside the button. |
| Switch | Switch type.<br>**NOTE**<br>The default value of the universal attribute [margin](ts-universal-attributes-size.md) is as follows:<br>{<br> top: 6px,<br> right: 14px,<br> bottom: 6 px,<br> left: 14 px<br> } | | Switch | Switch type.<br>**NOTE**<br>The default value of the universal attribute [margin](ts-universal-attributes-size.md) is as follows:<br>{<br> top: 6px,<br> right: 14px,<br> bottom: 6 px,<br> left: 14 px<br> } |
......
...@@ -29,7 +29,7 @@ Since API version 9, this API is supported in ArkTS widgets. ...@@ -29,7 +29,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| Name| Type| Mandatory| Description| | Name| Type| Mandatory| Description|
| -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- |
| count | number | Yes| Number of notifications.<br>**NOTE**<br>If the value is less than or equal to 0, no badge is displayed.<br>Value range: [-2147483648, 2147483647]<br>If the value is not an integer, it is rounded off to the nearest integer. For example, 5.5 is rounded off to 5.| | count | number | Yes| Number of notifications.<br>**NOTE**<br>If the value is less than or equal to 0, no badge is displayed.<br>Value range: [-2147483648, 2147483647]<br>If the value is not an integer, it is rounded off to the nearest integer. For example, 5.5 is rounded off to 5.|
| position | [BadgePosition](#badgeposition) | No| Position to display the badge relative to the parent component.<br>Default value: **BadgePosition.RightTop**| | position | [BadgePosition](#badgeposition)\|[Position<sup>10+</sup>](ts-types.md#position8) | No| Position to display the badge relative to the parent component.<br>Default value: **BadgePosition.RightTop**<br>**NOTE**<br> This parameter cannot be set in percentage. If it is set to an invalid value, the default value **(0,0)** will be used.|
| maxCount | number | No| Maximum number of notifications. When the maximum number is reached, only **maxCount+** is displayed.<br>Default value: **99**<br>Value range: [-2147483648, 2147483647]<br>If the value is not an integer, it is rounded off to the nearest integer. For example, 5.5 is rounded off to 5.| | maxCount | number | No| Maximum number of notifications. When the maximum number is reached, only **maxCount+** is displayed.<br>Default value: **99**<br>Value range: [-2147483648, 2147483647]<br>If the value is not an integer, it is rounded off to the nearest integer. For example, 5.5 is rounded off to 5.|
| style | [BadgeStyle](#badgestyle) | Yes| Style of the badge, including the font color, font size, badge color, and badge size.| | style | [BadgeStyle](#badgestyle) | Yes| Style of the badge, including the font color, font size, badge color, and badge size.|
...@@ -44,7 +44,7 @@ Since API version 9, this API is supported in ArkTS widgets. ...@@ -44,7 +44,7 @@ Since API version 9, this API is supported in ArkTS widgets.
| Name| Type| Mandatory| Default Value| Description| | Name| Type| Mandatory| Default Value| Description|
| -------- | -------- | -------- | -------- | -------- | | -------- | -------- | -------- | -------- | -------- |
| value | string | Yes| - | Prompt content.| | value | string | Yes| - | Prompt content.|
| position | [BadgePosition](#badgeposition) | No| BadgePosition.RightTop | Position to display the badge relative to the parent component.| | position | [BadgePosition](#badgeposition)\|[Position<sup>10+</sup>](ts-types.md#position8) | No| BadgePosition.RightTop | Position to display the badge relative to the parent component.|
| style | [BadgeStyle](#badgestyle) | Yes| - | Style of the badge, including the font color, font size, badge color, and badge size.| | style | [BadgeStyle](#badgestyle) | Yes| - | Style of the badge, including the font color, font size, badge color, and badge size.|
## BadgePosition ## BadgePosition
......
...@@ -40,6 +40,7 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the ...@@ -40,6 +40,7 @@ In addition to the [universal attributes](ts-universal-attributes-size.md), the
| miniHeight | string \| number | Panel height in the **PanelMode.Mini** mode.<br>Default value: **48**<br>Unit: vp<br>**NOTE**<br>This attribute cannot be set in percentage.| | miniHeight | string \| number | Panel height in the **PanelMode.Mini** mode.<br>Default value: **48**<br>Unit: vp<br>**NOTE**<br>This attribute cannot be set in percentage.|
| show | boolean | Whether to show the panel.| | show | boolean | Whether to show the panel.|
| backgroundMask<sup>9+</sup>|[ResourceColor](ts-types.md#resourcecolor)|Background mask of the panel.| | backgroundMask<sup>9+</sup>|[ResourceColor](ts-types.md#resourcecolor)|Background mask of the panel.|
| showCloseIcon<sup>10+</sup> | boolean | Whether to display the close icon. The value **true** means to display the close icon, and **false** means the opposite.<br>Default value: **false**|
## PanelType ## PanelType
...@@ -93,6 +94,7 @@ struct PanelExample { ...@@ -93,6 +94,7 @@ struct PanelExample {
.type(PanelType.Foldable).mode(PanelMode.Half) .type(PanelType.Foldable).mode(PanelMode.Half)
.dragBar(true) // The drag bar is enabled by default. .dragBar(true) // The drag bar is enabled by default.
.halfHeight(500) // The panel height is half of the screen height by default. .halfHeight(500) // The panel height is half of the screen height by default.
.showCloseIcon(true) // Display the close icon.
.onChange((width: number, height: number, mode: PanelMode) => { .onChange((width: number, height: number, mode: PanelMode) => {
console.info(`width:${width},height:${height},mode:${mode}`) console.info(`width:${width},height:${height},mode:${mode}`)
}) })
......
...@@ -8,13 +8,13 @@ ...@@ -8,13 +8,13 @@
## Attributes ## Attributes
| Name | Type | Description | | Name | Type | Description |
| ---------- | ------------------------------------------- | ------------------------------------------------------------ | | ------------ | ---------------------------------------- | ---------------------------------------- |
| flexBasis | number \| string | Base size of the component in the main axis of the parent container.<br>Default value: **'auto'** (indicating that the base size of the component in the main axis is the original size of the component)<br>This attribute cannot be set in percentage.<br>Since API version 9, this API is supported in ArkTS widgets.| | flexBasis | number \| string | Base size of the component in the main axis of the parent container.<br>Default value: **'auto'** (indicating that the base size of the component in the main axis is the original size of the component)<br>This attribute cannot be set in percentage.<br>Since API version 9, this API is supported in ArkTS widgets.|
| flexGrow | number | Percentage of the parent container's remaining space that is allocated to the component.<br>Default value: **0**<br>Since API version 9, this API is supported in ArkTS widgets.| | flexGrow | number | Percentage of the parent container's remaining space that is allocated to the component.<br>Default value: **0**<br>Since API version 9, this API is supported in ArkTS widgets.|
| flexShrink | number | Percentage of the parent container's shrink size that is allocated to the component.<br>When the parent container is **\<Row>** or **\<Column>**, the default value is **0**.<br> When the parent container is **\<Flex>**, the default value is **1**.<br>Since API version 9, this API is supported in ArkTS widgets.| | flexShrink | number | Percentage of the parent container's shrink size that is allocated to the component.<br>When the parent container is **\<Row>** or **\<Column>**, the default value is **0**.<br> When the parent container is **\<Flex>**, the default value is **1**.<br>Since API version 9, this API is supported in ArkTS widgets.|
| alignSelf | [ItemAlign](ts-appendix-enums.md#itemalign) | Alignment mode of the child components along the cross axis of the parent container. The setting overwrites the **alignItems** setting of the parent container (**\<Flex>**, **\<Column>**, **\<Row>**, or **\<GridRow>**).<br>**\<GridCol>** can have the **alignsSelf** attribute bound to change its own layout along the cross axis.<br>Default value: **ItemAlign.Auto**<br>Since API version 9, this API is supported in ArkTS widgets.| | alignSelf | [ItemAlign](ts-appendix-enums.md#itemalign) | Alignment mode of the child components along the cross axis of the parent container. The setting overwrites the **alignItems** setting of the parent container (**\<Flex>**, **\<Column>**, **\<Row>**, or **\<GridRow>**).<br>**\<GridCol>** can have the **alignsSelf** attribute bound to change its own layout along the cross axis.<br>Default value: **ItemAlign.Auto**<br>Since API version 9, this API is supported in ArkTS widgets.|
| layoutWeight | number \| string | Weight of the component during layout. When the container size is determined, the container space is allocated along the main axis among the component and sibling components based on the layout weight, and the component size setting is ignored.<br>Default value: **0**<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute is valid only for the **\<Row>**, **\<Column>**, and **\<Flex>** layouts.<br>The value can be a number greater than or equal to 0 or a string that can be converted to a number.| | layoutWeight | number \| string | Weight of the component during layout. When the container size is determined, the container space is allocated along the main axis among the component and sibling components based on the layout weight, and the component size setting is ignored.<br>Default value: **0**<br>Since API version 9, this API is supported in ArkTS widgets.<br>**NOTE**<br>This attribute is valid only for the **\<Row>**, **\<Column>**, and **\<Flex>** layouts.<br>The value can be a number greater than or equal to 0 or a string that can be converted to a number.|
## Example ## Example
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册