diff --git a/CODEOWNERS b/CODEOWNERS index 075b95363eddc75d792d3e65f5c34f3114e666fd..3598264d0126154e0a84838f393fb3864f493dd7 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -14,121 +14,7 @@ - limitations under the License. */ -zh-cn/device-dev/kernel/ @Austin23 -zh-cn/device-dev/website.md @Austin23 -zh-cn/device-dev/faqs/ @Austin23 -zh-cn/device-dev/porting/ @Austin23 -zh-cn/device-dev/security/ @Austin23 -zh-cn/device-dev/guide/ @duangavin123_admin -zh-cn/device-dev/quick-start/ @li-yan339 -zh-cn/device-dev/driver/ @li-yan339 -zh-cn/device-dev/get-code/ @li-yan339 -zh-cn/device-dev/hpm-part/ @duangavin123_admin -zh-cn/device-dev/reference/hdi-apis/ @li-yan339 -zh-cn/device-dev/quick-start/quickstart-standard-env-setup.md @li-yan339 @chenmudan -zh-cn/device-dev/quick-start/quickstart-lite-env-setup.md @li-yan339 @chenmudan -zh-cn/device-dev/porting/porting-thirdparty-overview.md @Austin23 @chenmudan -zh-cn/device-dev/porting/porting-thirdparty-makefile.md @Austin23 @chenmudan -zh-cn/device-dev/porting/porting-thirdparty-cmake.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-all.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-gn-coding-style-and-best-practice.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-gn-kconfig-visual-config-guide.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-product.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-zh-cn/device-dev/subsystems/subsystem.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-component.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-module.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-chip_solution.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-feature.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-syscap.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-reference.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-reference.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-reference.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-reference.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-gn-hap-compilation-guide.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-build-FAQ.md @Austin23 @chenmudan -zh-cn/device-dev/subsystems/subsys-remote-start.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-graphics-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-graphics-container-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-graphics-layout-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-graphics-common-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-graphics-animation-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-camera-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-camera-photo-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-camera-record-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-camera-preview-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-video-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-video-play-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-multimedia-video-record-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-utils-overview.md @Austin23 -zh-cn/device-dev/subsystems/subsys-utils-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-utils-faqs.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-envbuild.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-tech-codemanage.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-tech-name.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-tech-interface.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-devguide-sdk.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-devguide-plugin.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-devguide-conf.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-demo-sdk.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-demo-plugin.md @Austin23 -zh-cn/device-dev/subsystems/subsys-aiframework-demo-conf.md @Austin23 -zh-cn/device-dev/subsystems/subsys-data-relational-database.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-data-relational-database-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-data-relational-database-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-data-storage.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-data-storage-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-data-storage-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-sensor-overview.md @Austin23 -zh-cn/device-dev/subsystems/subsys-sensor-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-sensor-demo.md @Austin23 -zh-cn/device-dev/subsystems/subsys-usbservice-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-usbservice-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-usbservice-demo.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-application-framework-overview.md @Austin23 -zh-cn/device-dev/subsystems/subsys-application-framework-envbuild.md @Austin23 -zh-cn/device-dev/subsystems/subsys-application-framework-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-application-framework-demo.md @Austin23 -zh-cn/device-dev/subsystems/subsys-ota-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-tel-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-tel-guide.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-sigverify.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-rightmanagement.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-communicationverify.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-devicesecuritylevel.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-security-huks-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-appspawn.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-bootstrap.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-faqs.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-cfg.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-jobs.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-plugin.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-sandbox.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-service.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init-sysparam.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-init.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-overview.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot-ref.md @Austin23 -zh-cn/device-dev/subsystems/subsys-boot.md @Austin23 -zh-cn/device-dev/subsystems/subsys-testguide-test.md @Austin23 -zh-cn/device-dev/subsystems/subsys-dfx-overview.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hilog-rich.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hilog-lite.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hitrace.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hicollie.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging-config.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-logging.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-listening.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-query.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hisysevent-tool.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hidumper.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-hichecker.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-dfx-faultlogger.md @duangavin123_admin -zh-cn/device-dev/subsystems/subsys-toolchain-bytrace-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-toolchain-hdc-guide.md @Austin23 -zh-cn/device-dev/subsystems/subsys-toolchain-hiperf.md @Austin23 -zh-cn/device-dev/subsystems/subsys-xts-guide.md @Austin23 +zh-cn/device-dev/ @li-yan339 @zengyawen zh-cn/application-dev/quick-start/arkts-get-started.md @HelloCrease @tomatodevboy @s10021109 zh-cn/application-dev/quick-start/arkts-basic-syntax-overview.md @HelloCrease @tomatodevboy @s10021109 zh-cn/application-dev/quick-start/arkts-declarative-ui-description.md @HelloCrease @tomatodevboy @s10021109 @@ -371,7 +257,7 @@ zh-cn/application-dev/reference/apis/js-apis-distributed-account.md @nianCode @z zh-cn/application-dev/reference/apis/js-apis-distributed-data.md @feng-aiwen @ge-yafang @gong-a-shi @logic42 zh-cn/application-dev/reference/apis/js-apis-distributedMissionManager.md @chenmingJay @ningningW @nan-xiansen @iceice1001 zh-cn/application-dev/reference/apis/js-apis-document.md @panqinxu @zengyawen @bubble_mao @jinhaihw -zh-cn/application-dev/reference/apis/js-apis-effectKit.md @zhangqiang183 @ge-yafang @wind_zj @zxg-gitee +zh-cn/application-dev/reference/apis/js-apis-effectKit.md @zhangqiang183 @ge-yafang @liuchao92 @zxg-gitee zh-cn/application-dev/reference/apis/js-apis-emitter.md @jayleehw @RayShih @li-weifeng2 @currydavids zh-cn/application-dev/reference/apis/js-apis-EnterpriseAdminExtensionAbility.md @liuzuming @ningningW @yangqing3 zh-cn/application-dev/reference/apis/js-apis-environment.md @panqinxu @zengyawen @bubble_mao @jinhaihw @@ -517,7 +403,7 @@ zh-cn/application-dev/reference/apis/js-apis-webSocket.md @zhang-hai-feng @zengy zh-cn/application-dev/reference/apis/js-apis-wifi.md @cheng_guohong @RayShih @cheng_guohong @quanli125 zh-cn/application-dev/reference/apis/js-apis-wifiext.md @cheng_guohong @RayShih @cheng_guohong @quanli125 zh-cn/application-dev/reference/apis/js-apis-window.md @zhangqiang183 @ge-yafang @zhouyaoying @zxg-gitee @nobuggers -zh-cn/application-dev/reference/apis/js-apis-windowAnimationManager.md @zhangqiang183 @ge-yafang @wind_zj @zxg-gitee +zh-cn/application-dev/reference/apis/js-apis-windowAnimationManager.md @zhangqiang183 @ge-yafang @liuchao92 @zxg-gitee zh-cn/application-dev/reference/apis/js-apis-worker.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-taskpool.md @gongjunsong @ge-yafang @flyingwolf @BlackStone zh-cn/application-dev/reference/apis/js-apis-workScheduler.md @chenmingJay @ningningW @nan-xiansen @iceice1001 @@ -557,7 +443,7 @@ zh-cn/application-dev/reference/apis/js-apis-bundleManager-remoteAbilityInfo.md zh-cn/application-dev/reference/apis/js-apis-bundleManager-shortcutInfo.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundleManager.md @shuaytao @RayShih @wangzhen107 @inter515 zh-cn/application-dev/reference/apis/js-apis-bundleMonitor.md @shuaytao @RayShih @wangzhen107 @inter515 -zh-cn/application-dev/reference/apis/js-apis-colorSpaceManager.md @zhangqiang183 @ge-yafang @wind_zj @zxg-gitee +zh-cn/application-dev/reference/apis/js-apis-colorSpaceManager.md @zhangqiang183 @ge-yafang @liuchao92 @zxg-gitee zh-cn/application-dev/reference/apis/js-apis-commonEventManager.md @jayleehw @RayShih @li-weifeng2 @currydavids zh-cn/application-dev/reference/apis/js-apis-configPolicy.md @liuzuming @ningningW @yangqing3 zh-cn/application-dev/reference/apis/js-apis-cooperate.md @yuanxinying @ningningW @cococoler @alien0208 diff --git a/en/application-dev/Readme-EN.md b/en/application-dev/Readme-EN.md index 5147d8c5200157e6a2edc8091630bbf22a6b9fb6..6e40eb34a50e798f7ae75f7c8cba2804cb6dfc51 100644 --- a/en/application-dev/Readme-EN.md +++ b/en/application-dev/Readme-EN.md @@ -50,6 +50,7 @@ - [\@BuilderParam Decorator: \@Builder Function Reference](quick-start/arkts-builderparam.md) - [\@Styles Decorator: Definition of Resusable Styles](quick-start/arkts-style.md) - [\@Extend Decorator: Extension of Built-in Components](quick-start/arkts-extend.md) + - [\@AnimatableExtend Decorator: Definition of Animatable Attributes](quick-start/arkts-animatable-extend.md) - [stateStyles: Polymorphic Style](quick-start/arkts-statestyles.md) - State Management - [State Management Overview](quick-start/arkts-state-management-overview.md) @@ -77,6 +78,7 @@ - Development - [Application Models](application-models/Readme-EN.md) - [UI Development](ui/Readme-EN.md) + - [ArkTS Common Library](arkts-utils/Readme-EN.md) - [Web](web/Readme-EN.md) - [Notification](notification/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md) diff --git a/en/application-dev/ai/Readme-EN.md b/en/application-dev/ai/Readme-EN.md index 525a9f3afe7e1e951d2216160cc23ba4c9b3335b..ac075993f5e8dc7ecba94e4f101f73005344b76f 100644 --- a/en/application-dev/ai/Readme-EN.md +++ b/en/application-dev/ai/Readme-EN.md @@ -1,3 +1,7 @@ # 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) + + diff --git a/en/application-dev/ai/ai-overview.md b/en/application-dev/ai/ai-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..968ee3439966cf2062e35cbb7929e4783566a2c6 --- /dev/null +++ b/en/application-dev/ai/ai-overview.md @@ -0,0 +1,40 @@ +# 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). diff --git a/en/application-dev/ai/figures/mindspore_workflow.png b/en/application-dev/ai/figures/mindspore_workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..babb4b4e1ec2b8961b79a324e3ac556fd1522e81 Binary files /dev/null and b/en/application-dev/ai/figures/mindspore_workflow.png differ diff --git a/en/application-dev/ai/mindspore-lite-js-guidelines.md b/en/application-dev/ai/mindspore-guidelines-based-js.md similarity index 62% rename from en/application-dev/ai/mindspore-lite-js-guidelines.md rename to en/application-dev/ai/mindspore-guidelines-based-js.md index 1f309acf19ba608ac698892ed64bb2e75ffdc437..a504d6b2c9a1936b6408d76b3d5e34ed5da23db4 100644 --- a/en/application-dev/ai/mindspore-lite-js-guidelines.md +++ b/en/application-dev/ai/mindspore-guidelines-based-js.md @@ -1,10 +1,8 @@ -# Using MindSpore Lite for Model Inference (JS) +# Using MindSpore Lite JavaScript APIs to Develop AI Applications ## 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. - -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). +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. ## Basic Concepts @@ -27,16 +25,14 @@ APIs involved in MindSpore Lite model inference are categorized into context API ## 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 @State inputName: string = 'mnet_caffemodel_nhwc.bin'; @State T_model_predict: string = 'Test_MSLiteModel_predict' @@ -49,7 +45,6 @@ build() { .fontSize(30) .fontWeight(FontWeight.Bold) .onClick(async () => { - // 1. Prepare for a model. let syscontext = globalThis.context; syscontext.resourceManager.getRawFileContent(this.inputName).then((buffer) => { this.inputBuffer = buffer; @@ -57,20 +52,24 @@ build() { }).catch(error => { 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 = {}; context.target = ['cpu']; context.cpu = {} context.cpu.threadNum = 1; context.cpu.threadAffinityMode = 0; 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 msLiteModel = await mindSporeLite.loadModelFromFile(modelFile, context); - // 4. Load data. + + // 3. Set the input data. const modelInputs = msLiteModel.getInputs(); 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=====') msLiteModel.predict(modelInputs).then((modelOutputs) => { let output0 = new Float32Array(modelOutputs[0].getData()); @@ -89,21 +88,21 @@ build() { ## 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 Launching 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 ``` -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 - 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 08-27 23:25:50.278 31782-31782/? I C03d00/JSAPP: =========MSLITE predict start===== diff --git a/en/application-dev/ai/mindspore-guidelines-based-native.md b/en/application-dev/ai/mindspore-guidelines-based-native.md new file mode 100644 index 0000000000000000000000000000000000000000..8294ad4c3213ecd7815962cb96751e2af72a77d5 --- /dev/null +++ b/en/application-dev/ai/mindspore-guidelines-based-native.md @@ -0,0 +1,244 @@ +# 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 +#include +#include +#include +``` + +(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(i), OH_AI_TensorGetName(tensor)); + LOGI("- Tensor %{public}d size is: %{public}d.\n", static_cast(i), (int)OH_AI_TensorGetDataSize(tensor)); + auto out_data = reinterpret_cast(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 *** +``` diff --git a/en/application-dev/application-dev-guide-for-gitee.md b/en/application-dev/application-dev-guide-for-gitee.md index 2a5ff1426182d415f09b55868c48d4238974ba64..9770de4b35a7e23d38a0266e5e0daf826e656e00 100644 --- a/en/application-dev/application-dev-guide-for-gitee.md +++ b/en/application-dev/application-dev-guide-for-gitee.md @@ -25,7 +25,8 @@ All applications should be developed on top of these frameworks. Then, equip yourself for developing the key features, with the following guidelines: -- [Web](web/web-component-overview.md) +- [ArkTS Common Library](arkts-utils/Readme-EN.md) +- [Web](web/Readme-EN.md) - [Notification](notification/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md) - [WebGL](webgl/Readme-EN.md) diff --git a/en/application-dev/application-dev-guide.md b/en/application-dev/application-dev-guide.md index d8bf473f9527e26eaf61c1a19757623879c4ef1d..65d3f42d95a37f3bdb9728a8e0b2897dd1ff8519 100644 --- a/en/application-dev/application-dev-guide.md +++ b/en/application-dev/application-dev-guide.md @@ -25,6 +25,7 @@ All applications should be developed on top of these frameworks. Then, equip yourself for developing the key features, with the following guidelines: +- [ArkTS Common Library](arkts-utils/arkts-commonlibrary-overview.md) - [Web](web/web-component-overview.md) - [Notification](notification/notification-overview.md) - [Window Manager](windowmanager/window-overview.md) diff --git a/en/application-dev/application-models/arkts-ui-widget-update-by-proxy.md b/en/application-dev/application-models/arkts-ui-widget-update-by-proxy.md index c655582af7834992c42025823b94ecab71eaa4ab..b7c1c93d21ec24673b3d07c4e971eadd7cd13661 100644 --- a/en/application-dev/application-models/arkts-ui-widget-update-by-proxy.md +++ b/en/application-dev/application-models/arkts-ui-widget-update-by-proxy.md @@ -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 **\** 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 **\** component. ```ts let storage = new LocalStorage(); @Entry(storage) @@ -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 **\** 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 **\** component. ```ts let storage = new LocalStorage(); @Entry(storage) @@ -215,4 +215,4 @@ The update-through-proxy configuration varies by the type of shared data. ## 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). diff --git a/en/application-dev/application-models/arkts-ui-widget-working-principles.md b/en/application-dev/application-models/arkts-ui-widget-working-principles.md index b1b09dc409380da8e530f571b2e5711ec63edd10..25cb66f1b05eaf845c11ab05350f2e705de6cec8 100644 --- a/en/application-dev/application-models/arkts-ui-widget-working-principles.md +++ b/en/application-dev/application-models/arkts-ui-widget-working-principles.md @@ -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. - **Figure 2** Working principles of the ArkTS widget rendering service -![WidgetRender](figures/WidgetRender.png) + **Figure 2** Working principles of the ArkTS widget rendering service -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 @@ -57,6 +58,8 @@ In addition, ArkTS widgets do not support the following features: - Instant preview -- Breakpoint debugging. +- Breakpoint debugging - Hot reload + +- **setTimeOut** diff --git a/en/application-dev/application-test/arkxtest-guidelines.md b/en/application-dev/application-test/arkxtest-guidelines.md index 00c733c7c37676511ccab4d53dc9992b01456ff8..aaec95077225a4d761eff173a2ef4bfdf46abdc3 100644 --- a/en/application-dev/application-test/arkxtest-guidelines.md +++ b/en/application-dev/application-test/arkxtest-guidelines.md @@ -3,42 +3,48 @@ ## Overview -To accelerate test automation of OpenHarmony, arkXtest — an automated unit and UI test framework that supports both the JavaScript (JS) and TypeScript (TS) programming languages — is provided. +arkXtest is an automated test framework that supports both the JavaScript (JS) and TypeScript (TS) programming languages. It consists of JsUnit and UiTest. -In this document you will learn about the key functions of arkXtest and how to use it to perform unit testing on application or system APIs and to write UI automated test scripts. +JsUnit is a unit test framework that provides basic APIs for compiling test cases and generating test reports for testing system and application APIs. +UiTest is a UI test framework that provides the UI component search and operation capabilities through simple and easy-to-use APIs, and allows you to develop automated test scripts based on GUI operations. -### Introduction +This document describes the main functions, implementation principles, environment setup, and test script compilation and execution of arkXtest. -arkXtest is part of the OpenHarmony toolkit and provides basic capabilities of writing and running OpenHarmony automated test scripts. In terms of test script writing, arkXtest offers a wide range of APIs, including basic process APIs, assertion APIs, and APIs related to UI operations. In terms of test script running, arkXtest offers such features as identifying, scheduling, and executing test scripts, as well as summarizing test script execution results. - -### Implementation +## Implementation arkXtest is divided into two parts: unit test framework and UI test framework. -- Unit Test Framework +As the backbone of arkXtest, the unit test framework offers such features as identifying, scheduling, and executing test scripts, as well as summarizing test script execution results. + +The UI test framework provides UiTest APIs for you to call in different test scenarios. The UI test scripts are executed on top of the unit test framework. + +### Unit Test Framework - As the backbone of arkXtest, the unit test framework offers such features as identifying, scheduling, and executing test scripts, as well as summarizing test script execution results. The figure below shows the main functions of the unit test framework. +Figure 1 Main functions of the unit test framework - ![](figures/UnitTest.PNG) +![](figures/UnitTest.PNG) - The following figure shows the basic unit test process. To start the unit test framework, run the **aa test** command. - - ![](figures/TestFlow.PNG) +Figure 2 Basic script process -- UI Test Framework +![](figures/TestFlow.PNG) - The UI test framework provides [UiTest APIs](../reference/apis/js-apis-uitest.md) for you to call in different test scenarios. The UI test scripts are executed on top of the aformentioned unit test framework. +> **NOTE** +> +> For details about the API in the unit test framework, see [Function Definition](https://gitee.com/openharmony/testfwk_arkxtest/blob/master/README_en.md#how-to-use). - The figure below shows the main functions of the UI test framework. +### UI Test Framework - ![](figures/Uitest.PNG) +Figure 3 Main functions of the UI test framework +![](figures/Uitest.PNG) -### Constraints -- The features of the UI test framework are available only in OpenHarmony 3.1 and later versions. +## Constraints + +- The features of the UI test framework are available only in OpenHarmony 3.1 Release and later versions. + - The feature availability of the unit test framework varies by version. For details about the mappings between the features and versions, see [arkXtest](https://gitee.com/openharmony/testfwk_arkxtest/blob/master/README_en.md). @@ -54,13 +60,24 @@ Hardware: PC connected to an OpenHarmony device, such as the RK3568 development [Download DevEco Studio](https://developer.harmonyos.com/cn/develop/deveco-studio#download) and set it up as instructed on the official website. +## Creating and Compiling a Test Script -## Creating a Test Script +### Creating a Test Script 1. Open DevEco Studio and create a project, in which the **ohos** directory is where the test script is located. 2. Open the .ets file of the module to be tested in the project directory. Move the cursor to any position in the code, and then right-click and choose **Show Context Actions** > **Create Ohos Test** or press **Alt+Enter** and choose **Create Ohos Test** to create a test class. -## Writing a Unit Test Script +### Writing a Unit Test Script + + The unit test script must contain the following basic elements: + +1. Import of the dependencies so that the dependent test APIs can be used. + +2. Test code, mainly about the related logic, such as API invoking. + +3. Invoking of the assertion APIs and setting of checkpoints. If there is no checkpoint, the test script is considered as incomplete. + +The following sample code is used to start the test page to check whether the page displayed on the device is the expected page. ```TS import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; @@ -93,25 +110,17 @@ export default function abilityTest() { } ``` -The unit test script must contain the following basic elements: - -1. Import of the dependencies so that the dependent test APIs can be used. +### Writing a UI Test Script -2. Test code, mainly about the related logic, such as API invoking. +The UI test is based on the unit test. The UI test script adds the invoking of the UiTest interface (providing a link) to the unit test script to complete the corresponding test activities. In this example, the UI test script is written based on the preceding unit test script. It implements the click operation on the started application page and checks whether the page changes as expected. -3. Invoking of the assertion APIs and setting of checkpoints. If there is no checkpoint, the test script is considered as incomplete. - -## Writing a UI Test Script - -You write a UI test script based on the unit test framework, adding the invoking of APIs provided by the UI test framework to implement the corresponding test logic. - -In this example, the UI test script is written based on the preceding unit test script. First, import the dependency, as shown below: +1. Import the dependency. ```js import {Driver,ON,Component,MatchPattern} from '@ohos.uitest' ``` -Then, write specific test code. Specifically, implement the click action on the started application page and add checkpoint check cases. +2. Write test code. ```js export default function abilityTest() { @@ -125,21 +134,21 @@ export default function abilityTest() { console.info('Uitest, start ability failed: ' + err) }) await sleep(1000); - //check top display ability + // Check the top display ability. await delegator.getCurrentTopAbility().then((Ability)=>{ console.info("get top ability"); expect(Ability.context.abilityInfo.name).assertEqual('EntryAbility'); }) - //ui test code - //init driver + // UI test code + // Initialize the driver. var driver = await Driver.create(); await driver.delayMs(1000); - //find button on text 'Next' + // Find the button on text 'Next'. var button = await driver.findComponent(ON.text('Next')); - //click button + // Click the button. await button.click(); await driver.delayMs(1000); - //check text + // Check text. await driver.assertComponentExist(ON.text('after click')); await driver.pressBack(); done(); @@ -158,9 +167,11 @@ export default function abilityTest() { You can run a test script in DevEco Studio in any of the following modes: -- Test package level: All test cases in the test package are executed. -- Test suite level: All test cases defined in the **describe** method are executed. -- Test method level: The specified **it** method, that is, a single test case, is executed. +1. Test package level: All test cases in the test package are executed. + +2. Test suite level: All test cases defined in the **describe** method are executed. + +3. Test method level: The specified **it** method, that is, a single test case, is executed. ![](figures/Execute.PNG) @@ -170,9 +181,17 @@ After the test is complete, you can view the test result in DevEco Studio, as sh ![](figures/TestResult.PNG) +**Viewing the Test Case Coverage** + +After the test is complete, you can view the test case coverage. + ### In the CLI -To run a test script in the CLI, execute **aa** commands with different execution control keywords. +Install the application test package on the test device and run the **aa** command with different execution control keywords in the CLI. + +> **NOTE** +> +> Before running commands in the CLI, make sure hdc-related environment variables have been configured. The table below lists the keywords in **aa** test commands. @@ -196,15 +215,13 @@ The framework supports multiple test case execution modes, which are triggered b | random | Whether to execute test cases in random sequence.| **true**/**false** (default value) | -s random true | | testType | Type of the test case to be executed. | function, performance, power, reliability, security, global, compatibility, user, standard, safety, resilience| -s testType function | | level | Level of the test case to be executed. | 0, 1, 2, 3, 4 | -s level 0 | -| size | Size of the test case to be executed. | small, medium, large | -s size small +| size | Size of the test case to be executed. | small, medium, large | -s size small | | stress | Number of times that the test case is executed. | Positive integer | -s stress 1000 | **Running Commands** -> Before running commands in the CLI, make sure hdc-related environment variables have been configured. - -- Open the CLI. -- Run the **aa test** commands. +1. Open the CLI. +2. Run the **aa test** commands. Example 1: Execute all test cases. @@ -303,8 +320,8 @@ OHOS_REPORT_STATUS: consuming=4 | OHOS_REPORT_STATUS: numtests | Total number of test cases in the test package.| | OHOS_REPORT_STATUS: stream | Error information of the current test case.| | OHOS_REPORT_STATUS: test| Name of the current test case.| -| OHOS_REPORT_STATUS_CODE | Execution result of the current test case. The options are as follows:
**0**: pass
**1**: error
**2**: fail| -| OHOS_REPORT_STATUS: consuming | Time spent in executing the current test case.| +| OHOS_REPORT_STATUS_CODE | Execution result of the current test case.
**0**: pass.
**1**: error.
**2**: fail. | +| OHOS_REPORT_STATUS: consuming | Time spent in executing the current test case, in milliseconds.| - After the commands are executed, the log information similar to the following is displayed: @@ -323,10 +340,69 @@ OHOS_REPORT_STATUS: taskconsuming=16029 | Error | Number of test cases whose execution encounters errors. | | Pass | Number of passed test cases.| | Ignore | Number of test cases not yet executed.| -| taskconsuming| Total time spent in executing the current test case.| +| taskconsuming| Total time spent in executing the current test case, in milliseconds.| > When an error occurs in break-on-error mode, check the **Ignore** and interrupt information. +## Recording User Operations +### Using the Recording Feature +> You can record the operations performed on the current page to **/data/local/tmp/layout/record.csv**. To end the recording, press **Ctrl+C**. + +```shell + hdc shell uitest uiRecord record +``` +### Viewing Recording Data +You can view the recording data in either of the following ways. + +#### Reading and Printing Recording Data + +```shell + hdc shell uitest uiRecord read +``` +#### Exporting the record.csv File +```shell +hdc file recv /data/local/tmp/layout/record.csv D:\tool # D:\tool indicates the local save path, which can be customized. +``` +- The following describes the fields in the recording data: +``` +{ + "ABILITY": "com.ohos.launcher.MainAbility", // Foreground application page. + "BUNDLE": "com.ohos.launcher", // Application. + "CENTER_X": "", // X-coordinate of the center of the pinch gesture. + "CENTER_Y": "", // Y-coordinate of the center of the pinch gesture. + "EVENT_TYPE": "pointer", // + "LENGTH": "0", // Total length. + "OP_TYPE": "click", // Event type. Currently, click, double-click, long-press, drag, pinch, swipe, and fling types are supported. + "VELO": "0.000000", // Hands-off velocity. + "direction.X": "0.000000",// Movement along the x-axis. + "direction.Y": "0.000000", // Movement along the y-axis. + "duration": 33885000.0, // Gesture duration. + "fingerList": [{ + "LENGTH": "0", // Total length. + "MAX_VEL": "40000", // Maximum velocity. + "VELO": "0.000000", // Hands-off velocity. + "W1_BOUNDS": "{"bottom":361,"left":37,"right":118,"top":280}", // Starting component bounds. + "W1_HIER": "ROOT,3,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0", // Starting component hierarchy. + "W1_ID": "", // ID of the starting component. + "W1_Text": "", // Text of the starting component. + "W1_Type": "Image", // Type of the starting component. + "W2_BOUNDS": "{"bottom":361,"left":37,"right":118,"top":280}", // Ending component bounds. + "W2_HIER": "ROOT,3,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0", // Ending component hierarchy. + "W2_ID": "", // ID of the ending component. + "W2_Text": "", // Text of the ending component. + "W2_Type": "Image", // Type of the ending component. + "X2_POSI": "47", // X coordinate of the ending point. + "X_POSI": "47", // X coordinate of the starting point. + "Y2_POSI": "301", // Y coordinate of the ending point. + "Y_POSI": "301", // Y coordinate of the starting point. + "direction.X": "0.000000", // Movement along the x-axis. + "direction.Y": "0.000000" // Movement along the y-axis. + }], + "fingerNumber": "1" // Number of fingers. +} +``` + + ## FAQs ### FAQs About Unit Test Cases @@ -404,7 +480,7 @@ hdc shell param set persist.ace.testmode.enabled 1 **Problem** -The UI test case fails to be executed. The HiLog file contains the error message "uitest-api does not allow calling concurrently". +The UI test case fails to be executed. The HiLog file contains the error message "uitest-api does not allow calling concurrently." **Possible Causes** @@ -415,7 +491,6 @@ The UI test case fails to be executed. The HiLog file contains the error message **Solution** 1. Check the case implementation and add the **await** operator to the asynchronous API. - 2. Do not execute UI test cases in multiple processes. #### The failure log contains "does not exist on current UI! Check if the UI has changed after you got the widget object" diff --git a/en/application-dev/arkts-utils/Readme-EN.md b/en/application-dev/arkts-utils/Readme-EN.md new file mode 100644 index 0000000000000000000000000000000000000000..a574b494aea8d5bc7b62a000044fa81ae269b43e --- /dev/null +++ b/en/application-dev/arkts-utils/Readme-EN.md @@ -0,0 +1,23 @@ +# ArkTS Common Library + +- [Overview of ArkTS Common Library](arkts-commonlibrary-overview.md) +- Concurrency + - [Concurrency Overview](concurrency-overview.md) + - Using Asynchronous Concurrency for Development + - [Asynchronous Concurrency Overview](async-concurrency-overview.md) + - [Single I/O Task Development](single-io-development.md) + - Using Multithread Concurrency for Development + - [Multithread Concurrency Overview](multi-thread-concurrency-overview.md) + - [Comparison Between TaskPool and Worker](taskpool-vs-worker.md) + - [CPU Intensive Task Development](cpu-intensive-task-development.md) + - [I/O Intensive Task Development](io-intensive-task-development.md) + - [Synchronous Task Development](sync-task-development.md) +- Container + - [Container Overview](container-overview.md) + - [Linear Containers](linear-container.md) + - [Nonlinear Containers](nonlinear-container.md) +- XML Generation, Parsing, and Conversion + - [XML Overview](xml-overview.md) + - [XML Generation](xml-generation.md) + - [XML Parsing](xml-parsing.md) + - [XML Conversion](xml-conversion.md) diff --git a/en/application-dev/arkts-utils/arkts-commonlibrary-overview.md b/en/application-dev/arkts-utils/arkts-commonlibrary-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..708f14c78da7c7bd53d66cd62826ff01c5d6e212 --- /dev/null +++ b/en/application-dev/arkts-utils/arkts-commonlibrary-overview.md @@ -0,0 +1,31 @@ +# Overview of ArkTS Common Library + + +The ArkTS common library provides common basic capabilities, as illustrated in the figure below. + + +**Figure 1** Capabilities of the ArkTS common library + +![arkts-commonlibrary](figures/arkts-commonlibrary.png) + + +- Supporting [asynchronous concurrency and multithread concurrency](concurrency-overview.md) + - Supports standard JavaScript asynchronous concurrency capabilities such as Promise and async/await. + - Uses **TaskPool** to provide a multithread running environment for applications. The use of **TaskPool** helps reduce resource consumption and improve system performance. It also frees you from caring about the lifecycle of thread instances. + - Uses **Worker** to support multithread concurrency. The worker thread can communicate with the host thread. You need to proactively create and close a worker thread. + +- Providing common capabilities of [adding, deleting, modifying, and querying elements in containers](container-overview.md) + +- Constructing and parsing XML files, URLs, and URIs + - Extensible Markup Language (XML) is designed for data transmission and storage. The common library provides APIs for [XML generation, parsing, and conversion](xml-overview.md). + - [URI](../reference/apis/js-apis-uri.md) is a uniform resource identifier that uniquely identifies a resource. [URL](../reference/apis/js-apis-url.md) is a uniform resource locator that provides a path for locating a resource. + +- Supporting common [string and binary data processing](../reference/apis/js-apis-util.md) and [logging](../reference/apis/js-apis-logs.md) + - Provides APIs to encode and decode strings. + - Provides APIs to encode and decode Base64-encoded bytes. + - Supports common rational number operations, including comparing rational numbers and obtaining numerators and denominators. + - Provides **Scope** APIs to define the valid range of a field. + - Provides APIs to process binary data in scenarios such as TCP flows or file system operations. + - Supports logging using the console. + +- Providing the capability of [obtaining process information and operating processes](../reference/apis/js-apis-process.md) \ No newline at end of file diff --git a/en/application-dev/arkts-utils/async-concurrency-overview.md b/en/application-dev/arkts-utils/async-concurrency-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..32bed2f143b9dbf32369fb439290d762269e055a --- /dev/null +++ b/en/application-dev/arkts-utils/async-concurrency-overview.md @@ -0,0 +1,87 @@ +# Asynchronous Concurrency Overview + + +Promise and async/await are standard JavaScript syntax that provides asynchronous concurrency. Asynchronous code ensures that actions initiated now finish later. It allows the execution of only one segment of code at a time and is therefore applicable to the development of a single I/O task, for example, a network request or a file read/write operation. + + +Promise and async/await allow an application to perform other operations without waiting for the completion of certain actions. + + +## Promise + +Promise is an object used to process asynchronous operations. It converts asynchronous operations into a style similar to synchronous operations for easier code writing and maintenance. Promise provides a state mechanism to manage different phases of asynchronous operations. It also provides methods to register callback functions to handle the success or failure of these operations. + +Promise has three states: pending, fulfilled, and rejected. After being created, a Promise object is in the pending state and changes to the fulfilled or rejected state when the asynchronous operation is complete. + +The most common usage for Promise is to instantiate a Promise object through a constructor and pass in a function (usually named **executor**) with two parameters. The **executor** function receives two parameters: **resolve** and **reject**, which represent the callback functions that should be called when the asynchronous operation succeeds and fails, respectively. The code snippet below creates a Promise object and simulates an asynchronous operation: + + +```js +const promise = new Promise((resolve, reject) => { + setTimeout(() => { + const randomNumber = Math.random(); + if (randomNumber > 0.5) { + resolve(randomNumber); + } else { + reject(new Error('Random number is too small')); + } + }, 1000); +}); +``` + +In the preceding code, the **setTimeout** function simulates an asynchronous operation that randomly generates a number one second later. If the random number is greater than 0.5, the **resolve** callback function is executed and the generated random number is passed in as a parameter. Otherwise, the **reject** callback function is executed and an error object is passed in as a parameter. + +After the Promise object is created, you can use the **then** and **catch** methods to register the callback functions for the fulfilled and rejected states. The **then** method can receive two parameters: one for processing the fulfilled state and the other for processing the rejected state. If only one parameter is passed in, the callback function is executed as long as the state changes. The **catch** method receives a callback function to process the failure result, that is, capture the exception thrown when the Promise state changes to **rejected** or the operation fails. The code snippet below shows the use of **then** and **catch** methods: + + +```js +promise.then(result => { + console.info(`Random number is ${result}`); +}).catch(error => { + console.error(error.message); +}); +``` + +In the preceding code, the callback function of the **then** method receives the success result of the Promise object as a parameter and outputs it to the console. If the Promise object enters the rejected state, the callback function of the **catch** method receives the error object as a parameter and outputs it to the console. + + +## Async/Await + +Async/Await is a Promise syntax sugar used to process asynchronous operations, making it easier to read and write asynchronous code. The async keyword is used to declare an asynchronous function, and the await keyword is used to wait for Promise parsing (fulfilled or rejected). In this way, the asynchronous operation logic is coded as a synchronous operation. + +The **async** function returns a Promise object to represent an asynchronous operation. Inside the **async** function, you can use the await keyword to wait for the parsing of the Promise object and return its parsed value. If an **async** function throws an exception, the Promise object returned by the function is rejected, and the exception information is passed to the **onRejected()** method of the Promise object. + +The code snippet below uses async/await to simulate an asynchronous operation that returns a string three seconds later. + + +```js +async function myAsyncFunction() { + const result = await new Promise((resolve) => { + setTimeout(() => { + resolve('Hello, world!'); + }, 3000); + }); + console.info(String(result)); // Output: Hello, world! +} + +myAsyncFunction(); +``` + +In the preceding code, the await keyword is used to wait for the parsing of the Promise object and store its parsed value in the **result** variable. + +Note that the entire operation must be packaged in the **async** function because the code needs to wait for the asynchronous operation to complete. In addition to **await**, you can use the try/catch block to capture exceptions in asynchronous operations. + + +```js +async function myAsyncFunction() { + try { + const result = await new Promise((resolve) => { + resolve('Hello, world!'); + }); + } catch (e) { + console.error(`Get exception: ${e}`); + } +} + +myAsyncFunction(); +``` diff --git a/en/application-dev/arkts-utils/concurrency-overview.md b/en/application-dev/arkts-utils/concurrency-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..3f091f42ea1257468aba5ba9ed94e64c92f661bb --- /dev/null +++ b/en/application-dev/arkts-utils/concurrency-overview.md @@ -0,0 +1,15 @@ +# Concurrency Overview + +Concurrency refers to the capability of processing multiple tasks in the same period. To improve the response speed and frame rate of applications and prevent time-consuming tasks from blocking the main thread, OpenHarmony provides two policies: asynchronous concurrency and multithread concurrency. + +- Asynchronous concurrency means that an action in asynchronous code is suspended and will continue later. Only one segment of code is being executed at a time. + +- Multithread concurrency allows multiple segments of code to be executed at a time. When the main thread continues to respond to user operations and update the UI, time-consuming operations are performed in the background to avoid application freezing. + +Concurrency is used in a variety of scenarios, including a single I/O task, CPU intensive task, I/O intensive task, synchronous task, and the like. You can select a concurrency policy based on your scenario. + +ArkTS provides the following mechanisms to support asynchronous concurrency and multithread concurrency: + +- Promise and async/await: provide asynchronous concurrency and apply to the development of a single I/O task. For details, see [Asynchronous Concurrency Overview](async-concurrency-overview.md). + +- **TaskPool** and **Worker**: provide multithread concurrency and apply to the development of CPU intensive tasks, I/O intensive tasks, and synchronous tasks. For details, see [Multithread Concurrency Overview](multi-thread-concurrency-overview.md). diff --git a/en/application-dev/arkts-utils/container-overview.md b/en/application-dev/arkts-utils/container-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..8a317e23167644ad72287c05672c3aa4a641cd29 --- /dev/null +++ b/en/application-dev/arkts-utils/container-overview.md @@ -0,0 +1,7 @@ +# Overview of Containers + +The container classes provide a set of methods to process elements of various data types stored in containers. + +The container classes are implemented in a way similar to static languages. By restricting storage locations and attributes, they remove redundant logic for each type of data while providing the complete functionalities, ensuring efficient data access and improving application performance. + +Currently, OpenHarmony provides 14 types of linear and non-linear containers, each of which has its own features and uses cases. For details, see [Linear Containers](linear-container.md) and [Nonlinear Containers](nonlinear-container.md). diff --git a/en/application-dev/arkts-utils/cpu-intensive-task-development.md b/en/application-dev/arkts-utils/cpu-intensive-task-development.md new file mode 100644 index 0000000000000000000000000000000000000000..80ecd66bb86f11bb4b0dace92e8e6924bdbd8b4f --- /dev/null +++ b/en/application-dev/arkts-utils/cpu-intensive-task-development.md @@ -0,0 +1,191 @@ +# CPU Intensive Task Development + + +CPU intensive tasks occupy lots of system computing resources for a long period of time, during which other events of the thread are blocked. Example CPU intensive tasks are image processing, video encoding, and data analysis. + + +OpenHarmony uses multithread concurrency to process CPU intensive tasks. This improves CPU utilization and application response speed. + + +If a task does not need to occupy a background thread for a long time (3 minutes), you are advised to use **TaskPool**. Otherwise, use **Worker**. The following uses histogram processing and a time-consuming model prediction task in the background as examples. + + +## Using TaskPool to Process Histograms + +1. Implement the logic of image processing. + +2. Segment the data, and initiate associated task scheduling through task groups. + Create a [task group](../reference/apis/js-apis-taskpool.md#taskgroup10), call [addTask()](../reference/apis/js-apis-taskpool.md#addtask10) to add tasks, call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute10) to execute the tasks in the task group, and set [a high priority](../reference/apis/js-apis-taskpool.md#priority) for the task group. After all the tasks in the task group are complete, the histogram processing result is returned simultaneously. + +3. Summarize and process the result arrays. + + +```ts +import taskpool from '@ohos.taskpool'; + +@Concurrent +function imageProcessing(dataSlice: ArrayBuffer) { + // Step 1: Perform specific image processing operations and other time-consuming operations. + return dataSlice; +} + +function histogramStatistic(pixelBuffer: ArrayBuffer) { + // Step 2: Perform concurrent scheduling for data in three segments. + let number = pixelBuffer.byteLength / 3; + let buffer1 = pixelBuffer.slice(0, number); + let buffer2 = pixelBuffer.slice(number, number * 2); + let buffer3 = pixelBuffer.slice(number * 2); + + let group = new taskpool.TaskGroup(); + group.addTask(imageProcessing, buffer1); + group.addTask(imageProcessing, buffer2); + group.addTask(imageProcessing, buffer3); + + taskpool.execute(group, taskpool.Priority.HIGH).then((ret: ArrayBuffer[]) => { + // Step 3: Summarize and process the result arrays. + }) +} + +@Entry +@Component +struct Index { + @State message: string = 'Hello World' + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + let data: ArrayBuffer; + histogramStatistic(data); + }) + } + .width('100%') + } + .height('100%') + } +} +``` + + +## Using Worker for Time-Consuming Data Analysis + +The following uses the training of a region-specific house price prediction model as an example. This model can be used to predict house prices in the region based on the house area and number of rooms. The model needs to run for a long time, and the prediction will use the previous running result. Due to these considerations, **Worker** is used for the development. + +1. Add the worker creation template provided on DevEco Studio to your project, and name it **MyWorker**. + + ![newWorker](figures/newWorker.png) + +2. In the main thread, call [ThreadWorker()](../reference/apis/js-apis-worker.md#threadworker9) to create a **Worker** object. The calling thread is the host thread. + + ```js + import worker from '@ohos.worker'; + + const workerInstance = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts'); + ``` + +3. In the host thread, call [onmessage()](../reference/apis/js-apis-worker.md#onmessage9) to receive messages from the worker thread, and call [postMessage()](../reference/apis/js-apis-worker.md#postmessage9) to send messages to the worker thread. + + For example, the host thread sends training and prediction messages to the worker thread, and receives messages sent back by the worker thread. + + + ```js + // Receive the result of the worker thread. + workerInstance.onmessage = function(e) { + // data carries the information sent by the main thread. + let data = e.data; + console.info('MyWorker.ts onmessage'); + // Perform time-consuming operations in the worker thread. + } + + workerInstance.onerror = function (d) { + // Receive error information of the worker thread. + } + + // Send a training message to the worker thread. + workerInstance.postMessage({ 'type': 0 }); + // Send a prediction message to the worker thread. + workerInstance.postMessage({ 'type': 1, 'value': [90, 5] }); + ``` + +4. Bind the **Worker** object in the **MyWorker.ts** file. The calling thread is the worker thread. + + ```js + import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker'; + + let workerPort: ThreadWorkerGlobalScope = worker.workerPort; + ``` + +5. In the worker thread, call [onmessage()](../reference/apis/js-apis-worker.md#onmessage9-1) to receive messages sent by the host thread, and call [postMessage()](../reference/apis/js-apis-worker.md#postmessage9-2) to send messages to the host thread. + + For example, the prediction model and its training process are defined in the worker thread, and messages are exchanged with the main thread. + + + ```js + import worker, { ThreadWorkerGlobalScope, MessageEvents, ErrorEvent } from '@ohos.worker'; + + let workerPort: ThreadWorkerGlobalScope = worker.workerPort; + + // Define the training model and result. + let result; + + // Define the prediction function. + function predict(x) { + return result[x]; + } + + // Define the optimizer training process. + function optimize() { + result = {}; + } + + // onmessage logic of the worker thread. + workerPort.onmessage = function (e: MessageEvents) { + let data = e.data + // Perform operations based on the type of data to transmit. + switch (data.type) { + case 0: + // Perform training. + optimize(); + // Send a training success message to the main thread after training is complete. + workerPort.postMessage({ type: 'message', value: 'train success.' }); + break; + case 1: + // Execute the prediction. + const output = predict(data.value); + // Send the prediction result to the main thread. + workerPort.postMessage({ type: 'predict', value: output }); + break; + default: + workerPort.postMessage({ type: 'message', value: 'send message is invalid' }); + break; + } + } + ``` + +6. After the task is completed in the worker thread, destroy the worker thread. The worker thread can be destroyed by itself or the host thread. Then, call [onexit()](../reference/apis/js-apis-worker.md#onexit9) in the host thread to define the processing logic after the worker thread is destroyed. + + + ```js + // After the worker thread is destroyed, execute the onexit() callback. + workerInstance.onexit = function() { + console.info("main thread terminate"); + } + ``` + + In the host thread, call [terminate()](../reference/apis/js-apis-worker.md#terminate9) to destroy the worker thread and stop the worker thread from receiving messages. + + + ```js + // Destroy the worker thread. + workerInstance.terminate(); + ``` + + In the worker thread, call [close()](../reference/apis/js-apis-worker.md#close9) to destroy the worker thread and stop the worker thread from receiving messages. + + ```js + // Destroy the worker thread. + workerPort.close(); + ``` diff --git a/en/application-dev/arkts-utils/figures/arkts-commonlibrary.png b/en/application-dev/arkts-utils/figures/arkts-commonlibrary.png new file mode 100644 index 0000000000000000000000000000000000000000..e8590f0115429b7857e3d7d44b7dceb821857515 Binary files /dev/null and b/en/application-dev/arkts-utils/figures/arkts-commonlibrary.png differ diff --git a/en/application-dev/arkts-utils/figures/newWorker.png b/en/application-dev/arkts-utils/figures/newWorker.png new file mode 100644 index 0000000000000000000000000000000000000000..c1b47bb4a885cf793ed467db43151a99720ea48d Binary files /dev/null and b/en/application-dev/arkts-utils/figures/newWorker.png differ diff --git a/en/application-dev/arkts-utils/figures/taskpool.png b/en/application-dev/arkts-utils/figures/taskpool.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ae190d4dece65d196fd635b08ae6b420b6033b Binary files /dev/null and b/en/application-dev/arkts-utils/figures/taskpool.png differ diff --git a/en/application-dev/arkts-utils/figures/worker.png b/en/application-dev/arkts-utils/figures/worker.png new file mode 100644 index 0000000000000000000000000000000000000000..5feb2f6e5a01e4686a3cb3fe2451d72ae80c8685 Binary files /dev/null and b/en/application-dev/arkts-utils/figures/worker.png differ diff --git a/en/application-dev/arkts-utils/io-intensive-task-development.md b/en/application-dev/arkts-utils/io-intensive-task-development.md new file mode 100644 index 0000000000000000000000000000000000000000..562f749fd99daff42359af2598bc42483dc5595c --- /dev/null +++ b/en/application-dev/arkts-utils/io-intensive-task-development.md @@ -0,0 +1,51 @@ +# I/O Intensive Task Development + + +Asynchronous concurrency can solve the problem of a single blocking I/O operation. In the case of I/O intensive tasks, the execution of other tasks in the thread is still blocked. To resolve this issue, multithread concurrency is introduced. + + +The performance focus of I/O intensive tasks is not the CPU processing capability, but the speed and efficiency of I/O operations, since such a task usually requires frequent operations such as disk read/write and network communication. The following uses frequent read/write operations on a system file to simulate concurrency processing of I/O intensive tasks. + + +1. Define a concurrency function that internally calls I/O capabilities intensively. + + ```ts + import fs from '@ohos.file.fs'; + + // Define a concurrency function that internally calls I/O capabilities intensively. + @Concurrent + async function concurrentTest(fileList: string[]) { + // Implement file writing. + async function write(data, filePath) { + let file = await fs.open(filePath, fs.OpenMode.READ_WRITE); + await fs.write(file.fd, data); + fs.close(file); + } + // Write the file cyclically. + for (let i = 0; i < fileList.length; i++) { + write('Hello World!', fileList[i]).then(() => { + console.info(`Succeeded in writing the file. FileList: ${fileList[i]}`); + }).catch((err) => { + console.error(`Failed to write the file. Code is ${err.code}, message is ${err.message}`) + return false; + }) + } + return true; + } + ``` + +2. Use **TaskPool** to execute the concurrency function that contains the intensive I/O operations. Specifically, call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute) to execute the tasks and process the scheduling result in a callback. For details about how to obtain **filePath1** and **filePath2** in the example, see [Obtaining Application File Paths](../application-models/application-context-stage.md#obtaining-application-file-paths). + + ```ts + import taskpool from '@ohos.taskpool'; + + let filePath1 =...; // Application file path + let filePath2 = ...; + + // Use TaskPool to execute the concurrency function that contains the intensive I/O operations. + // In the case of a large array, the distribution of I/O intensive tasks also preempts the main thread. Therefore, multiple threads are required. + taskpool.execute(concurrentTest, [filePath1, filePath2]).then((ret) => { + // Process the scheduling result. + console.info(`The result: ${ret}`); + }) + ``` diff --git a/en/application-dev/arkts-utils/linear-container.md b/en/application-dev/arkts-utils/linear-container.md new file mode 100644 index 0000000000000000000000000000000000000000..2a160f113f68e3257278e5a18168bb811a8efe30 --- /dev/null +++ b/en/application-dev/arkts-utils/linear-container.md @@ -0,0 +1,253 @@ +# Linear Containers + + +Linear containers implement a data structure that enables sequential access. The bottom layer of linear containers is implemented through arrays. OpenHarmony provides the following linear containers: **ArrayList**, **Vector**, **List**, **LinkedList**, **Deque**, **Queue**, and **Stack**. + + +Fully considering the data access speed, linear containers support Create, Read, Update, and Delete (CRUD) through a bytecode instruction at runtime. + + +## ArrayList + +[ArrayList](../reference/apis/js-apis-arraylist.md) is a dynamic array that can be used to construct a global array object. You are advised to use **ArrayList** when elements in a container need to be frequently read. + +**ArrayList** uses generics and must be stored in a contiguous memory space. Its initial capacity is 10, and it increases capacity 1.5-fold in each dynamic expansion. + +**ArrayList** provides the following CRUD APIs. + +| Operation| Description| +| --------- | ------- | +| Adding elements| Use **add(element: T)** to add an element at the end of this container.| +| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).| +| Accessing elements| Use **arr\[index]** to obtain the value at a given position (specified by **index**).| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, arrlist?: ArrayList<T>) => void, thisArg?: Object): void** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **arr\[index] = xxx** to change the value at a given position (specified by **index**).| +| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.| +| Deleting elements| Use **removeByRange(fromIndex: number, toIndex: number)** to remove all of the elements within a range.| + + +## Vector + +[Vector](../reference/apis/js-apis-vector.md) is a continuous storage structure that can be used to construct a global array object. **Vector** uses generics and must be stored in a contiguous memory space. Its initial capacity is 10, and it has capacity doubled in each dynamic expansion. + +Both **Vector** and [ArrayList](../reference/apis/js-apis-arraylist.md) are implemented based on arrays, but **Vector** provides more interfaces for operating the arrays. In addition to operator access, **Vector** provides the getter and setter to provide a more complete verification and error tolerance mechanism. + +The APIs provided by **Vector** are deprecated since API version 9. You are advised to use [ArrayList](../reference/apis/js-apis-arraylist.md). + +**Vector** provides the following CRUD APIs. + +| Operation| Description| +| --------- | ------- | +| Adding elements| Use **add(element: T)** to add an element at the end of this container.| +| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).| +| Accessing elements| Use **vec\[index]** to obtain the value at a given position (specified by **index**).| +| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).| +| Accessing elements| Use **getLastElement()** to obtain the last element in this container.| +| Accessing elements| Use **getlndexOf(element: T)** to obtain the index of the first occurrence of the specified element.| +| Accessing elements| Use **getLastlndexOf(element: T)** to obtain the index of the last occurrence of the specified element.| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, Vector?: Vector<T>) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **vec\[index]=xxx** to change the value at a given position (specified by **index**).| +| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.| +| Modifying elements| Use **setLength(newSize: number)** to set the size of this container.| +| Deleting elements| Use **removeBylndex(index: number)** to remove the value at a given position (specified by **index**).| +| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.| +| Deleting elements| Use **removeByRange(fromIndex: number, toIndex: number)** to remove all of the elements within a range.| + + +## List + +[List](../reference/apis/js-apis-list.md) can be used to construct a singly linked list, which supports access only through the head node to the tail node. **List** uses generics and can be stored in a non-contiguous memory space. + +Unlike [LinkedList](../reference/apis/js-apis-linkedlist.md), which is a doubly linked list, **List** is a singly linked list that does not support insertion or removal at both ends. + +You are advised to use **List** for frequent insertion and removal operations. + +**List** provides the following CRUD APIs. + +| Operation| Description| +| --------- | ------ | +| Adding elements| Use **add(element: T)** to add an element at the end of this container.| +| Adding elements| Use **insert(element: T, index: number)** to insert an element at a given position (specified by **index**).| +| Accessing elements| Use **list\[index]** to obtain the value at a given position (specified by **index**).| +| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).| +| Accessing elements| Use **getFirst()** to obtain the first element in this container.| +| Accessing elements| Use **getLast()** to obtain the last element in this container.| +| Accessing elements| Use **getlndexOf(element: T)** to obtain the index of the first occurrence of the specified element.| +| Accessing elements| Use **getLastlndexOf(element: T)** to obtain the index of the last occurrence of the specified element.| +| Accessing elements| Use **forEach(callbackfn: (value: T, index?: number, list?: List<T>)=> void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **list\[index] = xxx** to change the value at a given position (specified by **index**).| +| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.| +| Modifying elements| Use **replaceAllElements(callbackFn:(value: T,index?: number,list?: List<T>)=>T,thisArg?: Object)** to replace all elements in this container with new elements.| +| Deleting elements| Use **removeBylndex(index: number)** to remove the value at a given position (specified by **index**).| +| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.| + + +## LinkedList + +[LinkedList](../reference/apis/js-apis-linkedlist.md) can be used to construct a doubly linked list, which can be traversed at both ends. **LinkedList** uses generics and can be stored in a non-contiguous memory space. + +Unlike [List](../reference/apis/js-apis-list.md), which is a singly linked list, **LinkedList** is a doubly linked list that supports insertion and removal at both ends. + +**LinkedList** is more efficient in data insertion than [ArrayList](../reference/apis/js-apis-arraylist.md), but less efficient in data access. + +You are advised to use **LinkedList** for frequent insertion and removal operations. + +**LinkedList** provides the following CRUD APIs. + +| Operation| Description| +| ---------- | ------ | +| Adding elements| Use **add(element: T)** to add an element at the end of this container.| +| Adding elements| Use **insert(index: number, element: T)** to insert an element at a given position (specified by **index**).| +| Accessing elements| Use **list\[index]** to obtain the value at a given position (specified by **index**).| +| Accessing elements| Use **get(index: number)** to obtain the element at a given position (specified by **index**).| +| Accessing elements| Use **getFirst()** to obtain the first element in this container.| +| Accessing elements| Use **getLast()** to obtain the last element in this container.| +| Accessing elements| Use **getlndexOf(element: T)** to obtain the index of the first occurrence of the specified element.| +| Accessing elements| Use **getLastlndexOf(element: T)** to obtain the index of the last occurrence of the specified element.| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, list?: LinkedList<T>) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **list\[index]=xxx** to change the value at a given position (specified by **index**).| +| Modifying elements| Use **set(index: number, element: T)** to replace an element at a given position (specified by **index**) with a given element.| +| Deleting elements| Use **removeBylndex(index: number)** to remove the value at a given position (specified by **index**).| +| Deleting elements| Use **remove(element: T)** to remove the first occurrence of the specified element.| + + +## Deque + +[Deque](../reference/apis/js-apis-deque.md) can be used to construct a double-ended queue (deque) that follows the principles of First In First Out (FIFO) and Last In First Out (LIFO). It allows insertion and removal of elements at both the ends. + +**Deque** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it has capacity doubled in each dynamic expansion. The bottom layer of **Deque** is implemented by cyclic queues, delivering a high efficiency in enqueuing and dequeuing. + +[Queue](../reference/apis/js-apis-queue.md) follows the principle of FIFO only and allows element removal at the front and insertion at the rear. + +[Vector](../reference/apis/js-apis-vector.md) supports insertion and deletion of elements in between, as well as at both the ends. When compared with **Vector**, **Deque** is more efficient in inserting and removing header elements, but less efficient in accessing elements. + +You are advised to use **Deque** when you need to frequently insert or remove elements at both the ends of a container. + +**Deque** provides the following CRUD APIs. + +| Operation| Description| +| ---------- | ------ | +| Adding elements| Use **insertFront(element: T)** to insert an element at the front of this container.| +| Adding elements| Use **insertEnd(element: T)** to insert an element at the end of this container.| +| Accessing elements| Use **getFirst()** to obtain the value of the first element in this container, without removing it from the container.| +| Accessing elements| Use **getLast()** to obtain the value of the last element in this container, without removing it from the container.| +| Accessing elements| Use **popFirst()** to obtain the value of the first element in this container and remove it from the container.| +| Accessing elements| Use **popLast()** to obtain the value of the last element in this container and remove it from the container.| +| Accessing elements| Use **forEach(callbackFn:(value: T, index?: number, deque?: Deque<T>) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, deque?: Deque<T>)=> void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **popFirst()** to remove the first element from this container.| +| Deleting elements| Use **popLast()** to remove the last element from this container.| + + +## Queue + +[Queue](../reference/apis/js-apis-queue.md) can be used to construct a queue that follows the FIFO principle. + +**Queue** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it has capacity doubled in each dynamic expansion. + +The bottom layer of **Queue** is implemented by cyclic queues, delivering a high efficiency in enqueuing and dequeuing. + +Unlike [Deque](../reference/apis/js-apis-deque.md), which supports insertion and removal at both the ends, **Queue** supports insertion at one end and removal at the other end. + +You are advised to use **Queue** in FIFO scenarios. + +**Queue** provides the following CRUD APIs. + +| Operation| Description| +| ---------- | ------ | +| Adding elements| Use **add(element: T)** to add an element at the end of this container.| +| Accessing elements| Use **getFirst()** to obtain the value of the first element in this container, without removing it from the container.| +| Accessing elements| Use **pop()** to obtain the value of the first element in this container and remove it from the container.| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, queue?: Queue<T>) => void,thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, queue?: Queue<T>) => void,thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **pop()** to remove the first element from this container.| + + +## Stack + +[Stack](../reference/apis/js-apis-stack.md) can be used to construct a stack that follows the Last Out First In (LOFI) principle. + +**Stack** uses generics and must be stored in a contiguous memory space. Its initial capacity is 8, and it increases capacity 1.5-fold in each dynamic expansion. The bottom layer of **Stack** is implemented based on arrays. It supports data insertion and removal at one end. + +Unlike [Queue](../reference/apis/js-apis-queue.md), which is implemented based on the queue data structure and supports insertion at one end and removal at the other end, **Stack** supports insertion and removal at the same end. + +You are advised to use **Stack** in LOFI scenarios. + +**Stack** provides the following CRUD APIs. + +| Operation| Description| +| ---------- | ------ | +| Adding elements| Use **push(item: T)** to add an element at the top of this container.| +| Accessing elements| Use **peek()** to obtain the value of the top element in this container, without removing it from the container.| +| Accessing elements| Use **pop()** to obtain the value of the top element in this container and remove it from the container.| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, stack?: Stack<T>) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Accessing elements| Use **locate(element: T)** to obtain the index of the first occurrence of the specified element.| +| Modifying elements| Use **forEach(callbackFn:(value: T, index?: number, stack?: Stack<T>) => void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **pop()** to remove the top element from this container.| + + +## Use of Linear Containers + +Refer to the code snippet below to add, access, and modify elements in **ArrayList**, **Vector**, **Deque**, **Stack**, and **List**. + + +```js +// ArrayList +import ArrayList from '@ohos.util.ArrayList'; // Import the ArrayList module. + +let arrayList = new ArrayList(); +arrayList.add('a'); +arrayList.add(1); // Add an element. +console.info(`result: ${arrayList[0]}`); // Access an element. +arrayList[0] = 'one'; // Modify an element. +console.info(`result: ${arrayList[0]}`); + +// Vector +import Vector from '@ohos.util.Vector'; // Import the Vector module. + +let vector = new Vector(); +vector.add('a'); +let b1 = [1, 2, 3]; +vector.add(b1); +vector.add(false); // Add an element. +console.info(`result: ${vector[0]}`); // Access an element. +console.info(`result: ${vector.getFirstElement()}`); // Access an element. + +// Deque +import Deque from '@ohos.util.Deque'; // Import the Deque module. + +let deque = new Deque; +deque.insertFront('a'); +deque.insertFront(1); // Add an element. +console.info(`result: ${deque[0]}`); // Access an element. +deque[0] = 'one'; // Modify an element. +console.info(`result: ${deque[0]}`); + +// Stack +import Stack from '@ohos.util.Stack'; // Import the Stack module. + +let stack = new Stack(); +stack.push('a'); +stack.push(1); // Add an element. +console.info(`result: ${stack[0]}`); // Access an element. +stack.pop(); // Remove an element. +console.info(`result: ${stack.length}`); + +// List +import List from '@ohos.util.List'; // Import the List module. + +let list = new List; +list.add('a'); +list.add(1); +let b2 = [1, 2, 3]; +list.add(b2); // Add an element. +console.info(`result: ${list[0]}`); // Access an element. +console.info(`result: ${list.get(0)}`); // Access an element. +``` diff --git a/en/application-dev/arkts-utils/multi-thread-concurrency-overview.md b/en/application-dev/arkts-utils/multi-thread-concurrency-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..2d42ea1a56cd56eb06cd1e8394735e17fed70a2b --- /dev/null +++ b/en/application-dev/arkts-utils/multi-thread-concurrency-overview.md @@ -0,0 +1,53 @@ +# Multithread Concurrency Overview + + +## Overview + +Concurrency models are used to implement concurrent tasks in different scenarios. Common concurrency models are classified into shared memory models and message passing models. + +A typical message passing model is actor. It provides a relatively high degree of concurrency while eliminating a series of complex and occasional issues caused by locks. For these reasons, ArkTS chooses the actor model. + +Due to the memory isolation feature of the actor model, cross-thread serialization is required. + + +## Data Transfer Objects + +Data objects that can be transferred are classified into three types: [common objects](#common-objects), [transferable objects](#transferable-objects), and [shared objects](#shared-objects). + + +### Common Objects + +The structured clone algorithm is used for serialization of common objects. This algorithm recursively transfers an object by clone. It supports more object types than other serialization algorithms. + +The following object types are supported: basic types except Symbol, Date, String, RegExp, Array, Map, Set, Object (simple objects only, for example, objects created using **{}** or **new Object**), ArrayBuffer, and typedArray. (Note that only attributes can be transferred for common objects. Prototypes and methods cannot be transferred.) + + +### Transferable Objects + +Transferable objects are serialized through address transfer. It transfers the ownership of an object of the ArrayBuffer type object, rather than the content in it. After the ownership is transferred, the object becomes unavailable in the sender and can be used only in the receiver. + + +```js +// Define a transferable object. +let buffer = new ArrayBuffer(100); +``` + + +### Shared Objects + +A shared object is of the **SharedArrayBuffer** type, has a fixed length, and can store any type of data including numbers and strings. + +An object of the SharedArrayBuffer type can be transferred between multiple threads. The objects before and after the transfer point to the same memory block, achieving memory sharing. + +If multiple operations are simultaneously performed to modify data stored in an object of the SharedArrayBuffer type, you must use atomics to ensure data synchronization. Atomics ensure that the current operation is complete before the next operation starts. + + +```js +// Define a shared object, which uses atomics to ensure data synchronization. +let sharedBuffer = new SharedArrayBuffer(1024); +``` + + +## TaskPool and Worker + +ArkTS provides two multithread concurrency capabilities: **TaskPool** and **Worker**, which differ in their implementation features and use cases. For details, see [Comparison Between TaskPool and Worker](taskpool-vs-worker.md). diff --git a/en/application-dev/arkts-utils/nonlinear-container.md b/en/application-dev/arkts-utils/nonlinear-container.md new file mode 100644 index 0000000000000000000000000000000000000000..3287d2675528cd73c0e43c562263b5af07bfaf46 --- /dev/null +++ b/en/application-dev/arkts-utils/nonlinear-container.md @@ -0,0 +1,253 @@ +# Nonlinear Containers + + +Nonlinear containers implement a data structure that enables quick search. The bottom layer of nonlinear containers is implemented through hash tables or red-black trees. OpenHarmony provides the following nonlinear containers: **HashMap**, **HashSet**, **TreeMap**, **TreeSet**, **LightWeightMap**, **LightWeightSet**, and **PlainArray**. The types of **key** and **value** in nonlinear containers must meet the ECMA standard. + + +## HashMap + +[HashMap](../reference/apis/js-apis-hashmap.md) is used to store a set of associated key-value (KV) pairs. In a hash map, each key is unique and corresponds to a value. + +**HashMap** uses generics. In a hash map, a key is located based on its hash code. The initial capacity of a hash map is 16, and it has capacity doubled in each dynamic expansion. The bottom layer of **HashMap** is implemented based on a hash table. It uses chaining to avoid collisions in hash tables. + +**HashMap** is faster in accessing data than [TreeMap](../reference/apis/js-apis-treemap.md), because the former accesses the keys based on the hash codes, whereas the latter stores and accesses the keys in sorted order. + +[HashSet](../reference/apis/js-apis-hashset.md) is implemented based on **HashMap**. The input parameter of **HashMap** consists of **key** and **value**. In **HashSet**, only the **value** object is processed. + +You are advised to use **HashMap** when you need to quickly access, remove, and insert KV pairs. + +**HashMap** provides the following Create, Read, Update, and Delete (CRUD) APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.| +| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.| +| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: HashMap) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<[K,V]>** for data access.| +| Modifying elements| Use **replace(key: K, newValue: V)** to change the value of the specified key.| +| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: HashMap) => void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.| +| Deleting elements| Use **clear()** to clear this container.| + + +## HashSet + +[HashSet](../reference/apis/js-apis-hashset.md) is used to store a set of values, each of which is unique in a hash set. + +**HashSet** uses generics. In a hash set, a value is located based on its hash code. The initial capacity of a hash set is 16, and it has capacity doubled in each dynamic expansion. The type of **value** must comply with the ECMA standard. The bottom layer of **HashSet** is implemented based on a hash table. It uses chaining to avoid collisions in hash tables. + +**HashSet** is implemented based on [HashMap](../reference/apis/js-apis-hashmap.md). In **HashSet**, only the **value** object is processed. + +Unlike [TreeSet](../reference/apis/js-apis-treeset.md), which stores and accesses data in sorted order, **HashSet** stores data in a random order. This means that **HashSet** may use a different order when storing and accessing elements. Both of them allows only unique elements. However, null values are allowed in **HashSet**, but not allowed in **TreeSet**. + +You are advised to use **HashSet** when you need a set that has only unique elements or need to deduplicate a set. + +**HashSet** provides the following CRUD APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **add(value: T)** to add a value to this container.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: HashSet) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: HashSet) => void, thisArg?: Object)** to change a value in this container.| +| Deleting elements| Use **remove(value: T)** to remove a value.| +| Deleting elements| Use **clear()** to clear this container.| + + +## TreeMap + +[TreeMap](../reference/apis/js-apis-treemap.md) is used to store a set of associated KV pairs. In a tree map, each key is unique and corresponds to a value. + +**TreeMap** uses generics, and the keys in a tree map are ordered. The bottom layer of **TreeMap** is a binary tree, which supports quick search of KV pairs through the children (left child and right child) of the tree. The type of **key** must comply with the ECMA standard. Keys in a tree map are stored in order. The bottom layer of **TreeMap** is implemented based on the red-black tree and supports quick insertion and removal. + +[HashMap](../reference/apis/js-apis-hashmap.md) is faster in accessing data than **TreeMap**, because the former accesses the keys based on the hash codes, whereas the latter stores and accesses the keys in sorted order. + +You are advised to use **TreeMap** when you need to store KV pairs in sorted order. + +**TreeMap** provides the following CRUD APIs. + +| Operation| Description| +| ------- | ------ | +| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.| +| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.| +| Accessing elements| Use **getFirstKey()** to obtain the first key in this container.| +| Accessing elements| Use **getLastKey()** to obtain the last key in this container.| +| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: TreeMap) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator\<[K,V]>** for data access.| +| Modifying elements| Use **replace(key: K, newValue: V)** to change the value of the specified key.| +| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: TreeMap) => void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.| +| Deleting elements| Use **clear()** to clear this container.| + + +## TreeSet + +[TreeSet](../reference/apis/js-apis-treeset.md) is used to store a set of values, each of which is unique in a tree set. + +**TreeSet** uses generics, and the values in a tree set are ordered. The bottom layer of **TreeSet** is a binary tree, which supports quick search of a value through the children (left child and right child) of the tree. The type of **value** must meet the ECMA standard. Values in a tree set are stored in order. The bottom layer of **TreeSet** is implemented based on the red-black tree and supports quick insertion and removal. + +**TreeSet** is implemented based on [TreeMap](../reference/apis/js-apis-treemap.md). In **TreeSet**, only **value** objects are processed. **TreeSet** can be used to store values, each of which must be unique. + +[HashSet](../reference/apis/js-apis-hashset.md) stores data in a random order, whereas **TreeSet** stores data in sorted order. Both of them allows only unique elements. However, null values are allowed in **HashSet**, but not allowed in **TreeSet**. + +You are advised to use **TreeSet** when you need to store data in sorted order. + +**TreeSet** provides the following CRUD APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **add(value: T)** to add a value to this container.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **getFirstValue()** to obtain the first value in this container.| +| Accessing elements| Use **getLastValue()** to obtain the last value in this container.| +| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: TreeSet) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: TreeSet) => void, thisArg?: Object)** to change a value in this container.| +| Deleting elements| Use **remove(value: T)** to remove a value.| +| Deleting elements| Use **clear()** to clear this container.| + + +## LightWeightMap + +[LightWeightMap](../reference/apis/js-apis-lightweightmap.md) is used to store a set of associated KV pairs. In a lightweight map, each key is unique and corresponds to a value. **LightWeightMap** uses generics and a more lightweight structure. It uses the hash code to uniquely identify a key at the bottom layer. It uses linear probing to avoid collisions. In a lightweight map, a key is located by using the hash code and binary search algorithm. The hash code is stored in an array and mapped to a key and its value in another array. The type of **key** must comply with the ECMA standard. + +The default initial capacity of a lightweight map is 8, and it has capacity doubled in each expansion. + +Compared with [HashMap](../reference/apis/js-apis-hashmap.md), which can also store KV pairs, **LightWeightMap** occupies less memory. + +You are advised to use **LightWeightMap** when you need to store and access **KV pairs**. + +**LightWeightMap** provides the following CRUD APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **set(key: K, value: V)** to add an element (a KV pair) to this container.| +| Accessing elements| Use **get(key: K)** to obtain the value of the specified key.| +| Accessing elements| Use **getIndexOfKey(key: K)** to obtain the index of the specified key.| +| Accessing elements| Use **getIndexOfValue(value: V)** to obtain the index of the first occurrence of the specified value.| +| Accessing elements| Use **keys()** to return an iterator that contains all the keys in this container.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **getKeyAt(index: number)** to obtain the key of an element at a given position (specified by **index**).| +| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).| +| Accessing elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: LightWeightMap) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<[K,V]>** for data access.| +| Modifying elements| Use **setValueAt(index: number, newValue: V)** to change the value of an element at a given position (specified by **index**).| +| Modifying elements| Use **forEach(callbackFn: (value?: V, key?: K, map?: LightWeightMap) => void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.| +| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).| +| Deleting elements| Use **clear()** to clear this container.| + + +## LightWeightSet + +[LightWeightSet](../reference/apis/js-apis-lightweightset.md) is used to store a set of values, each of which is unique in a lightweight set. + +**LightWeightSet** uses generics and a lightweight structure. Its default initial capacity is 8, and it has capacity doubled in each expansion. In a lightweight set, a value is located by using the hash code and binary search algorithm. The hash code is stored in an array and mapped to a value in another array. The type of **value** must comply with the ECMA standard. + +**LightWeightSet** uses the hash code to uniquely identify a value at the bottom layer. It uses linear probing to avoid collisions and adopts the binary search algorithm. + +Compared with [HashSet](../reference/apis/js-apis-hashset.md), which can also store values, **LightWeightSet** occupies less memory. + +You are advised to use **LightWeightSet** when you need a set that has only unique elements or need to deduplicate a set. + +**LightWeightSet** provides the following CRUD APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **add(obj: T)** to add a value to this container.| +| Accessing elements| Use **getIndexOf(key: T)** to obtain the index of a key.| +| Accessing elements| Use **values()** to return an iterator that contains all the values in this container.| +| Accessing elements| Use **entries()** to return an iterator that contains all the elements in this container.| +| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).| +| Accessing elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: LightWeightSet) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<T>** for data access.| +| Modifying elements| Use **forEach(callbackFn: (value?: T, key?: T, set?: LightWeightSet) => void, thisArg?: Object)** to change a value in this container.| +| Deleting elements| Use **remove(key: K)** to remove an element with the specified key.| +| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).| +| Deleting elements| Use **clear()** to clear this container.| + + +## PlainArray + +[PlainArray](../reference/apis/js-apis-plainarray.md) is used to store a set of associated KV pairs. In a plain array, each key is unique, corresponds to a value, and is of the number type. **PlainArray** uses generics and a more lightweight structure. In a plain array, a key is located by using the binary search algorithm and is mapped to a value in another array. + +The default initial capacity of a plain array is 16, and it has capacity doubled in each expansion. + +Both **PlainArray** and [LightWeightMap](../reference/apis/js-apis-lightweightmap.md) are used to store KV pairs in the lightweight structure. However, the key type of **PlainArray** can only be **number**. + +You are advised to use PlainArray when you need to store KV pairs whose keys are of the number type. + +**PlainArray** provides the following CRUD APIs. + +| Operation| Description| +| -------- | ------ | +| Adding elements| Use **add(key: number,value: T)** to add an element (a KV pair) to this container.| +| Accessing elements| Use **get(key: number)** to obtain the value of the specified key.| +| Accessing elements| Use **getIndexOfKey(key: number)** to obtain the index of the specified key.| +| Accessing elements| Use **getIndexOfValue(value: T)** to obtain the index of the specified value.| +| Accessing elements| Use **getKeyAt(index: number)** to obtain the key of an element at a given position (specified by **index**).| +| Accessing elements| Use **getValueAt(index: number)** to obtain the value of an element at a given position (specified by **index**).| +| Accessing elements| Use **forEach(callbackFn: (value: T, index?: number, PlainArray?: PlainArray) => void, thisArg?: Object)** to traverse the elements in this container.| +| Accessing elements| Use **\[Symbol.iterator]():IterableIterator<[number, T]>** for data access.| +| Modifying elements| Use **setValueAt(index:number, value: T)** to change the value of an element at a given position (specified by **index**).| +| Modifying elements| Use **forEach(callbackFn: (value: T, index?: number, PlainArray?: PlainArray) => void, thisArg?: Object)** to modify an element in this container.| +| Deleting elements| Use **remove(key: number)** to remove an element with the specified key.| +| Deleting elements| Use **removeAt(index: number)** to remove an element at a given position (specified by **index**).| +| Deleting elements| Use **removeRangeFrom(index: number, size: number)** to remove elements in a specified range.| +| Deleting elements| Use **clear()** to clear this container.| + + +## Use of Nonlinear Containers + +Refer to the code snippet below to add, access, and modify elements in **HashMap**, **TreeMap**, **LightWeightMap**, **Stack**, and **PlainArray**. + + +```js +// HashMap +import HashMap from '@ohos.util.HashMap'; // Import the HashMap module. + +let hashMap = new HashMap(); +hashMap.set('a', 123); +hashMap.set (4, 123);// Add an element. +console.info(`result: ${hashMap.hasKey(4)}`); // Check whether an element is contained. +console.info(`result: ${hashMap.get('a')}`); // Access an element. + +// TreeMap +import TreeMap from '@ohos.util.TreeMap'; // Import the TreeMap module. + +let treeMap = new TreeMap(); +treeMap.set('a', 123); +treeMap.set('6', 356); // Add an element. +console.info(`result: ${treeMap.get('a')}`); // Access an element. +console.info(`result: ${treeMap.getFirstKey()}`); // Access the first element. +console.info(`result: ${treeMap.getLastKey()}`); // Access the last element. + +// LightWeightMap +import LightWeightMap from '@ohos.util.LightWeightMap'; // Import the LightWeightMap module. + +let lightWeightMap = new LightWeightMap(); +lightWeightMap.set('x', 123); +lightWeightMap.set('8', 356); // Add an element. +console.info(`result: ${lightWeightMap.get('a')}`); // Access an element. +console.info(`result: ${lightWeightMap.get('x')}`); // Access an element. +console.info(`result: ${lightWeightMap.getIndexOfKey('8')}`); // Access an element. + +// PlainArray +import PlainArray from '@ohos.util.PlainArray' // Import the PlainArray module. + +let plainArray = new PlainArray(); +plainArray.add(1, 'sdd'); +plainArray.add(2,'sff'); // Add an element. +console.info(`result: ${plainArray.get(1)}`); // Access an element. +console.info(`result: ${plainArray.getKeyAt(1)}`); // Access an element. +``` diff --git a/en/application-dev/arkts-utils/single-io-development.md b/en/application-dev/arkts-utils/single-io-development.md new file mode 100644 index 0000000000000000000000000000000000000000..b488890b42c9cb5849077fd1f00815f1a3f5ddf8 --- /dev/null +++ b/en/application-dev/arkts-utils/single-io-development.md @@ -0,0 +1,29 @@ +# Single I/O Task Development + + +Asynchronous concurrency provided by Promise and async/await is applicable to the development of a single I/O task. The following uses the asynchronous concurrency capability to write a file as an example. + + +1. Implement the logic of a single I/O task. + + ```js + import fs from '@ohos.file.fs'; + + async function write(data: string, filePath: string) { + let file = await fs.open(filePath, fs.OpenMode.READ_WRITE); + fs.write(file.fd, data).then((writeLen) => { + fs.close(file); + }).catch((err) => { + console.error(`Failed to write data. Code is ${err.code}, message is ${err.message}`); + }) + } + ``` + +2. Use the asynchronous capability to invoke the single I/O task. For details about how to obtain **filePath** in the example, see [Obtaining Application File Paths](../application-models/application-context-stage.md#obtaining-application-file-paths). + + ```js + let filePath = ...; // Application file path + write('Hello World!', filePath).then(() => { + console.info('Succeeded in writing data.'); + }) + ``` diff --git a/en/application-dev/arkts-utils/sync-task-development.md b/en/application-dev/arkts-utils/sync-task-development.md new file mode 100644 index 0000000000000000000000000000000000000000..8ce4290a5122b30955c94a1e9116c92f53e0ea7b --- /dev/null +++ b/en/application-dev/arkts-utils/sync-task-development.md @@ -0,0 +1,173 @@ +# Synchronous Task Development + + +Synchronous tasks are executed in order among multiple threads. For example, as a synchronization primitive, locks prevent data contention. + + +To implement synchronous tasks, you must consider the collaboration and synchronization between multiple threads and ensure the correctness of data and the correct execution of programs. + +If synchronous tasks are independent of each other, you are advised to use **TaskPool**, since it focuses on single independent tasks. For example, a series of imported static methods or methods implemented in singletons are independent. If synchronous tasks are associated with each other, use **Worker**, for example, methods implemented in class objects (not singleton class objects). + + +## Using TaskPool to Process Independent Synchronous Tasks + +**TaskPool** is recommended for scheduling independent synchronous tasks. Typical synchronous tasks are those using static methods. If a unique handle or class object constructed using a singleton points to multiple tasks and these tasks can be used between different worker threads, you can also use **TaskPool**. + +1. Define a concurrency function that internally calls the synchronous methods. + +2. Create a task, execute the task through **TaskPool**, and perform operations on the asynchronous result. Create a [task](../reference/apis/js-apis-taskpool.md#task) and call [execute()](../reference/apis/js-apis-taskpool.md#taskpoolexecute-1) to execute the task synchronously. + +3. Perform concurrent operations. + +Simulate a singleton class that contains synchronous calls. + + +```ts +// handle.ts code +export default class Handle { + static getInstance() { + // Return a singleton object. + } + + static syncGet() { + // Synchronous getter. + return; + } + + static syncSet(num: number) { + // Synchronous setter. + return; + } +} +``` + +Use **TaskPool** to call the related synchronous methods. + + +```ts +// Index.ets code +import taskpool from '@ohos.taskpool'; +import Handle from './Handle'; // Return a static handle. + +// Step 1: Define a concurrency function that internally calls the synchronous methods. +@Concurrent +function func(num: number) { + // Call the synchronous wait implemented in a static class object. + Handle.syncSet(num); + // Alternatively, call the synchronous wait implemented in a singleton object. + Handle.getInstance().syncGet(); + return true; +} + +// Step 2: Create and execute a task. +async function asyncGet() { + // Create a task and pass in the function func. + let task = new taskpool.Task(func, 1); + // Execute the task and obtain the result res. + let res = await taskpool.execute(task); + // Perform operations on the synchronous result. + console.info(String(res)); +} + +@Entry +@Component +struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + // Step 3: Perform concurrent operations. + asyncGet(); + }) + } + .width('100%') + .height('100%') + } + } +} +``` + + +## Using Worker to Process Associated Synchronous Tasks + +Use **Worker** when you want to schedule a series of synchronous tasks using the same handle or depending on the same class object. + +1. Create a **Worker** object in the main thread and receive messages from the worker thread. + + ```js + import worker from '@ohos.worker'; + + @Entry + @Component + struct Index { + @State message: string = 'Hello World'; + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + .onClick(() => { + let w = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts'); + w.onmessage = function (d) { + // Receive the result of the worker thread. + } + w.onerror = function (d) { + // Receive error information of the worker thread. + } + // Send a Set message to the worker thread. + w.postMessage({'type': 0, 'data': 'data'}) + // Send a Get message to the worker thread. + w.postMessage({'type': 1}) + // Destroy the worker thread. + w.terminate() + }) + } + .width('100%') + } + .height('100%') + } + } + ``` + +2. Bind the **Worker** object in the worker thread and process the synchronous task logic. + + ```js + // handle.ts code + export default class Handle { + syncGet() { + return; + } + + syncSet(num: number) { + return; + } + } + + // Worker.ts code + import worker, { ThreadWorkerGlobalScope, MessageEvents } from '@ohos.worker'; + import Handle from './handle.ts' // Return a handle. + + var workerPort : ThreadWorkerGlobalScope = worker.workerPort; + + // Handle that cannot be transferred. All operations depend on this handle. + var handler = new Handle() + + // onmessage() logic of the worker thread. + workerPort.onmessage = function(e : MessageEvents) { + switch (e.data.type) { + case 0: + handler.syncSet(e.data.data); + workerPort.postMessage('success set'); + case 1: + handler.syncGet(); + workerPort.postMessage('success get'); + } + } + ``` diff --git a/en/application-dev/arkts-utils/taskpool-vs-worker.md b/en/application-dev/arkts-utils/taskpool-vs-worker.md new file mode 100644 index 0000000000000000000000000000000000000000..4a3dcd0557a3afbdcfba515752eeea5780f872ab --- /dev/null +++ b/en/application-dev/arkts-utils/taskpool-vs-worker.md @@ -0,0 +1,164 @@ +# Comparison Between TaskPool and Worker + +**TaskPool** and **Worker** provide a multithread running environment for applications to process time-consuming computing tasks or resource intensive tasks, preventing these tasks from blocking the main thread. This maximizes system utilization, reduces overall resource consumption, and improves overall system performance. + + +This topic compares **TaskPool** with **Worker** from two aspects: [implementation features](#implementation-feature-comparison) and [use cases](#use-case-comparison). It also describes their operating mechanisms and precautions. + + +## Implementation Feature Comparison + +**Table 1** Comparison between TaskPool and Worker in implementation features + +| Item| TaskPool | Worker | +| -------- | -------- | -------- | +| Memory model| Threads are isolated from each other, and memory is not shared.| Threads are isolated from each other, and memory is not shared.| +| Parameter passing mechanism| The structured clone algorithm is used for serialization and deserialization.
ArrayBuffer and SharedArrayBuffer are used for parameter passing and sharing.| The structured clone algorithm is used for serialization and deserialization.
ArrayBuffer and SharedArrayBuffer are used for parameter passing and sharing.| +| Parameter passing| Parameters are directly passed in, without being encapsulated.| Only one parameter can be carried in a message object. Therefore, you must encapsulate excess parameters.| +| Method invocation| Methods are directly passed in and called.| Messages are passed in the worker thread and the corresponding methods are called.| +| Return value| A value is returned by default after asynchronous calling.| Messages proactively sent must be parsed and assigned by calling **onmessage()**.| +| Lifecycle| The task pool manages its own lifecycle, without considering the load.| You are required to manage the number and lifecycle of worker threads.| +| Maximum number of task pools| The number is automatically managed, rather than being manually configured.| A maximum of eight worker threads are supported.| +| Maximum task execution duration| 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, the time consumed by I/O tasks such as network download and file read/write)| There is no restriction.| +| Task priority setting| Setting the task priority is supported.| Setting the task priority is not supported.| +| Task cancellation| Tasks that have been initiated can be canceled.| Tasks that have been initiated cannot be canceled.| + + +## Use Case Comparison + +Both **TaskPool** and **Worker** support multithread concurrency. **TaskPool** worker threads are bound to the system scheduling priority and support load balancing (automatic scaling). **Worker** threads are manually created and do not support scheduling priority setting. Therefore, **TaskPool** provides better performance than **Worker** and is recommended in most scenarios. + +**TaskPool** is oriented to thread-level independent tasks, and tasks running for more than 3 minutes are automatically reclaimed by the system. **Worker** is oriented to threads and supports thread execution for a long time. + +Common use cases are as follows: + +- Use **Worker** for a task that runs for more than 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, I/O tasks such as network download and file read/write). For example, use **Worker** for a 1-hour prediction algorithm training job in the background. + +- Use **Worker** for a series of associated synchronous tasks. For example, use **Worker** for a series of database operations, since the same handle is required. + +- Use **TaskPool** for a task for which the priority needs to be set. For example, in the histogram rendering scenario in Gallery, histogram data calculated in the background is used for UI display on the foreground. This requires high-priority processing. In this case, use **TaskPool**. + +- Use **TaskPool** for a task that needs to be canceled frequently. For example, in the large image browsing scenario in Gallery, both images on the left and right sides of the current image are cached. When the user slides to the next image, a cache task on one side needs to be canceled. In this case, use **TaskPool**. + +- Use **TaskPool** for a large number of tasks or tasks with scattered scheduling points. For example, a large-scale application with multiple modules has multiple time-consuming tasks, and it is inconvenient to use eight worker threads to manage load. In this case, **TaskPool** is recommended. + + +## TaskPool Operating Mechanism + +**Figure 1** TaskPool operating mechanism + +![taskpool](figures/taskpool.png) + +With **TaskPool**, you can encapsulate tasks in the main thread and throw the tasks to the task queue. The system selects proper worker threads to distribute and execute the tasks, and then returns the result to the main thread. **TaskPool** provides APIs to execute and cancel tasks, and set the task priority. It also minimizes system resource usage through unified thread management, dynamic scheduling, and load balancing algorithms. By default, the system starts a worker thread and increases the thread quantity as the number of tasks increases. The maximum number of worker threads that can be created depends on the number of physical cores of the device. The formula is max(3, Number of physical cores – 1). If no task is distributed for a long period of time, the system reduces the number of worker threads. + + +## Worker Operating Mechanism + +**Figure 2** Worker operating mechanism + +![worker](figures/worker.png) + +The thread that creates the worker thread is referred to as the host thread (not necessarily the main thread, since a worker thread can also create a worker subthread). A worker thread is also named an actor thread. Each worker thread has an independent instance from the host thread, including the infrastructure, object, and code segment. The worker thread communicates with the host thread by means of message exchange. They use the serialization technique to exchange commands and data. + + +## Precautions for TaskPool + +- A task function must be decorated with **\@Concurrent** and can be used only in .ets files. + +- A task function must be a common function or async function, but not a class member function or anonymous function. + +- A task function can use imported variables and input parameter variables only in a project created on the stage model. In a project created on the FA model, it can use input parameter variables only. + +- A task function in the **TaskPool** worker thread must finish the execution within 3 minutes (excluding the time used for Promise or async/await asynchronous call, for example, the duration of I/O tasks such as network download and file read/write). Otherwise, it forcibly exits. + +- Input parameter types in a task function must be those supported by serialization. For details, see [Common Objects](multi-thread-concurrency-overview.md#common-objects). + +- Parameters of the ArrayBuffer type are transferred in **TaskPool** by default. You can set the transfer list by calling [setTransferList()](../reference/apis/js-apis-taskpool.md#settransferlist10). + +- The context objects in different threads are different. Therefore, **TaskPool** worker threads can use only thread-safe libraries, rather than UI-related non-thread-safe libraries. + +- A maximum of 16 MB data can be serialized. + + +## Precautions for Worker + +- The rules for passing in the **Worker.ts** path during the worker creation vary in different API versions. For details, see [Precautions for File Paths](#precautions-for-file-paths). + +- After a worker thread is created, you must manually manage its lifecycle. A maximum of eight worker threads can run simultaneously. For details, see [Lifecycle Precautions](#lifecycle-precautions). + +- Modules of the [ability type](../quick-start/application-package-structure-stage.md) support **Worker**, but modules of the [library type](../quick-start/application-package-structure-stage.md) do not support **Worker**. + +- When creating a worker thread, the **Worker.ts** file of another module cannot be used. This means that a worker cannot be called across modules. + +- The context objects in different threads are different. Therefore, **Worker** threads can use only thread-safe libraries, rather than UI-related non-thread-safe libraries. + +- A maximum of 16 MB data can be serialized. + + +### Precautions for File Paths + +Before calling an API of the **Worker** module, you must create a **Worker** instance. The constructor function varies in different API versions. + +```js +// Use the following function in API version 9 and later versions: +const worker1 = new worker.ThreadWorker(scriptURL); +// Use the following function in API version 8 and earlier versions: +const worker1 = new worker.Worker(scriptURL); +``` + +The **Worker.ts** file path (specified by **scriptURL**) must be passed in the constructor function. By default, the **workers** directory (upper-level directory of the **Worker.ts** file) is at the same level as the **pages** directory. + +**Stage Model** + + +The following is an example of **scriptURL** in the constructor function: + +```js +// Method 1 +// In the stage model, the workers directory is at the same level as the pages directory in the entry module. +const worker1 = new worker.ThreadWorker('entry/ets/workers/MyWorker.ts', {name:"first worker in Stage model"}); +// In the stage model, the workers directory is a child directory of the pages directory in the entry module. +const worker2 = new worker.ThreadWorker('entry/ets/pages/workers/MyWorker.ts'); + +// Method 2 +// In the stage model, the workers directory is at the same level as the pages directory in the entry module, and bundlename is com.example.workerdemo. +const worker3 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/workers/worker'); +// In the stage model, the workers directory is a child directory of the pages directory in the entry module, and bundlename is com.example.workerdemo. +const worker4 = new worker.ThreadWorker('@bundle:com.example.workerdemo/entry/ets/pages/workers/worker'); +``` + + +- Based on the directory structure of the stage model project, the field meanings in method 1 are as follows: + - **entry**: value of the **name** attribute under **module** in the **module.json5** file. + - **ets**: directory for storing the ArkTS source code. It is fixed. + - **workers/MyWorker.ts**: path of the worker source file in the **ets** directory. + +- Based on the directory structure of the stage model project, the field meanings in method 2 are as follows: + - **\@bundle**: fixed label. + - **bundlename**: bundle name of the current application. + - **entryname**: value of the **name** attribute under **module** in the **module.json5** file. + - **ets**: directory for storing the ArkTS source code. It is fixed. + - **workerdir/workerfile**: path of the worker source file in the **ets** directory. + + +**FA Model** + + +The following is an example of **scriptURL** in the constructor function: + +```js +// In the FA model, the workers directory is at the same level as the pages directory in the entry module. +const worker1 = new worker.ThreadWorker('workers/worker.js', {name:'first worker in FA model'}); +// In the FA model, the workers directory is at the same level as the parent directory of the pages directory in the entry module. +const worker2 = new worker.ThreadWorker('../workers/worker.js'); +``` + + +### Lifecycle Precautions + +- Creating and terminating worker threads consume performance. Therefore, you are advised to manage available workers and reuse them. The worker threads keep running even when they are idle. Therefore, when a worker thread is not required, call [terminate()](../reference/apis/js-apis-worker.md#terminate9) interface or [parentPort.close()](../reference/apis/js-apis-worker.md#close9) to destroy it. If a worker thread is destroyed or being destroyed, an error is thrown when it is called. + + +- A maximum of eight worker threads can co-exist. + - In API version 8 and earlier versions, when the number of worker threads exceeds the upper limit, the error "Too many workers, the number of workers exceeds the maximum." is thrown. + - Since API version 9, when the number of worker threads exceeds the upper limit, the error "Worker initialization failure, the number of workers exceeds the maximum." is thrown. diff --git a/en/application-dev/arkts-utils/xml-conversion.md b/en/application-dev/arkts-utils/xml-conversion.md new file mode 100644 index 0000000000000000000000000000000000000000..96dc727635ea4449edd3bdc41e5ec016b268eca9 --- /dev/null +++ b/en/application-dev/arkts-utils/xml-conversion.md @@ -0,0 +1,92 @@ +# XML Conversion + + +Converting XML text into JavaScript objects makes it easier to process and manipulate data. In addition, JavaScript objects are more suitable than XML text for JavaScript applications. + + +The common library provides the **ConvertXML** class to convert XML text into JavaScript objects. The input is XML strings and conversion options, and the output is a JavaScript object. For details about the conversion options, see the API reference [@ohos.convertxml (XML-to-JavaScript Conversion)](../reference/apis/js-apis-convertxml.md). + + +## Precautions + +To ensure successful XML parsing and conversion, the input XML data must comply with the standard format. + + +## How to Develop + +The following steps walk you through on how to convert an XML file into a JavaScript object to obtain the tag values. + +1. Import the **convertxml** module. + + ```js + import convertxml from '@ohos.convertxml'; + ``` + +2. Pass in an XML file to be converted and set conversion options. + + ```js + let xml = + '' + + '' + + ' Happy' + + ' Work' + + ' Play' + + ''; + let options = { + // trim: false, indicating that spaces before and after the text are not deleted after conversion. + // declarationKey: "_declaration", indicating that _declaration is used to identify the file declaration after conversion. + // instructionKey: "_instruction", indicating that _instruction is used to identify instructions after conversion. + // attributesKey: "_attributes", indicating that _attributes is used to identify attributes after conversion. + // textKey: "_text", indicating that _text is used to identify tag values after conversion. + // cdataKey: "_cdata", indicating that _cdata is used to identify unparsed data after conversion. + // docTypeKey: "_doctype", indicating that _doctype is used to identify documents after conversion. + // commentKey: "_comment", indicating that _comment is used to identify comments after conversion. + // parentKey: "_parent", indicating that _parent is used to identify parent classes after conversion. + // typeKey: "_type", indicating that _type is used to identify types after conversion. + // nameKey: "_name", indicating that _name is used to identify tag names after conversion. + // elementsKey: "_elements", indicating that _elements is used to identify elements after conversion. + trim: false, + declarationKey: "_declaration", + instructionKey: "_instruction", + attributesKey: "_attributes", + textKey: "_text", + cdataKey: "_cdata", + docTypeKey: "_doctype", + commentKey: "_comment", + parentKey: "_parent", + typeKey: "_type", + nameKey: "_name", + elementsKey: "_elements" + } + ``` + +3. Call the conversion function and print the result. + + ```js + let conv = new convertxml.ConvertXML(); + let result = conv.convertToJSObject(xml, options); + let strRes = JSON.stringify(result); // Convert the JavaScript object into a JSON string for explicit output. + console.info(strRes); + // Alternatively, directly process the JavaScript object to obtain the tag values. + let title = result['_elements'][0]['_elements'][0]['_elements'][0]['_text']; // Parse the value of the tag. + let todo = result['_elements'][0]['_elements'][1]['_elements'][0]['_text']; // Parse the value of the <todo> tag. + let todo2 = result['_elements'][0]['_elements'][2]['_elements'][0]['_text']; // Parse the value of the <todo> tag. + console.info(title); // Happy + console.info(todo); // Work + console.info(todo2); // Play + ``` + + The output is as follows: + + + ```js + strRes: + {"_declaration":{"_attributes":{"version":"1.0","encoding":"utf-8"}},"_elements":[{"_type":"element","_name":"note", + "_attributes":{"importance":"high","logged":"true"},"_elements":[{"_type":"element","_name":"title", + "_elements":[{"_type":"text","_text":"Happy"}]},{"_type":"element","_name":"todo", + "_elements":[{"_type":"text","_text":"Work"}]},{"_type":"element","_name":"todo", + "_elements":[{"_type":"text","_text":"Play"}]}]}]} + title:Happy + todo:Work + todo2:Play + ``` diff --git a/en/application-dev/arkts-utils/xml-generation.md b/en/application-dev/arkts-utils/xml-generation.md new file mode 100644 index 0000000000000000000000000000000000000000..d05cd9779255c6dfa51a9d4bb4556e2467c92c1c --- /dev/null +++ b/en/application-dev/arkts-utils/xml-generation.md @@ -0,0 +1,82 @@ +# XML Generation + + +XML can be used as a data exchange format, which is supported by a wealth of systems and applications. For example, web services can transfer structured data in XML format. + + +XML can also be used as a message passing format for communication between nodes in a distributed system. + + +## Precautions + +- XML tags must appear in pairs: one start tag and one end tag. + +- XML tags are case sensitive. The start tag and end tag must use the same case. + + +## How to Develop + +The **xml** module provides the **XmlSerializer** class to generate XML files. The input is an object of the ArrayBuffer or DataView type with a fixed length, which is used to store the output XML data. + +You can call different to write different types of content. For example, call **startElement(name: string)** to write the start tag and **setText(text: string)** to write a tag value. + +For details about the APIs of the **XML** module, see [@ohos.xml (XML Parsing and Generation)](../reference/apis/js-apis-xml.md). + +The following steps walk you through on how to generate an XML file. + +1. Import the modules. + + ```js + import xml from '@ohos.xml'; + import util from '@ohos.util'; + ``` + +2. Create a buffer and create an **XmlSerializer** object, either based on an object of the ArrayBuffer or DataView type. + + ```js + // 1. Create an XmlSerializer object based on an object of the ArrayBuffer type. + let arrayBuffer = new ArrayBuffer(2048); // Create a 2048-byte object of the ArrayBuffer type. + let thatSer = new xml.XmlSerializer (arrayBuffer); // Create an XmlSerializer object based on the object of the ArrayBuffer type. + + // 2. Create an XmlSerializer object based on an object of the DataView type. + let arrayBuffer = new ArrayBuffer(2048); // Create a 2048-byte object of the ArrayBuffer type. + let dataView = new DataView(arrayBuffer); // Use an object of the DataView type to operate the object of the ArrayBuffer type. + let thatSer = new xml.XmlSerializer (dataView); // Create an XmlSerializer object based on the object of the DataView type. + ``` + +3. Call the functions to generate an XML file. + + ```js + thatSer.setDeclaration(); // Write the XML file declaration. + thatSer.startElement('bookstore'); // Write the start flag. + thatSer.startElement('book'); // Write the start tag of a nested element. + thatSer.setAttributes('category', 'COOKING'); // Write the attributes and attribute values. + thatSer.startElement('title'); + thatSer.setAttributes('lang', 'en'); + thatSer.setText('Everyday'); // Write the tag value. + thatSer.endElement(); // Write the end flag. + thatSer.startElement('author'); + thatSer.setText('Giada'); + thatSer.endElement(); + thatSer.startElement('year'); + thatSer.setText('2005'); + thatSer.endElement(); + thatSer.endElement(); + thatSer.endElement(); + ``` + +4. Use **Uint8Array** to operate the object of the ArrayBuffer type, and use **TextDecoder** to decode the Uint8Array. + + ```js + let view = new Uint8Array(arrayBuffer); // Use Uint8Array to read data from the object of the ArrayBuffer type. + let textDecoder = util.TextDecoder.create(); // Call the TextDecoder class of the util module. + let res = textDecoder.decodeWithStream (view); // Decode the view. + console.info(res); + ``` + + The output is as follows: + + + ```js + <?xml version=\"1.0\" encoding=\"utf-8\"?><bookstore>\r\n <book category=\"COOKING\">\r\n <title lang=\"en\">Everyday\r\n Giada\r\n 2005\r\n \r\n + ``` diff --git a/en/application-dev/arkts-utils/xml-overview.md b/en/application-dev/arkts-utils/xml-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..d376fea2134fbdac8a02d5a4c8e4ffdfdb99eb4d --- /dev/null +++ b/en/application-dev/arkts-utils/xml-overview.md @@ -0,0 +1,23 @@ +# XML Overview + + +Extensible Markup Language (XML) is a markup language used to describe data. It aims to provide a common way to transmit and store data, especially data frequently used in web applications. XML does not predefine tags. As a result, it is more flexible and widely used. + + +An XML file consists of elements, attributes, and content. + + +- An element refers to a tag pair that contains text, attributes, or other elements. + +- Attributes provide additional information about an element. + +- Content is the data or sub-element contained in an element. + + +XML supports the use of XML Schema Definition (XSD) or Document Type Definition (DTD) for defining the document structure. This allows you to customize rules to verify whether an XML document is in the expected format. + + +XML also supports features such as namespaces, entity references, comments, and processing instructions, making it easy to adapt to diverse data requirements. + + +The common library provides XML-related basic capabilities, including [XML generation](xml-generation.md), [XML parsing](xml-parsing.md), and [XML conversion](xml-conversion.md). diff --git a/en/application-dev/arkts-utils/xml-parsing.md b/en/application-dev/arkts-utils/xml-parsing.md new file mode 100644 index 0000000000000000000000000000000000000000..dd3e46517b3eec6aafce6d6566a2da982bbd8d6c --- /dev/null +++ b/en/application-dev/arkts-utils/xml-parsing.md @@ -0,0 +1,271 @@ +# XML Parsing + + +Data transferred in XML format must be parsed in actual use. Generally, three types of elements need to be parsed, as described in [Parsing XML Tags and Tag Values](#parsing-xml-tags-and-tag-values), [Parsing XML Attributes and Attribute Values](#parsing-xml-attributes-and-attribute-values), and [Parsing XML Event Types and Element Depths](#parsing-xml-event-types-and-element-depths). + + +The **xml** module provides the **XmlPullParser** class to parse XML files. The input is an object of the ArrayBufffer or DataView type containing XML text, and the output is the parsed information. + + +**Table 1** XML parsing options + +| Name| Type| Mandatory| Description| +| -------- | -------- | -------- | -------- | +| supportDoctype | boolean | No| Whether to ignore the document type. The default value is **false**, indicating that the document type is parsed.| +| ignoreNameSpace | boolean | No| Whether to ignore the namespace. The default value is **false**, indicating that the namespace is parsed.| +| tagValueCallbackFunction | (name: string, value: string) => boolean | No| Callback used to return **tagValue**, which consists of a tag and its value. The default value is **null**, indicating that XML tags and tag values are not parsed.| +| attributeValueCallbackFunction | (name: string, value: string) => boolean | No| Callback used to return **attributeValue**, which consists of an attribute and its value. The default value is **null**, indicating that XML attributes and attribute values are not parsed.| +| tokenValueCallbackFunction | (eventType: EventType, value: ParseInfo) => boolean | No| Callback used to return **tokenValue**, which consists of the event type and the attributes of **parseInfo**. The default value is **null**, indicating that the event type and the attributes of **parseInfo** are not parsed.| + + +## Precautions + +- To ensure successful XML parsing and conversion, the input XML data must comply with the standard format. + +- Currently, parsing a given node is not supported. + + +## Parsing XML Tags and Tag Values + +1. Import the modules. + + ```js + import xml from '@ohos.xml'; + import util from '@ohos.util'; // Use the API provided by the util module to encode the file. + ``` + +2. Create an **XmlPullParser** object. + + The **XmlPullParser** object can be created based on an object of the ArrayBuffer or DataView type. + + + ```js + let strXml = + '' + + '' + + 'Play' + + 'Work' + + ''; + let textEncoder = new util.TextEncoder(); + let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters. + // 1. Create an XmlPullParser object based on an object of the ArrayBuffer type. + let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8'); + + // 2. Create an XmlPullParser object based on an object of the DataView type. + let dataView = new DataView(arrBuffer.buffer); + let that = new xml.XmlPullParser(dataView, 'UTF-8'); + ``` + +3. Customize a callback function. In this example, the tag and tag value are directly printed. + + ```js + let str = ''; + function func(name, value){ + str = name + value; + console.info(str); + return true; // The value true means to continue parsing, and false means to stop parsing. + } + ``` + +4. Set parsing options and call the **parse()** function. + + ```js + let options = {supportDoctype:true, ignoreNameSpace:true, tagValueCallbackFunction:func}; + that.parse(options); + ``` + + The output is as follows: + + + ```js + note + title + Play + title + lens + Work + lens + note + ``` + + +## Parsing XML Attributes and Attribute Values + +1. Import the modules. + + ```js + import xml from '@ohos.xml'; + import util from '@ohos.util'; // Use the API provided by the util module to encode the file. + ``` + +2. Create an **XmlPullParser** object. + + ```js + let strXml = + '' + + '' + + ' Play' + + ' Happy' + + ' Work' + + ''; + let textEncoder = new util.TextEncoder(); + let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters. + let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8'); + ``` + +3. Customize a callback function. In this example, the attribute and attribute value are directly printed. + + ```js + let str = ''; + function func(name, value){ + str += name + ' ' + value + ' '; + return true; // The value true means to continue parsing, and false means to stop parsing. + } + ``` + +4. Set parsing options and call the **parse()** function. + + ```js + let options = {supportDoctype:true, ignoreNameSpace:true, attributeValueCallbackFunction:func}; + that.parse(options); + console.info(str); // Print all attributes and their values at a time. + ``` + + The output is as follows: + + + ```js + importance high logged true // Attributes and attribute values of the note node + ``` + + +## Parsing XML Event Types and Element Depths + +1. Import the modules. + + ```js + import xml from '@ohos.xml'; + import util from '@ohos.util'; // Use the API provided by the util module to encode the file. + ``` + +2. Create an **XmlPullParser** object. + + ```js + let strXml = + '' + + '' + + 'Play' + + ''; + let textEncoder = new util.TextEncoder(); + let arrBuffer = textEncoder.encodeInto(strXml); // Encode the data to prevent garbled characters. + let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8'); + ``` + +3. Customize a callback function. In this example, the event type and element depth are directly printed. + + ```js + let str = ''; + function func(name, value){ + str = name +' ' + value.getDepth(); // getDepth is called to obtain the element depth. + console.info(str) + return true; // The value true means to continue parsing, and false means to stop parsing. + } + ``` + +4. Set parsing options and call the **parse()** function. + + ```js + let options = {supportDoctype:true, ignoreNameSpace:true, tokenValueCallbackFunction:func}; + that.parse(options); + ``` + + The output is as follows: + + + ```js + 0 0 // 0: . The event type value of START_DOCUMENT is 0. 0: The depth is 0. + 2 1 // 2: . The event type value of START_TAG is 2. 1: The depth is 1. + 2 2 // 2: . The event type value of START_TAG is 2. 2: The depth is 2. + 4 2 // 4: Play. The event type value of TEXT is 4. 2: The depth is 2. + 3 2 // 3: . The event type value of END_TAG is 3. 2: The depth is 2. + 3 1 // 3: . The event type value of END_TAG is 3. 1: The depth is 1 (corresponding to ). + 1 0 // 1: The event type value of END_DOCUMENT is 1. 0: The depth is 0. + ``` + + +## Example Scenario + +The following uses invoking all parsing options as an example to describe how to parse XML tags, attributes, and event types. + + +```js +import xml from '@ohos.xml'; +import util from '@ohos.util'; + +let strXml = + '' + + '' + + 'Everyday' + + 'Giada' + + ''; +let textEncoder = new util.TextEncoder(); +let arrBuffer = textEncoder.encodeInto(strXml); +let that = new xml.XmlPullParser(arrBuffer.buffer, 'UTF-8'); +let str = ''; + +function tagFunc(name, value) { + str = name + value; + console.info('tag-' + str); + return true; +} + +function attFunc(name, value) { + str = name + ' ' + value; + console.info('attri-' + str); + return true; +} + +function tokenFunc(name, value) { + str = name + ' ' + value.getDepth(); + console.info('token-' + str); + return true; +} + +let options = { + supportDocType: true, + ignoreNameSpace: true, + tagValueCallbackFunction: tagFunc, + attributeValueCallbackFunction: attFunc, + tokenValueCallbackFunction: tokenFunc +}; +that.parse(options); + +``` + +The output is as follows: + + +```js +tag- +token-0 0 +tag-book +attri-category COOKING +token-2 1 +tag-title +attri-lang en +token-2 2 +tag-Everyday +token-4 2 +tag-title +token-3 2 +tag-author +token-2 2 +tag-Giada +token-4 2 +tag-author +token-3 2 +tag-book +token-3 1 +tag- +token-1 0 +``` diff --git a/en/application-dev/connectivity/net-mdns.md b/en/application-dev/connectivity/net-mdns.md index 16aa29609d0826388b244a7daebbcb1f849ed27e..de7982a5c03908a70e4005bdc5fbea3584c435f5 100644 --- a/en/application-dev/connectivity/net-mdns.md +++ b/en/application-dev/connectivity/net-mdns.md @@ -94,7 +94,7 @@ mdns.removeLocalService(context, localServiceInfo, function (error, data) { 1. Connect the device to the Wi-Fi network. 2. Import the **mdns** namespace from **@ohos.net.mdns**. -3. Create a **DiscoveryService** object, which is used to discover mDNS services of the specified type. +3. Creates a **DiscoveryService** object, which is used to discover mDNS services of the specified type. 4. Subscribe to mDNS service discovery status changes. 5. Enable discovery of mDNS services on the LAN. 6. Stop searching for mDNS services on the LAN. @@ -116,20 +116,6 @@ class EntryAbility extends UIAbility { } let context = globalThis.context; -// Create a LocalService object. -let localServiceInfo = { - serviceType: "_print._tcp", - serviceName: "servicename", - port: 5555, - host: { - address: "10.14.**.***", - }, - serviceAttribute: [{ - key: "111", - value: [1] - }] -} - // Create a DiscoveryService object, which is used to discover mDNS services of the specified type. let serviceType = "_print._tcp"; let discoveryService = mdns.createDiscoveryService(context, serviceType); diff --git a/en/application-dev/connectivity/net-statistics.md b/en/application-dev/connectivity/net-statistics.md new file mode 100644 index 0000000000000000000000000000000000000000..47ec62ff156448b3214885176c30b2f76d77b76c --- /dev/null +++ b/en/application-dev/connectivity/net-statistics.md @@ -0,0 +1,155 @@ +# 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\): void; |Obtains the real-time downlink data traffic of the specified NIC. | +| ohos.net.statistics | getIfaceTxBytes(nic: string, callback: AsyncCallback\): void; |Obtains the real-time uplink data traffic of the specified NIC. | +| ohos.net.statistics | getCellularRxBytes(callback: AsyncCallback\): void; |Obtains the real-time downlink data traffic of the cellular network.| +| ohos.net.statistics | getCellularTxBytes(callback: AsyncCallback\): void; |Obtains the real-time uplink data traffic of the cellular network.| +| ohos.net.statistics | getAllRxBytes(callback: AsyncCallback\): void; |Obtains the real-time downlink data traffic of the all NICs. | +| ohos.net.statistics | getAllTxBytes(callback: AsyncCallback\): void; |Obtains the real-time uplink data traffic of the all NICs. | +| ohos.net.statistics | getUidRxBytes(uid: number, callback: AsyncCallback\): void; |Obtains the real-time downlink data traffic of the specified application. | +| ohos.net.statistics | getUidTxBytes(uid: number, callback: AsyncCallback\): void; |Obtains the real-time uplink data traffic of the specified application. | +| ohos.net.statistics | getTrafficStatsByIface(ifaceInfo: IfaceInfo, callback: AsyncCallback\): void; |Obtains the historical data traffic of the specified NIC. | +| ohos.net.statistics | getTrafficStatsByUid(uidInfo: UidInfo, callback: AsyncCallback\): 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'); + +``` diff --git a/en/application-dev/database/Readme-EN.md b/en/application-dev/database/Readme-EN.md index 77e1d8f9738d949ce9b0f0396bf66f99b9bf924e..74a44f63945d867ff76bb783e2ef0a6feb35861c 100644 --- a/en/application-dev/database/Readme-EN.md +++ b/en/application-dev/database/Readme-EN.md @@ -16,7 +16,11 @@ - [Database Backup and Restoration](data-backup-and-restore.md) - [Database Encryption](data-encryption.md) - [Access Control by Device and Data Level](access-control-by-device-and-data-level.md) -- Cross-Application Data Sharing (for System Applications Only) - - [Cross-Application Data Sharing Overview](share-device-data-across-apps-overview.md) - - [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md) - - [Sharing Data in Silent Access](share-data-by-silent-access.md) +- Cross-Application Data Sharing + - [Data Sharing Overview](data-share-overview.md) + - [Unified Data Definition](unified-data-definition.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 diff --git a/en/application-dev/database/data-mgmt-overview.md b/en/application-dev/database/data-mgmt-overview.md index aa98d97da5acdce3a382a70d383e140463a5399a..e6b77c1d89c5cc31e6e1fb9db05e7ab8d2607a7e 100644 --- a/en/application-dev/database/data-mgmt-overview.md +++ b/en/application-dev/database/data-mgmt-overview.md @@ -3,7 +3,7 @@ ## 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. @@ -16,9 +16,9 @@ The database stores created by an application are saved to the application sandb ## 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) @@ -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.
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. + diff --git a/en/application-dev/database/data-persistence-by-rdb-store.md b/en/application-dev/database/data-persistence-by-rdb-store.md index f2bb5e2d4098bbb19b3c791ed61307ffd78f0ec3..ff37d0fdce056ca143015f39c81892c990f6545d 100644 --- a/en/application-dev/database/data-persistence-by-rdb-store.md +++ b/en/application-dev/database/data-persistence-by-rdb-store.md @@ -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. **Figure 1** Working mechanism - + ![relationStore_local](figures/relationStore_local.jpg) @@ -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). -| API| Description| +| API| Description| | -------- | -------- | -| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): 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<ValueType>, callback: AsyncCallback<void>):void | Executes an SQL statement that contains specified arguments but returns no value.| -| insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void | Inserts a row of data into a table.| -| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.| -| delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.| -| query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void | Queries data in the RDB store based on specified conditions.| -| deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void | Deletes an RDB store.| +| getRdbStore(context: Context, config: StoreConfig, callback: AsyncCallback<RdbStore>): 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<ValueType>, callback: AsyncCallback<void>):void | Executes an SQL statement that contains specified arguments but returns no value.| +| insert(table: string, values: ValuesBucket, callback: AsyncCallback<number>):void | Inserts a row of data into a table.| +| update(values: ValuesBucket, predicates: RdbPredicates, callback: AsyncCallback<number>):void | Updates data in the RDB store based on the specified **RdbPredicates** instance.| +| delete(predicates: RdbPredicates, callback: AsyncCallback<number>):void | Deletes data from the RDB store based on the specified **RdbPredicates** instance.| +| query(predicates: RdbPredicates, columns: Array<string>, callback: AsyncCallback<ResultSet>):void | Queries data in the RDB store based on specified conditions.| +| deleteRdbStore(context: Context, name: string, callback: AsyncCallback<void>): void | Deletes an RDB store.| ## How to Develop @@ -53,7 +53,7 @@ The following table lists the APIs used for RDB data persistence. Most of the AP 1. Obtain an **RdbStore** instance.
Example: Stage model: - + ```js import relationalStore from '@ohos.data.relationalStore'; // Import the module. 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 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. 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 FA model: - + ```js import relationalStore from '@ohos.data.relationalStore'; // Import the module. 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 > > - 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 const valueBucket = { 'NAME': 'Lisa', @@ -177,13 +179,13 @@ The following table lists the APIs used for RDB data persistence. Most of the AP return; } console.info(`Succeeded in inserting data. rowId:${rowId}`); - }) +}) ``` - + > **NOTE** - > +> > **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. 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 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: - + ```js 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 console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); return; } + store = null; 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 FA model: - + ```js 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 console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); return; } + store = null; console.info('Succeeded in deleting RdbStore.'); }); ``` diff --git a/en/application-dev/database/data-share-overview.md b/en/application-dev/database/data-share-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..4a163dcc56a5592038cd497bddafa572a90628f7 --- /dev/null +++ b/en/application-dev/database/data-share-overview.md @@ -0,0 +1,71 @@ +# 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. diff --git a/en/application-dev/database/figures/dataManagement.jpg b/en/application-dev/database/figures/dataManagement.jpg index a43ca576222ad1da550242ed34c5f82700d52392..6555d34202927dc202fcb0ab233bc42740f39dbe 100644 Binary files a/en/application-dev/database/figures/dataManagement.jpg and b/en/application-dev/database/figures/dataManagement.jpg differ diff --git a/en/application-dev/database/figures/udmf_type_ADT.png b/en/application-dev/database/figures/udmf_type_ADT.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb1e7b94080b6d611b1ed7abaceda31b04442e7 Binary files /dev/null and b/en/application-dev/database/figures/udmf_type_ADT.png differ diff --git a/en/application-dev/database/figures/udmf_type_File.png b/en/application-dev/database/figures/udmf_type_File.png new file mode 100644 index 0000000000000000000000000000000000000000..80bbec073de7e4e75da239a8a073453545260cc1 Binary files /dev/null and b/en/application-dev/database/figures/udmf_type_File.png differ diff --git a/en/application-dev/database/figures/udmf_type_SDT.png b/en/application-dev/database/figures/udmf_type_SDT.png new file mode 100644 index 0000000000000000000000000000000000000000..026ce5bd7ae68f4dbabdcf5bac63e721454e17f5 Binary files /dev/null and b/en/application-dev/database/figures/udmf_type_SDT.png differ diff --git a/en/application-dev/database/figures/udmf_type_Text.png b/en/application-dev/database/figures/udmf_type_Text.png new file mode 100644 index 0000000000000000000000000000000000000000..d12a2390dce2d08417e7024d15ee431dde66756b Binary files /dev/null and b/en/application-dev/database/figures/udmf_type_Text.png differ diff --git a/en/application-dev/database/share-data-by-datashareextensionability.md b/en/application-dev/database/share-data-by-datashareextensionability.md index 7f70ab30d4c04c421c1e18032a0da13e590f80a7..d3c28e31c20f0aa3d6720359aa28e84af2061a63 100644 --- a/en/application-dev/database/share-data-by-datashareextensionability.md +++ b/en/application-dev/database/share-data-by-datashareextensionability.md @@ -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). -**Figure 1** Data sharing mechanism - +**Figure 1** Data sharing mechanism ![dataShare](figures/dataShare.jpg) - 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**: ## 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. @@ -146,7 +145,7 @@ override the service implementation as required. For example, if the data provid "icon": "$media:icon", "description": "$string:description_datashareextability", "type": "dataShare", - "uri": "datashare://com.samples.datasharetest.DataShare", + "uri": "datashareproxy://com.samples.datasharetest.DataShare", "exported": true, "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 **Table 2** Fields in the data_share_config.json file - | Field| Description | Mandatory| - | ------------ | ------------------------------------------------------------ | --- | - | tableConfig | Label configuration.| Yes| - | uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:
1. *****: indicates all databases and tables.
2. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}**: specifies a database.
3. **datashare:///{*bundleName*}/{*moduleName*}/{*storeName*}/{*tableName*}**: specifies a table.
If URIs of different formats are configured, only the URI with higher priority takes effect. | 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| + | Field | Description | Mandatory | + | ------------- | ---------------------------------------- | ---- | + | tableConfig | Label configuration. | Yes | + | uri | Range for which the configuration takes effect. The URI supports the following formats in descending order by priority:
- *****: indicates all databases and tables.
- **datashareproxy://{bundleName}/{moduleName}/{storeName}**: specifies a database.
- **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 | **data_share_config.json Example** @@ -170,18 +169,18 @@ override the service implementation as required. For example, if the data provid "crossUserMode": 1 }, { - "uri": "datashare:///com.acts.datasharetest/entry/DB00", + "uri": "datashareproxy://com.acts.datasharetest/entry/DB00", "crossUserMode": 1 }, { - "uri": "datashare:///com.acts.datasharetest/entry/DB00/TBL00", + "uri": "datashareproxy://com.acts.datasharetest/entry/DB00/TBL00", "crossUserMode": 2 } ] ``` -### Data Consumer Application Development +### Data Consumer Application 1. Import the dependencies. @@ -195,7 +194,7 @@ override the service implementation as required. For example, if the data provid ```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 (/). - let dseUri = ('datashare:///com.samples.datasharetest.DataShare'); + let dseUri = ('datashareproxy://com.samples.datasharetest.DataShare'); ``` 3. Create a **DataShareHelper** instance. @@ -239,3 +238,4 @@ override the service implementation as required. For example, if the data provid console.info(`dsHelper delete result:${data}`); }); ``` + diff --git a/en/application-dev/database/share-data-by-silent-access.md b/en/application-dev/database/share-data-by-silent-access.md index 50ff03f084c889a807c6caf4d7c369bfbe0d2a51..046d78b9eac73717cd16b4218f108c1a7979e54f 100644 --- a/en/application-dev/database/share-data-by-silent-access.md +++ b/en/application-dev/database/share-data-by-silent-access.md @@ -1,19 +1,40 @@ -# Data Sharing Through Silent Access +# Silent Access via the DatamgrService ## 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 +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 ![silent_dataShare](figures/silent_dataShare.jpg) @@ -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. - 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<DataShareHelper>): void | Creates a **DataShareHelper** instance.| + +### APIs for Persistent Data + +| API | Description | +| ---------------------------------------- | -------------------- | +| insert(uri: string, value: ValuesBucket, callback: AsyncCallback<number>): void | Inserts a row of data into a table. | +| delete(uri: string, predicates: dataSharePredicates.DataSharePredicates, callback: AsyncCallback<number>): void | Deletes one or more data records from the database. | +| query(uri: string, predicates: dataSharePredicates.DataSharePredicates, columns: Array<string>, callback: AsyncCallback<DataShareResultSet>): void | Queries data in the database. | +| update(uri: string, predicates: dataSharePredicates.DataSharePredicates, value: ValuesBucket, callback: AsyncCallback<number>): 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<string>, templateId: TemplateId, callback: AsyncCallback<RdbDataChangeNode>): Array<OperationResult | Subscribes to the changes of the data corresponding to the specified URI and template.| + +### APIs for Process Data + +| API | Description | +| ---------------------------------------- | ------------------ | +| publish(data: Array<PublishedItem>, bundleName: string, version: number, callback: AsyncCallback<Array<OperationResult>>): void | Publish data to the **DatamgrService**.| +| on(type: 'publishedDataChange', uris: Array<string>, subscriberId: string, callback: AsyncCallback<PublishedDataChangeNode>): Array<OperationResult> | 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.
The **name** field identifies the configuration, which has a fixed value of **dataProperties**.
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.
- **module** indicates that the database is located in this module.
- **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 = 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 = [ + {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 = 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 = ["city", "datashareproxy://com.acts.ohos.data.datasharetest/weather"]; + let result: Array = dsProxyHelper.on("publishedDataChange", uris, "11", onPublishCallback); + ``` -For details about the development procedure and implementation, see [Sharing Data Using DataShareExtensionAbility](share-data-by-datashareextensionability.md). + diff --git a/en/application-dev/database/sync-app-data-across-devices-overview.md b/en/application-dev/database/sync-app-data-across-devices-overview.md index c2f6361786325ccd753aa8fa4afa3446d37b6e89..4a3543a44c2b9e6e7fa9a4248010254c6ce1b035 100644 --- a/en/application-dev/database/sync-app-data-across-devices-overview.md +++ b/en/application-dev/database/sync-app-data-across-devices-overview.md @@ -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 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: @@ -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: -- 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. diff --git a/en/application-dev/database/unified-data-channels.md b/en/application-dev/database/unified-data-channels.md new file mode 100644 index 0000000000000000000000000000000000000000..b31b9532eafb700da67c2dbcc5464f8f58867d23 --- /dev/null +++ b/en/application-dev/database/unified-data-channels.md @@ -0,0 +1,165 @@ +# 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\): void | Inserts data to the UDMF public data channel. A unique data identifier is returned.| +| updateData(options: Options, data: UnifiedData, callback: AsyncCallback\): void | Updates the data in the UDMF public data channel. | +| queryData(options: Options, callback: AsyncCallback\>): void | Queries data in the UDMF public data channel. | +| deleteData(options: Options, callback: AsyncCallback\>): 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 = (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 = (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} `); + } + ``` diff --git a/en/application-dev/database/unified-data-definition.md b/en/application-dev/database/unified-data-definition.md new file mode 100644 index 0000000000000000000000000000000000000000..d0a3c100b5dadff7ef56a0938cde5b4d98b489d4 --- /dev/null +++ b/en/application-dev/database/unified-data-definition.md @@ -0,0 +1,125 @@ +# 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**
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)**
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)**
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\ | 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 = (records[i]); + break; + case UDMF.UnifiedDataType.PLAIN_TEXT: + // Convert the data to obtain the original text record. + let plainText = (records[i]); + break; + default: + break; + } + } + ``` diff --git a/en/application-dev/device/Readme-EN.md b/en/application-dev/device/Readme-EN.md index 6ce8d1b16951d5fb739d97c102cb8d3be3f628d7..54b4e3d3da968f496f1a0a5ff6698e8459dcb58c 100644 --- a/en/application-dev/device/Readme-EN.md +++ b/en/application-dev/device/Readme-EN.md @@ -19,3 +19,5 @@ - [Sample Server Development](sample-server-guidelines.md) - Stationary - [Stationary Development](stationary-guidelines.md) +- Peripheral + - [Peripheral Management Development](externaldevice-guidelines.md) diff --git a/en/application-dev/device/externaldevice-guidelines.md b/en/application-dev/device/externaldevice-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..134f402f3b163d641746fec177a5b99709299279 --- /dev/null +++ b/en/application-dev/device/externaldevice-guidelines.md @@ -0,0 +1,103 @@ +# Peripheral Management Development + + +## When to Use + +Peripheral devices (or simply peripherals) are auxiliary devices connected to a device through physical ports, such as handwriting tablets, printers, and scanners. Applications can query and bind peripherals by using the peripheral management capabilities, so that the device can use the customized capabilities provided by the peripheral drivers, such as the printer software. + + +## Available APIs + +The following table lists the open capabilities of peripheral management. For more information, see [Peripheral Management](../reference/apis/js-apis-driver-deviceManager.md). + +**Table 1** Open APIs for peripheral management + +| API | Description | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| queryDevices(busType?: number): Array<Readonly<Device>> | Queries the peripheral list. | +| bindDevice(deviceId: number, onDisconnect: AsyncCallback<number>, callback: AsyncCallback<{deviceId: number, remote: rpc.IRemoteObject}>): void | Binds a peripheral device. This API uses an asynchronous callback to return the result. If the peripheral device is bound, the **IRemoteObject** of the device driver is returned for subsequent interaction with the device driver.| +| bindDevice(deviceId: number, onDisconnect: AsyncCallback<number>): Promise<{deviceId: number, remote: rpc.IRemoteObject}> | Binds a peripheral device. This API uses a promise to return the result. | +| unbindDevice(deviceId: number, callback: AsyncCallback<number>): void | Unbinds a peripheral device. This API uses an asynchronous callback to return the result. | +| unbindDevice(deviceId: number): Promise<number> | Unbinds a peripheral device. This API uses a promise to return the result. | + + +## How to Develop + +You can use the APIs to query and bind peripheral devices so as to use the customized driver capabilities of the peripherals. The development procedure is as follows: + + +1. Query the peripheral device list. + + ```js + var matchDevice; + try { + let devices = deviceManager.queryDevices(deviceManager.BusType.USB); + for (let item of devices) { + let device = item as deviceManager.USBDevice; + // Match the USB device based on productId and vendorId. + if (device.productId == 1234 && device.vendorId === 2345) { + matchDevice = device; + break; + } + } + } catch (error) { + console.error('Failed to query device. Code is ${error.code}, message is ${error.message}'); + } + if (!matchDevice) { + console.error('No match device'); + return; + } + ``` + +2. Bind a peripheral device. + + ```js + var remoteObject; + try { + deviceManager.bindDevice(matchDevice.deviceId, (error, data) => { + console.error('Device is disconnected'); + }, (error, data) => { + if (error) { + console.error('bindDevice async fail. Code is ${error.code}, message is ${error.message}'); + return; + } + console.info('bindDevice success'); + remoteObject = data.remote; + }); + } catch (error) { + console.error('bindDevice fail. Code is ${error.code}, message is ${error.message}'); + } + ``` + +3. Use the capabilities provided by the peripheral device driver. + + ```js + let option = new rpc.MessageOption(); + let data = rpc.MessageSequence.create(); + let repy = rpc.MessageSequence.create(); + data.writeString('hello'); + let code = 1; + // The code and data content varies depending on the interface provided by the driver. + remoteObject.sendMessageRequest(code, data, reply, option) + .then(result => { + console.info('sendMessageRequest finish.'); + }).catch(error => { + console.error('sendMessageRequest fail. code:' + error.code); + }); + ``` + +4. Unbind the peripheral device after the device is used. + + ```js + try { + deviceManager.unbindDevice(matchDevice.deviceId, (error, data) => { + if (error) { + console.error('unbindDevice async fail. Code is ${error.code}, message is ${error.message}'); + return; + } + console.info('unbindDevice success'); + }); + } catch (error) { + console.error('unbindDevice fail. Code is ${error.code}, message is ${error.message}'); + } + ``` diff --git a/en/application-dev/faqs/faqs-arkui-arkts.md b/en/application-dev/faqs/faqs-arkui-arkts.md index 30372ac1e4f810f87e225b397b2aa5f95208ed0c..4cae24c691f9dab62d4c3452f32f69d3cf5b0da0 100644 --- a/en/application-dev/faqs/faqs-arkui-arkts.md +++ b/en/application-dev/faqs/faqs-arkui-arkts.md @@ -760,147 +760,6 @@ Text in the **\** component is centered by default. You do not need to set [Text](../reference/arkui-ts/ts-basic-components-text.md#example-1) -## How do I set the controlButton attribute for the \ 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 \ component? - -Applicable to: OpenHarmony 3.2 Beta5 (API version 9) - -**Solution** - -1. Set the **editMode\(true\)** attribute of the **\** 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? diff --git a/en/application-dev/faqs/faqs-arkui-component.md b/en/application-dev/faqs/faqs-arkui-component.md index 0bb884119bda149effc09337d957ccd2231bf1c7..a61d4cb828cc8cab4b8a4636adbf66729caba10b 100644 --- a/en/application-dev/faqs/faqs-arkui-component.md +++ b/en/application-dev/faqs/faqs-arkui-component.md @@ -1,10 +1,10 @@ # 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) -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** @@ -245,8 +245,8 @@ When a custom dialog box contains a child component whose area size can be chang **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 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 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: 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** @@ -685,3 +685,64 @@ You can use **focusControl.requestFocus** to control the focus of the text input **Reference** [Focus Control](../reference/arkui-ts/ts-universal-attributes-focus.md) + +## How do I set the controlButton attribute for the \ 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) + }) + } +} +``` diff --git a/en/application-dev/file-management/Readme-EN.md b/en/application-dev/file-management/Readme-EN.md index f512a935bae109a31ae3ea1b530608c5dadf6864..f976a47e09917c74f143a5cd84f3e7824825dc47 100644 --- a/en/application-dev/file-management/Readme-EN.md +++ b/en/application-dev/file-management/Readme-EN.md @@ -19,6 +19,12 @@ - Selecting and Saving User Files (FilePicker) - [Selecting User Files](select-user-file.md) - [Saving User Files](save-user-file.md) + - Album Management (photoAccessHelper) + - [photoAccessHelper Overview](photoAccessHelper-overview.md) + - [Media Asset (Image and video) Management](photoAccessHelper-resource-guidelines.md) + - [User Album Management](photoAccessHelper-userAlbum-guidelines.md) + - [System Album Management](photoAccessHelper-systemAlbum-guidelines.md) + - [Media Asset Change Notification Management](photoAccessHelper-notify-guidelines.md) - [Developing a FileManager Application (for System Applications Only)](dev-user-file-manager.md) - [Managing External Storage Devices (for System Applications Only)](manage-external-storage.md) - Distributed File System diff --git a/en/application-dev/file-management/photoAccessHelper-notify-guidelines.md b/en/application-dev/file-management/photoAccessHelper-notify-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..6e85789e72b92b991a654482a232100017cc0cbd --- /dev/null +++ b/en/application-dev/file-management/photoAccessHelper-notify-guidelines.md @@ -0,0 +1,200 @@ +# Media Asset (Image, Video, and Album) Change Notification Management + +The **photoAccessHelper** module provides APIs for listening for media asset changes. + +> **NOTE** +> +> Before you start, refer to [photoAccessHelper Overview](photoAccessHelper-overview.md) to learn how to obtain a **photoAccessHelper** instance and apply for permissions required. +> By default, the **photoAccessHelper** instance obtained in [photoAccessHelper Overview](photoAccessHelper-overview.md) is used when **photoAccessHelper** APIs are used. If the code for obtaining the **photoAccessHelper** instance is not added, an error indicating that **photoAccessHelper** is not defined is reported. + +The APIs related to media asset change notifications can be called asynchronously only in callback mode. This topic describes how to use some APIs. For more information about the APIs, see [Album Management](../reference/apis/js-apis-photoAccessHelper.md). +Unless otherwise specified, all the media assets to be obtained in this document exist in the database. If no media asset is obtained when the sample code is executed, check whether the media assets exist in the database. + +## Listening for the Specified URI + +Use [registerChange](../reference/apis/js-apis-photoAccessHelper.md#registerchange) to listen for the specified URI. When the observed object changes, the value of the listener callback will be returned. + +### Registering a Listener for a File Asset + +Registers a listener for the specified file asset. When the observed file asset changes, the listener callback will be invoked to return the change. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Register a listener for an image. When the image is favorited, the listener callback will be invoked. + +**How to Develop** + +1. [Obtain a media asset](photoAccessHelper-resource-guidelines.md#obtaining-the-specified-media-assets). +2. Register a listener for the media asset obtained. +3. Add the media asset to **Favorites**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.uri : ' + fileAsset.uri); + + let onCallback = (changeData) => { + console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); + } + phAccessHelper.registerChange(fileAsset.uri, false, onCallback); + + await fileAsset.favorite(true); + fetchResult.close(); +} catch (err) { + console.error('onCallback failed with err: ' + err); +} +``` + +### Registering a Listener for an Album + +Registers a listener for an album. When the observed album changes, the listener callback will be invoked to return the change. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Register a listener for a user album. When the album is renamed, the listener callback will be invoked. + +**How to Develop** + +1. [Obtain the user album](photoAccessHelper-userAlbum-guidelines.md#obtaining-a-use-album]. +2. Register a listener for the user album. +3. Rename the user album. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +predicates.equalTo(albumName, 'albumName'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions); + let album = await fetchResult.getFirstObject(); + console.info('getAlbums successfullyfully, albumName: ' + album.albumUri); + + let onCallback = (changeData) => { + console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); + } + phAccessHelper.registerChange(album.albumUri, false, onCallback); + + album.albumName = 'newAlbumName' + Date.now(); + await album.commitModify(); + fetchResult.close(); +} catch (err) { + console.error('onCallback failed with err: ' + err); +} +``` + +## Fuzzy Listening + +You can set **forChildUris** to **true** to register fuzzy listening. When **uri** is an album URI, the value **true** of **forChildUris** listens for the changes of the files in the album, and the value **false** listens for only the changes of the album itself.
If **uri** is the URI of a **fileAsset**, there is no difference between **true** and **false** for **forChildUris**.
If **uri** is **DefaultChangeUri**, **forChildUris** must be set to **true**. If **forChildUris** is **false**, the URI cannot be found and no message can be received. + +### Registering a Listener for All FileAssets + +Register listening for all FileAssets. When an observed FileAsset changes, the listener callback will be invoked. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Register a listener for all FileAssets. When an observed FileAsset is favorited, the listener callback will be invoked. + +**How to Develop** + +1. Register a listener for all FileAssets. +2. [Obtain a media asset](photoAccessHelper-resource-guidelines.md#obtaining-the-specified-media-assets). +3. Add the media asset obtained to **Favorites**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let onCallback = (changeData) => { + console.info('onCallback successfully, changData: ' + JSON.stringify(changeData)); +} +phAccessHelper.registerChange(photoAccessHelper.DefaultChangeUri.DEFAULT_PHOTO_URI, true, onCallback); + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.uri : ' + fileAsset.uri); + await fileAsset.favorite(true); + fetchResult.close(); +} catch (err) { + console.error('onCallback failed with err: ' + err); +} +``` + +## Unregistering the Listening for a URI + +Use [unRegisterChange](../reference/apis/js-apis-photoAccessHelper.md#unregisterchange) to unregister the listening for the specified URI. Multiple listeners can be registered for a URI. If multiple listener callbacks exist, you can unregister a listener callback registered. If callback is not specified, all listeners of the URI will be unregistered. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Unregister the listening for an image. After that, the corresponding listener callback is not triggered when the image favorites status is changed. + +**How to Develop** + +1. [Obtain a media asset](photoAccessHelper-resource-guidelines.md#obtaining-the-specified-media-assets). +2. Unregister the listening for the URI of the media asset obtained. +3. Add the media asset obtained to **Favorites**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.uri : ' + fileAsset.uri); + + let onCallback1 = (changeData) => { + console.info('onCallback1, changData: ' + JSON.stringify(changeData)); + } + let onCallback2 = (changeData) => { + console.info('onCallback2, changData: ' + JSON.stringify(changeData)); + } + phAccessHelper.registerChange(fileAsset.uri, false, onCallback1); + phAccessHelper.registerChange(fileAsset.uri, false, onCallback2); + phAccessHelper.unRegisterChange(fileAsset.uri, onCallback1); + + await fileAsset.favorite(true); + fetchResult.close(); +} catch (err) { + console.error('onCallback failed with err: ' + err); +} +``` diff --git a/en/application-dev/file-management/photoAccessHelper-overview.md b/en/application-dev/file-management/photoAccessHelper-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..48bb753cf4c1b5d10132c4bb1b12e7cf9290fd4c --- /dev/null +++ b/en/application-dev/file-management/photoAccessHelper-overview.md @@ -0,0 +1,123 @@ +# PhotoAccessHelper Overview + +**PhotoAccessHelper** provides album management capabilities, including creating and accessing an album and accessing and modifying media data in albums. You can use the APIs provided by **PhotoAccessHelper** to manage: + +- [Media assets (images and videos)](photoAccessHelper-resource-guidelines.md), including: + - Obtaining the specified media assets. + - Obtaining an image or video thumbnail. + - Creating a media asset. + - Renaming a media asset. + - Moving a media asset to the trash. +- [User Albums](photoAccessHelper-userAlbum-guidelines.md), including: + - Creating a user album. + - Obtaining a user album. + - Renaming a user album. + - Adding images and videos to a user album. + - Obtaining images and videos from a user album. + - Removing images and videos from a user album. + - Deleting a user album. +- [System albums](photoAccessHelper-systemAlbum-guidelines.md), including: + - Favorites + - Video album + - Screenshot album +- [Media asset (image, video, and album) change notifications](photoAccessHelper-notify-guidelines.md), including: + - Registering listening for a specified URI. + - Unregistering the listening for a specified URI. + +> **NOTE** +> +> The **PhotoAccessHelper** development guides apply only to API version 10 based on the stage model. + +An application needs to obtain a **photoAccessHelper** instance before accessing and modifying the media data in an album. User personal data is involved in the **photoAccessHelper** module. Therefore, the application needs to apply for the related read and write permissions. Unless otherwise specified, the APIs of the **photoAccessHelper** module apply to **pages/index.ets** of the project or other customized .ets files by default. + +Before using the **PhotoAccessHelper** APIs, you need to: + +- [Obtain a **photoAccessHelper** instance.](#obtaining-a-photoaccesshelper-instance) +- [Apply for permissions.](#applying-for-permissions) + +## Obtaining a photoAccessHelper Instance + +The application needs to call [getPhotoAccessHelper](../reference/apis/js-apis-photoAccessHelper.md#photoaccesshelpergetphotoaccesshelper) to obtain a **photoAccessHelper** instance based on the application context. The instance obtained can be used to access or modify the media data (such as image and videos) in an album. + +**How to Develop** + +1. Import the **photoAccessHelper** module. +2. Use **getContext** to obtain the application context. +3. Obtain a **photoAccessHelper** instance. + +```ts +import photoAccessHelper from '@ohos.file.photoAccessHelper'; + +// The phAccessHelper instance obtained here is a global object. By default, the object obtained here is used in subsequent operations. If the code is not added, an undefined error will be reported. +const context = getContext(this); +let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context); +``` + +## Applying for Permissions + +Before applying for permission, ensure that the [basic principles for permission management](../security/accesstoken-overview.md#basic-principles-for-permission-management) are complied with. Apply for the following permissions. + +| Permission | Description | Authorization Mode | +| ------------------------------ | ------------------------------------------ | ---------- | +| ohos.permission.READ_IMAGEVIDEO | Allows an app to read image and video file information from a user's external storage.| user_grant | +| ohos.permission.WRITE_IMAGEVIDEO | Allows an app to read and write image and video file information on a user's external storage.| user_grant | + +The required permissions must be authorized by the user (user_grant). You need to add the permissions in the **module.json5** file, and use [abilityAccessCtrl.requestPermissionsFromUser](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9) to check whether the required permissions are granted by the user. If yes, the application can access the data. Otherwise, a dialog box will be displayed to request user authorization. + +> **NOTE**
Even if the user has granted the permission, the permission will still be checked before an API protected by the permission is called. The permission granted status should not be persisted, because the user can revoke the permission through the system application **Settings**. + +**How to Develop** + +1. Declare the permissions in the **module.json5** file.
Add **requestPermissions** under **module** in the file, and add the required permissions. For details, see [Applying for Permissions](../security/accesstoken-guidelines.md). + + ```json + { + "module": { + "requestPermissions": [ + { + "name": "ohos.permission.READ_IMAGEVIDEO", + "reason": "Permissions required for photoAccessHelper related operations", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.WRITE_IMAGEVIDEO", + "reason": "Permissions required for photoAccessHelper related operations", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "always" + } + }, + ] + } + } + ``` + +2. Call **requestPermissionsFromUser** in the **onWindowStageCreate** callback of **Ability.ts** to check for the required permissions. If the permissions are not granted, display a dialog box to request user authorization dynamically. + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility'; + import abilityAccessCtrl, {Permissions} from '@ohos.abilityAccessCtrl'; + + export default class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + let list : Array = ['ohos.permission.READ_IMAGEVIDEO', 'ohos.permission.WRITE_IMAGEVIDEO']; + let permissionRequestResult; + let atManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(this.context, list, (err, result) => { + if (err) { + console.error('requestPermissionsFromUserError: ' + JSON.stringify(err)); + } else { + permissionRequestResult = result; + console.info('permissionRequestResult: ' + JSON.stringify(permissionRequestResult)); + } + }); + } + } + ``` diff --git a/en/application-dev/file-management/photoAccessHelper-resource-guidelines.md b/en/application-dev/file-management/photoAccessHelper-resource-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..d145589c1ad96aa8c9e4cc11d3112e40f4772678 --- /dev/null +++ b/en/application-dev/file-management/photoAccessHelper-resource-guidelines.md @@ -0,0 +1,283 @@ +# Media Asset Management + +Applications can call **photoAccessHelper** APIs to manage media assets (images and videos). + +> **NOTE** +> +> Before you start, refer to [photoAccessHelper Overview](photoAccessHelper-overview.md) to learn how to obtain a **photoAccessHelper** instance and apply for permissions required. +> By default, the **photoAccessHelper** instance obtained in [photoAccessHelper Overview](photoAccessHelper-overview.md) is used when **photoAccessHelper** APIs are used. If the code for obtaining the **photoAccessHelper** instance is not added, an error indicating that **photoAccessHelper** is not defined is reported. + +To ensure application running efficiency, most **PhotoAccessHelper** calls are asynchronous in callback or promise mode. The following code samples use promise-based APIs. For details about the APIs, see [Album Management](../reference/apis/js-apis-photoAccessHelper.md). + +## Obtaining the Specified Media Assets + +You can query media assets by media type, date, or album name. + +Use [PhotoAccessHelper.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets) with the [FetchOptions](../reference/apis/js-apis-photoAccessHelper.md#fetchoptions) object to specify search criteria. Unless otherwise specified, all the media assets to be obtained in this document exist in the database. If no media asset is obtained when the sample code is executed, check whether the media assets exist in the database. + +To obtain the object at the specified position (for example, the first, the last, or object with the specified index) in the result set, use [FetchFileResult](../reference/apis/js-apis-photoAccessHelper.md#fetchresult). + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. +- The [dataSharePredicates](../reference/apis/js-apis-data-dataSharePredicates.md) module is imported. + +### Obtaining an Image or Video by Name + +Example: Obtain the image **test.jpg**. + +**How to Develop** + +Create a **FetchOptions** object and specify **test.jpg**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +predicates.equalTo(photoAccessHelper.PhotoKeys.DISPLAY_NAME, 'test.jpg'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; +``` + +Call **PhotoAccessHelper.getAssets** to obtain the image asset. + +```ts +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.displayName : ' + fileAsset.displayName); + fetchResult.close(); +} catch (err) { + console.error('getAssets failed with err: ' + err); +} +``` + +### Obtaining an Image or Video by URI + +Example: Obtain the image with the file URI **file://media/Photo/1**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +predicates.equalTo(photoAccessHelper.PhotoKeys.URI, 'file://media/Photo/1'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; +``` + +Call **PhotoAccessHelper.getAssets** to obtain the image asset. + +```ts +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.uri : ' + fileAsset.uri); + fetchResult.close(); +} catch (err) { + console.error('getAssets failed with err: ' + err); +} +``` + + +### Obtaining Images or Videos by Time + +Example: Obtain the media assets added from 2022-06-01 to 2023-06-01. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let startTime = Date.parse(new Date('2022-06-01').toString()) / 1000; // The value of the start time is the number of seconds elapsed since the Epoch time. +let endTime = Date.parse(new Date('2023-06-01').toString()) / 1000; // The value of the end time is the number of seconds elapsed since the Epoch time. +let date_added = photoAccessHelper.PhotoKeys.DATE_ADDED; +predicates.between(date_added, startTime, endTime); +predicates.orderByDesc(date_added); // The query results are sorted in descending order. +let fetchOptions = { + fetchColumns: [date_added], // The date_added attribute is not a default option and needs to be added. + predicates: predicates +}; +``` + +Call **PhotoAccessHelper.getAssets** to obtain the image assets. + +```ts +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + console.info('getAssets count: ' + fetchResult.getCount()); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.displayName : ' + fileAsset.displayName); + fetchResult.close(); +} catch (err) { + console.error('getAssets failed with err: ' + err); +} +``` + +## Obtaining an Image or Video Thumbnail + +Use [FileAsset.getThumbnail](../reference/apis/js-apis-photoAccessHelper.md#getthumbnail) with the thumbnail size passed in to obtain the image or video thumbnail. The thumbnails offer a quick preview on images and videos. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. +- The [dataSharePredicates](../reference/apis/js-apis-data-dataSharePredicates.md) module is imported. + +### Obtaining the Thumbnail of an Image + +Your application may need to obtain the thumbnail of an image or video for preview purposes. + +Example: Obtain the thumbnail of 720 x 720 of an image. + +**How to Develop** + +1. Set the fetch options. +2. Call **PhotoAccessHelper.getAssets** to obtain image assets. +3. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the result set. +4. Call **getThumbnail** to obtain the [PixelMap](../reference/apis/js-apis-image.md#pixelmap7) of the thumbnail of the image. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.displayName : ' + fileAsset.displayName); + let size = { width: 720, height: 720 }; + let pixelMap = await fileAsset.getThumbnail(size); + let imageInfo = await pixelMap.getImageInfo() + console.info('getThumbnail successful, pixelMap ImageInfo size: ' + JSON.stringify(imageInfo.size)); + fetchResult.close(); +} catch (err) { + console.error('getThumbnail failed with err: ' + err); +} +``` + +## Creating a Media Asset + +Use [createAsset](../reference/apis/js-apis-photoAccessHelper.md#createasset) to create a media asset. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.WRITE_IMAGEVIDEO** permission. + +### Creating an Image or Video Asset + +Example: Create an image asset. + +**How to Develop** + +1. Set the file name and create **createOption** for setting attributes for the image asset to create. +2. Call **createAsset** to create an image asset. + +```ts +try { + let displayName = 'testPhoto' + Date.now() + '.jpg'; + let createOption = { + subType: photoAccessHelper.PhotoSubtype.DEFAULT + }; + + let fileAsset = await phAccessHelper.createAsset(displayName, createOption); + console.info('createAsset successfully, file displayName: ' + fileAsset.displayName); +} catch (err) { + console.error('createAsset failed, message = ', err); +} +``` + +## Renaming a Media Asset + +Set the **FileAsset.displayName** attribute to modify the file name (including the file name extension) displayed. + +After the modification, use [FileAsset.commitModify](../reference/apis/js-apis-photoAccessHelper.md#commitmodify) to update the modification to the database. + +Before renaming a file, use [FetchResult](../reference/apis/js-apis-photoAccessHelper.md#fetchresult) to obtain the file. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.WRITE_IMAGEVIDEO** and **ohos.permission.READ_IMAGEVIDEO** permissions. + +Example: Rename the first file in the obtained image assets. + +**How to Develop** + +1. Set the fetch options. +2. Call **getAssets** to obtain image assets. +3. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the obtained file assets. +4. Call **FileAsset.set** to rename the image. +5. Call **FileAsset.commitModify** to update the modified image attributes to the database. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: ['title'], + predicates: predicates +}; +let newTitle = 'newTestPhoto'; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + let title = photoAccessHelper.PhotoKeys.TITLE; + let fileAssetTitle = fileAsset.get(title); + console.info('getAssets fileAsset.title : ' + fileAssetTitle); + fileAsset.set(title, newTitle); + await fileAsset.commitModify(); + fetchResult.close(); +} catch (err) { + console.error('commitModify failed with err: ' + err); +} +``` + +## Moving Media Assets to the Trash + +You can use [deleteAssets](../reference/apis/js-apis-photoAccessHelper.md#deleteassets) to move files to the trash. + +The files moved to the trash will be retained for 30 days, and deleted permanently after 30 days. Before a file is deleted permanently from the trash, the user can restore it using the system application **File Manager** or **Gallery**. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained +- The application has the **ohos.permission.WRITE_IMAGEVIDEO** and **ohos.permission.READ_IMAGEVIDEO** permissions. + +Example: Move the first file in the result set to the trash. + +**How to Develop** + +1. Set the fetch options. +2. Call **PhotoAccessHelper.getAssets** to obtain image assets. +3. Call [**FetchResult.getFirstObject**](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image, that is, the image object to be moved to the trash. +4. Call **deleteAssets** to move the file to the trash. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await fetchResult.getFirstObject(); + console.info('getAssets fileAsset.uri : ' + fileAsset.uri); + await phAccessHelper.deleteAssets([fileAsset.uri]); + fetchResult.close(); +} catch (err) { + console.error('deleteAssets failed with err: ' + err); +} +``` diff --git a/en/application-dev/file-management/photoAccessHelper-systemAlbum-guidelines.md b/en/application-dev/file-management/photoAccessHelper-systemAlbum-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..7c98b2f674aba77a5359179e2a56b56819784eb1 --- /dev/null +++ b/en/application-dev/file-management/photoAccessHelper-systemAlbum-guidelines.md @@ -0,0 +1,306 @@ +# System Album Management + +The **photoAccessHelper** module provides APIs for managing system albums, including the **Favorites**, video album, and screenshot album. + +> **NOTE** +> +> Before you start, refer to [photoAccessHelper Overview](photoAccessHelper-overview.md) to learn how to obtain a **photoAccessHelper** instance and apply for permissions required. +> By default, the **photoAccessHelper** instance obtained in [photoAccessHelper Overview](photoAccessHelper-overview.md) is used when **photoAccessHelper** APIs are used. If the code for obtaining the **photoAccessHelper** instance is not added, an error indicating that **photoAccessHelper** is not defined is reported. + +To ensure application running efficiency, most **photoAccessHelper** calls are asynchronous in callback or promise mode. The following code samples use promise-based APIs. For details about the APIs, see [Album Management](../reference/apis/js-apis-photoAccessHelper.md). +Unless otherwise specified, all the media assets to be obtained in this document exist in the database. If no media asset is obtained when the sample code is executed, check whether the media assets exist in the database. + +## Favorites + +The **Favorites** is a system album. When you favorite a photo or video, the photo or video is added to **Favorites**. When you favorite a photo or video, the photo or video is removed from **Favorites**. + +### Obtaining a Favorites Object + +Use [getAlbums](../reference/apis/js-apis-photoAccessHelper.md#getalbums) to obtain a **Favorites** object. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +**How to Develop** + +1. Set the album type to **photoAccessHelper.AlbumType.SYSTEM** and the album subtype to **photoAccessHelper.AlbumSubtype.FAVORITE**. +2. Call **getAlbums** to obtain a **Favorites** object. + +```ts +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.FAVORITE); + let album = await fetchResult.getFirstObject(); + console.info('get favorite Album successfully, albumUri: ' + album.albumUri); + fetchResult.close(); +} catch (err) { + console.error('get favorite Album failed with err: ' + err); +} +``` + +### Favoriting an Image or Video + +Use [setFavorite](../reference/apis/js-apis-photoAccessHelper.md#setfavorite) to add images or videos to **Favorites**. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Favorite an image. + +**How to Develop** + +1. [Obtain media assets](photoAccessHelper-resource-guidelines.md#obtaining-the-specified-media-assets). +2. Set **favoriteState** to **true** to favorite the image. +3. Use **FileAsset.setFavorite** to add the image to **Favorites**. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +predicates.equalTo(photoAccessHelper.ImageVideoKey.DISPLAY_NAME, 'test.jpg'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let photoFetchResult = await phAccessHelper.getAssets(fetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('getAssets fileAsset.displayName : ' + fileAsset.displayName); + let favoriteState = true; + await fileAsset.setFavorite(favoriteState); +} catch (err) { + console.error('setFavorite failed with err: ' + err); +} +``` + +### Obtaining Images and Videos in Favorites + +[Obtain a **Favorites** object](#obtaining-a-favorites-object), and call [Album.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets-2) to obtain the assets in **Favorites**. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +Example: Obtain an image from **Favorites**. + +**How to Develop** + +1. [Obtain a **Favorites** object](#obtaining-a-favorites-object). +2. Set **fetchOptions** for obtaining the image. +3. Call **Album.getAssets** to obtain the image assets. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the result set. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.FAVORITE); + let album = await albumFetchResult.getFirstObject(); + console.info('get favorite Album successfully, albumUri: ' + album.albumUri); + + let photoFetchResult = await album.getAssets(fetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('favorite album getAssets successfully, albumName: ' + fileAsset.displayName); + photoFetchResult.close(); + albumFetchResult.close(); +} catch (err) { + console.error('favorite failed with err: ' + err); +} +``` + +### Unfavoriting an Image or Video + +Use [setFavorite](../reference/apis/js-apis-photoAccessHelper.md#setfavorite) to remove an image or video from **Favorites**. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Unfavorite an image. + +**How to Develop** + +1. [Obtain the image and videos in **Favorites**](#obtaining-images-and-videos-in-favorites). +2. Set **isFavorite** to **false**. +3. Use **FileAsset.favorite** to remove the image from **Favorites**. + + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.FAVORITE); + let album = await albumFetchResult.getFirstObject(); + console.info('get favorite Album successfully, albumUri: ' + album.albumUri); + + let photoFetchResult = await album.getAssets(fetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('favorite album getAssets successfully, albumName: ' + fileAsset.displayName); + let favoriteState = false; + await fileAsset.setFavorite(favoriteState); + photoFetchResult.close(); + albumFetchResult.close(); +} catch (err) { + console.error('setFavorite failed with err: ' + err); +} +``` + +## Video Album + +The video album is a system album. The media assets of the video type in user files are automatically added to the video album. + +### Obtaining a Video Album Object + +Use [getAlbums](../reference/apis/js-apis-photoAccessHelper.md#getalbums) to obtain a video album object. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +**How to Develop** + +1. Set the album type to **photoAccessHelper.AlbumType.SYSTEM** and the album subtype to **photoAccessHelper.AlbumSubtype.VIDEO**. +2. Use **getAlbums** to obtain the video album object. + +```ts +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.VIDEO); + let album = await fetchResult.getFirstObject(); + console.info('get video Album successfully, albumUri: ' + album.albumUri); + fetchResult.close(); +} catch (err) { + console.error('get video Album failed with err: ' + err); +} +``` + +### Obtaining Videos from the Video Album + +[Obtain a video album object](#obtaining-a-video-album-object). Use [Album.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets-2) to obtain video assets in the video album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +Example: Obtain a video in the video album. + +**How to Develop** + +1. [Obtain a video album object](#obtaining-a-video-album-object). +2. Set **fetchOptions** for obtaining the video. +3. Call **Album.getAssets** to obtain video assets. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first video. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.VIDEO); + let album = await albumFetchResult.getFirstObject(); + console.info('get video Album successfully, albumUri: ' + album.albumUri); + + let videoFetchResult = await album.getAssets(fetchOptions); + let fileAsset = await videoFetchResult.getFirstObject(); + console.info('video album getAssets successfully, albumName: ' + fileAsset.displayName); + videoFetchResult.close(); + albumFetchResult.close(); +} catch (err) { + console.error('video failed with err: ' + err); +} +``` + +## Screenshot Album + +The screenshot album is a system album. The user's screenshots and screen recording files are automatically added to this album. + +### Obtaining a Screenshot Album Object + +Use [getAlbums](../reference/apis/js-apis-photoAccessHelper.md#getalbums) to obtain a screenshot album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +**How to Develop** + +1. Set the album type to **photoAccessHelper.AlbumType.SYSTEM** and the album subtype to **photoAccessHelper.AlbumSubtype.SCREENSHOT**. +2. Use **getAlbums** to obtain a screenshot album object. + +```ts +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.SCREENSHOT); + let album = await fetchResult.getFirstObject(); + console.info('get screenshot Album successfully, albumUri: ' + album.albumUri); + fetchResult.close(); +} catch (err) { + console.error('get screenshot Album failed with err: ' + err); +} +``` + +### Obtaining Media Assets in the Screenshot Album + +[Obtain a screenshot album object](#obtaining-a-screenshot-album-object), and call [Album.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets-2) to obtain the media assets in the album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +Example: Obtain a media asset from the screenshot album. + +**How to Develop** + +1. [Obtain a screenshot album object](#obtaining-a-screenshot-album-object). +2. Set **fetchOptions** for obtaining the media asset. +3. Call **Album.getAssets** to obtain media assets. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first media asset. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.SYSTEM, photoAccessHelper.AlbumSubtype.SCREENSHOT); + let album = await albumFetchResult.getFirstObject(); + console.info('get screenshot album successfully, albumUri: ' + album.albumUri); + + let screenshotFetchResult = await album.getAssets(fetchOptions); + let fileAsset = await screenshotFetchResult.getFirstObject(); + console.info('screenshot album getAssets successfully, albumName: ' + fileAsset.displayName); + screenshotFetchResult.close(); + albumFetchResult.close(); +} catch (err) { + console.error('screenshot album failed with err: ' + err); +} +``` diff --git a/en/application-dev/file-management/photoAccessHelper-userAlbum-guidelines.md b/en/application-dev/file-management/photoAccessHelper-userAlbum-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..57bed20da0011c46f9172a87c121adc5f5c37f41 --- /dev/null +++ b/en/application-dev/file-management/photoAccessHelper-userAlbum-guidelines.md @@ -0,0 +1,329 @@ +# User Album Management + +The **photoAccessHelper** module provides APIs for user album management, including creating or deleting a user album, adding images and videos to a user album, and deleting image and videos from a user album. + +> **NOTE** +> +> Before you start, refer to [photoAccessHelper Overview](photoAccessHelper-overview.md) to learn how to obtain a **photoAccessHelper** instance and apply for permissions required. +> By default, the **photoAccessHelper** instance obtained in [photoAccessHelper Overview](photoAccessHelper-overview.md) is used when **photoAccessHelper** APIs are used. If the code for obtaining the **photoAccessHelper** instance is not added, an error indicating that **photoAccessHelper** is not defined is reported. + +To ensure application running efficiency, most **PhotoAccessHelper** calls are asynchronous in callback or promise mode. The following code samples use promise-based APIs. For details about the APIs, see [Album Management](../reference/apis/js-apis-photoAccessHelper.md). +Unless otherwise specified, all the media assets to be obtained in this document exist in the database. If no media asset is obtained when the sample code is executed, check whether the media assets exist in the database. + +## Creating a User Album + +Use [createAlbum](../reference/apis/js-apis-photoAccessHelper.md#createalbum) to create a user album. + +The album name must meet the following requirements: + +- The album name is a string of 1 to 255 characters. +- The album name cannot contain any of the following characters:
\ / : * ? " ' ` < > | { } [ ] +- The album name is case-insensitive. +- Duplicate album names are not allowed. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.WRITE_IMAGEVIDEO** permission. + +Example: Create a user album. + +**How to Develop** + +1. Set the name of the album to create. +2. Use **createAlbum** to create an album. + +```ts +try { + let albumName = 'albumName'; + let album = await phAccessHelper.createAlbum(albumName); + console.info('createAlbum successfully, album: ' + album.albumName + ' album uri: ' + album.albumUri); +} catch (err) { + console.error('createAlbum failed with err: ' + err); +} +``` + +## Obtaining a User Album + +Use [getAlbums](../reference/apis/js-apis-photoAccessHelper.md#getalbums) to obtain a user album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** permission. + +Example: Obtain a user album named **albumName**. + +**How to Develop** + +1. Set **fetchOptions** for obtaining the user album. +2. Call **getAlbums** to obtain user albums. +3. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first user album. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +predicates.equalTo(albumName, 'albumName'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions); + let album = await fetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + fetchResult.close(); +} catch (err) { + console.error('getAlbums failed with err: ' + err); +} +``` + +## Renaming a User Album + +Modify the **Albums.albumName** attribute of the album, + +and use [Album.commitModify](../reference/apis/js-apis-photoAccessHelper.md#commitmodify-2) to update the modification to the database. + +Before renaming a user album, you need to obtain an album object. You can use the [FetchResult](../reference/apis/js-apis-photoAccessHelper.md#fetchresult) APIs to obtain the user album of the corresponding location. + +The new user album names must also comply with the user name requirements in [Creating a User Album](#creating-a-user-album). + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Rename an album named **albumName**. + +**How to Develop** + +1. Set **fetchOptions** for obtaining the user album. +2. Call **getAlbums** to obtain user albums. +3. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first user album. +4. Set a new album name. +5. Call **Album.commitModify** to update the modified album attributes to the database. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +predicates.equalTo(albumName, 'albumName'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions); + let album = await fetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + album.albumName = 'newAlbumName'; + await album.commitModify(); + fetchResult.close(); +} catch (err) { + console.error('commitModify failed with err: ' + err); +} +``` + +## Adding Images and Videos to a User Album + +[Obtain a user album](#obtaining-a-user-album) and the array of the images or videos to be added to the album, and then call [Album.addAssets](../reference/apis/js-apis-photoAccessHelper.md#addassets) to add the images or videos to the user album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Add an image to the album named **albumName**. + +**How to Develop** + +1. Set **albumFetchOptions** for obtaining the user album. +2. Set **photoFetchOptions** for obtaining the image. +3. Call **getAlbums** to obtain user albums. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first user album. +5. Call [PhotoAccessHelper.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets) to obtain image assets. +6. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the result set. +7. Call **Album.addAssets** to add the image to the user album. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let albumPredicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +albumPredicates.equalTo(albumName, 'albumName'); +let albumFetchOptions = { + fetchColumns: [], + predicates: albumPredicates +}; + +let photoPredicates = new dataSharePredicates.DataSharePredicates(); +let photoFetchOptions = { + fetchColumns: [], + predicates: photoPredicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, albumFetchOptions); + let album = await albumFetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + let photoFetchResult = await phAccessHelper.getAssets(photoFetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('getAssets successfully, albumName: ' + fileAsset.displayName); + await album.addAssets([fileAsset]); + albumFetchResult.close(); + photoFetchResult.close(); +} catch (err) { + console.error('addAssets failed with err: ' + err); +} +``` + +## Obtaining Images and Videos in a User Album + +[Obtain the user album](#obtaining-a-user-album) object, and call [Album.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets-2) to obtain the media assets in the user album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Obtain an image in a user album named **albumName**. + +**How to Develop** + +1. Set **albumFetchOptions** for obtaining the user album. +2. Set **photoFetchOptions** for obtaining the image. +3. Call **getAlbums** to obtain user albums. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first user album. +5. Call **Album.getAssets** to obtain the image assets in the user album. +6. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the result set. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let albumPredicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +albumPredicates.equalTo(albumName, 'albumName'); +let albumFetchOptions = { + fetchColumns: [], + predicates: albumPredicates +}; + +let photoPredicates = new dataSharePredicates.DataSharePredicates(); +let photoFetchOptions = { + fetchColumns: [], + predicates: photoPredicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, albumFetchOptions); + let album = await albumFetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + let photoFetchResult = await album.getAssets(photoFetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('album getAssets successfully, albumName: ' + fileAsset.displayName); + albumFetchResult.close(); + photoFetchResult.close(); +} catch (err) { + console.error('album getAssets failed with err: ' + err); +} +``` + +## Removing Images and Videos from a User Album + +[Obtain the user album](#obtaining-a-user-album) object, and call [Album.getAssets](../reference/apis/js-apis-photoAccessHelper.md#getassets-2) to obtain the media assets in the user album. + +Use [Album.removeAssets](../reference/apis/js-apis-photoAccessHelper.md#removeassets) to remove the specified images. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Remove an image from the album named **albumName**. + +**How to Develop** + +1. Set **albumFetchOptions** for obtaining the user album. +2. Set **photoFetchOptions** for obtaining the image. +3. Call **getAlbums** to obtain user albums. +4. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first user album. +5. Call **Album.getAssets** to obtain the image assets. +6. Call [FetchResult.getFirstObject](../reference/apis/js-apis-photoAccessHelper.md#getfirstobject) to obtain the first image from the result set. +7. Call **Album.removeAssets** to remove the image from the user album. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let albumPredicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +albumPredicates.equalTo(albumName, 'albumName'); +let albumFetchOptions = { + fetchColumns: [], + predicates: albumPredicates +}; + +let photoPredicates = new dataSharePredicates.DataSharePredicates(); +let photoFetchOptions = { + fetchColumns: [], + predicates: photoPredicates +}; + +try { + let albumFetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, albumFetchOptions); + let album = await albumFetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + let photoFetchResult = await album.getAssets(photoFetchOptions); + let fileAsset = await photoFetchResult.getFirstObject(); + console.info('album getAssets successfully, albumName: ' + fileAsset.displayName); + await album.removeAssets([fileAsset]); + albumFetchResult.close(); + photoFetchResult.close(); +} catch (err) { + console.error('removeAssets failed with err: ' + err); +} +``` + +## Deleting a User Album + +[Obtain the user album](#obtaining-a-user-album) object, and call [deleteAlbums](../reference/apis/js-apis-photoAccessHelper.md#deletealbums) to delete the user album. + +**Prerequisites** + +- A **photoAccessHelper** instance is obtained. +- The application has the **ohos.permission.READ_IMAGEVIDEO** and **ohos.permission.WRITE_IMAGEVIDEO** permissions. + +Example: Delete a user album named **albumName**. + +**How to Develop** + +1. Set **fetchOptions** for obtaining the user album. +2. Call **getAlbums** to obtain user albums. +3. Call **FetchResult.getFirstObject** to obtain the first user album. +4. Call **deleteAlbums** to delete the user album. + +```ts +import dataSharePredicates from '@ohos.data.dataSharePredicates'; + +let predicates = new dataSharePredicates.DataSharePredicates(); +let albumName = photoAccessHelper.AlbumKey.ALBUM_NAME; +predicates.equalTo(albumName, '%albumName%'); +let fetchOptions = { + fetchColumns: [], + predicates: predicates +}; + +try { + let fetchResult = await phAccessHelper.getAlbums(photoAccessHelper.AlbumType.USER, photoAccessHelper.AlbumSubtype.USER_GENERIC, fetchOptions); + let album = await fetchResult.getFirstObject(); + console.info('getAlbums successfully, albumName: ' + album.albumName); + phAccessHelper.deleteAlbums([album]); + fetchResult.close(); +} catch (err) { + console.error('deleteAlbums failed with err: ' + err); +} +``` diff --git a/en/application-dev/file-management/save-user-file.md b/en/application-dev/file-management/save-user-file.md index db6ad37908be0a1fe1dd00e36c4553830bf03c72..d47592c74dff6211a7301516c8a76f07f648812e 100644 --- a/en/application-dev/file-management/save-user-file.md +++ b/en/application-dev/file-management/save-user-file.md @@ -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 **save()** interface saves the file in the file manager, not in the Gallery. + ## Saving Images or Video Files @@ -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. -
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 - let URI = null; + let uri = null; const photoViewPicker = new picker.PhotoViewPicker(); photoViewPicker.save(photoSaveOptions).then((photoSaveResult) => { - URI = photoSaveResult[0]; - console.info('photoViewPicker.save to file succeed and URI is:' + URI); + uri = photoSaveResult[0]; + console.info('photoViewPicker.save to file succeed and uri is:' + uri); }).catch((err) => { 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 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 - let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); + let file = fs.openSync(uri, fs.OpenMode.READ_WRITE); console.info('file fd: ' + file.fd); ``` @@ -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. ```ts - let URI = null; + let uri = null; const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. documentViewPicker.save(documentSaveOptions).then((documentSaveResult) => { - URI = documentSaveResult[0]; - console.info('documentViewPicker.save to file succeed and URI is:' + URI); + uri = documentSaveResult[0]; + console.info('documentViewPicker.save to file succeed and uri is:' + uri); }).catch((err) => { 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 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 - let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); + let file = fs.openSync(uri, fs.OpenMode.READ_WRITE); console.info('file fd: ' + file.fd); ``` @@ -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. ```ts - let URI = null; + let uri = null; const audioViewPicker = new picker.AudioViewPicker(); audioViewPicker.save(audioSaveOptions).then((audioSelectResult) => { - URI = audioSelectResult[0]; - console.info('audioViewPicker.save to file succeed and URI is:' + URI); + uri = audioSelectResult[0]; + console.info('audioViewPicker.save to file succeed and uri is:' + uri); }).catch((err) => { 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 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 - let file = fs.openSync(URI, fs.OpenMode.READ_WRITE); + let file = fs.openSync(uri, fs.OpenMode.READ_WRITE); 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 let writeLen = fs.writeSync(file.fd, 'hello, world'); diff --git a/en/application-dev/file-management/select-user-file.md b/en/application-dev/file-management/select-user-file.md index 853aae60d7e73fa4238e388eefb19ded0ca59b1d..af4bae83b13b50eb64e0063012dcaea4a51234bd 100644 --- a/en/application-dev/file-management/select-user-file.md +++ b/en/application-dev/file-management/select-user-file.md @@ -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. -
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 - let URI = null; + let uri = null; const photoViewPicker = new picker.PhotoViewPicker(); photoViewPicker.select(photoSelectOptions).then((photoSelectResult) => { - URI = photoSelectResult.photoUris[0]; - console.info('photoViewPicker.select to file succeed and URI is:' + URI); + uri = photoSelectResult.photoUris[0]; + console.info('photoViewPicker.select to file succeed and uri is:' + uri); }).catch((err) => { 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: 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 - let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); + let file = fs.openSync(uri, fs.OpenMode.READ_ONLY); console.info('file fd: ' + file.fd); ``` @@ -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. -
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. -
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** > > Currently, **DocumentSelectOptions** is not configurable. By default, all types of user files are selected. ```ts - let URI = null; + let uri = null; const documentViewPicker = new picker.DocumentViewPicker(); // Create a documentViewPicker instance. documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => { - URI = documentSelectResult[0]; - console.info('documentViewPicker.select to file succeed and URI is:' + URI); + uri = documentSelectResult[0]; + console.info('documentViewPicker.select to file succeed and uri is:' + uri); }).catch((err) => { 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: 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 - let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); + let file = fs.openSync(uri, fs.OpenMode.READ_ONLY); console.info('file fd: ' + file.fd); ``` @@ -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. -
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. -
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** > > Currently, **AudioSelectOptions** is not configurable. By default, all types of user files are selected. ```ts - let URI = null; + let uri = null; const audioViewPicker = new picker.AudioViewPicker(); audioViewPicker.select(audioSelectOptions).then(audioSelectResult => { - URI = audioSelectOptions[0]; - console.info('audioViewPicker.select to file succeed and URI is:' + URI); + uri = audioSelectOptions[0]; + console.info('audioViewPicker.select to file succeed and uri is:' + uri); }).catch((err) => { 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: 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 - let file = fs.openSync(URI, fs.OpenMode.READ_ONLY); + let file = fs.openSync(uri, fs.OpenMode.READ_ONLY); console.info('file fd: ' + file.fd); ``` diff --git a/en/application-dev/internationalization/i18n-guidelines.md b/en/application-dev/internationalization/i18n-guidelines.md index e78bdb6437b26b8a30ee23f9fdec380087297b33..f2393be5fb12c8011d267f0df295acfaacaec607 100644 --- a/en/application-dev/internationalization/i18n-guidelines.md +++ b/en/application-dev/internationalization/i18n-guidelines.md @@ -610,8 +610,10 @@ Call [Transliterator](../reference/apis/js-apis-i18n.md#transliterator9) APIs to Call **transform** to obtain the transliterated string. ```js - let transliterator = I18n.Transliterator.getInstance("Any-Latn"); // Any-Latn means to convert any text to Latn text. + let transliterator = I18n.Transliterator.getInstance("Any-Latin"); // Any-Latin means to convert any text to Latin text. let transformText = transliterator.transform ("Hello"); // transformText = "nǐ hǎo" + let transliterator2 = I18n.Transliterator.getInstance("Latin-ASCII"); // Latin-ASCII means to convert Latin text to ASCII text. + transformText = transliterator2.transform(transformText); // transformText = "ni hao" ``` ## Obtaining the Character Type diff --git a/en/application-dev/media/audio-decoding.md b/en/application-dev/media/audio-decoding.md index 77631eefec952c5142d76680abd49bd694541ab1..2fc2d11ed4bf946f90466e45b6160e8880e4d7e1 100644 --- a/en/application-dev/media/audio-decoding.md +++ b/en/application-dev/media/audio-decoding.md @@ -73,33 +73,33 @@ The figure below shows the call relationship of audio decoding. ``` 2. Call **OH_AudioDecoder_SetCallback()** to set callback functions. - + Register the **OH_AVCodecAsyncCallback** struct that defines the following callback function pointers: - - - **OnError**, a callback used to report a codec operation error - - **OnOutputFormatChanged**, a callback used to report a codec stream change, for example, audio channel change - - **OnInputBufferAvailable**, a callback used to report input data required, which means that the decoder is ready for receiving data - - **OnOutputBufferAvailable**, a callback used to report output data generated, which means that decoding is complete + + - **OH_AVCodecOnError**, a callback used to report a codec operation error + - **OH_AVCodecOnStreamChanged**, a callback used to report a codec stream change, for example, audio channel change + - **OH_AVCodecOnNeedInputData**, a callback used to report input data required, which means that the decoder is ready for receiving data + - **OH_AVCodecOnNewOutputData**, a callback used to report output data generated, which means that decoding is complete You need to process the callback functions to ensure that the decoder runs properly. - - ```cpp - // Set the OnError callback function. + + ```cpp + // Implement the OH_AVCodecOnError callback function. static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) { (void)codec; (void)errorCode; (void)userData; } - // Set the OnOutputFormatChanged callback function. - static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void*userData) + // Implement the OH_AVCodecOnStreamChanged callback function. + static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void*userData) { (void)codec; (void)format; (void)userData; } - // Set the OnInputBufferAvailable callback function, which is used to send the input stream to the InputBuffer queue. - static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory*data, void *userData) + // Implement the OH_AVCodecOnNeedInputData callback function. + static void onNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory*data, void *userData) { (void)codec; ADecSignal *signal = static_cast(userData); @@ -109,8 +109,8 @@ The figure below shows the call relationship of audio decoding. signal->inCond_.notify_all(); // The input stream is sent to the InputBuffer queue. } - // Set the OnOutputBufferAvailable callback function, which is used to send the PCM stream obtained after decoding to the OutputBuffer queue. - static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory*data, OH_AVCodecBufferAttr *attr, + // Implement the OH_AVCodecOnNewOutputData callback function. + static void onNeedOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory*data, OH_AVCodecBufferAttr *attr, void *userData) { (void)codec; @@ -126,14 +126,13 @@ The figure below shows the call relationship of audio decoding. // The decoded data is sent to the OutputBuffer queue. } signal_ = new ADecSignal(); - OH_AVCodecAsyncCallback cb = {&OnError, &OnOutputFormatChanged, OnInputBufferAvailable, &OnOutputBufferAvailable}; + OH_AVCodecAsyncCallback cb = {&OnError, &OnStreamChanged, &onNeedInputData, &onNeedOutputData}; // Set the asynchronous callbacks. int32_t ret = OH_AudioDecoder_SetCallback(audioDec, cb, signal_); if (ret != AV_ERR_OK) { // Exception handling. } - ``` - + ``` 3. Call **OH_AudioDecoder_Configure()** to configure the decoder. The following options are mandatory: sampling rate, bit rate, and number of audio channels. The maximum input length is optional. @@ -141,7 +140,7 @@ The figure below shows the call relationship of audio decoding. - For AAC decoding, the parameter that specifies whether the data type is Audio Data Transport Stream (ADTS) must be specified. If this parameter is not specified, the data type is considered as Low Overhead Audio Transport Multiplex (LATM). - For Vorbis decoding, the ID header and setup header must also be specified. - ```cpp + ```cpp enum AudioFormatType : int32_t { TYPE_AAC = 0, TYPE_FLAC = 1, @@ -176,8 +175,7 @@ The figure below shows the call relationship of audio decoding. if (ret != AV_ERR_OK) { // Exception handling. } - ``` - + ``` 4. Call **OH_AudioDecoder_Prepare()** to prepare internal resources for the decoder. ```cpp @@ -186,7 +184,6 @@ The figure below shows the call relationship of audio decoding. // Exception handling. } ``` - 5. Call **OH_AudioDecoder_Start()** to start the decoder. ```c++ @@ -202,12 +199,10 @@ The figure below shows the call relationship of audio decoding. // Exception handling. } ``` - 6. Call **OH_AudioDecoder_PushInputData()** to write the data to decode. To indicate the End of Stream (EOS), pass in the **AVCODEC_BUFFER_FLAGS_EOS** flag. - - ```c++ + ```c++ // Configure the buffer information. OH_AVCodecBufferAttr info; // Set the package size, offset, and timestamp. @@ -228,8 +223,7 @@ The figure below shows the call relationship of audio decoding. if (ret != AV_ERR_OK) { // Exception handling. } - ``` - + ``` 7. Call **OH_AudioDecoder_FreeOutputData()** to output decoded PCM streams. ```c++ @@ -244,7 +238,6 @@ The figure below shows the call relationship of audio decoding. // Exception handling. } ``` - 8. (Optional) Call **OH_AudioDecoder_Flush()** to refresh the decoder. After **OH_AudioDecoder_Flush()** is called, the decoder remains in the running state, but the current queue is cleared and the buffer storing the decoded data is freed. To continue decoding, you must call **OH_AudioDecoder_Start()** again. @@ -265,24 +258,22 @@ The figure below shows the call relationship of audio decoding. // Exception handling. } ``` - 9. (Optional) Call **OH_AudioDecoder_Reset()** to reset the decoder. After **OH_AudioDecoder_Reset()** is called, the decoder returns to the initialized state. To continue decoding, you must call **OH_AudioDecoder_Configure()** and then **OH_AudioDecoder_Start()**. - - ```c++ - // Reset the decoder. - ret = OH_AudioDecoder_Reset(audioDec); - if (ret != AV_ERR_OK) { - // Exception handling. - } - // Reconfigure the decoder. - ret = OH_AudioDecoder_Configure(audioDec, format); - if (ret != AV_ERR_OK) { - // Exception handling. - } - ``` - + + ```c++ + // Reset the decoder. + ret = OH_AudioDecoder_Reset(audioDec); + if (ret != AV_ERR_OK) { + // Exception handling. + } + // Reconfigure the decoder. + ret = OH_AudioDecoder_Configure(audioDec, format); + if (ret != AV_ERR_OK) { + // Exception handling. + } + ``` 10. Call **OH_AudioDecoder_Stop()** to stop the decoder. ```c++ @@ -293,18 +284,17 @@ The figure below shows the call relationship of audio decoding. } return ret; ``` - 11. Call **OH_AudioDecoder_Destroy()** to destroy the decoder instance and release resources. - **NOTE**: You only need to call this API once. - - ```c++ - // Call OH_AudioDecoder_Destroy to destroy the decoder. - ret = OH_AudioDecoder_Destroy(audioDec); - if (ret != AV_ERR_OK) { - // Exception handling. - } else { - audioEnc = NULL; // The decoder cannot be destroyed repeatedly. - } - return ret; - ``` \ No newline at end of file + **NOTE**: You only need to call this API once. + + ```c++ + // Call OH_AudioDecoder_Destroy to destroy the decoder. + ret = OH_AudioDecoder_Destroy(audioDec); + if (ret != AV_ERR_OK) { + // Exception handling. + } else { + audioEnc = NULL; // The decoder cannot be destroyed repeatedly. + } + return ret; + ``` diff --git a/en/application-dev/media/audio-encoding.md b/en/application-dev/media/audio-encoding.md index df2a0ace83f52fdbf1a9ae002dc6dae639088e4d..607c89560a052ce98b4170d098840f84df3286f7 100644 --- a/en/application-dev/media/audio-encoding.md +++ b/en/application-dev/media/audio-encoding.md @@ -50,7 +50,7 @@ The figure below shows the call relationship of audio encoding. ```cpp // Create an encoder by MIME type. - OH_AVCodec *audioEnc = OH_AudioEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC); + OH_AVCodec *audioEnc = OH_AudioEncoder_CreateByMime(OH_AVCODEC_MIMETYPE_AUDIO_AAC); ``` ```cpp @@ -76,47 +76,46 @@ The figure below shows the call relationship of audio encoding. Register the **OH_AVCodecAsyncCallback** struct that defines the following callback function pointers: - - **OnError**, a callback used to report a codec operation error - - **OnOutputFormatChanged**, a callback used to report a codec stream change, for example, audio channel change - - **OnInputBufferAvailable**, a callback used to report input data required, which means that the encoder is ready for receiving PCM data - - **OnOutputBufferAvailable**, a callback used to report output data generated, which means that encoding is complete + - **OH_AVCodecOnError**, a callback used to report a codec operation error + - **OH_AVCodecOnStreamChanged**, a callback used to report a codec stream change, for example, audio channel change + - **OH_AVCodecOnNeedInputData**, a callback used to report input data required, which means that the encoder is ready for receiving PCM data + - **OH_AVCodecOnNewOutputData**, a callback used to report output data generated, which means that encoding is complete You need to process the callback functions to ensure that the encoder runs properly. ```cpp - // Set the OnError callback function. + // Implement the OH_AVCodecOnError callback function. static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) { (void)codec; (void)errorCode; (void)userData; } - // Set the OnOutputFormatChanged callback function. - static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) + // Implement the OH_AVCodecOnStreamChanged callback function. + static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { (void)codec; (void)format; (void)userData; } - // Set the OnInputBufferAvailable callback function, which is used to send the input PCM data to the InputBuffer queue. - static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData) + // Implement the OH_AVCodecOnNeedInputData callback function. + static void OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData) { (void)codec; // The input stream is sent to the InputBuffer queue. AEncSignal *signal = static_cast(userData); - cout << "OnInputBufferAvailable received, index:" << index << endl; unique_lock lock(signal->inMutex_); signal->inQueue_.push(index); signal->inBufferQueue_.push(data); signal->inCond_.notify_all(); } - // Set the OnOutputBufferAvailable callback function, which is used to send the encoded stream to the OutputBuffer queue. - static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, + // Implement the OH_AVCodecOnNewOutputData callback function. + static void OnNeedOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, void *userData) { (void)codec; - // The index of the output buffer is sent to the OutputQueue. - // The encoded data is sent to the OutputBuffer queue. + // The index of the output buffer is sent to OutputQueue_. + // The encoded data is sent to the outBuffer queue. AEncSignal *signal = static_cast(userData); unique_lock lock(signal->outMutex_); signal->outQueue_.push(index); @@ -125,7 +124,7 @@ The figure below shows the call relationship of audio encoding. signal->attrQueue_.push(*attr); } } - OH_AVCodecAsyncCallback cb = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable}; + OH_AVCodecAsyncCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputData, &OnNeedOutputData}; // Set the asynchronous callbacks. int32_t ret = OH_AudioEncoder_SetCallback(audioEnc, cb, userData); ``` @@ -143,7 +142,7 @@ The figure below shows the call relationship of audio encoding. }; int32_t ret; // (Mandatory) Configure the audio sampling rate. - constexpr uint32_t DEFAULT_SMAPLERATE = 44100; + constexpr uint32_t DEFAULT_SMAPLERATE = 44100; // (Mandatory) Configure the audio bit rate. constexpr uint32_t DEFAULT_BITRATE = 32000; // (Mandatory) Configure the number of audio channels. @@ -193,7 +192,7 @@ The figure below shows the call relationship of audio encoding. ```c++ inputFile_ = std::make_unique(); // Open the path of the binary file to be encoded. - inputFile_->open(inputFilePath.data(), std::ios::in |std::ios::binary); + inputFile_->open(inputFilePath.data(), std::ios::in |std::ios::binary); // Configure the path of the output file. outFile_ = std::make_unique(); outFile_->open(outputFilePath.data(), std::ios::out |std::ios::binary); diff --git a/en/application-dev/media/figures/audio-decode.png b/en/application-dev/media/figures/audio-decode.png index f76febe618c2529b3530436ddab6cb954a42d3b2..388cc259a9500e7b0db7c4d6f9a91f15449de69c 100644 Binary files a/en/application-dev/media/figures/audio-decode.png and b/en/application-dev/media/figures/audio-decode.png differ diff --git a/en/application-dev/media/figures/audio-encode.png b/en/application-dev/media/figures/audio-encode.png index 06be355cc372f7d8fc5614268f4358bbcea1e1a3..f36aca830c3d8831f53cfec1deb77ddf56d0ccac 100644 Binary files a/en/application-dev/media/figures/audio-encode.png and b/en/application-dev/media/figures/audio-encode.png differ diff --git a/en/application-dev/media/figures/video-decode.png b/en/application-dev/media/figures/video-decode.png new file mode 100644 index 0000000000000000000000000000000000000000..cb3fd0f7351359d83572f89605c5c99a306740ba Binary files /dev/null and b/en/application-dev/media/figures/video-decode.png differ diff --git a/en/application-dev/media/figures/video-encode.png b/en/application-dev/media/figures/video-encode.png new file mode 100644 index 0000000000000000000000000000000000000000..4f29b68813155f2b79b3f5cd6d8756f9c28bcfd5 Binary files /dev/null and b/en/application-dev/media/figures/video-encode.png differ diff --git a/en/application-dev/media/video-decoding.md b/en/application-dev/media/video-decoding.md index 7e1dd04c05bb519b1c16cb480050f4625967a474..a267a9195cade7bcb44eecbbeba30450d686eb4f 100644 --- a/en/application-dev/media/video-decoding.md +++ b/en/application-dev/media/video-decoding.md @@ -15,21 +15,24 @@ Video software decoding and hardware decoding are different. When a decoder is c Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API reference. +The figure below shows the call relationship of video decoding. + +![Call relationship of video decoding](figures/video-decode.png) + 1. Create a decoder instance. You can create a decoder by name or MIME type. ``` c++ - // Create a decoder by name. + // To create a decoder by name, call OH_AVCapability_GetName to obtain the codec names available and then call OH_VideoDecoder_CreateByName. If your application has special requirements, for example, expecting a decoder that supports a certain resolution, you can call OH_AVCodec_GetCapability to query the capability first. OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, false); const char *name = OH_AVCapability_GetName(capability); - OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name); // name:"OH.Media.Codec.Decoder.Video.AVC" + OH_AVCodec *videoDec = OH_VideoDecoder_CreateByName(name); ``` ```c++ // Create a decoder by MIME type. - // Create an H.264 decoder for software/hardware decoding. + // Create an H.264 decoder for software/hardware decoding. The system creates the most appropriate decoder if multiple decoders are available. OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_AVC); - // Create a decoder by MIME type. // Create an H.265 decoder for hardware decoding. OH_AVCodec *videoDec = OH_VideoDecoder_CreateByMime(OH_AVCODEC_MIMETYPE_VIDEO_HEVC); ``` @@ -54,30 +57,32 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe Register the **OH_AVCodecAsyncCallback** struct that defines the following callback function pointers: - - **OnError**, a callback used to report a codec operation error - - **OnOutputFormatChanged**, a callback used to report a codec stream change, for example, stream width or height change. - - **OnInputBufferAvailable**, a callback used to report input data required, which means that the decoder is ready for receiving data - - **OnOutputBufferAvailable**, a callback used to report output data generated, which means that decoding is complete (Note: The **data** parameter in the callback function is empty in surface output mode.) + - **OH_AVCodecOnError**, a callback used to report a codec operation error + - **OH_AVCodecOnStreamChanged**, a callback used to report a codec stream change, for example, stream width or height change. + - **OH_AVCodecOnNeedInputData**, a callback used to report input data required, which means that the decoder is ready for receiving data + - **OH_AVCodecOnNewOutputData**, a callback used to report output data generated, which means that decoding is complete (Note: The **data** parameter in the callback function is empty in surface output mode.) You need to process the callback functions to ensure that the decoder runs properly. ``` c++ - // Set the OnError callback function. + // Implement the OH_AVCodecOnError callback function. static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) { (void)codec; (void)errorCode; (void)userData; } - // Set the OnOutputFormatChanged callback function. - static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) + + // Implement the OH_AVCodecOnStreamChanged callback function. + static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { (void)codec; (void)format; (void)userData; } - // Set the OnInputBufferAvailable callback function, which is used to obtain the input frame information. - static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData) + + // Implement the OH_AVCodecOnNeedInputData callback function. + static void OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData) { (void)codec; VDecSignal *signal_ = static_cast(userData); @@ -88,8 +93,9 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe signal_->inBufferQueue_.push(data); signal_->inCond_.notify_all(); } - // Set the OnOutputBufferAvailable callback function, which is used to obtain the output frame information. - static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, + + // Implement the OH_AVCodecOnNewOutputData callback function. + static void OnNeedOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, void *userData) { (void)codec; @@ -102,7 +108,7 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe signal_->attrQueue_.push(*attr); signal_->outCond_.notify_all(); } - OH_AVCodecAsyncCallback cb = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable}; + OH_AVCodecAsyncCallback cb = {&OnError, &OnStreamChanged, &OnNeedInputData, &OnNeedOutputData}; // Set the asynchronous callbacks. int32_t ret = OH_VideoDecoder_SetCallback(videoDec, cb, signal_); ``` @@ -146,7 +152,7 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe ``` 5. (Optional) Configure the surface parameters of the decoder. This step is required only when the surface is used. - + ``` c++ OH_AVFormat *format = OH_AVFormat_Create(); // Configure the display rotation angle. @@ -179,7 +185,7 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe ``` c++ // Configure the buffer information. OH_AVCodecBufferAttr info; - // Call av_packet_alloc to initialize and return a container packet. + // Call av_packet_alloc of FFmpeg to initialize and return a container packet. AVPacket pkt = av_packet_alloc(); // Configure the input size, offset, and timestamp of the buffer. info.size = pkt->size; @@ -208,9 +214,9 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe ``` 9. (Optional) Call **OH_VideoDecoder_Flush()** to refresh the decoder. - + After **OH_VideoDecoder_Flush()** is called, the decoder remains in the running state, but the current queue is cleared and the buffer storing the decoded data is freed. - + To continue decoding, you must call **OH_VideoDecoder_Start()** again. ``` c++ @@ -225,7 +231,7 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe ``` 10. (Optional) Call **OH_VideoDecoder_Reset()** to reset the decoder. - + After **OH_VideoDecoder_Reset()** is called, the decoder returns to the initialized state. To continue decoding, you must call **OH_VideoDecoder_Configure()** and then **OH_VideoDecoder_Start()**. ``` c++ @@ -240,7 +246,7 @@ Read [VideoDecoder](../reference/native-apis/_video_decoder.md) for the API refe ``` 11. Call **OH_VideoDecoder_Stop()** to stop the decoder. - + ``` c++ int32_t ret; // Stop the decoder. diff --git a/en/application-dev/media/video-encoding.md b/en/application-dev/media/video-encoding.md index 410922d95b5e66cb2ceab1aee20640a13283d54b..fee759c06f0baaf2c57ee0a4068a68af2048e5ed 100644 --- a/en/application-dev/media/video-encoding.md +++ b/en/application-dev/media/video-encoding.md @@ -29,14 +29,19 @@ For details about the development procedure, see [Buffer Input](#buffer-input) a Read [VideoEncoder](../reference/native-apis/_video_encoder.md) for the API reference. +The figure below shows the call relationship of video encoding. + +![Call relationship of video encoding](figures/video-encode.png) + ### Buffer Input The following walks you through how to implement the entire video encoding process in buffer input mode. It uses the YUV file input and H.264 encoding format as an example. + Currently, the **VideoEncoder** module supports only data transferring in asynchronous mode. 1. Create an encoder instance. - You can create an encoder by name or Multipurpose Internet Mail Extensions (MIME) type. + You can create an encoder by name or MIME type. ``` c++ // To create an encoder by MIME type, call OH_VideoEncoder_CreateByMime. The system creates the most appropriate encoder based on the MIME type. @@ -58,15 +63,15 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch Register the **OH_AVCodecAsyncCallback** struct that defines the following callback function pointers: - - **OnError**, a callback used to report a codec operation error - - **OnStreamChanged**, a callback used to report a codec stream change, for example, audio channel change - - **OnNeedInputData**, a callback used to report input data required, which means that the encoder is ready for receiving PCM data - - **OnNeedOutputData**, a callback used to report output data generated, which means that encoding is complete + - **OH_AVCodecOnError**, a callback used to report a codec operation error + - **OH_AVCodecOnStreamChanged**, a callback used to report a codec stream change, for example, audio channel change + - **OH_AVCodecOnNeedInputData**, a callback used to report input data required, which means that the encoder is ready for receiving PCM data + - **OH_AVCodecOnNewOutputData**, a callback used to report output data generated, which means that encoding is complete You need to process the callback functions to ensure that the encoder runs properly. ``` c++ - // Set the OnError callback function. + // Implement the OH_AVCodecOnError callback function. static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) { (void)codec; @@ -74,7 +79,7 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch (void)userData; } - // Set the OnStreamChanged callback function. + // Implement the OH_AVCodecOnStreamChanged callback function. static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { (void)codec; @@ -82,7 +87,7 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch (void)userData; } - // Set the OnNeedInputData callback function, which is used to send an input frame to the data queue. + // Implement the OH_AVCodecOnNeedInputData callback function. static void OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *mem, void *userData) { (void)userData; @@ -92,8 +97,8 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch // 7. Write the stream to encode. // 8. Notify the encoder of EOS. } - - // Set the OnNeedOutputData callback function, which is used to send an encoded frame to the output queue. + + // Implement the OH_AVCodecOnNewOutputData callback function. static void OnNeedOutputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *mem, OH_AVCodecBufferAttr *attr, void *userData) { @@ -259,7 +264,7 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch 9. Call **OH_VideoEncoder_FreeOutputData()** to output the encoded frames. - In the code snippet below, the following parameters are used: + In the code snippet below, the following parameters are used: - **index**: index of the data queue, which is passed in by the callback function **OnNeedOutputData**. - **attr**: buffer storing the output data, which is passed in by the callback function **OnNeedOutputData**. - **mem**: parameter passed in by the callback function **OnNeedOutputData**. You can call **OH_AVMemory_GetAddr** to obtain the pointer to the shared memory address. @@ -424,9 +429,9 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch Currently, the following options must be configured for all supported formats: video frame width, video frame height, and video pixel format. In the code snippet below, the following data is used: - - **DEFAULT_WIDTH**: 320 pixels - - **DEFAULT_HEIGHT**: 240 pixels - - **DEFAULT_PIXELFORMAT**: **VideoPixelFormat::YUV420P** (the pixel format of the YUV file is YUV420P) + - **DEFAULT_WIDTH**: 320 pixels + - **DEFAULT_HEIGHT**: 240 pixels + - **DEFAULT_PIXELFORMAT**: **VideoPixelFormat::YUV420P** (the pixel format of the YUV file is YUV420P) ``` c++ // (Mandatory) Configure the video frame width. @@ -547,7 +552,7 @@ Currently, the **VideoEncoder** module supports only data transferring in asynch 10. Call **OH_VideoEncoder_FreeOutputData()** to output the encoded frames. - In the code snippet below, the following parameters are used: + In the code snippet below, the following parameters are used: - **index**: index of the data queue, which is passed in by the callback function **OnNeedOutputData**. - **attr**: buffer storing the output data, which is passed in by the callback function **OnNeedOutputData**. - **mem**: parameter passed in by the callback function **OnNeedOutputData**. You can call **OH_AVMemory_GetAddr** to obtain the pointer to the shared memory address. diff --git a/en/application-dev/napi/Readme-EN.md b/en/application-dev/napi/Readme-EN.md index 0a94576a94274b066059ff37b07816db1e964294..ad566d55f740fd7ada74fe22b95e0f1824271c2d 100644 --- a/en/application-dev/napi/Readme-EN.md +++ b/en/application-dev/napi/Readme-EN.md @@ -1,17 +1,11 @@ # Native APIs - [Using Native APIs in Application Projects](napi-guidelines.md) -- Graphics - - [Drawing Development](drawing-guidelines.md) - - [NativeBuffer Development](native-buffer-guidelines.md) - - [NativeImage Development](native-image-guidelines.md) - - [NativeVsync Development](native-vsync-guidelines.md) - - [Native Window Development](native-window-guidelines.md) -- Resource Management - - [Raw File Development](rawfile-guidelines.md) -- AI - - [Using MindSpore Lite for Model Inference](mindspore-lite-guidelines.md) - - [Using MindSpore Lite for Offline Model Conversion and Inference](mindspore-lite-offline-model-guidelines.md) - - [Connecting the Neural Network Runtime to an AI Inference Framework](neural-network-runtime-guidelines.md) -- Memory Development +- [Drawing Development](drawing-guidelines.md) +- [Raw File Development](rawfile-guidelines.md) +- [Native Window Development](native-window-guidelines.md) +- [Using MindSpore Lite for Model Inference](mindspore-lite-guidelines.md) +- [Using MindSpore Lite for Offline Model Conversion and Inference](mindspore-lite-offline-model-guidelines.md) +- [Connecting the Neural Network Runtime to an AI Inference Framework](neural-network-runtime-guidelines.md) - [Purgeable Memory Development](purgeable-memory-guidelines.md) +- [USB DDK Development](usb-ddk-guidelines.md) \ No newline at end of file diff --git a/en/application-dev/napi/usb-ddk-guidelines.md b/en/application-dev/napi/usb-ddk-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..708a82dc905ce78b9f3823269c2bef392d595046 --- /dev/null +++ b/en/application-dev/napi/usb-ddk-guidelines.md @@ -0,0 +1,79 @@ +# USB DDK Development + +## When to Use + +USB Driver Development Kit (USB DDK) is a tool kit provided by OpenHarmony to help you develop USB device drivers for your applications based on the user mode. It provides a series of device access APIs, which implement functions such as opening and closing USB interfaces, performing non-isochronous and isochronous data transfer, and implementing control transfer and interrupt transfer over USB pipes, etc. + +## Available APIs + +| Name| Description| +| -------- | -------- | +| OH_Usb_Init(void) | Initializes the DDK.| +| OH_Usb_Release(void) | Releases the DDK.| +| OH_Usb_GetDeviceDescriptor(uint64_t deviceId, struct UsbDeviceDescriptor *desc) | Obtains the device descriptor.| +| OH_Usb_GetConfigDescriptor(uint64_t deviceId, uint8_t configIndex, struct UsbDdkConfigDescriptor **const config) | Obtains the configuration descriptor. To avoid memory leakage, use **OH_Usb_FreeConfigDescriptor()** to release a descriptor after use.| +| OH_Usb_FreeConfigDescriptor(const struct UsbDdkConfigDescriptor *const config) | Releases a configuration descriptor. To avoid memory leakage, release a descriptor after use.| +| OH_Usb_ClaimInterface(uint64_t deviceId, uint8_t interfaceIndex, uint64_t *interfaceHandle) | Declares a USB interface.| +| OH_Usb_ReleaseInterface(uint64_t interfaceHandle) | Releases a USB interface.| +| OH_Usb_SendPipeRequest(const struct UsbRequestPipe *pipe, UsbDeviceMemMap *devMmap) | Sends a pipe request. This API works in a synchronous manner. It applies to interrupt transfer and bulk transfer.| +| OH_Usb_CreateDeviceMemMap(uint64_t deviceId, size_t size, UsbDeviceMemMap **devMmap) | Creates a buffer. To avoid memory leakage, use **OH_Usb_DestroyDeviceMemMap()** to destroy a buffer after use.| +| OH_Usb_DestroyDeviceMemMap(UsbDeviceMemMap *devMmap) | Destroys a buffer. To avoid resource leakage, destroy a buffer in time after use.| + +For details about the APIs, see [USB DDK](../reference/native-apis/_usb_ddk.md). + +## How to Develop + +To develop a USB driver using the USB DDK in OpenHarmony, perform the following steps: + +1. Obtain the device descriptor. Initialize the DDK by calling **OH_Usb_Init** of **usb_ddk_api.h**, and obtain the device descriptor by calling **OH_Usb_GetDeviceDescriptor**. + + ```c++ + // Initialize the USB DDK. + OH_Usb_Init(); + struct UsbDeviceDescriptor devDesc; + uint64_t deviceId = 0; + // Obtain the device descriptor. + OH_Usb_GetDeviceDescriptor(deviceId, &devDesc); + ``` + +2. Obtain the configuration descriptor, and declare the USB interface. Obtain the configuration descriptor **config** by calling **OH_Usb_GetConfigDescriptor** of **usb_ddk_api.h**, and declare the USB interface by calling **OH_Usb_ClaimInterface**. + + ```c++ + struct UsbDdkConfigDescriptor *config = nullptr; + // Obtain the configuration descriptor. + OH_Usb_GetConfigDescriptor(deviceId, 1, &config); + // Obtain the index of the target USB interface based on the configuration descriptor. + uint8_t interfaceIndex = 0; + // Declare the USB interface. + uint64_t interfaceHandle = 0; + OH_Usb_ClaimInterface(deviceId, interfaceIndex, &interfaceHandle); + // Release the configuration descriptor. + OH_Usb_FreeConfigDescriptor(config); + ``` + +3. Create a device memory map, and send a request. Create the device memory map **devMmap** by calling **OH_Usb_CreateDeviceMemMap** of **usb_ddk_api.h**, and send a request by calling **OH_Usb_SendPipeRequest**. + + ```c++ + struct UsbDeviceMemMap *devMmap = nullptr; + // Create a buffer for storing data. + size_t bufferLen = 10; + OH_Usb_CreateDeviceMemMap(deviceId, bufferLen, &devMmap); + struct UsbRequestPipe pipe; + pipe.interfaceHandle = interfaceHandle; + // Obtain the target endpoint based on the configuration descriptor. + pipe.endpoint = 128; + pipe.timeout = UINT32_MAX; + // Send a request. + OH_Usb_SendPipeRequest(&pipe, devMmap); + ``` + +4. Release resources. After all requests are processed and before the application exits, destroy the buffer by calling **OH_Usb_DestroyDeviceMemMap** of **usb_ddk_api.h** to release resources. Release the USB interface by calling **OH_Usb_ReleaseInterface**. Release the USB DDK by calling **OH_Usb_Release**. + + ```c++ + // Destroy the buffer. + OH_Usb_DestroyDeviceMemMap(devMmap); + // Release the USB interface. + OH_Usb_ReleaseInterface(interfaceHandle); + // Release the USB DDK. + OH_Usb_Release(); + ``` diff --git a/en/application-dev/performance/Readme.md b/en/application-dev/performance/Readme.md new file mode 100644 index 0000000000000000000000000000000000000000..8172a210af94348fdb22831295c90a19b74965e6 --- /dev/null +++ b/en/application-dev/performance/Readme.md @@ -0,0 +1,33 @@ +# 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. diff --git a/en/application-dev/performance/figure/application-cold-start.png b/en/application-dev/performance/figure/application-cold-start.png new file mode 100644 index 0000000000000000000000000000000000000000..210664879280518713b3ccb309875a059523319c Binary files /dev/null and b/en/application-dev/performance/figure/application-cold-start.png differ diff --git a/en/application-dev/performance/improve-application-startup-and-response/improve-application-cold-start-speed.md b/en/application-dev/performance/improve-application-startup-and-response/improve-application-cold-start-speed.md new file mode 100644 index 0000000000000000000000000000000000000000..b99c5dfc0ac84d75954b53b3a5c291bf60f4314e --- /dev/null +++ b/en/application-dev/performance/improve-application-startup-and-response/improve-application-cold-start-speed.md @@ -0,0 +1,110 @@ +# 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) + }) + } +} +``` diff --git a/en/application-dev/performance/improve-application-startup-and-response/improve-application-response.md b/en/application-dev/performance/improve-application-startup-and-response/improve-application-response.md new file mode 100644 index 0000000000000000000000000000000000000000..b740edb6f1f4e2df007631f6feef966762e0d2ba --- /dev/null +++ b/en/application-dev/performance/improve-application-startup-and-response/improve-application-response.md @@ -0,0 +1,325 @@ +# 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 **\** 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 containers are omitted here. Each container contains the preceding 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 containers are omitted here. Each container contains the preceding 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 **\** 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 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 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) + } + } +} +``` diff --git a/en/application-dev/performance/reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md b/en/application-dev/performance/reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md new file mode 100644 index 0000000000000000000000000000000000000000..a61c8649e392b7fd0055e63ab48e0fa335faab7f --- /dev/null +++ b/en/application-dev/performance/reduce-frame-loss-and-frame-freezing/reduce-animation-frame-loss.md @@ -0,0 +1,142 @@ +# 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