diff --git a/CODEOWNERS b/CODEOWNERS index b9dc401e4f05c544e1d852ee80637dc4c9ae38f7..827a13005c46f79db59e5379ddf23269b690f7c8 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -187,7 +187,8 @@ zh-cn/application-dev/dfx/errormanager-guidelines.md @RayShih zh-cn/application-dev/key-features/multi-device-app-dev/ @lingminghw zh-cn/application-dev/database/ @ge-yafang zh-cn/application-dev/napi/native-window-guidelines.md @ge-yafang -zh-cn/application-dev/napi/mindspore-lite-guidelines.md @ge-yafang +zh-cn/application-dev/napi/mindspore-lite-guidelines.md @ge-yafang +zh-cn/application-dev/napi/neural-network-runtime-guidelines.md @ge-yafang zh-cn/application-dev/napi/rawfile_guidelines.md @ningningW zh-cn/application-dev/background-agent-scheduled-reminder/ @RayShih zh-cn/application-dev/background-task-management/ @ningningW @@ -383,7 +384,7 @@ zh-cn/application-dev/reference/apis/js-apis-lightweightmap.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-lightweightset.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-linkedlist.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-list.md @ge-yafang -zh-cn/application-dev/reference/apis/js-apis-logs.md @HelloCrease +zh-cn/application-dev/reference/apis/js-apis-logs.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-media.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-medialibrary.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-mediaquery.md @HelloCrease @@ -454,7 +455,7 @@ zh-cn/application-dev/reference/apis/js-apis-system-vibrate.md @ningningW zh-cn/application-dev/reference/apis/js-apis-telephony-data.md @zengyawen zh-cn/application-dev/reference/apis/js-apis-testRunner.md @RayShih zh-cn/application-dev/reference/apis/js-apis-thermal.md @zengyawen -zh-cn/application-dev/reference/apis/js-apis-timer.md @HelloCrease +zh-cn/application-dev/reference/apis/js-apis-timer.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-touchevent.md @ningningW zh-cn/application-dev/reference/apis/js-apis-treemap.md @ge-yafang zh-cn/application-dev/reference/apis/js-apis-treeset.md @ge-yafang diff --git a/en/OpenHarmony-Overview.md b/en/OpenHarmony-Overview.md index b2c79a7aba52d99d5536ea9e01ea96839b4c2411..c7cf440d327d8bb03d14e3d8f076e8a1c3756d00 100644 --- a/en/OpenHarmony-Overview.md +++ b/en/OpenHarmony-Overview.md @@ -1,5 +1,9 @@ # OpenHarmony Project +> **NOTE** +> +> You are reading documents of OpenHarmony 3.2 Release. Obtain the [compatible SDK](../release-notes/OpenHarmony-v3.2-release.md#version-mapping) during your application development. + ## Introduction OpenHarmony is an open-source project incubated and operated by the OpenAtom Foundation. The purpose of this project is to build an open-source, distributed operating system (OS) framework for smart devices in all scenarios of a fully-connected world. @@ -189,7 +193,7 @@ For details about how to obtain the source code of OpenHarmony, see [Source Code ## How to Participate -For details about how to join in the OpenHarmony community, see [OpenHarmony Community](https://gitee.com/openharmony/community/blob/master/README-EN.md) +For details about how to join in the OpenHarmony community, see [OpenHarmony Community](https://gitee.com/openharmony/community/blob/master/README_EN.md) For details about how to contribute, see [How to contribute](contribute/how-to-contribute.md). diff --git a/en/application-dev/IDL/idl-guidelines.md b/en/application-dev/IDL/idl-guidelines.md index b770a7bb881b780ff7b231f04fe02bc18aa3026e..018d639341ef02753bca7cc03b2c471160da4ec8 100644 --- a/en/application-dev/IDL/idl-guidelines.md +++ b/en/application-dev/IDL/idl-guidelines.md @@ -3,7 +3,7 @@ ## IDL Overview To ensure successful communications between the client and server, interfaces recognized by both parties must be defined. The OpenHarmony Interface Definition Language (IDL) is a tool for defining such interfaces. OpenHarmony IDL decomposes objects to be transferred into primitives that can be understood by the operating system and encapsulates cross-boundary objects based on developers' requirements. - **Figure 1** IDL interface description +**Figure 1** IDL interface description ![IDL-interface-description](./figures/IDL-interface-description.png) @@ -156,9 +156,11 @@ On DevEco Studio, choose **Tools > SDK Manager** to view the local installation Go to the local installation path, choose **toolchains > 3.x.x.x** (the folder named after the version number), and check whether the executable file of IDL exists. -> **NOTE**: Use the SDK of the latest version. The use of an earlier version may cause errors in some statements. +> **NOTE** +> +> Use the SDK of the latest version. The use of an earlier version may cause errors in some statements. -If the executable file does not exist, download the SDK package from the mirror as instructed in the [Release Notes](../../release-notes). The following uses the [3.2 Beta5]((../../release-notes/OpenHarmony-v3.2-beta5.md#acquiring-source-code-from-mirrors) as an example. +If the executable file does not exist, download the SDK package from the mirror as instructed in the [Release Notes](../../release-notes). The following uses [3.2 Beta5](../../release-notes/OpenHarmony-v3.2-beta5.md#acquiring-source-code-from-mirrors) as an example. For details about how to replace the SDK package, see [Guide to Switching to Full SDK](../quick-start/full-sdk-switch-guide.md). @@ -176,6 +178,8 @@ You can use TS to create IDL files. interface OHOS.IIdlTestService { int TestIntTransaction([in] int data); void TestStringTransaction([in] String data); + void TestMapTransaction([in] Map data); + int TestArrayTransaction([in] String[] data); } ``` @@ -183,7 +187,9 @@ Run the **idl -gen-ts -d *dir* -c dir/IIdlTestService.idl** command in the folde -*dir* next to **d** is the target output folder. For example, if the target output folder is **IIdlTestServiceTs**, run the **idl -gen-ts -d IIdlTestServiceTs -c IIdlTestServiceTs/IIdlTestService.idl** command in the folder where the executable file is located. The interface file, stub file, and proxy file are generated in the *dir* directory (**IIdlTestServiceTs** directory in this example) in the execution environment. -> **NOTE**: The generated interface class file name must be the same as that of the .idl file. Otherwise, an error occurs during code generation. +> **NOTE** +> +> The generated interface class file name must be the same as that of the .idl file. Otherwise, an error occurs during code generation. For example, for an .idl file named **IIdlTestService.idl** and target output directory named **IIdlTestServiceTs**, the directory structure is similar to the following: @@ -203,6 +209,8 @@ The stub class generated by IDL is an abstract implementation of the interface c ```ts import {testIntTransactionCallback} from "./i_idl_test_service"; import {testStringTransactionCallback} from "./i_idl_test_service"; +import {testMapTransactionCallback} from "./i_idl_test_service"; +import {testArrayTransactionCallback} from "./i_idl_test_service"; import IIdlTestService from "./i_idl_test_service"; import rpc from "@ohos.rpc"; @@ -211,8 +219,8 @@ export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdl super(des); } - async onRemoteRequestEx(code: number, data, reply, option): Promise { - console.log("onRemoteRequestEx called, code = " + code); + async onRemoteMessageRequest(code: number, data, reply, option): Promise { + console.log("onRemoteMessageRequest called, code = " + code); switch(code) { case IdlTestServiceStub.COMMAND_TEST_INT_TRANSACTION: { let _data = data.readInt(); @@ -231,6 +239,29 @@ export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdl }); return true; } + case IdlTestServiceStub.COMMAND_TEST_MAP_TRANSACTION: { + let _data = new Map(); + let _dataSize = data.readInt(); + for (let i = 0; i < _dataSize; ++i) { + let key = data.readInt(); + let value = data.readInt(); + _data.set(key, value); + } + this.testMapTransaction(_data, (errCode) => { + reply.writeInt(errCode); + }); + return true; + } + case IdlTestServiceStub.COMMAND_TEST_ARRAY_TRANSACTION: { + let _data = data.readStringArray(); + this.testArrayTransaction(_data, (errCode, returnValue) => { + reply.writeInt(errCode); + if (errCode == 0) { + reply.writeInt(returnValue); + } + }); + return true; + } default: { console.log("invalid request code" + code); break; @@ -241,17 +272,23 @@ export default class IdlTestServiceStub extends rpc.RemoteObject implements IIdl testIntTransaction(data: number, callback: testIntTransactionCallback): void{} testStringTransaction(data: string, callback: testStringTransactionCallback): void{} + testMapTransaction(data: Map, callback: testMapTransactionCallback): void{} + testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void{} static readonly COMMAND_TEST_INT_TRANSACTION = 1; static readonly COMMAND_TEST_STRING_TRANSACTION = 2; + static readonly COMMAND_TEST_MAP_TRANSACTION = 3; + static readonly COMMAND_TEST_ARRAY_TRANSACTION = 4; } ``` -You need to inherit the interface class defined in the IDL file and implement the methods in the class. The following code snippet shows how to inherit the **IdlTestServiceStub** interface class and implement the **testIntTransaction** and **testStringTransaction** methods. +You need to inherit the interface class defined in the IDL file and implement the methods in the class. The following code snippet shows how to inherit the **IdlTestServiceStub** interface class and implement the **testIntTransaction**, **testStringTransaction**, **testMapTransaction**, and **testArrayTransaction** methods. ```ts import {testIntTransactionCallback} from "./i_idl_test_service" import {testStringTransactionCallback} from "./i_idl_test_service" +import {testMapTransactionCallback} from "./i_idl_test_service"; +import {testArrayTransactionCallback} from "./i_idl_test_service"; import IdlTestServiceStub from "./idl_test_service_stub" @@ -265,6 +302,14 @@ class IdlTestImp extends IdlTestServiceStub { { callback(0); } + testMapTransaction(data: Map, callback: testMapTransactionCallback): void + { + callback(0); + } + testArrayTransaction(data: string[], callback: testArrayTransactionCallback): void + { + callback(0, 1); + } } ``` @@ -320,11 +365,28 @@ function callbackTestStringTransaction(result: number): void { } } +function callbackTestMapTransaction(result: number): void { + if (result == 0) { + console.log('case 3 success'); + } +} + +function callbackTestArrayTransaction(result: number, ret: number): void { + if (result == 0 && ret == 124) { + console.log('case 4 success'); + } +} + var onAbilityConnectDone = { onConnect:function (elementName, proxy) { let testProxy = new IdlTestServiceProxy(proxy); + let testMap = new Map(); + testMap.set(1, 1); + testMap.set(1, 2); testProxy.testIntTransaction(123, callbackTestIntTransaction); testProxy.testStringTransaction('hello', callbackTestStringTransaction); + testProxy.testMapTransaction(testMap, callbackTestMapTransaction); + testProxy.testArrayTransaction(['1','2'], callbackTestMapTransaction); }, onDisconnect:function (elementName) { console.log('onDisconnectService onDisconnect'); diff --git a/en/application-dev/Readme-EN.md b/en/application-dev/Readme-EN.md index b2f52c8da3608cfa7b61d5f309ea0a775c9c6980..95504e593a1ae40a3f8853ca28f3067c71729315 100644 --- a/en/application-dev/Readme-EN.md +++ b/en/application-dev/Readme-EN.md @@ -17,7 +17,6 @@ - Application Package Structure - [Application Package Structure in Stage Model)](quick-start/application-package-structure-stage.md) - [Application Package Structure in FA Model](quick-start/application-package-structure-fa.md) - - [HAR File Structure](quick-start/har-structure.md) - Multi-HAP Mechanism - [Multi-HAP Design Objectives](quick-start/multi-hap-objective.md) - [Multi-HAP Build View](quick-start/multi-hap-build-view.md) @@ -25,6 +24,11 @@ - [Multi-HAP Usage Rules](quick-start/multi-hap-rules.md) - [Multi-HAP Operation Mechanism and Data Communication Modes](quick-start/multi-hap-principles.md) - [Application Installation and Uninstallation Process](quick-start/application-package-install-uninstall.md) + - Shared Package + - [Shared Package Overview](quick-start/shared-guide.md) + - [HAR](quick-start/har-package.md) + - HSP + - [In-Application HSP Development](quick-start/in-app-hsp.md) - Application Configuration Files in Stage Model - [Application Configuration File Overview (Stage Model)](quick-start/application-configuration-file-overview-stage.md) - [app.json5 Configuration File](quick-start/app-configuration-file.md) @@ -37,18 +41,46 @@ - [Resource Categories and Access](quick-start/resource-categories-and-access.md) - Learning ArkTS - [Getting Started with ArkTS](quick-start/arkts-get-started.md) - - ArkTS Syntax (Declarative UI) - - [Basic UI Description](quick-start/arkts-basic-ui-description.md) - - State Management - - [Basic Concepts](quick-start/arkts-state-mgmt-concepts.md) - - [State Management with Page-level Variables](quick-start/arkts-state-mgmt-page-level.md) - - [State Management with Application-level Variables](quick-start/arkts-state-mgmt-application-level.md) - - [Dynamic UI Element Building](quick-start/arkts-dynamic-ui-elememt-building.md) - - [Rendering Control](quick-start/arkts-rendering-control.md) - - [Restrictions and Extensions](quick-start/arkts-restrictions-and-extensions.md) + - Basic Syntax + - [Basic Syntax Overview](quick-start/arkts-basic-syntax-overview.md) + - [Declarative UI Description](quick-start/arkts-declarative-ui-description.md) + - Custom Component + - [Creating a Custom Component](quick-start/arkts-create-custom-components.md) + - [Page and Custom Component Lifecycle](quick-start/arkts-page-custom-components-lifecycle.md) + - [\@Builder: Custom Builder Function](quick-start/arkts-builder.md) + - [\@BuilderParam: @Builder Function Reference](quick-start/arkts-builderparam.md) + - [\@Styles: Definition of Resusable Styles](quick-start/arkts-style.md) + - [\@Extend: Extension of Built-in Components](quick-start/arkts-extend.md) + - [stateStyles: Polymorphic Style](quick-start/arkts-statestyles.md) + - State Management + - [State Management Overview](quick-start/arkts-state-management-overview.md) + - Component State Management + - [\@State: State Owned by Component](quick-start/arkts-state.md) + - [\@Prop: One-Way Synchronization from Parent to Child Components](quick-start/arkts-prop.md) + - [\@Link: Two-Way Synchronization Between Parent and Child Components](quick-start/arkts-link.md) + - [\@Provide and \@Consume: Two-Way Synchronization with Descendant Components](quick-start/arkts-provide-and-consume.md) + - [\@Observed and \@ObjectLink: Observing Attribute Changes in Nested Class Objects](quick-start/arkts-observed-and-objectlink.md) + - Application State Management + - [Application State Management Overview](quick-start/arkts-application-state-management-overview.md) + - [LocalStorage: UI State Storage](quick-start/arkts-localstorage.md) + - [AppStorage: Application-wide UI State Storage](quick-start/arkts-appstorage.md) + - [PersistentStorage: Application State Persistence](quick-start/arkts-persiststorage.md) + - [Environment: Device Environment Query](quick-start/arkts-environment.md) + - Other State Management Features + - [Overview of Other State Management Features](quick-start/arkts-other-state-mgmt-functions-overview.md) + - [\@Watch: Getting Notified of State Variable Changes](quick-start/arkts-watch.md) + - [$$ Syntax: Two-Way Synchronization of Built-in Components](quick-start/arkts-two-way-sync.md) + - Rendering Control + - [Rendering Control Overview](quick-start/arkts-rendering-control-overview.md) + - [if/else: Conditional Rendering](quick-start/arkts-rendering-control-ifelse.md) + - [ForEach: Rendering of Repeated Content](quick-start/arkts-rendering-control-foreach.md) + - [LazyForEach: Lazy Data Loading](quick-start/arkts-rendering-control-lazyforeach.md) + + - Development - [Application Models](application-models/Readme-EN.md) - [UI Development](ui/Readme-EN.md) + - [Web](web/Readme-EN.md) - [Notification](notification/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md) - [WebGL](webgl/Readme-EN.md) @@ -82,6 +114,7 @@ - [ArkTS and JS APIs](reference/apis/Readme-EN.md) - [Error Codes](reference/errorcodes/Readme-EN.md) - Native APIs + - [Native APIs](reference/native-apis/Readme-EN.md) - [Standard Libraries](reference/native-lib/third_party_libc/musl.md) - [Node_API](reference/native-lib/third_party_napi/napi.md) - [FAQs](faqs/Readme-EN.md) diff --git a/en/application-dev/ability-deprecated/fa-dataability.md b/en/application-dev/ability-deprecated/fa-dataability.md index 8d94e8f225a3966d676e6c7631968c25f5634531..cc3003eaffb772fdf0dd13c564c019430d016bf1 100644 --- a/en/application-dev/ability-deprecated/fa-dataability.md +++ b/en/application-dev/ability-deprecated/fa-dataability.md @@ -116,7 +116,7 @@ Example URIs: | "name" | Ability name, corresponding to the **Data** class name derived from **Ability**. | | "type" | Ability type, which is **Data** for a Data ability. | | "uri" | URI used for communication. | - | "visible" | Whether the Data ability is visible to other applications. When this parameter is set to **true**, the Data ability can communicate with other applications.| + | "exported" | Whether the Data ability is visible to other applications. When this parameter is set to **true**, the Data ability can communicate with other applications.| **config.json configuration example** @@ -128,7 +128,7 @@ Example URIs: "srcLanguage": "ets", "description": "$string:description_dataability", "type": "data", - "visible": true, + "exported": true, "uri": "dataability://ohos.samples.etsdataability.DataAbility" }] ``` @@ -154,7 +154,7 @@ The basic dependency packages include: import featureAbility from '@ohos.ability.featureAbility' import ohos_data_ability from '@ohos.data.dataAbility' import ohos_data_rdb from '@ohos.data.rdb' - + var urivar = "dataability:///com.ix.DataAbility" var DAHelper = featureAbility.acquireDataAbilityHelper( urivar diff --git a/en/application-dev/ability-deprecated/fa-formability.md b/en/application-dev/ability-deprecated/fa-formability.md index 7d64f9cb95a26ebb3a1767b7fbde5fe2943ba691..4302e8d83c4b2cc6585de29b6bdaa3504c671527 100644 --- a/en/application-dev/ability-deprecated/fa-formability.md +++ b/en/application-dev/ability-deprecated/fa-formability.md @@ -25,7 +25,7 @@ Carry out the following operations to develop the widget provider based on the [ 1. Implement lifecycle callbacks by using the **LifecycleForm** APIs. 2. Create a **FormBindingData** instance. 3. Update a widget by using the **FormProvider** APIs. -4. Develop the widget UI pages. +4. Develop the widget UI page. ## Available APIs @@ -231,7 +231,7 @@ You should override **onDestroy** to implement widget data deletion. } ``` -For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). +For details about how to implement persistent data storage, see [Data Persistence by User Preferences](../database/data-persistence-by-preferences.md). The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. @@ -355,7 +355,7 @@ You can set router and message events for components on a widget. The router eve 1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file. 2. Set the router event. - **action**: **"router"**, which indicates a router event. - - **abilityName**: target ability name, for example, **com.example.entry.MainAbility**, which is the default UIAbility name in DevEco Studio for the FA model. + - **abilityName**: target ability name, for example, **com.example.entry.MainAbility**, which is the default main ability name in DevEco Studio for the FA model. - **params**: custom parameters of the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the main ability in the FA model, **featureAbility.getWant()** can be used to obtain **want** and its **parameters** field. 3. Set the message event. - **action**: **"message"**, which indicates a message event. diff --git a/en/application-dev/ability-deprecated/fa-pageability.md b/en/application-dev/ability-deprecated/fa-pageability.md index 2f4741b80ef771c9b478d32a7713b597fb65c2d4..4af75cc8448516f338d1da31d6a5ec8c68f97b11 100644 --- a/en/application-dev/ability-deprecated/fa-pageability.md +++ b/en/application-dev/ability-deprecated/fa-pageability.md @@ -47,7 +47,7 @@ You can specify the launch type by setting **launchType** in the **config.json** | Launch Type | Description |Description | | ----------- | ------- |---------------- | -| standard | Multi-instance | A new instance is started each time an ability starts.| +| standard | Multi-instance | A new instance is started each time an ability starts.| | singleton | Singleton | The ability has only one instance in the system. If an instance already exists when an ability is started, that instance is reused.| By default, **singleton** is used. diff --git a/en/application-dev/ability-deprecated/stage-ability-continuation.md b/en/application-dev/ability-deprecated/stage-ability-continuation.md index 6bd39284047003d17302beb6420ffe48d056183c..854362d6731bba4edadfbf667d3186013c4b9033 100644 --- a/en/application-dev/ability-deprecated/stage-ability-continuation.md +++ b/en/application-dev/ability-deprecated/stage-ability-continuation.md @@ -48,96 +48,89 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar } ``` - - - + - Configure the application startup type. - - If **launchType** is set to **standard** in the **module.json5** file, the application is of the multi-instance launch type. During ability continuation, regardless of whether the application is already open, the target starts the application and restores the UI page. If **launchType** is set to **singleton**, the application is of the singleton launch type. If the application is already open, the target clears the existing page stack and restores the UI page. For more information, see "Launch Type" in [Ability Development](./stage-ability.md). + + If **launchType** is set to **multiton** in the **module.json5** file, the application is of the multi-instance launch type. During ability continuation, regardless of whether the application is already open, the target starts the application and restores the UI page. If **launchType** is set to **singleton**, the application is of the singleton launch type. If the application is already open, the target clears the existing page stack and restores the UI page. For more information, see "Launch Type" in [Ability Development](./stage-ability.md). + + Configure a multi-instance application as follows: + + ```javascript + { + "module": { + "abilities": [ + { + "launchType": "multiton" + } + ] + } + } + ``` + + Configure a singleton application as follows or retain the default settings of **launchType**: + + ```javascript + { + "module": { + "abilities": [ + { + "launchType": "singleton" + } + ] + } + } + ``` + + - Apply for the distributed permissions. - Configure a multi-instance application as follows: + Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application. - ```javascript - { - "module": { - "abilities": [ - { - "launchType": "standard" - } - ] - } - } - ``` + ```javascript + "requestPermissions": [ + { + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }, + ``` - Configure a singleton application as follows or retain the default settings of **launchType**: + This permission must be granted by the user in a dialog box when the application is started for the first time. To enable the application to display a dialog box to ask for the permission, add the following code to **onWindowStageCreate** of the **Ability** class: - ```javascript - { - "module": { - "abilities": [ - { - "launchType": "singleton" + ```javascript + requestPermissions = async () => { + let permissions: Array = [ + "ohos.permission.DISTRIBUTED_DATASYNC" + ]; + let needGrantPermission = false + let accessManger = accessControl.createAtManager() + Logger.info("app permission get bundle info") + let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100) + Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`) + for (const permission of permissions) { + Logger.info(`app permission query grant status ${permission}`) + try { + let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission) + if (grantStatus === PERMISSION_REJECT) { + needGrantPermission = true + break; + } + } catch (err) { + Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`) + needGrantPermission = true + break; + } + } + if (needGrantPermission) { + Logger.info("app permission needGrantPermission") + try { + await accessManger.requestPermissionsFromUser(this.context, permissions) + } catch (err) { + Logger.error(`app permission ${JSON.stringify(err)}`) + } + } else { + Logger.info("app permission already granted") + } } - ] - } - } - ``` - - - - - Apply for the distributed permissions. - - Declare the **DISTRIBUTED_DATASYNC** permission in the **module.json5** file for the application. - - ```javascript - "requestPermissions": [ - { - "name": "ohos.permission.DISTRIBUTED_DATASYNC" - }, - ``` - - - - This permission must be granted by the user in a dialog box when the application is started for the first time. To enable the application to display a dialog box to ask for the permission, add the following code to **onWindowStageCreate** of the **Ability** class: - - ```javascript - requestPermissions = async () => { - let permissions: Array = [ - "ohos.permission.DISTRIBUTED_DATASYNC" - ]; - let needGrantPermission = false - let accessManger = accessControl.createAtManager() - Logger.info("app permission get bundle info") - let bundleInfo = await bundle.getApplicationInfo(BUNDLE_NAME, 0, 100) - Logger.info(`app permission query permission ${bundleInfo.accessTokenId.toString()}`) - for (const permission of permissions) { - Logger.info(`app permission query grant status ${permission}`) - try { - let grantStatus = await accessManger.verifyAccessToken(bundleInfo.accessTokenId, permission) - if (grantStatus === PERMISSION_REJECT) { - needGrantPermission = true - break; - } - } catch (err) { - Logger.error(`app permission query grant status error ${permission} ${JSON.stringify(err)}`) - needGrantPermission = true - break; - } - } - if (needGrantPermission) { - Logger.info("app permission needGrantPermission") - try { - await accessManger.requestPermissionsFromUser(this.context, permissions) - } catch (err) { - Logger.error(`app permission ${JSON.stringify(err)}`) - } - } else { - Logger.info("app permission already granted") - } - } - ``` - - + ``` + 2. Implement the **onContinue()** API. @@ -155,7 +148,7 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar You can obtain the target device ID (identified by the key **targetDevice**) and the version number (identified by the key **version**) of the application installed on the target device from the **wantParam** parameter of this API. The version number can be used for compatibility check. If the current application version is incompatible with that on the target device, **OnContinueResult.MISMATCH** can be returned to reject the continuation request. - Example + Example: ```javascript onContinue(wantParam : {[key: string]: any}) { @@ -168,8 +161,6 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar } ``` - - 3. Implement the continuation logic in the **onCreate()** or **onNewWant()** API. The **onCreate()** API is called by the target. When the ability is started on the target device, this API is called to instruct the application to synchronize the memory data and UI component state, and triggers page restoration after the synchronization is complete. If the continuation logic is not implemented, the ability will be started in common startup mode and the page cannot be restored. @@ -178,11 +169,9 @@ The code snippets provided below are all from [Sample](https://gitee.com/openhar After data restore is complete, call **restoreWindowStage** to trigger page restoration. - - You can also use **want.parameters.version** in the **want** parameter to obtain the application version number of the initiator. - - Example + + Example: ```javascript import Ability from '@ohos.application.Ability'; @@ -211,9 +200,9 @@ For a singleton ability, use **onNewWant()** to achieve the same implementation. Use distributed objects. -Distributed objects allow cross-device data synchronization like local variables. For two devices that form a Super Device, when data in the distributed data object of an application is added, deleted, or modified on a device, the data for the same application is also updated on the other device. Both devices can listen for the data changes and online and offline states of the other. For details, see [Distributed Data Object Development](../database/database-distributedobject-guidelines.md). +Distributed objects allow cross-device data synchronization like local variables. For two devices that form a Super Device, when data in the distributed data object of an application is added, deleted, or modified on a device, the data for the same application is also updated on the other device. Both devices can listen for the data changes and online and offline states of the other. For details, see [Sharing Distributed Data Objects](../database/data-sync-of-distributed-data-object.md). -In the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device. +In the ability continuation scenario, the distributed data object is used to synchronize the memory data from the local device to the target device. For details, see [Distributed Data Object Development](../database/database-distributedobject-guidelines.md). - In **onContinue()**, the initiator saves the data to be migrated to the distributed object, calls the **save()** API to save the data and synchronize the data to the target device, sets the session ID, and sends the session ID to the target device through **wantParam**. @@ -249,8 +238,6 @@ In the ability continuation scenario, the distributed data object is used to syn }); ``` - - - The target device obtains the session ID from **onCreate()**, creates a distributed object, and associates the distributed object with the session ID. In this way, the distributed object can be synchronized. Before calling **restoreWindowStage**, ensure that all distributed objects required for continuation have been associated. ```javascript @@ -266,11 +253,11 @@ In the ability continuation scenario, the distributed data object is used to syn onCreate(want, launchParam) { Logger.info(`MainAbility onCreate ${AbilityConstant.LaunchReason.CONTINUATION}`) if (launchParam.launchReason == AbilityConstant.LaunchReason.CONTINUATION) { - // Obtain the session ID of the distributed data object from the want parameter. + // Obtain the session ID of the distributed data object from the want parameter. this.sessionId = want.parameters.session Logger.info(`onCreate for continuation sessionId: ${this.sessionId}`) - // Before fetching data from the remote device, reset g_object.data to undefined. + // Before fetching data from the remote device, reset g_object.data to undefined. g_object.data = undefined; // Set the session ID, so the target will fetch data from the remote device. g_object.setSessionId(this.sessionId); @@ -284,8 +271,6 @@ In the ability continuation scenario, the distributed data object is used to syn } ``` - - ### More Information 1. Timeout @@ -295,15 +280,11 @@ In the ability continuation scenario, the distributed data object is used to syn 2. By default, the system supports page stack information migration, which means that the page stack of the initiator will be automatically migrated to the target device. No adaptation is required. - - ### Restrictions 1. The continuation must be performed between the same ability, which means the same bundle name, module name, and ability name. For details, see [Application Package Structure Configuration File](../quick-start/module-configuration-file.md). 2. Currently, the application can only implement the continuation capability. The continuation action must be initiated by the system. - - ### Best Practice For better user experience, you are advised to use the **wantParam** parameter to transmit data smaller than 100 KB and use distributed objects to transmit data larger than 100 KB. diff --git a/en/application-dev/ability-deprecated/stage-ability.md b/en/application-dev/ability-deprecated/stage-ability.md index 7bfab2a10f9b3578bca47659e6b797ee85fd0d13..4ee2ac9dc8ef07ec4ff7020156e9536d6b5b980b 100644 --- a/en/application-dev/ability-deprecated/stage-ability.md +++ b/en/application-dev/ability-deprecated/stage-ability.md @@ -12,7 +12,7 @@ An ability can be launched in the **standard**, **singleton**, or **specified** | Launch Type | Description |Action | | ----------- | ------- |---------------- | -| standard | Standard mode | A new instance is started each time an ability starts.| +| multiton | Multi-instance mode| A new instance is started each time an ability starts.| | singleton | Singleton mode | The ability has only one instance in the system. If an instance already exists when an ability is started, that instance is reused.| | specified | Instance-specific| The internal service of an ability determines whether to create multiple instances during running.| @@ -71,7 +71,7 @@ To create Page abilities for an application in the stage model, you must impleme ```js import Ability from '@ohos.application.Ability' ``` -4. Implement the lifecycle callbacks of the **UIAbility** class. The default relative path generated by the APIs is **entry\src\main\ets\MainAbility\MainAbility.ts**. +4. Implement the lifecycle callbacks of the **UIAbility** class. The default relative path generated by the APIs is **entry\src\main\ets\entryability\EntryAbility.ts**. In the **onWindowStageCreate(windowStage)** API, use **loadContent** to set the application page to be loaded. For details about how to use the **Window** APIs, see [Window Development](../windowmanager/application-window-stage.md). ```ts @@ -79,29 +79,29 @@ To create Page abilities for an application in the stage model, you must impleme onCreate(want, launchParam) { console.log("MainAbility onCreate") } - + onDestroy() { console.log("MainAbility onDestroy") } - + onWindowStageCreate(windowStage) { console.log("MainAbility onWindowStageCreate") - + windowStage.loadContent("pages/index").then(() => { console.log("MainAbility load content succeed") }).catch((error) => { console.error("MainAbility load content failed with error: " + JSON.stringify(error)) }) } - + onWindowStageDestroy() { console.log("MainAbility onWindowStageDestroy") } - + onForeground() { console.log("MainAbility onForeground") } - + onBackground() { console.log("MainAbility onBackground") } @@ -114,6 +114,7 @@ The following example shows how an application obtains the bundle code directory ```ts import AbilityStage from "@ohos.application.AbilityStage" + export default class MyAbilityStage extends AbilityStage { onCreate() { console.log("MyAbilityStage onCreate") @@ -188,7 +189,7 @@ export default class MainAbility extends Ability { ``` ## Starting an Ability ### Available APIs -The **Ability** class has the **context** attribute, which belongs to the **AbilityContext** class. The **AbilityContext** class has the **abilityInfo**, **currentHapModuleInfo**, and other attributes as well as the APIs used for starting abilities. For details, see [AbilityContext](../reference/apis/js-apis-ability-context.md). +The **Ability** class has the **context** attribute, which belongs to the **AbilityContext** class. The **AbilityContext** class has the **abilityInfo**, **currentHapModuleInfo**, and other attributes as well as the APIs used for starting abilities. For details, see [AbilityContext](../reference/apis/js-apis-inner-application-applicationContext.md). **Table 3** AbilityContext APIs |API|Description| diff --git a/en/application-dev/ability-deprecated/stage-call.md b/en/application-dev/ability-deprecated/stage-call.md index 02528a741072ba20bc27111289c78318dc259feb..91388980580d8ca22188ead3e58288bf9da658af 100644 --- a/en/application-dev/ability-deprecated/stage-call.md +++ b/en/application-dev/ability-deprecated/stage-call.md @@ -47,242 +47,263 @@ The table below describes the ability call APIs. For details, see [Ability](../r ## How to Develop The procedure for developing the ability call is as follows: 1. Create a callee ability. - 2. Access the callee ability. ### Creating a Callee Ability For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. -**1. Configure the ability launch type.** - - Set **launchType** of the callee ability to **singleton** in the **module.json5** file. -|JSON Field|Description| -|:------|:------| -|"launchType"|Ability launch type. Set this parameter to **singleton**.| - -An example of the ability configuration is as follows: -```json -"abilities":[{ - "name": ".CalleeAbility", - "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", - "launchType": "singleton", - "description": "$string:CalleeAbility_desc", - "icon": "$media:icon", - "label": "$string:CalleeAbility_label", - "visible": true -}] -``` -**2. Import the Ability module.** -```ts -import Ability from '@ohos.app.ability.UIAbility' -``` -**3. Define the agreed sequenceable data.** - - The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. The code snippet is as follows: -```ts -export default class MySequenceable { - num: number = 0 - str: string = "" - - constructor(num, string) { - this.num = num - this.str = string - } - - marshalling(messageParcel) { - messageParcel.writeInt(this.num) - messageParcel.writeString(this.str) - return true - } - - unmarshalling(messageParcel) { - this.num = messageParcel.readInt() - this.str = messageParcel.readString() - return true - } -} -``` -**4. Implement Callee.on and Callee.off.** - - The time to register a listener for the callee ability depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The code snippet is as follows: -```ts -const TAG: string = '[CalleeAbility]' -const MSG_SEND_METHOD: string = 'CallSendMsg' - -function sendMsgCallback(data) { - console.log('CalleeSortFunc called') - - // Obtain the sequenceable data sent by the caller ability. - let receivedData = new MySequenceable(0, '') - data.readSequenceable(receivedData) - console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`) - - // Process the data. - // Return the sequenceable data result to the caller ability. - return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`) -} - -export default class CalleeAbility extends Ability { - onCreate(want, launchParam) { - try { - this.callee.on(MSG_SEND_METHOD, sendMsgCallback) - } catch (error) { - console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) - } - } - - onDestroy() { - try { - this.callee.off(MSG_SEND_METHOD) - } catch (error) { - console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) - } - } -} -``` +1. **Configure the ability launch type.** + + Set **launchType** of the callee ability to **singleton** in the **module.json5** file. + + |JSON Field|Description| + |:------|:------| + |"launchType"|Ability launch type. Set this parameter to **singleton**.| + + An example of the ability configuration is as follows: + + ```json + "abilities":[{ + "name": ".CalleeAbility", + "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts", + "launchType": "singleton", + "description": "$string:CalleeAbility_desc", + "icon": "$media:icon", + "label": "$string:CalleeAbility_label", + "exported": true + }] + ``` + +2. **Import the ability module.** + + ```ts + import Ability from '@ohos.app.ability.UIAbility'; + ``` + +3. **Define the agreed sequenceable data.** + + The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. The code snippet is as follows: + + ```ts + export default class MySequenceable { + num: number = 0 + str: string = "" + + constructor(num, string) { + this.num = num + this.str = string + } + + marshalling(messageParcel) { + messageParcel.writeInt(this.num) + messageParcel.writeString(this.str) + return true + } + + unmarshalling(messageParcel) { + this.num = messageParcel.readInt() + this.str = messageParcel.readString() + return true + } + } + ``` + +4. **Implement Callee.on and Callee.off.** + + The time to register a listener for the callee ability depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving sequenceable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The code snippet is as follows: + + ```ts + const TAG: string = '[CalleeAbility]' + const MSG_SEND_METHOD: string = 'CallSendMsg' + + function sendMsgCallback(data) { + console.log('CalleeSortFunc called') + + // Obtain the sequenceable data sent by the caller ability. + let receivedData = new MySequenceable(0, '') + data.readSequenceable(receivedData) + console.log(`receiveData[${receivedData.num}, ${receivedData.str}]`) + + // Process the data. + // Return the sequenceable data result to the caller ability. + return new MySequenceable(receivedData.num + 1, `send ${receivedData.str} succeed`) + } + + export default class CalleeAbility extends Ability { + onCreate(want, launchParam) { + try { + this.callee.on(MSG_SEND_METHOD, sendMsgCallback) + } catch (error) { + console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) + } + } + + onDestroy() { + try { + this.callee.off(MSG_SEND_METHOD) + } catch (error) { + console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) + } + } + } + ``` ### Accessing the Callee Ability -**1. Import the Ability module.** -```ts -import Ability from '@ohos.app.ability.UIAbility' -``` -**2. Obtain the Caller object.** - - The **context** attribute of the ability implements **startAbilityByCall** to obtain the **Caller** object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the **Caller** object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. The code snippet is as follows: -```ts -// Register the onRelease listener of the caller ability. -private regOnRelease(caller) { - try { - caller.on("release", (msg) => { - console.log(`caller onRelease is called ${msg}`) - }) - console.log('caller register OnRelease succeed') - } catch (error) { - console.log(`caller register OnRelease failed with ${error}`) - } -} - -async onButtonGetCaller() { - try { - this.caller = await context.startAbilityByCall({ - bundleName: 'com.samples.CallApplication', - abilityName: 'CalleeAbility' - }) - if (this.caller === undefined) { - console.log('get caller failed') - return - } - console.log('get caller success') - this.regOnRelease(this.caller) - } catch (error) { - console.log(`get caller failed with ${error}`) - } -} -``` - In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows: -```ts -async onButtonGetRemoteCaller() { - var caller = undefined - var context = this.context - - context.startAbilityByCall({ - deviceId: getRemoteDeviceId(), - bundleName: 'com.samples.CallApplication', - abilityName: 'CalleeAbility' - }).then((data) => { - if (data != null) { - caller = data - console.log('get remote caller success') - // Register the onRelease listener of the caller ability. - caller.on("release", (msg) => { - console.log(`remote caller onRelease is called ${msg}`) - }) - console.log('remote caller register OnRelease succeed') - } - }).catch((error) => { - console.error(`get remote caller failed with ${error}`) - }) -} -``` - Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The code snippet is as follows: -```ts -import deviceManager from '@ohos.distributedHardware.deviceManager'; -var dmClass; -function getRemoteDeviceId() { - if (typeof dmClass === 'object' && dmClass != null) { - var list = dmClass.getTrustedDeviceListSync() - if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { - console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null") - return - } - console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId) - return list[0].deviceId - } else { - console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null") - } -} -``` - In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows: -```ts -import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts'; - -requestPermission() { - let context = this.context - let permissions: Array = ['ohos.permission.DISTRIBUTED_DATASYNC'] - let atManager = abilityAccessCtrl.createAtManager(); - atManager.requestPermissionsFromUser(context, permissions).then((data) => { - console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) - }).catch((error) => { - console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) - }) -} -``` -**3. Send agreed sequenceable data.** - - The sequenceable data can be sent to the callee ability with or without a return value. The method and sequenceable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. The code snippet is as follows: -```ts -const MSG_SEND_METHOD: string = 'CallSendMsg' -async onButtonCall() { - try { - let msg = new MySequenceable(1, 'origin_Msg') - await this.caller.call(MSG_SEND_METHOD, msg) - } catch (error) { - console.log(`caller call failed with ${error}`) - } -} -``` - - In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. The code snippet is as follows: -```ts -const MSG_SEND_METHOD: string = 'CallSendMsg' -originMsg: string = '' -backMsg: string = '' -async onButtonCallWithResult(originMsg, backMsg) { - try { - let msg = new MySequenceable(1, originMsg) - const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) - console.log('caller callWithResult succeed') - - let result = new MySequenceable(0, '') - data.readSequenceable(result) - backMsg(result.str) - console.log(`caller result is [${result.num}, ${result.str}]`) - } catch (error) { - console.log(`caller callWithResult failed with ${error}`) - } -} -``` -**4. Release the Caller object.** - - When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows: -```ts -releaseCall() { - try { - this.caller.release() - this.caller = undefined - console.log('caller release succeed') - } catch (error) { - console.log(`caller release failed with ${error}`) - } -} -``` +1. **Import the Ability module.** + + ```ts + import Ability from '@ohos.app.ability.UIAbility'; + ``` + +2. **Obtain the Caller object.** + + The **context** attribute of the ability implements **startAbilityByCall** to obtain the **Caller** object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the **Caller** object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. The code snippet is as follows: + + ```ts + // Register the onRelease listener of the caller ability. + private regOnRelease(caller) { + try { + caller.on("release", (msg) => { + console.log(`caller onRelease is called ${msg}`) + }) + console.log('caller register OnRelease succeed') + } catch (error) { + console.log(`caller register OnRelease failed with ${error}`) + } + } + + async onButtonGetCaller() { + try { + this.caller = await context.startAbilityByCall({ + bundleName: 'com.samples.CallApplication', + abilityName: 'CalleeAbility' + }) + if (this.caller === undefined) { + console.log('get caller failed') + return + } + console.log('get caller success') + this.regOnRelease(this.caller) + } catch (error) { + console.log(`get caller failed with ${error}`) + } + } + ``` + + In the cross-device scenario, you need to specify the ID of the peer device. The code snippet is as follows: + + ```ts + async onButtonGetRemoteCaller() { + var caller = undefined + var context = this.context + + context.startAbilityByCall({ + deviceId: getRemoteDeviceId(), + bundleName: 'com.samples.CallApplication', + abilityName: 'CalleeAbility' + }).then((data) => { + if (data != null) { + caller = data + console.log('get remote caller success') + // Register the onRelease listener of the caller ability. + caller.on("release", (msg) => { + console.log(`remote caller onRelease is called ${msg}`) + }) + console.log('remote caller register OnRelease succeed') + } + }).catch((error) => { + console.error(`get remote caller failed with ${error}`) + }) + } + ``` + + Obtain the ID of the peer device from **DeviceManager**. Note that the **getTrustedDeviceListSync** API is open only to system applications. The code snippet is as follows: + + ```ts + import deviceManager from '@ohos.distributedHardware.deviceManager'; + var dmClass; + function getRemoteDeviceId() { + if (typeof dmClass === 'object' && dmClass != null) { + var list = dmClass.getTrustedDeviceListSync() + if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { + console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null") + return + } + console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId) + return list[0].deviceId + } else { + console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null") + } + } + ``` + + In the cross-device scenario, your application must also apply for the data synchronization permission from end users. The code snippet is as follows: + + ```ts + import abilityAccessCtrl from '@ohos.abilityAccessCtrl.d.ts'; + + requestPermission() { + let context = this.context + let permissions: Array = ['ohos.permission.DISTRIBUTED_DATASYNC'] + let atManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(context, permissions).then((data) => { + console.log("Succeed to request permission from user with data: "+ JSON.stringify(data)) + }).catch((error) => { + console.log("Failed to request permission from user with error: "+ JSON.stringify(error)) + }) + } + ``` + +3. **Send agreed sequenceable data.** + + The sequenceable data can be sent to the callee ability with or without a return value. The method and sequenceable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. The code snippet is as follows: + + ```ts + const MSG_SEND_METHOD: string = 'CallSendMsg' + async onButtonCall() { + try { + let msg = new MySequenceable(1, 'origin_Msg') + await this.caller.call(MSG_SEND_METHOD, msg) + } catch (error) { + console.log(`caller call failed with ${error}`) + } + } + ``` + + In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. The code snippet is as follows: + + ```ts + const MSG_SEND_METHOD: string = 'CallSendMsg' + originMsg: string = '' + backMsg: string = '' + async onButtonCallWithResult(originMsg, backMsg) { + try { + let msg = new MySequenceable(1, originMsg) + const data = await this.caller.callWithResult(MSG_SEND_METHOD, msg) + console.log('caller callWithResult succeed') + + let result = new MySequenceable(0, '') + data.readSequenceable(result) + backMsg(result.str) + console.log(`caller result is [${result.num}, ${result.str}]`) + } catch (error) { + console.log(`caller callWithResult failed with ${error}`) + } + } + ``` + +4. **Release the Caller object.** + + When the **Caller** object is no longer required, use **release()** to release it. The code snippet is as follows: + + ```ts + releaseCall() { + try { + this.caller.release() + this.caller = undefined + console.log('caller release succeed') + } catch (error) { + console.log(`caller release failed with ${error}`) + } + } + ``` \ No newline at end of file diff --git a/en/application-dev/ability-deprecated/stage-formextension.md b/en/application-dev/ability-deprecated/stage-formextension.md index 8c20e8e0e12d9504de6ee76547393180797922b0..d7abb35881e99620daa888b6e4a97bd8697b2521 100644 --- a/en/application-dev/ability-deprecated/stage-formextension.md +++ b/en/application-dev/ability-deprecated/stage-formextension.md @@ -135,7 +135,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks | Name | Description | Data Type | Default Value Allowed | | ----------- | ------------------------------------------------------------ | ---------- | -------------------- | | name | Name of the Extension ability. This field must be specified. | String | No | - | srcEntrance | Path of the Extension ability lifecycle code. This field must be specified.| String | No | + | srcEnty | Path of the Extension ability lifecycle code. This field must be specified.| String | No | | description | Description of the Extension ability. The value can be a string or a resource index to descriptions in multiple languages.| String | Yes (initial value: left empty)| | icon | Index of the Extension ability icon file. | String | Yes (initial value: left empty)| | label | Descriptive information about the Extension ability presented externally. The value can be a string or a resource index to the description.| String | Yes (initial value: left empty)| @@ -150,7 +150,7 @@ To create a widget in the stage model, you need to implement lifecycle callbacks ```json "extensionAbilities": [{ "name": "FormAbility", - "srcEntrance": "./ets/FormAbility/FormAbility.ts", + "srcEnty": "./ets/FormAbility/FormAbility.ts", "label": "$string:form_FormAbility_label", "description": "$string:form_FormAbility_desc", "type": "form", @@ -242,7 +242,7 @@ You should override **onDestroy** to implement widget data deletion. } ``` -For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). +For details about how to implement persistent data storage, see [Persisting Preferences Data](../database/data-persistence-by-preferences.md). The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. diff --git a/en/application-dev/ability-deprecated/stage-serviceextension.md b/en/application-dev/ability-deprecated/stage-serviceextension.md index a59b281341e062af3ecf76a000e0f7d40e99093b..9c59d949bab7c362f922cc4b52b024b5c411101b 100644 --- a/en/application-dev/ability-deprecated/stage-serviceextension.md +++ b/en/application-dev/ability-deprecated/stage-serviceextension.md @@ -33,8 +33,8 @@ OpenHarmony does not support creation of a Service Extension ability for third-p "icon": "$media:icon", "description": "service", "type": "service", - "visible": true, - "srcEntrance": "./ets/ServiceExtAbility/ServiceExtAbility.ts" + "exported": true, + "srcEnty": "./ets/ServiceExtAbility/ServiceExtAbility.ts" }] ``` @@ -44,7 +44,7 @@ OpenHarmony does not support creation of a Service Extension ability for third-p ```js import ServiceExtensionAbility from '@ohos.application.ServiceExtensionAbility' import rpc from '@ohos.rpc' - + class StubTest extends rpc.RemoteObject { constructor(des) { super(des); diff --git a/en/application-dev/application-dev-guide-for-gitee.md b/en/application-dev/application-dev-guide-for-gitee.md index 1d09d2b25137067cb62e5460145c9eb05eebb9c5..c582245c9c784a513fbc6db110059f728a76a2e2 100644 --- a/en/application-dev/application-dev-guide-for-gitee.md +++ b/en/application-dev/application-dev-guide-for-gitee.md @@ -24,6 +24,8 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O All applications should be developed on top of these frameworks. Then, equip yourself for developing the key features, with the following guidelines: + +- [Web](web/web-component-overview.md) - [Notification](notification/Readme-EN.md) - [Window Manager](windowmanager/Readme-EN.md) - [WebGL](webgl/Readme-EN.md) @@ -32,7 +34,8 @@ Then, equip yourself for developing the key features, with the following guideli - [Connectivity](connectivity/Readme-EN.md) - [Telephony Service](telephony/Readme-EN.md) - [Data Management](database/Readme-EN.md) -- [Background Task Management](task-management/Readme-EN.md) +- [File Management](file-management/Readme-EN.md) +- [Task Management](task-management/Readme-EN.md) - [Device Management](device/Readme-EN.md) - [Device Usage Statistics](device-usage-statistics/Readme-EN.md) - [DFX](dfx/Readme-EN.md) @@ -40,7 +43,6 @@ Then, equip yourself for developing the key features, with the following guideli - [Application Test](application-test/Readme-EN.md) - [IDL Specifications and User Guide](IDL/idl-guidelines.md) - [Using Native APIs in Application Projects](napi/Readme-EN.md) -- [File Management](file-management/medialibrary-overview.md) ### Tools @@ -61,11 +63,10 @@ They are organized as follows: - [Component Reference (JavaScript-based Web-like Development Paradigm)](reference/arkui-js/Readme-EN.md) - [JS Service Widget UI Components](reference/js-service-widget-ui/Readme-EN.md) - APIs - - [JS and TS APIs](reference/apis/Readme-EN.md) - - Native APIs - - [Standard Library](reference/native-lib/third_party_libc/musl.md) - - [Node_API](reference/native-lib/third_party_napi/napi.md) - + - [JS and TS APIs](reference/apis/Readme-EN.md) + - Native APIs + - [Standard Library](reference/native-lib/third_party_libc/musl.md) + - [Node_API](reference/native-lib/third_party_napi/napi.md) ### Readme diff --git a/en/application-dev/application-dev-guide.md b/en/application-dev/application-dev-guide.md index 1dfe9c7739177a1cb5bc83241170c613125216bd..ad34cdd099a470dd7731cc0c3c086125044261b7 100644 --- a/en/application-dev/application-dev-guide.md +++ b/en/application-dev/application-dev-guide.md @@ -1,10 +1,14 @@ # Application Development Overview +> **NOTE** +> +> You are reading documents of OpenHarmony 3.2 Release. Obtain the [compatible SDK](../release-notes/OpenHarmony-v3.2-release.md#version-mapping) during your application development. + The application development documents provide reference for you to develop applications using the APIs provided by OpenHarmony. They walk you through how to use JavaScript APIs to develop applications on the standard system. The documents are carefully organized as follows: -### Getting Started +## Getting Started [Here](quick-start/start-overview.md) you'll learn how to quickly get started with OpenHarmony application development. @@ -12,7 +16,7 @@ Browse the documents on the instructions for quickly building your first applica Check out the development fundamentals, which comprise descriptions of the package structure configuration file for OpenHarmony applications and the instructions for use of resource files. -### Development +## Development To facilitate your application development, we provide development guidelines for key features. @@ -24,48 +28,46 @@ First thing first, familiarize yourself with the two cornerstone frameworks in O 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) - [Notification](notification/notification-overview.md) - [Window Manager](windowmanager/window-overview.md) - [WebGL](webgl/webgl-overview.md) -- [Media](media/audio-overview.md) +- [Media](media/media-application-overview.md) - [Security](security/userauth-overview.md) - [Connectivity](connectivity/ipc-rpc-overview.md) - [Telephony Service](telephony/telephony-overview.md) -- [Data Management](database/database-mdds-overview.md) -- [Background Task Management](task-management/background-task-overview.md) -- [Device Management](device/usb-overview.md) +- [Data Management](database/data-mgmt-overview.md) +- [File Management](file-management/file-management-overview.md) +- [Task Management](task-management/background-task-overview.md) +- [Device](device/usb-overview.md) - [Device Usage Statistics](device-usage-statistics/device-usage-statistics-overview.md) - [DFX](dfx/hiappevent-guidelines.md) - [Internationalization](internationalization/international-overview.md) - [Application Test](application-test/arkxtest-guidelines.md) - [OpenHarmony IDL Specifications and User Guide](IDL/idl-guidelines.md) - [Using Native APIs in Application Projects](napi/napi-guidelines.md) -- [File Management](file-management/medialibrary-overview.md) -### Tools +## Tools DevEco Studio is a high-performance integrated development environment (IDE) recommended for developing OpenHarmony applications. [Here](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-deveco-studio-overview-0000001263280421) you can learn everything about DevEco Studio, including how to use this tool to create a project and sign, debug, and run an application. -### Hands-On Tutorials +## Hands-On Tutorials To make you better understand how functions work together and jumpstart your application development projects, we provide stripped-down, real-world [samples](https://gitee.com/openharmony/applications_app_samples/blob/OpenHarmony-3.2-Release/README.md) and [codelabs](https://gitee.com/openharmony/codelabs). -### API References +## API References API references encompass all components and APIs available in OpenHarmony, helping you use and integrate APIs more effectively. They are organized as follows: -- [Component Reference (TypeScript-based Declarative Development Paradigm)](reference/arkui-ts/Readme-EN.md) - -- [Component Reference (JavaScript-based Web-like Development Paradigm)](reference/arkui-js/Readme-EN.md) - -- [JS Service Widget UI Components](reference/js-service-widget-ui/Readme-EN.md) - -- [JS and TS APIs](reference/apis/js-apis-ability-dataUriUtils.md) - +- [Component Reference (TypeScript-based Declarative Development Paradigm)](reference/arkui-ts/ts-components-summary.md) +- [Component Reference (JavaScript-compatible Web-like Development Paradigm- ArkUI.Full)](reference/arkui-js/Readme-EN.md) +- [Component Reference (JavaScript-compatible Web-like Development Paradigm- ArkUI.Lite)](reference/arkui-js-lite/Readme-EN.md) +- [JS Service Widget UI Components](reference/js-service-widget-ui/js-service-widget-file.md) +- [JS and TS APIs](reference/apis/development-intro.md) - Native APIs - [Standard Library](reference/native-lib/third_party_libc/musl.md) - - [Node_API](reference/native-lib/third_party_napi/napi.md) - + - [Node_API](reference/native-lib/third_party_napi/napi.md) diff --git a/en/application-dev/application-models/Readme-EN.md b/en/application-dev/application-models/Readme-EN.md index d2bca637ca2d62cc7a6ea5ca64a9f55cdf5793f0..270c50cd7be93f369e8b75512d5b99b36ae9545a 100644 --- a/en/application-dev/application-models/Readme-EN.md +++ b/en/application-dev/application-models/Readme-EN.md @@ -17,19 +17,43 @@ - ExtensionAbility Component - [ExtensionAbility Component Overview](extensionability-overview.md) - [ServiceExtensionAbility](serviceextensionability.md) - - [DataShareExtensionAbility (for System Applications Only)](datashareextensionability.md) - - [FormExtensionAbility (Widget)](widget-development-stage.md) - [AccessibilityExtensionAbility](accessibilityextensionability.md) - [EnterpriseAdminExtensionAbility](enterprise-extensionAbility.md) - [InputMethodExtensionAbility](inputmethodextentionability.md) - - [WindowExtensionAbility](windowextensionability.md) + - [WindowExtensionAbility (for System Applications Only)](windowextensionability.md) + - Service Widget Development in Stage Model + - [Service Widget Overview](service-widget-overview.md) + - Developing an ArkTS Widget + - [ArkTS Widget Working Principles](arkts-ui-widget-working-principles.md) + - [ArkTS Widget Related Modules](arkts-ui-widget-modules.md) + - ArkTS Widget Development + - [Creating an ArkTS Widget](arkts-ui-widget-creation.md) + - [Configuring Widget Configuration Files](arkts-ui-widget-configuration.md) + - [Widget Lifecycle Management](arkts-ui-widget-lifecycle.md) + - Widget Page Development + - [Widget Page Capability Overview](arkts-ui-widget-page-overview.md) + - [Using Animations in the Widget](arkts-ui-widget-page-animation.md) + - [Applying Custom Drawing in the Widget](arkts-ui-widget-page-custom-drawing.md) + - Widget Event Development + - [Widget Event Capability Overview](arkts-ui-widget-event-overview.md) + - [Redirecting to a UIAbility Through the router Event](arkts-ui-widget-event-router.md) + - [Launching a UIAbility in the Background Through the call Event](arkts-ui-widget-event-call.md) + - [Updating Widget Content Through the message Event](arkts-ui-widget-event-formextensionability.md) + - [Updating Widget Content Through the router or call Event](arkts-ui-widget-event-uiability.md) + - Widget Data Interaction + - [Widget Data Interaction Overview](arkts-ui-widget-interaction-overview.md) + - [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md) + - [Updating Local and Online Images in the Widget](arkts-ui-widget-image-update.md) + - [Updating Widget Content by State](arkts-ui-widget-update-by-status.md) + - [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md) + - [Developing a JS Widget](js-ui-widget-development.md) - [AbilityStage Component Container](abilitystage.md) - [Context](application-context-stage.md) - Want - [Want Overview](want-overview.md) - [Matching Rules of Explicit Want and Implicit Want](explicit-implicit-want-mappings.md) - [Common action and entities Values](actions-entities.md) - - [Using Explicit Want to Start an Ability](ability-startup-with-explicit-want.md) + - [Using Explicit Want to Start an Application Component](ability-startup-with-explicit-want.md) - [Using Implicit Want to Open a Website](ability-startup-with-implicit-want.md) - [Using Want to Share Data Between Applications](data-share-via-want.md) - [Component Startup Rules](component-startup-rules.md) @@ -37,8 +61,9 @@ - [Continuation Overview](inter-device-interaction-hop-overview.md) - [Cross-Device Migration (for System Applications Only)](hop-cross-device-migration.md) - [Multi-device Collaboration (for System Applications Only)](hop-multi-device-collaboration.md) - - IPC - - [Process Model](process-model-stage.md) + - [Subscribing to System Environment Variable Changes](subscribe-system-environment-variable-changes.md) + - Process Model + - [Process Model Overview](process-model-stage.md) - Common Events - [Introduction to Common Events](common-event-overview.md) - Common Event Subscription @@ -48,14 +73,16 @@ - [Unsubscribing from Common Events](common-event-unsubscription.md) - [Publishing Common Events](common-event-publish.md) - [Background Services](background-services.md) - - Inter-Thread Communication - - [Thread Model](thread-model-stage.md) + - Thread Model + - [Thread Model Overview](thread-model-stage.md) - [Using Emitter for Inter-Thread Communication](itc-with-emitter.md) - [Using Worker for Inter-Thread Communication](itc-with-worker.md) - Mission Management - [Mission Management Scenarios](mission-management-overview.md) - - [Mission Management and Launch Type](mission-management-launch-type.md) + - [Mission and Launch Type](mission-management-launch-type.md) - [Page Stack and MissionList](page-mission-stack.md) + - [Setting the Icon and Name of a Mission Snapshot](mission-set-icon-name-for-task-snapshot.md) + - [Application Configuration File](config-file-stage.md) - FA Model Development - [FA Model Development Overview](fa-model-development-overview.md) - FA Mode Application Components @@ -92,12 +119,12 @@ - [Context](application-context-fa.md) - [Want](want-fa.md) - [Component Startup Rules](component-startup-rules-fa.md) - - IPC - - [Process Model](process-model-fa.md) + - Process Model + - [Process Model Overview](process-model-fa.md) - [Common Events](common-event-fa.md) - [Background Services](rpc.md) - - Inter-Thread Communication - - [Thread Model](thread-model-fa.md) + - Thread Model + - [Thread Model Overview](thread-model-fa.md) - [Inter-Thread Communication](itc-fa-overview.md) - [Mission Management](mission-management-fa.md) - Development of Component Interaction Between the FA Model and Stage Model diff --git a/en/application-dev/application-models/ability-startup-with-explicit-want.md b/en/application-dev/application-models/ability-startup-with-explicit-want.md index cb4642d108382c48a2e2b2285adfafc1df99d62b..36d41c555bd8d4a5cd0d4d0b7bd291bb2569cee3 100644 --- a/en/application-dev/application-models/ability-startup-with-explicit-want.md +++ b/en/application-dev/application-models/ability-startup-with-explicit-want.md @@ -1,73 +1,7 @@ -# Using Explicit Want to Start an Ability +# Using Explicit Want to Start an Application Component +When a user touches a button in an application, the application often needs to start a UIAbility component to complete a specific task. If the **abilityName** and **bundleName** parameters are specified when starting a UIAbility, then the explicit Want is used. -When a user touches a button in an application, the application often needs to start a UIAbility component to complete a specific task. The following describes how to use explicit Want to start a UIAbility component in an application. +## Using Explicit Want - -## How to Develop - -1. In a project of the stage model, create an ability and a page, named **callerAbility** and **Index.ets**, respectively. Use the **windowStage.loadContent()** method in the **onWindowStageCreate** function in the **callerAbility.ts** file to bind the two. - - ```ts - // ... - // callerAbility.ts - onWindowStageCreate(windowStage) { - // Main window is created, set main page for this ability - console.info('[Demo] EntryAbility onWindowStageCreate') - // Bind callerAbility with a paged named Index - windowStage.loadContent('pages/Index') - } - // ... - ``` - -2. Repeat the preceding operation to create another ability named **calleeAbility**. - -3. Add a button to the **Index.ets** page of **callerAbility**. - - ```ts - // ... - build() { - Row() { - Column() { - Text('hello') - .fontSize(50) - .fontWeight(FontWeight.Bold) - // A new button with will call explicitStartAbility() when clicked. - Button("CLICKME") - .onClick(this.explicitStartAbility) // For details about explicitStartAbility, see the sample code below. - // ... - } - .width('100%') - } - .height('100%') - } - // ... - ``` - -4. Override the **onClick** method and use explicit Want to start **calleeAbility** in the method. The **bundleName** field can be obtained from the **AppScope > app.json5** file of the project. The **abilityName** field can be obtained from the **yourModuleName > src > main > module.json5** file of the corresponding module. - - ```ts - import common from '@ohos.app.ability.common'; - - // ... - async explicitStartAbility() { - try { - // Explicit want with abilityName specified. - let want = { - deviceId: "", - bundleName: "com.example.myapplication", - abilityName: "calleeAbility" - }; - let context = getContext(this) as common.UIAbilityContext; - await context.startAbility(want); - console.info(`explicit start ability succeed`); - } catch (error) { - console.info(`explicit start ability failed with ${error.code}`); - } - } - // ... - ``` - -5. When you touch **CLICKME**, the corresponding page is displayed. - - startAbilityWtExplicitWant +The user touches a button in the application to start the UIAbility component to complete a specific task. To start the UIAbility component in explicit Want mode, the **abilityName** and **bundleName** parameters must be specified. For details, see [Starting UIAbility in the Same Application](uiability-intra-device-interaction.md#starting-uiability-in-the-same-application). diff --git a/en/application-dev/application-models/ability-startup-with-implicit-want.md b/en/application-dev/application-models/ability-startup-with-implicit-want.md index 6550e5c628c642cf227cfde5f74eef7b61c8a52b..1f3732b00d6b0a9446d18b4c45019f0820f21be4 100644 --- a/en/application-dev/application-models/ability-startup-with-implicit-want.md +++ b/en/application-dev/application-models/ability-startup-with-implicit-want.md @@ -1,77 +1,77 @@ # Using Implicit Want to Open a Website - -## Prerequisites - -One or more browsers are installed on your device. - -The **module.json5** of a browser application is as follows: +This section uses the operation of using a browser to open a website as an example. It is assumed that one or more browser applications are installed on the device. To ensure that the browser application can work properly, configure the [module.json5 file](../quick-start/module-configuration-file.md) as follows: ```json -"skills": [ - { - "entities": [ - "entity.system.browsable" - // ... - ], - "actions": [ - "ohos.want.action.viewData" - // ... - ], - "uris": [ - { - "scheme": "https", - "host": "www.test.com", - "port": "8080", - // Prefix matching is used. - "pathStartWith": "query", - "type": "text/*" - }, +{ + "module": { + ... + "abilities": [ { - "scheme": "http", - // ... + ... + "skills": [ + { + "entities": [ + "entity.system.home", + "entity.system.browsable" + ... + ], + "actions": [ + "action.system.home", + "ohos.want.action.viewData" + ... + ], + "uris": [ + { + "scheme": "https", + "host": "www.test.com", + "port": "8080", + // Prefix matching is used. + "pathStartWith": "query" + }, + { + "scheme": "http", + ... + } + ... + ] + } + ] } - // ... ] - }, -] + } +} ``` +In the initiator UIAbility, use implicit Want to start the browser application. -## How to Develop +```ts +import common from '@ohos.app.ability.common'; -1. Use the custom function **implicitStartAbility** to start an ability. - - ```ts - async implicitStartAbility() { - try { - let want = { - // Uncomment the line below if you want to implicitly query data only in the specific bundle. - // bundleName: "com.example.myapplication", - "action": "ohos.want.action.viewData", - // entities can be omitted. - "entities": [ "entity.system.browsable" ], - "uri": "https://www.test.com:8080/query/student", - "type": "text/plain" - } - let context = getContext(this) as common.UIAbilityContext; - await context.startAbility(want) - console.info(`explicit start ability succeed`) - } catch (error) { - console.info(`explicit start ability failed with ${error.code}`) - } - } - ``` - - The matching process is as follows: - 1. If **action** in the passed **want** parameter is specified and is included in **actions** under **skills**, the matching is successful. - - 2. If **entities** in the passed **want** parameter is specified and is included in **entities** under **skills**, the matching is successful. +function implicitStartAbility() { + let context = getContext(this) as common.UIAbilityContext; + let wantInfo = { + // Uncomment the line below if you want to implicitly query data only in the specific bundle. + // bundleName: 'com.example.myapplication', + 'action': 'ohos.want.action.viewData', + // entities can be omitted. + 'entities': ['entity.system.browsable'], + 'uri': 'https://www.test.com:8080/query/student' + } + context.startAbility(wantInfo).then(() => { + ... + }).catch((err) => { + ... + }) +} +``` - 3. If **uri** in the passed **want** parameter is included in **uris** under **skills**, which is concatenated into `https://www.test.com:8080/query*` (where \* is a wildcard), the matching is successful. +The matching process is as follows: - 4. If **type** in the passed **want** parameter is specified and is included in **type** under **skills**, the matching is successful. +1. If **action** in the passed **want** parameter is specified and is included in **actions** under **skills** of the application component to match, the matching is successful. +2. If **entities** in the passed **want** parameter is specified and is included in **entities** under **skills** of the application component to match, the matching is successful. +3. If **uri** in the passed **want** parameter is included in **uris** under **skills** of the application component to match, which is concatenated into https://www.test.com:8080/query* (where * is a wildcard), the matching is successful. -2. When there are multiple matching applications, a dialog box is displayed for you to select one of them. +If there are multiple matching applications, the system displays a dialog box for you to select one of them. The following figure shows an example. - ![stage-want1](figures/stage-want1.png) +![](figures/ability-startup-with-implicit-want1.png) \ No newline at end of file diff --git a/en/application-dev/application-models/abilitystage.md b/en/application-dev/application-models/abilitystage.md index 4e0a273f850b4919d0964580ebed89c053c273f7..da764a445a6d1a79a601719f069798191641a986 100644 --- a/en/application-dev/application-models/abilitystage.md +++ b/en/application-dev/application-models/abilitystage.md @@ -12,7 +12,7 @@ AbilityStage is not automatically generated in the default project of DevEco Stu 1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **myabilitystage**. -2. In the **myabilitystage** directory, right-click and choose **New > ts File** to create a file named **MyAbilityStage.ts**. +2. In the **myabilitystage** directory, right-click and choose **New > TypeScript File** to create a file named **MyAbilityStage.ts**. 3. Open the **MyAbilityStage.ts** file, and import the dependency package of AbilityStage. Customize a class that inherits from AbilityStage, and add the required lifecycle callbacks. The following code snippet adds the **onCreate()** lifecycle callback. @@ -20,16 +20,27 @@ AbilityStage is not automatically generated in the default project of DevEco Stu import AbilityStage from '@ohos.app.ability.AbilityStage'; export default class MyAbilityStage extends AbilityStage { - onCreate() { - // When the HAP of the application is loaded for the first time, initialize the module. - } - onAcceptWant(want) { - // Triggered only for the ability with the specified launch type. - return "MyAbilityStage"; - } + onCreate() { + // When the HAP of the application is loaded for the first time, initialize the module. + } + onAcceptWant(want) { + // Triggered only for the UIAbility with the specified launch type. + return "MyAbilityStage"; + } + } + ``` + +4. In the [module.json5 file](../quick-start/module-configuration-file.md), set **srcEntry** to specify the code path of the module as the entry for loading the HAP. + ```json + { + "module": { + "name": "entry", + "type": "entry", + "srcEntry": "./ets/myabilitystage/MyAbilityStage.ts", + ... + } } ``` - [AbilityStage](../reference/apis/js-apis-app-ability-abilityStage.md) has the lifecycle callback [onCreate()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageoncreate) and the event callbacks [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant), [onConfigurationUpdated()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonconfigurationupdate), and [onMemoryLevel()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonmemorylevel). @@ -49,9 +60,8 @@ When an application is switched to the background, it is cached in the backgroun import AbilityStage from '@ohos.app.ability.AbilityStage'; export default class MyAbilityStage extends AbilityStage { - onMemoryLevel(level) { - // Release unnecessary memory based on the change of available system memory. - } + onMemoryLevel(level) { + // Release unnecessary memory based on the change of available system memory. + } } ``` - diff --git a/en/application-dev/application-models/accessibilityextensionability.md b/en/application-dev/application-models/accessibilityextensionability.md index 4c912d5e58a1b8083ba1037cccf449dd953d245c..62f0bd981504d1249e04dcf62c0041e8091496bf 100644 --- a/en/application-dev/application-models/accessibilityextensionability.md +++ b/en/application-dev/application-models/accessibilityextensionability.md @@ -1,4 +1,4 @@ -# AccessibilityExtensionAbility Development +# AccessibilityExtensionAbility The **AccessibilityExtensionAbility** module provides accessibility extension capabilities based on the **ExtensionAbility** framework. You can develop your accessibility applications by applying the **AccessibilityExtensionAbility** template to enhance usability. @@ -12,11 +12,28 @@ The **AccessibilityExtensionAbility** module provides accessibility extension ca This document is organized as follows: -- [Creating an AccessibilityExtAbility File](#creating-an-accessibility-extension-service) +- [AccessibilityExtensionAbility Overview](#accessibilityextensionability-overview) +- [Creating an Accessibility Extension Service](#creating-an-accessibility-extension-service) - [Processing an Accessibility Event](#processing-an-accessibility-event) - [Declaring Capabilities of Accessibility Extension Services](#declaring-capabilities-of-accessibility-extension-services) - [Enabling a Custom Accessibility Extension Service](#enabling-a-custom-accessibility-extension-service) +## AccessibilityExtensionAbility Overview + +Accessibility is about giving equal access to everyone so that they can access and use information equally and conveniently under any circumstances. It helps narrow the digital divide between people of different classes, regions, ages, and health status in terms of information understanding, information exchange, and information utilization, so that they can participate in social life more conveniently and enjoy the benefits of technological advances. + +AccessibilityExtensionAbility is an accessibility extension service framework. It allows you to develop your own extension services and provides a standard mechanism for exchanging information between applications and extension services. You can make use of the provided capabilities and APIs to develop accessibility features for users with disabilities or physical limitations. For example, you can develop a screen reader for users with vision impairments. + +Below shows the AccessibilityExtensionAbility framework. + +![AccessibilityFramework](figures/AccessibilityFramework.png) + +1. Accessibility app: extension service application developed based on the AccessibilityExtensionAbility framework, for example, a screen reader application. +2. Target app: application assisted by the accessibility app. +3. AccessibilityAbilityManagerService (AAMS): main service of the AccessibilityExtensionAbility framework, which is used to manage the lifecycle of accessibility apps and provide a bridge for information exchange between accessibility apps and target apps. +4. AccessibilityAbility (AAkit): ability that is used by the accessibility app to build an extension service ability operating environment and that provides interfaces for the accessibility app to query and operate the target app, including performing click/long press operations. +5. AccessibilitySystemAbilityClient (ASACkit): used by the target app to send accessibility events, such as content change events, to AAMS, and respond to the instructions (such as performing click/long press operations) sent by the accessibility app through AAMS. + ## Creating an Accessibility Extension Service You can create an accessibility extension service by creating a project from scratch or adding the service to an existing project. @@ -37,15 +54,15 @@ import AccessibilityExtensionAbility from '@ohos.application.AccessibilityExtens class AccessibilityExtAbility extends AccessibilityExtensionAbility { onConnect() { - console.log('AccessibilityExtAbility onConnect'); + console.info('AccessibilityExtAbility onConnect'); } onDisconnect() { - console.log('AccessibilityExtAbility onDisconnect'); + console.info('AccessibilityExtAbility onDisconnect'); } onAccessibilityEvent(accessibilityEvent) { - console.log('AccessibilityExtAbility onAccessibilityEvent: ' + JSON.stringify(accessibilityEvent)); + console.info('AccessibilityExtAbility onAccessibilityEvent: ' + JSON.stringify(accessibilityEvent)); } } @@ -66,9 +83,9 @@ You can process the service logic for accessibility events in the **onAccessibil ```typescript onAccessibilityEvent(accessibilityEvent) { - console.log('AccessibilityExtAbility onAccessibilityEvent: ' + JSON.stringify(accessibilityEvent)); + console.info('AccessibilityExtAbility onAccessibilityEvent: ' + JSON.stringify(accessibilityEvent)); if (accessibilityEvent.eventType === 'pageStateUpdate') { - console.log('AccessibilityExtAbility onAccessibilityEvent: pageStateUpdate'); + console.info('AccessibilityExtAbility onAccessibilityEvent: pageStateUpdate'); // TODO: Develop custom logic. } } @@ -79,13 +96,13 @@ You can also process physical key events in the accessibility extension service. ## Declaring Capabilities of Accessibility Extension Services -After developing the custom logic for an accessibility extension service, you must add the configuration information of the service to the corresponding module-level **module.json5** file in the project directory. In the file, the **srcEntrance** tag indicates the path to the accessibility extension service. Make sure the value of the **type** tag is fixed at **accessibility**. Otherwise, the connection to the service will fail. +After developing the custom logic for an accessibility extension service, you must add the configuration information of the service to the corresponding module-level **module.json5** file in the project directory. In the file, the **srcEntry** tag indicates the path to the accessibility extension service. Make sure the value of the **type** tag is fixed at **accessibility**. Otherwise, the connection to the service will fail. ```json "extensionAbilities": [ { "name": "AccessibilityExtAbility", - "srcEntrance": "./ets/AccessibilityExtAbility/AccessibilityExtAbility.ts", + "srcEntry": "./ets/AccessibilityExtAbility/AccessibilityExtAbility.ts", "label": "$string:MainAbility_label", "description": "$string:MainAbility_desc", "type": "accessibility", @@ -116,3 +133,4 @@ To enable or disable an accessibility extension service, run the following comma In the preceding commands, **AccessibilityExtAbility** indicates the name of the accessibility extension service, **com.example.demo** indicates the bundle name, and **rg** indicates the capabilities (**r** is short for retrieve). If the service is enabled or disabled successfully, the message "enable ability successfully" or "disable ability successfully" is displayed. + diff --git a/en/application-dev/application-models/actions-entities.md b/en/application-dev/application-models/actions-entities.md index 85dfb9523ca117e691480bcbd2321b5fb3b22304..38119a1b1fe16067f7e89629811327ad079a5d15 100644 --- a/en/application-dev/application-models/actions-entities.md +++ b/en/application-dev/application-models/actions-entities.md @@ -1,9 +1,8 @@ # Common action and entities Values -The [action](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction) field specifies the common operation (such as viewing, sharing, and application details) to be performed by the caller. In implicit Want, you can define this field and use it together with **uri** or **parameters** to specify the operation to be performed on the data, for example, viewing URI data. For example, if the URI is a website and the action is **ohos.want.action.viewData**, the ability that supports website viewing is matched. Declaring the **action** field in Want indicates that the invoked application should support the declared operation. The **actions** field under **skills** in the configuration file indicates the operations supported by the application. +The **action** field specifies the common operation (such as viewing, sharing, and application details) to be performed by the caller. In implicit [Want](../reference/apis/js-apis-app-ability-want.md), you can define this field and use it together with **uri** or **parameters** to specify the operation to be performed on the data, for example, viewing URI data. For example, if the URI is a website and the action is **ohos.want.action.viewData**, the application component that supports website viewing is matched. Declaring the **action** field in [Want](../reference/apis/js-apis-app-ability-want.md) indicates that the invoked application is expected to support the declared operation. The **actions** field under **skills** in the configuration file indicates the operations supported by the application. - -**Common action Values** +The following **action** values are available: - **ACTION_HOME**: action of starting the application entry component. It must be used together with **ENTITY_HOME**. The application icon on the home screen is an explicit entry component. Users can touch the icon to start the entry component. Multiple entry components can be configured for an application. @@ -14,14 +13,13 @@ The [action](../reference/apis/js-apis-ability-wantConstant.md#wantconstantactio - **ACTION_VIEW_MULTIPLE_DATA**: action of launching the UI for sending multiple data records. -The [entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity) field specifies the additional category information (such as browser and video player) of the target ability. It is a supplement to **action** in implicit Want. You can define this field to filter application categories, for example, browser. Declaring the **entities** field in Want indicates that the invoked application should belong to the declared category. The **entities** field under **skills** in the configuration file indicates the categories supported by the application. - +The **entities** field specify the category information (such as browser and video player) of the target application component. It is a supplement to **action** in implicit Want. You can define this field to filter application categories, for example, browser. Declaring the **entities** field in Want indicates that the invoked application should belong to the declared category. The **entities** field under **skills** in the configuration file indicates the categories supported by the application. -**Common entities Values** +The following **entities** values are available: - **ENTITY_DEFAULT**: default category, which is meaningless. -- **ENTITY_HOME**: abilities with an icon displayed on the home screen. +- **ENTITY_HOME**: application components with an icon displayed on the home screen. - **ENTITY_BROWSABLE**: browser type. diff --git a/en/application-dev/application-models/app-deviceconfig-switch.md b/en/application-dev/application-models/app-deviceconfig-switch.md index 1092c21081cd9a8d62c92a1a68ba434efee7c8c9..6c872f0c167bd4779516d611ee07d9036ca550ee 100644 --- a/en/application-dev/application-models/app-deviceconfig-switch.md +++ b/en/application-dev/application-models/app-deviceconfig-switch.md @@ -22,7 +22,7 @@ OpenHarmony has reconstructed the [deviceConfig](../quick-start/deviceconfig-str | deviceConfig in the FA Model| Description| Stage Model| Difference| | -------- | -------- | -------- | -------- | | deviceConfig| Device information.| / | This tag is no longer available in the stage model. In the stage model, device information is configured under the **app** tag.| -| process | Name of the process running the application or ability. If the **process** attribute is configured in the **deviceConfig** tag, all abilities of the application run in this process. You can set the **process** attribute for a specific ability in the **abilities** attribute, so that the ability can run in the particular process.| / | The stage model does not support the configuration of process names.| +| process | Name of the process running the application or UIAbility. If the **process** attribute is configured in the **deviceConfig** tag, all UIAbilities of the application run in this process. You can set the **process** attribute for a specific UIAbility in the **abilities** attribute, so that the UIAbility can run in the particular process.| / | The stage model does not support the configuration of process names.| | keepAlive | Whether the application is always running. This attribute applies only to system applications and does not take effect for third-party applications.| / | The stage model does not support changing of the model control mode for system applications.| | supportBackup | Whether the application supports data backup and restore.| / | This configuration is not supported in the stage model.| | compressNativeLibs | Whether the **libs** libraries are packaged in the HAP file after being compressed.| / | This configuration is not supported in the stage model.| diff --git a/en/application-dev/application-models/application-component-configuration-fa.md b/en/application-dev/application-models/application-component-configuration-fa.md index 4cc1c9ad6831f0e54ae4c70f4f7229a7abc7c62f..336ad698d0f2298045b00fe0af1563574bccfd24 100644 --- a/en/application-dev/application-models/application-component-configuration-fa.md +++ b/en/application-dev/application-models/application-component-configuration-fa.md @@ -22,7 +22,7 @@ When developing an application, you may need to configure certain tags to identi "actions": ["action.system.home"] } ] - // ... + ... } ``` diff --git a/en/application-dev/application-models/application-component-configuration-stage.md b/en/application-dev/application-models/application-component-configuration-stage.md index bcf9b095464ba0110c35be9cfef44b078a091ffb..db1f4b9f8205993e30c1d4de1000fae72c6d5b08 100644 --- a/en/application-dev/application-models/application-component-configuration-stage.md +++ b/en/application-dev/application-models/application-component-configuration-stage.md @@ -1,10 +1,12 @@ # Application- or Component-Level Configuration (Stage Model) +When developing an application, you may need to configure certain tags to identify the application, such as the bundle name and application icon. This topic describes key tags that need to be configured during application development. -When developing an application, you may need to configure certain tags to identify the application, such as the bundle name and application icon. This topic describes key tags that need to be configured during application development. Icons and labels are usually configured together. There is the application icon, application label, entry icon, and entry label, which correspond to the **icon** and **label** fields in the [app.json5 file](../quick-start/app-configuration-file.md) and [module.json5 file](../quick-start/module-configuration-file.md). The application icon and label are used in **Settings**. For example, they are displayed in the application list in **Settings**. The entry icon is displayed on the device's home screen after the application is installed. The entry icon maps to a [UIAbility](uiability-overview.md) component. Therefore, an application can have multiple entry icons and labels. When you touch one of them, the corresponding UIAbility page is displayed. +Icons and labels are usually configured together. There is the application icon, application label, entry icon, and entry label, which correspond to the **icon** and **label** fields in the [app.json5 file](../quick-start/app-configuration-file.md) and [module.json5 file](../quick-start/module-configuration-file.md). +The application icon and label are used in **Settings**. For example, they are displayed in the application list in **Settings**. The entry icon is displayed on the device's home screen after the application is installed. The entry icon maps to a [UIAbility](uiability-overview.md) component. Therefore, an application can have multiple entry icons and entry labels. When you touch one of them, the corresponding UIAbility page is displayed. - **Figure 1** Icons and labels +**Figure 1** Icons and labels ![application-component-configuration-stage](figures/application-component-configuration-stage.png) @@ -22,13 +24,13 @@ When developing an application, you may need to configure certain tags to identi The application label is specified by the **label** field in the [app.json5 file](../quick-start/app-configuration-file.md) in the **AppScope** module of the project. The **label** field specifies the application name displayed to users. It must be set to the index of a string resource. ```json - { - "app": { - "icon": "$media:app_icon", - "label": "$string:app_name" - // ... - } + { + "app": { + "icon": "$media:app_icon", + "label": "$string:app_name" + ... } + } ``` - **Configuring the entry icon and label** @@ -40,7 +42,7 @@ When developing an application, you may need to configure certain tags to identi ```json { "module": { - // ... + ... "abilities": [ { // The information starting with $ is the resource value. @@ -61,6 +63,35 @@ When developing an application, you may need to configure certain tags to identi } } ``` + OpenHarmony strictly controls applications without icons to prevent malicious applications from deliberately configuring no icon to block uninstall attempts. + + To hide an application icon from the home screen, you must configure the **AllowAppDesktopIconHide** privilege. For details, see [Application Privilege Configuration Guide](../../device-dev/subsystems/subsys-app-privilege-config-guide.md). The rules for displaying the entry icon and entry label are as follows: + + 1. The HAP file contains UIAbility configuration. + * An entry icon is set in the **abilities** field of the **module.json5** file. + * The application does not have the privilege to hide its icon from the home screen. + * The system uses the icon configured for the UIAbility as the entry icon and displays it on the home screen. Touching this icon will direct the user to the home page of the UIAbility. + * The system uses the label configured for the UIAbility as the entry label and displays it on the home screen. If no label is configured, the system uses the label specified in the **app.json5** file as the entry label and displays it on the home screen. + * The application has the privilege to hide its icon from the home screen. + * The application information is not returned when the home screen queries the information, and the entry icon and label of the application are not displayed on the home screen. + * No entry icon is set in the **abilities** field of the **module.json5** file. + * The application does not have the privilege to hide its icon from the home screen. + * The system uses the icon specified in the **app.json5** file as the entry icon and displays it on the home screen. Touching this icon will direct the user to the application details page, as shown below. + * The system uses the label specified in the **app.json5** file as the entry label and displays it on the home screen. + * The application has the privilege to hide its icon from the home screen. + * The application information is not returned when the home screen queries the information, and the entry icon and label of the application are not displayed on the home screen. + + 2. The HAP file does not contain UIAbility configuration. + * The application does not have the privilege to hide its icon from the home screen. + * The system uses the icon specified in the **app.json5** file as the entry icon and displays it on the home screen. Touching this icon will direct the user to the application details page, as shown below. + * The system uses the label specified in the **app.json5** file as the entry label and displays it on the home screen. + * The application has the privilege to hide its icon from the home screen. + * The application information is not returned when the home screen queries the information, and the entry icon and label of the application are not displayed on the home screen. + + **Figure 2** Application details page + + ![Application details page](figures/application_details.jpg) + - **Configuring application version declaration** To declare the application version, configure the **versionCode** and **versionName** fields in the [app.json5 file](../quick-start/app-configuration-file.md) in the **AppScope** directory of the project. **versionCode** specifies the version number of the application. The value is a 32-bit non-negative integer. It is used only to determine whether a version is later than another version. A larger value indicates a later version. **versionName** provides the text description of the version number. diff --git a/en/application-dev/application-models/application-context-stage.md b/en/application-dev/application-models/application-context-stage.md index 23bd8d29ec742f7591d8f7982245cc17ddc770ec..2063eee286c25e360a1700d3e1771d865b875f1c 100644 --- a/en/application-dev/application-models/application-context-stage.md +++ b/en/application-dev/application-models/application-context-stage.md @@ -1,61 +1,62 @@ # Context (Stage Model) - ## Overview -[Context](../reference/apis/js-apis-inner-application-context.md) is the context of an object in an application. It provides basic information about the application, for example, **resourceManager**, **applicationInfo**, **dir** (application development path), and **area** (encrypted area). It also provides basic methods such as **createBundleContext()** and **getApplicationContext()**. The UIAbility component and ExtensionAbility derived class components have their own **Context** classes, for example, the base class **Context**, **ApplicationContext**, **AbilityStageContext**, **UIAbilityContext**, **ExtensionContext**, and **ServiceExtensionContext**. +[Context](../reference/apis/js-apis-inner-application-context.md) is the context of an object in an application. It provides basic information about the application, for example, **resourceManager**, **applicationInfo**, **dir** (application development path), and **area** (encrypted level). It also provides basic methods such as **createBundleContext()** and **getApplicationContext()**. The UIAbility component and ExtensionAbility derived class components have their own **Context** classes, for example, the base class **Context**, **ApplicationContext**, **AbilityStageContext**, **UIAbilityContext**, **ExtensionContext**, and **ServiceExtensionContext**. - The figure below illustrates the inheritance relationship of contexts. - ![context-inheritance](figures/context-inheritance.png) - -- The figure below illustrates the holding relationship of contexts. +- The figure below illustrates the holding relationship of contexts. ![context-holding](figures/context-holding.png) -- The following describes the information provided by different contexts. - - [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md): Each UIAbility has the **Context** attribute, which provides APIs to operate the ability, obtain the ability configuration, and more. +The following describes the information provided by different contexts. + - [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md): Each UIAbility has the **Context** attribute, which provides APIs to operate an application component, obtain the application component configuration, and more. ```ts import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let uiAbilityContext = this.context; - // ... - } + onCreate(want, launchParam) { + let uiAbilityContext = this.context; + ... + } } ``` + + > **NOTE** + > + > For details about how to obtain the context of a **UIAbility** instance on the page, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). - Scenario-specific [ExtensionContext](../reference/apis/js-apis-inner-application-extensionContext.md): For example, ServiceExtensionContext, inherited from ExtensionContext, provides APIs related to background services. ```ts import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility'; export default class MyService extends ServiceExtensionAbility { - onCreate(want) { - let serviceExtensionContext = this.context; - // ... - } + onCreate(want) { + let serviceExtensionContext = this.context; + ... + } } ``` - [AbilityStageContext](../reference/apis/js-apis-inner-application-abilityStageContext.md): module-level context. It provides **HapModuleInfo** and **Configuration** in addition to those provided by the base class **Context**. ```ts - import AbilityStage from "@ohos.app.ability.AbilityStage"; + import AbilityStage from '@ohos.app.ability.AbilityStage'; export default class MyAbilityStage extends AbilityStage { - onCreate() { - let abilityStageContext = this.context; - // ... - } + onCreate() { + let abilityStageContext = this.context; + ... + } } ``` - - [ApplicationContext](../reference/apis/js-apis-inner-application-applicationContext.md): application-level context. It provides APIs for subscribing to ability lifecycle changes, system memory changes, and system environment changes. The application-level context can be obtained from UIAbility, ExtensionAbility, and AbilityStage. + - [ApplicationContext](../reference/apis/js-apis-inner-application-applicationContext.md): application-level context. It provides APIs for subscribing to application component lifecycle changes, system memory changes, and system environment changes. The application-level context can be obtained from UIAbility, ExtensionAbility, and AbilityStage. ```ts import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let applicationContext = this.context.getApplicationContext(); - // ... - } + onCreate(want, launchParam) { + let applicationContext = this.context.getApplicationContext(); + ... + } } ``` @@ -67,9 +68,9 @@ This topic describes how to use the context in the following scenarios: - [Obtaining the Application Development Path](#obtaining-the-application-development-path) -- [Obtaining and Modifying Encrypted Areas](#obtaining-and-modifying-encrypted-areas) +- [Obtaining and Modifying Encryption Levels](#obtaining-and-modifying-encryption-levels) - [Creating Context of Another Application or Module](#creating-context-of-another-application-or-module) -- [Subscribing to Ability Lifecycle Changes in a Process](#subscribing-to-ability-lifecycle-changes-in-a-process) +- [Subscribing to UIAbility Lifecycle Changes in a Process](#subscribing-to-uiability-lifecycle-changes-in-a-process) ### Obtaining the Application Development Path @@ -80,13 +81,13 @@ The following table describes the application development paths obtained from co | Name| Type| Readable| Writable| Description| | -------- | -------- | -------- | -------- | -------- | -| cacheDir | string | Yes| No| Cache directory of the application on the internal storage.
It is the content of **Storage** of an application under **Settings > Apps & services > Apps**.| -| tempDir | string | Yes| No| Temporary file directory of the application.
Files in this directory are deleted after the application is uninstalled.| -| filesDir | string | Yes| No| File directory of the application on the internal storage.
Files in this directory may be synchronized to other directories during application migration or backup.| -| databaseDir | string | Yes| No| Storage directory of the local database.| -| bundleCodeDir | string | Yes| No| Installation directory of the application on the internal storage. A resource file cannot be accessed by combining paths. Use [Resource Manager](../reference/apis/js-apis-resource-manager.md) to access it. | -| distributedFilesDir | string | Yes| No| Storage directory of distributed application data files.| -| preferencesDir | string | Yes| Yes| Preferences directory of the application.| +| bundleCodeDir | string | Yes | No | Path for storing the application's installation package, that is, installation directory of the application on the internal storage. | +| cacheDir | string | Yes| No| Path for storing the cache files, that is, cache directory of the application on the internal storage.
It is the content of **Storage** of an application under **Settings > Apps & services > Apps**.| +| filesDir | string | Yes | No | Path for storing the common files, that is, file directory of the application on the internal storage.
Files in this directory may be synchronized to other directories during application migration or backup.| +| preferencesDir | string | Yes | Yes | Path for storing the preference files, that is, preferences directory of the application. | +| tempDir | string | Yes | No | Path for storing the temporary files.
Files in this directory are deleted after the application is uninstalled.| +| databaseDir | string | Yes | No | Path for storing the application's database, that is, storage directory of the local database. | +| distributedFilesDir | string | Yes| No| Path for storing the distributed files.| The capability of obtaining the application development path is provided by the base class **Context**. This capability is also provided by **ApplicationContext**, **AbilityStageContext**, **UIAbilityContext**, and **ExtensionContext**. However, the paths obtained from different contexts may differ, as shown below. @@ -111,7 +112,7 @@ The capability of obtaining the application development path is provided by the | bundleCodeDir | {Path prefix}/el1/bundle/| | cacheDir | {Path prefix}/{Encryption level}/base/**haps/{moduleName}**/cache/| | filesDir | {Path prefix}/{Encryption level}/base/**haps/{moduleName}**/files/| - | preferencesDir | {path prefix}/{encryption level}/base/**haps/{moduleName}**/preferences/| + | preferencesDir | {Path prefix}/{Encryption level}/base/**haps/{moduleName}**/preferences/| | tempDir | {Path prefix}/{Encryption level}/base/**haps/{moduleName}**/temp/| | databaseDir | {Path prefix}/{Encryption level}/database/**{moduleName}**/| | distributedFilesDir | {Path prefix}/el2/distributedFiles/**{moduleName}**/| @@ -123,83 +124,92 @@ The sample code for obtaining the application development paths is as follows: import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let cacheDir = this.context.cacheDir; - let tempDir = this.context.tempDir; - let filesDir = this.context.filesDir; - let databaseDir = this.context.databaseDir; - let bundleCodeDir = this.context.bundleCodeDir; - let distributedFilesDir = this.context.distributedFilesDir; - let preferencesDir = this.context.preferencesDir; - // ... - } + onCreate(want, launchParam) { + let cacheDir = this.context.cacheDir; + let tempDir = this.context.tempDir; + let filesDir = this.context.filesDir; + let databaseDir = this.context.databaseDir; + let bundleCodeDir = this.context.bundleCodeDir; + let distributedFilesDir = this.context.distributedFilesDir; + let preferencesDir = this.context.preferencesDir; + ... + } } ``` +> **NOTE** +> +> The sample code obtains the sandbox path of the application development path. The absolute path can be obtained by running the **find / -name ** command in the hdc shell after file creation or modification. + +### Obtaining and Modifying Encryption Levels -### Obtaining and Modifying Encrypted Areas +Encrypting application files enhances data security by preventing files from unauthorized access. Different application files require different levels of protection. For private files, such as alarms and wallpapers, the application must place them in a directory with the device-level encryption (EL1) to ensure that they can be accessed before the user enters the password. For sensitive files, such as personal privacy data, the application must place them in a directory with the user-level encryption (EL2). -You can read and write [the area attribute in the context](../reference/apis/js-apis-inner-application-context.md) to obtain and set an encrypted area. Two encryption levels are supported: +In practice, you need to select a proper encryption level based on scenario-specific requirements to protect application data security. The proper use of EL1 and the EL2 can efficiently improve the security. -- AreaMode.EL1: device-level encryption area, which is accessible after the device is powered on. +> **NOTE** +> +> - AreaMode.EL1: device-level encryption. Directories with this encryption level are accessible after the device is powered on. +> +> - AreaMode.EL2: user-level encryption. Directories with this encryption level are accessible only after the device is powered on and the password is entered (for the first time). -- AreaMode.EL2: user-level encryption area, which is accessible only after the device is powered on and the password is entered (for the first time). +You can obtain and set the encryption level by reading and writing the [area attribute in Context](../reference/apis/js-apis-inner-application-context.md). ```ts import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - // Before storing common information, switch the encryption level to EL1. - if (this.context.area === 1) {// Obtain the area. - this.context.area = 0; // Modify the area. - } - // Store common information. - - // Before storing sensitive information, switch the encryption level to EL2. - if (this.context.area === 0) { // Obtain the area. - this.context.area = 1; // Modify the area. - } - // Store sensitive information. + onCreate(want, launchParam) { + // Before storing common information, switch the encryption level to EL1. + if (this.context.area === 1) {// Obtain the area. + this.context.area = 0; // Modify the area. + } + // Store common information. + + // Before storing sensitive information, switch the encryption level to EL2. + if (this.context.area === 0) { // Obtain the area. + this.context.area = 1; // Modify the area. } + // Store sensitive information. + } } ``` ### Creating Context of Another Application or Module -The base class **Context** provides the [createBundleContext(bundleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatebundlecontext), [createModuleContext(moduleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatemodulecontext), and [createModuleContext(bundleName:string, moduleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatemodulecontext-1) methods for creating the context of other applications or modules, so as to obtain the resource information, for example, [obtaining the application development paths](#obtaining-the-application-development-path) of other modules. +The base class **Context** provides [createBundleContext(bundleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatebundlecontext), [createModuleContext(moduleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatemodulecontext), and [createModuleContext(bundleName:string, moduleName:string)](../reference/apis/js-apis-inner-application-context.md#contextcreatemodulecontext-1) to create the context of other applications or modules, so as to obtain the resource information, for example, [obtaining the application development paths](#obtaining-the-application-development-path) of other modules. - Call **createBundleContext(bundleName:string)** to create the context of another application. > **NOTE** > > To obtain the context of another application: > - > - Request the **ohos.permission.GET_BUNDLE_INFO_PRIVILEGED** permission. For details, see [Permission Application Guide](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + > - Request the **ohos.permission.GET_BUNDLE_INFO_PRIVILEGED** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). > > - This is a system API and cannot be called by third-party applications. - + For example, application information displayed on the home screen includes the application name and icon. The home screen application calls the foregoing method to obtain the context information, so as to obtain the resource information including the application name and icon. - + ```ts import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let bundleName2 = "com.example.application"; - let context2 = this.context.createBundleContext(bundleName2); - let label2 = context2.applicationInfo.label; - // ... - } + onCreate(want, launchParam) { + let bundleName2 = 'com.example.application'; + let context2 = this.context.createBundleContext(bundleName2); + let label2 = context2.applicationInfo.label; + ... + } } ``` - + - Call **createModuleContext(bundleName:string, moduleName:string)** to obtain the context of a specified module of another application. After obtaining the context, you can obtain the resource information of that module. > **NOTE** > > To obtain the context of a specified module of another application: > - > - Request the **ohos.permission.GET_BUNDLE_INFO_PRIVILEGED** permission. For details, see [Permission Application Guide](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + > - Request the **ohos.permission.GET_BUNDLE_INFO_PRIVILEGED** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). > > - This is a system API and cannot be called by third-party applications. @@ -207,12 +217,12 @@ The base class **Context** provides the [createBundleContext(bundleName:string)] import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let bundleName2 = "com.example.application"; - let moduleName2 = "module1"; - let context2 = this.context.createModuleContext(bundleName2, moduleName2); - // ... - } + onCreate(want, launchParam) { + let bundleName2 = 'com.example.application'; + let moduleName2 = 'module1'; + let context2 = this.context.createModuleContext(bundleName2, moduleName2); + ... + } } ``` @@ -222,77 +232,90 @@ The base class **Context** provides the [createBundleContext(bundleName:string)] import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - let moduleName2 = "module1"; - let context2 = this.context.createModuleContext(moduleName2); - // ... - } + onCreate(want, launchParam) { + let moduleName2 = 'module1'; + let context2 = this.context.createModuleContext(moduleName2); + ... + } } ``` -### Subscribing to Ability Lifecycle Changes in a Process +### Subscribing to UIAbility Lifecycle Changes in a Process -In the DFX statistics scenario of an application, if you need to collect statistics on the stay duration and access frequency of a page, you can subscribe to ability lifecycle changes. +In the DFX statistics scenario of an application, if you need to collect statistics on the stay duration and access frequency of a page, you can subscribe to UIAbility lifecycle changes in a process. -When the ability lifecycle changes in a process, for example, being created or destroyed, becoming visible or invisible, or gaining or losing focus, the corresponding callback is triggered, and a listener ID is returned. The ID is incremented by 1 each time the listener is registered. When the number of listeners exceeds the upper limit (2^63-1), -1 is returned. The following uses [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) as an example. +[ApplicationContext](../reference/apis/js-apis-inner-application-applicationContext.md) provides APIs for subscribing to UIAbility lifecycle changes in a process. When the UIAbility lifecycle changes in a process, for example, being created or destroyed, becoming visible or invisible, or gaining or losing focus, the corresponding callback is triggered. Each time the callback is registered, a listener lifecycle ID is returned, with the value incremented by 1 each time. When the number of listeners exceeds the upper limit (2^63-1), **-1** is returned. The following uses [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) as an example. ```ts import UIAbility from '@ohos.app.ability.UIAbility'; import window from '@ohos.window'; -const TAG: string = "[Example].[Entry].[EntryAbility]"; +const TAG: string = '[Example].[Entry].[EntryAbility]'; export default class EntryAbility extends UIAbility { - lifecycleId: number; - - onCreate(want, launchParam) { - let abilityLifecycleCallback = { - onAbilityCreate(ability) { - console.info(TAG, "onAbilityCreate ability:" + JSON.stringify(ability)); - }, - onWindowStageCreate(ability, windowStage) { - console.info(TAG, "onWindowStageCreate ability:" + JSON.stringify(ability)); - console.info(TAG, "onWindowStageCreate windowStage:" + JSON.stringify(windowStage)); - }, - onWindowStageActive(ability, windowStage) { - console.info(TAG, "onWindowStageActive ability:" + JSON.stringify(ability)); - console.info(TAG, "onWindowStageActive windowStage:" + JSON.stringify(windowStage)); - }, - onWindowStageInactive(ability, windowStage) { - console.info(TAG, "onWindowStageInactive ability:" + JSON.stringify(ability)); - console.info(TAG, "onWindowStageInactive windowStage:" + JSON.stringify(windowStage)); - }, - onWindowStageDestroy(ability, windowStage) { - console.info(TAG, "onWindowStageDestroy ability:" + JSON.stringify(ability)); - console.info(TAG, "onWindowStageDestroy windowStage:" + JSON.stringify(windowStage)); - }, - onAbilityDestroy(ability) { - console.info(TAG, "onAbilityDestroy ability:" + JSON.stringify(ability)); - }, - onAbilityForeground(ability) { - console.info(TAG, "onAbilityForeground ability:" + JSON.stringify(ability)); - }, - onAbilityBackground(ability) { - console.info(TAG, "onAbilityBackground ability:" + JSON.stringify(ability)); - }, - onAbilityContinue(ability) { - console.info(TAG, "onAbilityContinue ability:" + JSON.stringify(ability)); - } - } - // 1. Obtain the application context through the context attribute. - let applicationContext = this.context.getApplicationContext(); - // 2. Register a listener for the lifecycle changes through the application context. - this.lifecycleId = applicationContext.on("abilityLifecycle", abilityLifecycleCallback); - console.info(TAG, "register callback number: " + JSON.stringify(this.lifecycleId)); + // Define a lifecycle ID. + lifecycleId: number; + + onCreate(want, launchParam) { + // Define a lifecycle callback object. + let abilityLifecycleCallback = { + // Called when a UIAbility is created. + onAbilityCreate(uiAbility) { + console.info(TAG, `onAbilityCreate uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + }, + // Called when a window is created. + onWindowStageCreate(uiAbility, windowStage: window.WindowStage) { + console.info(TAG, `onWindowStageCreate uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + console.info(TAG, `onWindowStageCreate windowStage: ${JSON.stringify(windowStage)}`); + }, + // Called when the window becomes active. + onWindowStageActive(uiAbility, windowStage: window.WindowStage) { + console.info(TAG, `onWindowStageActive uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + console.info(TAG, `onWindowStageActive windowStage: ${JSON.stringify(windowStage)}`); + }, + // Called when the window becomes inactive. + onWindowStageInactive(uiAbility, windowStage: window.WindowStage) { + console.info(TAG, `onWindowStageInactive uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + console.info(TAG, `onWindowStageInactive windowStage: ${JSON.stringify(windowStage)}`); + }, + // Called when the window is destroyed. + onWindowStageDestroy(uiAbility, windowStage: window.WindowStage) { + console.info(TAG, `onWindowStageDestroy uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + console.info(TAG, `onWindowStageDestroy windowStage: ${JSON.stringify(windowStage)}`); + }, + // Called when the UIAbility is destroyed. + onAbilityDestroy(uiAbility) { + console.info(TAG, `onAbilityDestroy uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + }, + // Called when the UIAbility is switched from the background to the foreground. + onAbilityForeground(uiAbility) { + console.info(TAG, `onAbilityForeground uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + }, + // Called when the UIAbility is switched from the foreground to the background. + onAbilityBackground(uiAbility) { + console.info(TAG, `onAbilityBackground uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + }, + // Called when UIAbility is continued on another device. + onAbilityContinue(uiAbility) { + console.info(TAG, `onAbilityContinue uiAbility.launchWant: ${JSON.stringify(uiAbility.launchWant)}`); + } } + // Obtain the application context. + let applicationContext = this.context.getApplicationContext(); + // Register the application lifecycle callback. + this.lifecycleId = applicationContext.on('abilityLifecycle', abilityLifecycleCallback); + console.info(TAG, `register callback number: ${this.lifecycleId}`); + } - onDestroy() { - let applicationContext = this.context.getApplicationContext(); - applicationContext.off("abilityLifecycle", this.lifecycleId, (error, data) => { - console.info(TAG, "unregister callback success, err: " + JSON.stringify(error)); - }); - } + ... + + onDestroy() { + // Obtain the application context. + let applicationContext = this.context.getApplicationContext(); + // Deregister the application lifecycle callback. + applicationContext.off('abilityLifecycle', this.lifecycleId); + } } ``` diff --git a/en/application-dev/application-models/application-model-description.md b/en/application-dev/application-models/application-model-description.md index 0cdfa7323c6ef367a47a44e2c30104d3201ca159..20fa9019d10c8d242e34b309debec78e722e6c27 100644 --- a/en/application-dev/application-models/application-model-description.md +++ b/en/application-dev/application-models/application-model-description.md @@ -12,10 +12,9 @@ Along its evolution, OpenHarmony has provided two application models: The stage model is designed based on the following considerations, which make it become the recommended model: 1. **Designed for complex applications** - - In the stage model, multiple application components share an ArkTS engine (VM running the programming language ArkTS) instance, making it easy for application components to share objects and status while requiring less memory. - The object-oriented development mode makes the code of complex applications easy to read, maintain, and scale. - + 2. **Native support for [cross-device migration](hop-cross-device-migration.md) and [multi-device collaboration](hop-multi-device-collaboration.md) at the application component level** The stage model decouples application components from User Interfaces (UIs). @@ -38,7 +37,7 @@ The stage model is designed based on the following considerations, which make it The stage model redefines the boundary of application capabilities to well balance application capabilities and system management costs. - - Diverse application components (such as widgets and input methods) for specific scenarios. + - Diverse application components (such as service widgets and input methods) for specific scenarios. - Standardized background process management. To deliver a better user experience, the stage model manages background application processes in a more orderly manner. Applications cannot reside in the background randomly, and their background behavior is strictly managed to minimize malicious behavior. @@ -52,8 +51,8 @@ The table below describes their differences in detail. | Item| FA model| Stage model| | -------- | -------- | -------- | -| **Application component**| 1. Component classification
![fa-model-component](figures/fa-model-component.png)
- PageAbility: has the UI and supports user interaction For details, see [PageAbility Component Overview](pageability-overview.md).
- ServiceAbility: provides background services and has no UI. For details, see [ServiceAbility Component Overview](serviceability-overview.md).
- DataAbility: provides the data sharing capability and has no UI. For details, see [DataAbility Component Overview](dataability-overview.md).
2. Development mode
Application components are specified by exporting anonymous objects and fixed entry files. You cannot perform derivation. It is inconvenient for capability expansion. | 1. Component classification
![stage-model-component](figures/stage-model-component.png)
- UIAbility: has the UI and supports user interaction. For details, see [UIAbility Component Overview](uiability-overview.md).
- ExtensionAbility: provides extension capabilities (such as widget and input methods) for specific scenarios. For details, see [ExtensionAbility Component Overview](extensionability-overview.md).
2. Development mode
The object-oriented mode is used to provide open application components as classes. You can derive application components for capability expansion. | -| **Process model**| There are two types of processes:
1. Main process
2. Rendering process
For details, see [Process Model (FA Model)](process-model-fa.md).| There are three types of processes:
1. Main process
2. ExtensionAbility process
3. Rendering process
For details, see [Process Model (Stage Model)](process-model-stage.md).| -| **Thread model**| 1. ArkTS engine instance creation
A process can run multiple application component instances, and each application component instance runs in an independent ArkTS engine instance.
2. Thread model
Each ArkTS engine instance is created on an independent thread (non-main thread). The main thread does not have an ArkTS engine instance.
3. Intra-process object sharing: not supported.
For details, see [Thread Model (FA Model)](thread-model-fa.md).| 1. ArkTS engine instance creation
A process can run multiple application component instances, and all application component instances share one ArkTS engine instance.
2. Thread model
The ArkTS engine instance is created on the main thread.
3. Intra-process object sharing: supported.
For details, see [Thread Model (Stage Model)](thread-model-stage.md).| +| **Application component**| 1. Component classification
![fa-model-component](figures/fa-model-component.png)
- PageAbility: has the UI and supports user interaction For details, see [PageAbility Component Overview](pageability-overview.md).
- ServiceAbility: provides background services and has no UI. For details, see [ServiceAbility Component Overview](serviceability-overview.md).
- DataAbility: provides the data sharing capability and has no UI. For details, see [DataAbility Component Overview](dataability-overview.md).
2. Development mode
Application components are specified by exporting anonymous objects and fixed entry files. You cannot perform derivation. It is inconvenient for capability expansion.| 1. Component classification
![stage-model-component](figures/stage-model-component.png)
- UIAbility: has the UI and supports user interaction. For details, see [UIAbility Component Overview](uiability-overview.md).
- ExtensionAbility: provides extension capabilities (such as widget and input methods) for specific scenarios. For details, see [ExtensionAbility Component Overview](extensionability-overview.md).
2. Development mode
The object-oriented mode is used to provide open application components as classes. You can derive application components for capability expansion.| +| **Process model**| There are two types of processes:
1. Main process
2. Rendering process
For details, see [Process Model Overview (FA Model)](process-model-fa.md). | There are three types of processes:
1. Main process
2. ExtensionAbility process
3. Rendering process
For details, see [Process Model Overview (Stage Model)](process-model-stage.md). | +| **Thread model**| 1. ArkTS engine instance creation
A process can run multiple application component instances, and each application component instance runs in an independent ArkTS engine instance.
2. Thread model
Each ArkTS engine instance is created on an independent thread (non-main thread). The main thread does not have an ArkTS engine instance.
3. Intra-process object sharing: not supported.
For details, see [Thread Model Overview (FA Model)](thread-model-fa.md). | 1. ArkTS engine instance creation
A process can run multiple application component instances, and all application component instances share one ArkTS engine instance.
2. Thread model
The ArkTS engine instance is created on the main thread.
3. Intra-process object sharing: supported.
For details, see [Thread Model Overview (Stage Model)](thread-model-stage.md). | | **Mission management model**| - A mission is created for each PageAbility component instance.
- Missions are stored persistently until the number of missions exceeds the maximum (customized based on the product configuration) or users delete missions.
- PageAbility components do not form a stack structure.
For details, see [Mission Management Scenarios](mission-management-overview.md).| - A mission is created for each UIAbility component instance.
- Missions are stored persistently until the number of missions exceeds the maximum (customized based on the product configuration) or users delete missions.
- UIAbility components do not form a stack structure.
For details, see [Mission Management Scenarios](mission-management-overview.md).| | **Application configuration file**| The **config.json** file is used to describe the application, HAP, and application component information.
For details, see [Application Configuration File Overview (FA Model)](../quick-start/application-configuration-file-overview-fa.md).| The **app.json5** file is used to describe the application information, and the **module.json5** file is used to describe the HAP and application component information.
For details, see [Application Configuration File Overview (Stage Model)](../quick-start/application-configuration-file-overview-stage.md).| diff --git a/en/application-dev/application-models/arkts-ui-widget-configuration.md b/en/application-dev/application-models/arkts-ui-widget-configuration.md new file mode 100644 index 0000000000000000000000000000000000000000..7e438ce4215a583c4ad7ccebc8cfc591b5251ad6 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-configuration.md @@ -0,0 +1,84 @@ +# Configuring Widget Configuration Files + + +Widget-related configuration includes **FormExtensionAbility** configuration and widget configuration. + + +1. Configure FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information. + + Example configuration: + + + ```json + { + "module": { + ... + "extensionAbilities": [ + { + "name": "EntryFormAbility", + "srcEntry": "./ets/entryformability/EntryFormAbility.ts", + "label": "$string:EntryFormAbility_label", + "description": "$string:EntryFormAbility_desc", + "type": "form", + "metadata": [ + { + "name": "ohos.extension.form", + "resource": "$profile:form_config" + } + ] + } + ] + } + } + ``` + +2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if resource is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal field structure. + + **Table 1** form_config.json file + + | Field| Description| Data Type| Default Value Allowed| + | -------- | -------- | -------- | -------- | + | name | Class name of the widget. The value is a string with a maximum of 127 bytes.| String| No| + | description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes.| String| Yes (initial value: left empty)| + | src | Full path of the UI code corresponding to the widget. For an ArkTS widget, the full path must contain the widget file name extension, for example, **./ets/widget/pages/WidgetCard.ets**. For a JS widget, the full path does not need to contain the widget file name extension, for example, **./js/widget/pages/WidgetCard**.| String| No| + | uiSyntax | Type of the widget.
- **arkts**: ArkTS widget
- **hml**: JS widget| String| Yes (initial value: **hml**)| + | window | Window-related configurations.| Object| Yes| + | isDefault | Whether the widget is a default one. Each UIAbility has only one default widget.
- **true**: The widget is the default one.
- **false**: The widget is not the default one.| Boolean| No| + | colorMode | Color mode of the widget.
- **auto**: auto-adaptive color mode
- **dark**: dark color mode
- **light**: light color mode| String| Yes (initial value: **auto**)| + | supportDimensions | Grid styles supported by the widget.
- **1 * 2**: indicates a grid with one row and two columns.
- **2 * 2**: indicates a grid with two rows and two columns.
- **2 * 4**: indicates a grid with two rows and four columns.
- **4 * 4**: indicates a grid with four rows and four columns.| String array| No| + | defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget.| String| No| + | updateEnabled | Whether the widget can be updated periodically.
- **true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.
- **false**: The widget cannot be updated periodically.| Boolean| No| + | scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.
**NOTE**
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| String| Yes (initial value: The widget cannot be updated periodically.)| + | updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.
If the value is **0**, this field does not take effect.
If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.
**NOTE**
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| Number| Yes (initial value: **0**)| + | formConfigAbility | Link to a specific page of the application. The value is a URI.| String| Yes (initial value: left empty)| + | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)| + | metadata | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)| + + Example configuration: + + + ```json + { + "forms": [ + { + "name": "widget", + "description": "This is a service widget.", + "src": "./ets/widget/pages/WidgetCard.ets", + "uiSyntax": "arkts", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDefault": true, + "updateEnabled": true, + "scheduledUpdateTime": "10:30", + "updateDuration": 1, + "defaultDimension": "2*2", + "supportDimensions": [ + "2*2" + ] + } + ] + } + ``` diff --git a/en/application-dev/application-models/arkts-ui-widget-content-update.md b/en/application-dev/application-models/arkts-ui-widget-content-update.md new file mode 100644 index 0000000000000000000000000000000000000000..c0f4c840b6c11b497405ce8777a04317b5ffca4d --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-content-update.md @@ -0,0 +1,30 @@ +# Updating Widget Content by Widget Host (for System Applications Only) + + +Widgets that are updated periodically are subject to the scheduled time or interval settings. To offer more flexible updates, the widget host can provide a button to proactively trigger a widget update. Specifically, the widget host calls the [requestForm](../reference/apis/js-apis-app-form-formHost.md#requestform) API to request a widget update. The system then calls the [onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback in the FormExtensionAbility of the widget provider. In the callback, the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API can be used to update the widget content. For details about the **onUpdateForm** lifecycle callback, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md). + +```ts +import formHost from '@ohos.app.form.formHost'; + +@Entry() +@Component +struct WidgetCard { + formId = ...; // Widget ID + + build() { + Button (`Update Widget`) + .type(ButtonType.Capsule) + .width('50%') + .height(50) + .onClick(() => { + console.info('FormAbility update form click'); + // formId is the ID of the widget to be updated. + formHost.requestForm(this.formId.toString()).then(() => { + console.info('Succeeded in requestForming.'); + }); + }) + + ... + } +} +``` diff --git a/en/application-dev/application-models/arkts-ui-widget-creation.md b/en/application-dev/application-models/arkts-ui-widget-creation.md new file mode 100644 index 0000000000000000000000000000000000000000..cc8843c0294cf745e7737164e46cb8f69256efa2 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-creation.md @@ -0,0 +1,19 @@ +# Creating an ArkTS Widget + +To create an ArkTS widget in an existing application project, perform the following steps: + +1. Create a widget. + + ![WidgetProjectCreate1](figures/WidgetProjectCreate1.png) + +2. Select a widget template based on the actual service scenario. + + ![WidgetProjectCreate2](figures/WidgetProjectCreate2.png) + +3. Set **Language** to **ArkTS** and click **Finish**. + + ![WidgetProjectCreate3](figures/WidgetProjectCreate3.png) + +After an ArkTS widget is created, the following widget-related files are added to the project directory: **EntryFormAbility.ts** (widget lifecycle management file), **WidgetCard.ets** (widget page file), and **form_config.json** (widget configuration file). + +![WidgetProjectView](figures/WidgetProjectView.png) diff --git a/en/application-dev/application-models/arkts-ui-widget-event-call.md b/en/application-dev/application-models/arkts-ui-widget-event-call.md new file mode 100644 index 0000000000000000000000000000000000000000..842c418ba64a8fd3b01b8d981c209ad1c7d769cf --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-event-call.md @@ -0,0 +1,89 @@ +# Launching a UIAbility in the Background Through the call Event + + +There may be cases you want to provide in a widget access to features available in your application when it is running in the foreground, for example, the play, pause, and stop buttons in a music application widget. This is where the **call** capability of the **postCardAction** API comes in handy. This capability, when used in a widget, can start the specified UIAbility of the widget provider in the background. It also allows the widget to call the specified method of the application and transfer data so that the application, while in the background, can behave accordingly in response to touching of the buttons on the widget. + + +Generally, buttons are used to trigger the **call** event. Below is an example. + + +- In this example, two buttons are laid out on the widget page. When one button is clicked, the **postCardAction** API is called to send a **call** event to the target UIAbility. Note that the **method** parameter in the API indicates the method to call in the target UIAbility. It is mandatory and of the string type. + + ```ts + @Entry + @Component + struct WidgetCard { + build() { + Column() { + Button ('Feature A') + .margin('20%') + .onClick(() => { + console.info('call EntryAbility funA'); + postCardAction(this, { + 'action': 'call', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'method': 'funA' // Set the name of the method to call in the EntryAbility. + } + }); + }) + + Button ('Feature B') + .margin('20%') + .onClick(() => { + console.info('call EntryAbility funB'); + postCardAction(this, { + 'action': 'call', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'method': 'funB', // Set the name of the method to call in the EntryAbility. + 'num': 1 // Set other parameters to be transferred. + } + }); + }) + } + .width('100%') + .height('100%') + } + } + ``` + +- The UIAbility receives the **call** event and obtains the transferred parameters. It then executes the target method specified by the **method** parameter. Other data can be obtained in readString mode. Listen for the method required by the **call** event in the **onCreate** callback of the UIAbility. + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility'; + + function FunACall(data) { + // Obtain all parameters transferred in the call event. + console.log('FunACall param:' + JSON.stringify(data.readString())); + return null; + } + + function FunBCall(data) { + console.log('FunACall param:' + JSON.stringify(data.readString())); + return null; + } + + export default class CameraAbility extends UIAbility { + // If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the call event is received. + onCreate(want, launchParam) { + try { + // Listen for the method required by the call event. + this.callee.on('funA', FunACall); + this.callee.on('funB', FunBCall); + } catch (error) { + console.log('register failed with error. Cause: ' + JSON.stringify(error)); + } + } + + // Deregister the listener when the process exits. + onDestroy() { + try { + this.callee.off('funA'); + this.callee.off('funB'); + } catch (error) { + console.log('register failed with error. Cause: ' + JSON.stringify(error)); + } + } + }; + ``` diff --git a/en/application-dev/application-models/arkts-ui-widget-event-formextensionability.md b/en/application-dev/application-models/arkts-ui-widget-event-formextensionability.md new file mode 100644 index 0000000000000000000000000000000000000000..be7761d8d78da5102afadd2c37043c228dfcd53e --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-event-formextensionability.md @@ -0,0 +1,66 @@ +# Updating Widget Content Through the message Event + + +On the widget page, the **postCardAction** API can be used to trigger a message event to start a FormExtensionAbility, which then updates the widget content. The following is an example of this widget update mode. + + +- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the FormExtensionAbility. + + ```ts + let storage = new LocalStorage(); + @Entry(storage) + @Component + struct WidgetCard { + @LocalStorageProp('title') title: string = 'init'; + @LocalStorageProp('detail') detail: string = 'init'; + + build() { + Column() { + Button ('Update') + .onClick(() => { + postCardAction(this, { + 'action': 'message', + 'params': { + 'msgTest': 'messageEvent' + } + }); + }) + Text(`${this.title}`) + Text(`${this.detail}`) + } + .width('100%') + .height('100%') + } + } + ``` + +- Call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget in the **onFormEvent** callback of the FormExtensionAbility. + + ```ts + import formBindingData from '@ohos.app.form.formBindingData'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import formProvider from '@ohos.app.form.formProvider'; + + export default class EntryFormAbility extends FormExtensionAbility { + onFormEvent(formId, message) { + // Called when a specified message event defined by the form provider is triggered. + console.info(`FormAbility onEvent, formId = ${formId}, message: ${JSON.stringify(message)}`); + let formData = { + 'title':'Title Update Success.', // Matches the widget layout. + 'detail':'Detail Update Success.', // Matches the widget layout. + }; + let formInfo = formBindingData.createFormBindingData(formData) + formProvider.updateForm(formId, formInfo).then((data) => { + console.info('FormAbility updateForm success.' + JSON.stringify(data)); + }).catch((error) => { + console.error('FormAbility updateForm failed: ' + JSON.stringify(error)); + }) + } + + ... + } + ``` + + The figure below shows the effect. + + ![WidgetUpdatePage](figures/WidgetUpdatePage.png) diff --git a/en/application-dev/application-models/arkts-ui-widget-event-overview.md b/en/application-dev/application-models/arkts-ui-widget-event-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..ed029fc3017d00a7d4c2cf14e1b905139bd7eb49 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-event-overview.md @@ -0,0 +1,61 @@ +# Widget Event Capability Overview + +The ArkTS widget provides the **postCardAction()** API for interaction between the widget internal and the provider application. Currently, this API supports the router, message, and call events and can be called only in the widget. + +![WidgetPostCardAction](figures/WidgetPostCardAction.png) + +**Definition**: postCardAction(component: Object, action: Object): void + +**Parameters** + +| Name| Type| Mandatory| Description| +| -------- | -------- | -------- | -------- | +| component | Object | Yes| Instance of the current custom component. Generally, **this** is transferred.| +| action | Object | Yes| Action description. For details, see the following table.| + +**Description of the action parameter** + +| Key | Value | Description| +| -------- | -------- | -------- | +| "action" | string | Action type.
- **"router"**: redirection to the specified UIAbility of the widget provider.
- **"message"**: custom message. If this type of action is triggered, the [onFormEvent()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onformevent) lifecycle callback of the provider FormExtensionAbility is called.
- **"call"**: launch of the widget provider in the background. If this type of action is triggered, the specified UIAbility of the widget provider is started in the background, but not displayed in the foreground. This action type requires that the widget provider should have the [ohos.permission.KEEP_BACKGROUND_RUNNING](../security/permission-list.md#ohospermissionkeep_background_running) permission.| +| "bundleName" | string | Name of the target bundle when **action** is **"router"** or **"call"**. This parameter is optional.| +| "moduleName" | string | Name of the target module when **action** is **"router"** or **"call"**. This parameter is optional.| +| "abilityName" | string | Name of the target UIAbility when **action** is **"router"** or **"call"**. This parameter is mandatory.| +| "params" | Object | Additional parameters carried in the current action. The value is a key-value pair in JSON format. For the **"call"** action type, the **method** parameter must be set and its value type must be string. This parameter is mandatory.| + + +Sample code of the **postCardAction()** API: + +```typescript +Button ('Jump') + .width('40%') + .height('20%') + .onClick(() => { + postCardAction(this, { + 'action': 'router', + 'bundleName': 'com.example.myapplication', + 'abilityName': 'EntryAbility', + 'params': { + 'message': 'testForRouter' // Customize the message to send. + } + }); + }) + +Button ('Start in Background') + .width('40%') + .height('20%') + .onClick(() => { + postCardAction(this, { + 'action': 'call', + 'bundleName': 'com.example.myapplication', + 'abilityName': 'EntryAbility', + 'params': { + 'method': 'fun', // Set the name of the method to call. It is mandatory. + 'message': 'testForcall' // Customize the message to send. + } + }); + }) +``` + + +Read on to learn the typical widget development scenarios that can be implemented through widget events. diff --git a/en/application-dev/application-models/arkts-ui-widget-event-router.md b/en/application-dev/application-models/arkts-ui-widget-event-router.md new file mode 100644 index 0000000000000000000000000000000000000000..733ff7f59b57ec4295fa21cb4d83ae8a5b2b8eb4 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-event-router.md @@ -0,0 +1,108 @@ +# Redirecting to a UIAbility Through the router Event + +The **router** capability of the **postCardAction** API can be used in a widget to quickly start a specific UIAbility of the widget provider. By leveraging this capability, an application can provide in the widget multiple buttons, each of which targets a different target UIAbility. For example, a camera widget can provide the buttons that redirect the user to the UIAbility for taking a photo and the UIAbility for recording a video. + +![WidgerCameraCard](figures/WidgerCameraCard.png) + + +Generally, a button is used to start a page. + + +- Design two buttons on the widget page. When one of the buttons is clicked, **postCardAction** is called to send a router event to the specified UIAbility, with the content to be transferred defined in the event. + + ```ts + @Entry + @Component + struct WidgetCard { + build() { + Column() { + Button ('Function A') + .margin('20%') + .onClick(() => { + console.info('Jump to EntryAbility funA'); + postCardAction(this, { + 'action': 'router', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'targetPage': 'funA' // Process the information in the EntryAbility. + } + }); + }) + + Button ('Function B') + .margin('20%') + .onClick(() => { + console.info('Jump to EntryAbility funB'); + postCardAction(this, { + 'action': 'router', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'targetPage': 'funB' // Process the information in the EntryAbility. + } + }); + }) + } + .width('100%') + .height('100%') + } + } + ``` + +- The UIAbility receives the router event and obtains parameters. It then starts the page specified in the received message. + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility'; + import window from '@ohos.window'; + + let selectPage = ""; + let currentWindowStage = null; + + export default class CameraAbility extends UIAbility { + // If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received. + onCreate(want, launchParam) { + // Obtain the targetPage parameter passed in the router event. + console.info("onCreate want:" + JSON.stringify(want)); + if (want.parameters.params !== undefined) { + let params = JSON.parse(want.parameters.params); + console.info("onCreate router targetPage:" + params.targetPage); + selectPage = params.targetPage; + } + } + // If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received. + onNewWant(want, launchParam) { + console.info("onNewWant want:" + JSON.stringify(want)); + if (want.parameters.params !== undefined) { + let params = JSON.parse(want.parameters.params); + console.info("onNewWant router targetPage:" + params.targetPage); + selectPage = params.targetPage; + } + if (currentWindowStage != null) { + this.onWindowStageCreate(currentWindowStage); + } + } + + onWindowStageCreate(windowStage: window.WindowStage) { + let targetPage; + // Start the page specified by targetPage. + switch (selectPage) { + case 'funA': + targetPage = 'pages/FunA'; + break; + case 'funB': + targetPage = 'pages/FunB'; + break; + default: + targetPage = 'pages/Index'; + } + if (currentWindowStage === null) { + currentWindowStage = windowStage; + } + windowStage.loadContent(targetPage, (err, data) => { + if (err && err.code) { + console.info('Failed to load the content. Cause: %{public}s', JSON.stringify(err)); + return; + } + }); + } + }; + ``` diff --git a/en/application-dev/application-models/arkts-ui-widget-event-uiability.md b/en/application-dev/application-models/arkts-ui-widget-event-uiability.md new file mode 100644 index 0000000000000000000000000000000000000000..a9e0ca1544a3f22e8c6460c992459a182bf142b0 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-event-uiability.md @@ -0,0 +1,188 @@ +# Updating Widget Content Through the router or call Event + + +On the widget page, the **postCardAction** API can be used to trigger a router or call event to start a UIAbility, which then updates the widget content. The following is an example of this widget update mode. + +## Updating Widget Content Through the router Event + +- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the **router** event to the FormExtensionAbility. + + ```ts + let storage = new LocalStorage(); + @Entry(storage) + @Component + struct WidgetCard { + @LocalStorageProp('detail') detail: string = 'init'; + + build() { + Column() { + Button ('Jump') + .margin('20%') + .onClick(() => { + console.info('postCardAction to EntryAbility'); + postCardAction(this, { + 'action': 'router', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'detail': 'RouterFromCard' + } + }); + }) + Text(`${this.detail}`).margin('20%') + } + .width('100%') + .height('100%') + } + } + ``` + +- In the **onCreate()** or **onNewWant()** lifecycle callback of the UIAbility, use the input parameter **want** to obtain the ID (**formID**) and other information of the widget, and then call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API to update the widget. + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility'; + import formBindingData from '@ohos.app.form.formBindingData'; + import formProvider from '@ohos.app.form.formProvider'; + import formInfo from '@ohos.app.form.formInfo'; + + export default class EntryAbility extends UIAbility { + // If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the router event is received. + onCreate(want, launchParam) { + console.info('Want:' + JSON.stringify(want)); + if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) { + let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY]; + let message = JSON.parse(want.parameters.params).detail; + console.info(`UpdateForm formId: ${curFormId}, message: ${message}`); + let formData = { + "detail": message +': onCreate UIAbility.', // Matches the widget layout. + }; + let formMsg = formBindingData.createFormBindingData(formData) + formProvider.updateForm(curFormId, formMsg).then((data) => { + console.info('updateForm success.' + JSON.stringify(data)); + }).catch((error) => { + console.error('updateForm failed:' + JSON.stringify(error)); + }) + } + } + // If the UIAbility is running in the background, the onNewWant lifecycle callback is triggered after the router event is received. + onNewWant(want, launchParam) { + console.info('onNewWant Want:' + JSON.stringify(want)); + if (want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) { + let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY]; + let message = JSON.parse(want.parameters.params).detail; + console.info(`UpdateForm formId: ${curFormId}, message: ${message}`); + let formData = { + "detail": message +': onNewWant UIAbility.', // Matches the widget layout. + }; + let formMsg = formBindingData.createFormBindingData(formData) + formProvider.updateForm(curFormId, formMsg).then((data) => { + console.info('updateForm success.' + JSON.stringify(data)); + }).catch((error) => { + console.error('updateForm failed:' + JSON.stringify(error)); + }) + } + } + + ... + } + ``` + +## Updating Widget Content Through the call Event + +- When using the **call** event of the **postCardAction** API, the value of **formId** must be updated in the **onAddForm** callback of the FormExtensionAbility. + + ```ts + import formBindingData from '@ohos.app.form.formBindingData'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + + export default class EntryFormAbility extends FormExtensionAbility { + onAddForm(want) { + let formId = want.parameters["ohos.extra.param.key.form_identity"]; + let dataObj1 = { + "formId": formId + }; + let obj1 = formBindingData.createFormBindingData(dataObj1); + return obj1; + } + + ... + }; + ``` + +- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the event to the UIAbility. + + ```ts + let storage = new LocalStorage(); + @Entry(storage) + @Component + struct WidgetCard { + @LocalStorageProp('detail') detail: string = 'init'; + @LocalStorageProp('formId') formId: string = '0'; + + build() { + Column() { + Button ('Start in Background') + .margin('20%') + .onClick(() => { + console.info('postCardAction to EntryAbility'); + postCardAction(this, { + 'action': 'call', + 'abilityName': 'EntryAbility', // Only the UIAbility of the current application is allowed. + 'params': { + 'method': 'funA', + 'formId': this.formId, + 'detail': 'CallFromCard' + } + }); + }) + Text(`${this.detail}`).margin('20%') + } + .width('100%') + .height('100%') + } + } + ``` + +- Listen for the method required by the **call** event in the **onCreate** callback of the UIAbility, and then call the [updateForm](../reference/apis/js-apis-app-form-formProvider.md#updateform) API in the corresponding method to update the widget. + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility'; + import formBindingData from '@ohos.app.form.formBindingData'; + import formProvider from '@ohos.app.form.formProvider'; + import formInfo from '@ohos.app.form.formInfo'; + + const MSG_SEND_METHOD: string = 'funA' + + // After the call event is received, the method listened for by the callee is triggered. + function FunACall(data) { + // Obtain all parameters transferred in the call event. + let params = JSON.parse(data.readString()) + if (params.formId !== undefined) { + let curFormId = params.formId; + let message = params.detail; + console.info(`UpdateForm formId: ${curFormId}, message: ${message}`); + let formData = { + "detail": message + }; + let formMsg = formBindingData.createFormBindingData(formData) + formProvider.updateForm(curFormId, formMsg).then((data) => { + console.info('updateForm success.' + JSON.stringify(data)); + }).catch((error) => { + console.error('updateForm failed:' + JSON.stringify(error)); + }) + } + return null; + } + export default class EntryAbility extends UIAbility { + // If the UIAbility is started for the first time, the onCreate lifecycle callback is triggered after the call event is received. + onCreate(want, launchParam) { + console.info('Want:' + JSON.stringify(want)); + try { + // Listen for the method required by the call event. + this.callee.on(MSG_SEND_METHOD, FunACall); + } catch (error) { + console.log(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`) + } + } + ... + } + ``` diff --git a/en/application-dev/application-models/arkts-ui-widget-image-update.md b/en/application-dev/application-models/arkts-ui-widget-image-update.md new file mode 100644 index 0000000000000000000000000000000000000000..dfe6dbf0e9249c66c3fb1d0723f1c7b296443381 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-image-update.md @@ -0,0 +1,166 @@ +# Updating Local and Online Images in the Widget + + +Typically, a widget includes local images or online images downloaded from the network. To obtain local and online images, use the FormExtensionAbility. The following exemplifies how to show local and online images on a widget. + + +1. For the widget to download online images, declare the **ohos.permission.INTERNET** permission for the widget. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md). + +2. Update local files in the **onAddForm** lifecycle callback of the EntryFormAbility. + + ```ts + import formBindingData from '@ohos.app.form.formBindingData'; + import formProvider from '@ohos.app.form.formProvider'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import request from '@ohos.request'; + import fs from '@ohos.file.fs'; + + export default class EntryFormAbility extends FormExtensionAbility { + ... + // When the widget is added, a local image is opened and transferred to the widget page for display. + onAddForm(want) { + // Assume that the local image head.PNG is in the tmp directory of the current widget. + let tempDir = this.context.getApplicationContext().tempDir; + // Open the local image and obtain the FD after the image is opened. + let file; + try { + file = fs.openSync(tempDir + '/' + 'head.PNG'); + } catch (e) { + console.error(`openSync failed: ${JSON.stringify(e)}`); + } + let formData = { + 'text': 'Image: Bear', + 'imgName': 'imgBear', + 'formImages': { + 'imgBear': file.fd + }, + 'loaded': true + } + // Encapsulate the FD in formData and return it to the widget page. + return formBindingData.createFormBindingData(formData); + } + + ... + } + ``` + +3. Update online images in the **onFormEvent** lifecycle callback of the EntryFormAbility. + + ```ts + import formBindingData from '@ohos.app.form.formBindingData'; + import formProvider from '@ohos.app.form.formProvider'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import request from '@ohos.request'; + import fs from '@ohos.file.fs'; + + export default class EntryFormAbility extends FormExtensionAbility { + // When the message event is triggered on the widget page, an online image is downloaded and transferred to the widget page for display. + onFormEvent(formId, message) { + let formInfo = formBindingData.createFormBindingData({ + 'text': 'Updating...' + }) + formProvider.updateForm(formId, formInfo) + // Note: After being started with the triggering of the lifecycle callback, the FormExtensionAbility can run in the background for only 5 seconds. + // When possible, limit the size of the image to download. If an image cannot be downloaded within 5 seconds, it will not be updated to the widget page. + let netFile = 'https://xxxx/xxxx.png'; // Specify the URL of the image to download. + let tempDir = this.context.getApplicationContext().tempDir; + let fileName = 'file' + Date.now(); + let tmpFile = tempDir + '/' + fileName; + request.downloadFile(this.context, { + url: netFile, filePath: tmpFile, enableMetered: true, enableRoaming: true + }).then((task) => { + task.on('complete', function callback() { + console.info('ArkTSCard download complete:' + tmpFile); + let file; + try { + file = fs.openSync(tmpFile); + } catch (e) { + console.error(`openSync failed: ${JSON.stringify(e)}`); + } + let fileInfo = {}; + fileInfo[fileName] = file.fd; + let formData = { + 'text': 'Image:' + fileName, + 'imgName': fileName, + 'formImages': fileInfo, + 'loaded': true + }; + let formInfo = formBindingData.createFormBindingData(formData) + formProvider.updateForm(formId, formInfo).then((data) => { + console.info('FormAbility updateForm success.' + JSON.stringify(data)); + }).catch((error) => { + console.error('FormAbility updateForm failed: ' + JSON.stringify(error)); + }) + }) + task.on('fail', function callBack(err) { + console.info('ArkTSCard download task failed. Cause:' + err); + let formInfo = formBindingData.createFormBindingData({ + 'text':'Update failed.' + }) + formProvider.updateForm(formId, formInfo) + }); + }).catch((err) => { + console.error('Failed to request the download. Cause: ' + JSON.stringify(err)); + }); + } + + ... + }; + ``` + +4. On the widget page, use the **\** component to display the widget content transferred from the EntryFormAbility. + + ```ts + let storage = new LocalStorage(); + @Entry(storage) + @Component + struct WidgetCard { + @LocalStorageProp('text') text: string = 'Loading...'; + @LocalStorageProp('loaded') loaded: boolean = false; + @LocalStorageProp('imgName') imgName: string = 'name'; + + build() { + Column() { + Text(this.text) + .fontSize('12vp') + .textAlign(TextAlign.Center) + .width('100%') + .height('15%') + + Row() { + if (this.loaded) { + Image('memory://' + this.imgName) + .width('50%') + .height('50%') + .margin('5%') + } else { + Image('common/start.PNG') + .width('50%') + .height('50%') + .margin('5%') + } + }.alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + + Button ('Update') + .height('15%') + .onClick(() => { + postCardAction(this, { + 'action': 'message', + 'params': { + 'info': 'refreshImage' + } + }); + }) + } + .width('100%').height('100%') + .alignItems(HorizontalAlign.Center) + .padding('5%') + } + } + ``` + +> **NOTE** +> - The **\** component displays images in the remote memory based on the **memory://** identifier in the input parameter (**memory://fileName**). The value of **fileName** must be consistent with the key in the object (**'formImages': {key: fd}**) passed by the EntryFormAbility. +> +> - The **\** component determines whether to update the image by comparing the values of **imgName** consecutively passed by the EntryFormAbility. It updates the image only when the values are different. diff --git a/en/application-dev/application-models/arkts-ui-widget-interaction-overview.md b/en/application-dev/application-models/arkts-ui-widget-interaction-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..76c4a202543c00f3df44f71b0a33d417831b5f53 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-interaction-overview.md @@ -0,0 +1,20 @@ +# Widget Data Interaction + + +The ArkTS widget framework provides the **updateForm()** and **requestForm()** APIs to proactively trigger widget updates. + + +![WidgetLocalStorageProp](figures/WidgetLocalStorageProp.png) + + +| API| System Capability| Constraints| +| -------- | -------- | -------- | +| updateForm | No| 1. Invoked by the provider.
2. Allows only the widget provider to update its own widgets. It cannot be used to update widgets by other providers.| +| requestForm | Yes| 1. Invoked by the host.
2. Allows only the widget host to update the widgets added to it. It cannot be used to update widgets added to other hosts.| + +The following describes the typical use cases of widget updates: + +- [Configuring a Widget to Update Periodically](arkts-ui-widget-update-by-time.md) +- [Updating Local and Online Images](arkts-ui-widget-image-update.md) +- [Updating Widget Content by State](arkts-ui-widget-update-by-status.md) +- [Updating Widget Content by Widget Host (for System Applications Only)](arkts-ui-widget-content-update.md) diff --git a/en/application-dev/application-models/arkts-ui-widget-lifecycle.md b/en/application-dev/application-models/arkts-ui-widget-lifecycle.md new file mode 100644 index 0000000000000000000000000000000000000000..fb25fd362f67646d65853b870a6a9cb518c4d760 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-lifecycle.md @@ -0,0 +1,96 @@ +# Widget Lifecycle Management + + +When creating an ArkTS widget, you need to implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs. + + +1. Import related modules to **EntryFormAbility.ts**. + + ```ts + import formInfo from '@ohos.app.form.formInfo'; + import formBindingData from '@ohos.app.form.formBindingData'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import formProvider from '@ohos.app.form.formProvider'; + ``` + +2. In **EntryFormAbility.ts**, implement the [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) lifecycle APIs, including **onAddForm**, whose **want** parameter can be used to obtain the widget information through [FormParam](../reference/apis/js-apis-app-form-formInfo.md#formparam). + + ```typescript + import formInfo from '@ohos.app.form.formInfo'; + import formBindingData from '@ohos.app.form.formBindingData'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import formProvider from '@ohos.app.form.formProvider'; + + export default class EntryFormAbility extends FormExtensionAbility { + onAddForm(want) { + console.info('[EntryFormAbility] onAddForm'); + // Obtain the unique widget ID formId from the want parameter. + let formId: string = want.parameters[formInfo.FormParam.IDENTITY_KEY]; + // Called when the widget is created. The widget provider should return the widget data binding class. + let obj = { + 'title': 'titleOnAddForm', + 'detail': 'detailOnAddForm' + }; + let formData = formBindingData.createFormBindingData(obj); + return formData; + } + + onCastToNormalForm(formId) { + // Called when the form provider is notified that a temporary form is successfully + // converted to a normal form. + // Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion. + console.info(`[EntryFormAbility] onCastToNormalForm, formId: ${formId}`); + } + + onUpdateForm(formId) { + // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. + console.info('[EntryFormAbility] onUpdateForm'); + let obj = { + 'title': 'titleOnUpdateForm', + 'detail': 'detailOnUpdateForm' + }; + let formData = formBindingData.createFormBindingData(obj); + formProvider.updateForm(formId, formData).catch((err) => { + if (err) { + // Print errors. + console.error(`[EntryFormAbility] Failed to updateForm. Code: ${err.code}, message: ${err.message}`); + return; + } + }); + } + + onChangeFormVisibility(newStatus) { + // Called when the form provider receives form events from the system. + // The callback is performed only when formVisibleNotify is set to true and the application is a system application. + console.info('[EntryFormAbility] onChangeFormVisibility'); + } + + onFormEvent(formId, message) { + // Called when a specified message event defined by the form provider is triggered. + // If the widget supports event triggering, override this method and implement the trigger. + console.info('[EntryFormAbility] onFormEvent'); + } + + onRemoveForm(formId) { + // Called to notify the form provider that a specified form has been destroyed. + // Called when the corresponding widget is deleted. The input parameter is the ID of the deleted card. + console.info('[EntryFormAbility] onRemoveForm'); + } + + onConfigurationUpdate(config) { + // Called when the system configuration is updated. + console.info('[EntryFormAbility] configurationUpdate:' + JSON.stringify(config)); + } + + onAcquireFormState(want) { + // Called to return a {@link FormState} object. + // Called when the widget provider receives the status query result of a widget. By default, the initial state of the widget is returned. + return formInfo.FormState.READY; + } + } + ``` + + +> **NOTE** +> +> The FormExtensionAbility cannot reside in the background. It persists for 5 seconds after the lifecycle callback is completed and exist if no new lifecycle callback is invoked during this time frame. This means that continuous tasks cannot be processed in the widget lifecycle callbacks. For the service logic that may take more than 5 seconds to complete, it is recommended that you [start the application](arkts-ui-widget-event-uiability.md) for processing. After the processing is complete, use [updateForm()](../reference/apis/js-apis-app-form-formProvider.md#updateform) to notify the widget of the update. diff --git a/en/application-dev/application-models/arkts-ui-widget-modules.md b/en/application-dev/application-models/arkts-ui-widget-modules.md new file mode 100644 index 0000000000000000000000000000000000000000..b9a411426db84a4c1af12e70eab956adc8f25806 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-modules.md @@ -0,0 +1,24 @@ +# ArkTS Widget Related Modules + +**Figure 1** ArkTS widget related modules + +![WidgetModules](figures/WidgetModules.png) + + +- [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md): provides lifecycle callbacks invoked when a widget is created, destroyed, or updated. + +- [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md): provides context for FormExtensionAbilities. You can use the APIs of this module to start FormExtensionAbilities. + +- [formProvider](../reference/apis/js-apis-app-form-formProvider.md): provides APIs related to the widget provider. You can use the APIs to update a widget, set the next update time for a widget, obtain widget information, and request a widget release. + +- [formInfo](../reference/apis/js-apis-app-form-formInfo.md): provides types and enums related to the widget information and state. + +- [formBindingData](../reference/apis/js-apis-app-form-formBindingData.md): provides APIs for widget data binding. You can use the APIs to create a **FormBindingData** object and obtain related information. + +- [Page layout (Card.ets)](arkts-ui-widget-page-overview.md): provides APIs for a declarative paradigm UI. + - [Capabilities exclusive to ArkTS widgets](arkts-ui-widget-event-overview.md): include the **postCardAction** API used for interaction between the widget internal and the provider application and can be called only in the widget. + - [ArkTS widget capability list](arkts-ui-widget-page-overview.md#page-capabilities-supported-by-arkts-widgets): contain the APIs, components, events, attributes, and lifecycle callbacks that can be used in ArkTS widgets. + +- [Widget configuration](arkts-ui-widget-configuration.md): includes FormExtensionAbility configuration and widget configuration. + - Configure the FormExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). + - Configure the widget configuration information (**WidgetCard.ets**) in the [form_config.json](arkts-ui-widget-configuration.md) file in **resources/base/profile**. diff --git a/en/application-dev/application-models/arkts-ui-widget-page-animation.md b/en/application-dev/application-models/arkts-ui-widget-page-animation.md new file mode 100644 index 0000000000000000000000000000000000000000..0cb8e356c61155d367e55c0f39bbf491d03e2e12 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-page-animation.md @@ -0,0 +1,41 @@ +# Using Animations in the Widget + + +To make your ArkTS widget more engaging, you can apply animations to it, including [explicit animation](../reference/arkui-ts/ts-explicit-animation.md), [attribute animation](../reference/arkui-ts/ts-animatorproperty.md), and [component transition](../reference/arkui-ts/ts-transition-animation-component.md). Just note the following restrictions when using the animations in ArkTS widgets. + + +**Table 1** Restrictions on animation parameters + +| Name| Description| Description| +| -------- | -------- | -------- | +| duration | Animation playback duration| The maximum value is 1 second. If a larger value is set, the animation is still played for 1 second.| +| tempo | Animation playback speed.| Do not set this parameter in the widget. Use the default value 1.| +| delay | Animation delay duration.| Do not set this parameter in the widget. Use the default value 0.| +| iterations | Number of times that the animation is played.| Do not set this parameter in the widget. Use the default value 1.| + +The following sample code implements the animation effect of button rotation: + +![WidgetAnimation](figures/WidgetAnimation.gif) + +```ts +@Entry +@Component +struct AttrAnimationExample { + @State rotateAngle: number = 0; + + build() { + Column() { + Button('change rotate angle') + .onClick(() => { + this.rotateAngle = 90; + }) + .margin(50) + .rotate({ angle: this.rotateAngle }) + .animation({ + curve: Curve.EaseOut, + playMode: PlayMode.AlternateReverse + }) + }.width('100%').margin({ top: 20 }) + } +} +``` diff --git a/en/application-dev/application-models/arkts-ui-widget-page-custom-drawing.md b/en/application-dev/application-models/arkts-ui-widget-page-custom-drawing.md new file mode 100644 index 0000000000000000000000000000000000000000..a55cb9cd17cda67cc2989e5916db19c5cf36cc47 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-page-custom-drawing.md @@ -0,0 +1,77 @@ +# Applying Custom Drawing in the Widget + + +You can apply custom drawing in your ArkTS widget to create a more vibrant experience. Use the [Canvas](../reference/arkui-ts/ts-components-canvas-canvas.md) component to create a canvas on the widget, and then use the [CanvasRenderingContext2D](../reference/arkui-ts/ts-canvasrenderingcontext2d.md) object to draw custom graphics on the canvas. The following code snippet draws a smiling face in the center of a canvas. + +```ts +@Entry +@Component +struct Card { + private canvasWidth: number = 0; + private canvasHeight: number = 0; + // Initialize CanvasRenderingContext2D and RenderingContextSettings. + private settings: RenderingContextSettings = new RenderingContextSettings(true); + private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings); + + build() { + Column() { + Row() { + Canvas(this.context) + .margin('5%') + .width('90%') + .height('90%') + .onReady(() => { + console.info('[ArkTSCard] onReady for canvas draw content'); + // Obtain the actual width and height of the canvas in the onReady callback. + this.canvasWidth = this.context.width; + this.canvasHeight = this.context.height; + // Draw the background of the canvas. + this.context.fillStyle = 'rgba(203, 154, 126, 1.00)'; + this.context.fillRect(0, 0, this.canvasWidth, this.canvasHeight); + // Draw a red circle in the center of the canvas. + this.context.beginPath(); + let radius = this.context.width / 3; + let circleX = this.context.width / 2; + let circleY = this.context.height / 2; + this.context.moveTo(circleX - radius, circleY); + this.context.arc(circleX, circleY, radius, 2 * Math.PI, 0, true); + this.context.closePath(); + this.context.fillStyle = 'red'; + this.context.fill(); + // Draw the left eye of the smiling face. + let leftR = radius / 4; + let leftX = circleX - (radius / 2); + let leftY = circleY - (radius / 3.5); + this.context.beginPath(); + this.context.arc(leftX, leftY, leftR, 0, Math.PI, true); + this.context.strokeStyle = '#ffff00'; + this.context.lineWidth = 10; + this.context.stroke(); + // Draw the right eye of the smiling face. + let rightR = radius / 4; + let rightX = circleX + (radius / 2); + let rightY = circleY - (radius / 3.5); + this.context.beginPath(); + this.context.arc(rightX, rightY, rightR, 0, Math.PI, true); + this.context.strokeStyle = '#ffff00'; + this.context.lineWidth = 10; + this.context.stroke(); + // Draw the mouth of the smiling face. + let mouthR = radius / 2.5; + let mouthX = circleX; + let mouthY = circleY + (radius / 3); + this.context.beginPath(); + this.context.arc(mouthX, mouthY, mouthR, Math.PI, 0, true); + this.context.strokeStyle = '#ffff00'; + this.context.lineWidth = 10; + this.context.stroke(); + }) + } + }.height('100%').width('100%') + } +} +``` + +The figure below shows the effect. + +![WidgetCanvasDemo](figures/WidgetCanvasDemo.png) \ No newline at end of file diff --git a/en/application-dev/application-models/arkts-ui-widget-page-overview.md b/en/application-dev/application-models/arkts-ui-widget-page-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..2c709ff5c7c13c09e5a303f0adfeebe5c97690bd --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-page-overview.md @@ -0,0 +1,21 @@ +# Widget Page Capability Overview + + +You can leverage the ArkUI declarative paradigm to develop ArkTS widget pages. The following widget pages are automatically generated by a DevEco Studio template. You can adjust the pages based on the real-world service scenarios. + + +![WidgetPreviewPage](figures/WidgetPreviewPage.png) + + +ArkTS widgets have full capabilities of JS widgets, with added animation and custom drawing capabilities plus partial support for components, events, animations, data management, and state management capabilities of the [declarative paradigm](../reference/arkui-ts/ts-components-summary.md). For details, see [Page Capabilities Supported by ArkTS Widgets](#page-capabilities-supported-by-arkts-widgets). + + +## Page Capabilities Supported by ArkTS Widgets + +For details about the page capabilities supported by ArkTS widgets, see [Learning ArkTS](../quick-start/arkts-create-custom-components.md) and [ArkTS-based Declarative Development Paradigm](../reference/arkui-ts/ts-components-summary.md). + +Only the APIs marked with "supported in ArkTS widgets" can be used for ArkTS widgets. Pay special attention to the capability differences with applications. + +For example, the following description indicates that the @Component decorator can be used in ArkTS widgets. + +![WidgetSupportApi](figures/WidgetSupportApi.png) diff --git a/en/application-dev/application-models/arkts-ui-widget-update-by-status.md b/en/application-dev/application-models/arkts-ui-widget-update-by-status.md new file mode 100644 index 0000000000000000000000000000000000000000..b27958c66d3e174a66c80c90e46cdd71f5ecf668 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-update-by-status.md @@ -0,0 +1,171 @@ +# Updating Widget Content by State + + +There are cases where multiple copies of the same widget are added to the home screen to accommodate different needs. In these cases, the widget content needs to be dynamically updated based on the state. This topic exemplifies how this is implemented. In the following example, two weather widgets are added to the home screen: one for displaying the weather of London, and the other Beijing, both configured to be updated at 07:00 every morning. The widget provider detects the target city, and then displays the city-specific weather information on the widgets. + + +- Widget configuration file: Configure the widget to be updated at 07:00 every morning. + + ```json + { + "forms": [ + { + "name": "widget", + "description": "This is a service widget.", + "src": "./ets/widget/pages/WidgetCard.ets", + "uiSyntax": "arkts", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDefault": true, + "updateEnabled": true,"scheduledUpdateTime": "07:00", + "updateDuration": 0, + "defaultDimension": "2*2", + "supportDimensions": ["2*2"] + } + ] + } + ``` + +- Widget page: A widget has different states and needs to be updated by state. When the state changes, **postCardAction** is called to notify the EntryFormAbility. + + ```ts + let storage = new LocalStorage(); + @Entry(storage) + @Component + struct WidgetCard { + @LocalStorageProp('textA') textA: string = 'To be updated...'; + @LocalStorageProp('textB') textB: string ='To be updated...'; + @State selectA: boolean = false; + @State selectB: boolean = false; + + build() { + Column() { + Row() { + Checkbox({ name: 'checkbox1', group: 'checkboxGroup' }) + .select(false) + .onChange((value: boolean) => { + this.selectA = value; + postCardAction(this, { + 'action': 'message', + 'params': { + 'selectA': JSON.stringify(value) + } + }); + }) + Text ('State A') + } + + Row() { + Checkbox({ name: 'checkbox2', group: 'checkboxGroup' }) + .select(false) + .onChange((value: boolean) => { + this.selectB = value; + postCardAction(this, { + 'action': 'message', + 'params': { + 'selectB': JSON.stringify(value) + } + }); + }) + Text ('State B') + } + + Row() {// Content that is updated only in state A + Text ('State A:') + Text(this.textA) + } + + Row() { // Content that is updated only in state B + Text ('State B:') + Text(this.textB) + } + }.padding('10%') + } + } + ``` + +- EntryFormAbility: The widget state data is stored in the local database. When the update event callback is triggered, the current widget state is obtained through **formId**, and then content is updated based on the state obtained. + + ```ts + import formInfo from '@ohos.app.form.formInfo' + import formProvider from '@ohos.app.form.formProvider'; + import formBindingData from '@ohos.app.form.formBindingData'; + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import dataStorage from '@ohos.data.storage' + + export default class EntryFormAbility extends FormExtensionAbility { + onAddForm(want) { + let formId = want.parameters[formInfo.FormParam.IDENTITY_KEY]; + let isTempCard: boolean = want.parameters[formInfo.FormParam.TEMPORARY_KEY]; + if (isTempCard === false) {// If the widget is a normal one, the widget information is persisted. + console.info('Not temp card, init db for:' + formId); + let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore') + storeDB.putSync('A' + formId, 'false'); + storeDB.putSync('B' + formId, 'false'); + storeDB.flushSync(); + } + let formData = {}; + return formBindingData.createFormBindingData(formData); + } + + onRemoveForm(formId) { + console.info('onRemoveForm, formId:' + formId); + let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore') + storeDB.deleteSync('A' + formId); + storeDB.deleteSync('B' + formId); + } + + // If the widget is a temporary one, it is recommended that the widget information be persisted when the widget is converted to a normal one. + onCastToNormalForm(formId) { + console.info('onCastToNormalForm, formId:' + formId); + let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore') + storeDB.putSync('A' + formId, 'false'); + storeDB.putSync('B' + formId, 'false'); + storeDB.flushSync(); + } + + onUpdateForm(formId) { + let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore') + let stateA = storeDB.getSync('A' + formId, 'false').toString() + let stateB = storeDB.getSync('B' + formId, 'false').toString() + // Update textA in state A. + if (stateA === 'true') { + let formInfo = formBindingData.createFormBindingData({ + 'textA': 'AAA' + }) + formProvider.updateForm(formId, formInfo) + } + // Update textB in state B. + if (stateB === 'true') { + let formInfo = formBindingData.createFormBindingData({ + 'textB': 'BBB' + }) + formProvider.updateForm(formId, formInfo) + } + } + + onFormEvent(formId, message) { + // Store the widget state. + console.info('onFormEvent formId:' + formId + 'msg:' + message); + let storeDB = dataStorage.getStorageSync(this.context.filesDir + 'myStore') + let msg = JSON.parse(message) + if (msg.selectA != undefined) { + console.info('onFormEvent selectA info:' + msg.selectA); + storeDB.putSync('A' + formId, msg.selectA); + } + if (msg.selectB != undefined) { + console.info('onFormEvent selectB info:' + msg.selectB); + storeDB.putSync('B' + formId, msg.selectB); + } + storeDB.flushSync(); + } + }; + ``` + + +> **NOTE** +> +> When the local database is used for widget information persistence, it is recommended that [TEMPORARY_KEY](../reference/apis/js-apis-app-form-formInfo.md#formparam) be used to determine whether the currently added widget is a normal one in the [onAddForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onaddform) lifecycle callback. If the widget is a normal one, the widget information is directly persisted. If the widget is a temporary one, the widget information is persisted when the widget is converted to a normal one ([onCastToNormalForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#oncasttonormalform)). In addition, the persistent widget information needs to be deleted when the widget is destroyed ([onRemoveForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onremoveform)), preventing the database size from continuously increasing due to repeated widget addition and deletion. diff --git a/en/application-dev/application-models/arkts-ui-widget-update-by-time.md b/en/application-dev/application-models/arkts-ui-widget-update-by-time.md new file mode 100644 index 0000000000000000000000000000000000000000..2c2643c802aff436656f5855d67c00e1a3c38dcd --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-update-by-time.md @@ -0,0 +1,99 @@ +# Configuring a Widget to Update Periodically + +Before configuring a widget to update periodically, enable the periodic update feature by setting the **updateEnabled** field to **true** in the **form_config.json** file. + +The widget framework provides the following modes of updating widgets periodically: + + +- Set the update interval: The widget will be updated at the specified interval by calling [onUpdateForm](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform). You can specify the interval by setting the [updateDuration](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update once an hour. + + > **NOTE** + > + > **updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used. + + ```json + { + "forms": [ + { + "name": "widget", + "description": "This is a service widget.", + "src": "./ets/widget/pages/WidgetCard.ets", + "uiSyntax": "arkts", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDefault": true, + "updateEnabled": true, // Enable the periodic update feature. + "scheduledUpdateTime": "10:30", + "updateDuration": 2, // Set the interval to update the widget. The value is a natural number, in the unit of 30 minutes. + "defaultDimension": "2*2", + "supportDimensions": ["2*2"] + } + ] + } + ``` + +- Set the scheduled update time: The widget will be updated at the scheduled time every day. You can specify the time by setting the [scheduledUpdateTime](arkts-ui-widget-configuration.md) field in the **form_config.json** file. For example, you can configure the widget to update at 10:30 a.m. every day. + + > **NOTE** + > + > **updateDuration** takes precedence over **scheduledUpdateTime**. For the **scheduledUpdateTime** settings to take effect, set **updateDuration** to **0**. + + + ```json + { + "forms": [ + { + "name": "widget", + "description": "This is a service widget.", + "src": "./ets/widget/pages/WidgetCard.ets", + "uiSyntax": "arkts", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDefault": true, + "updateEnabled": true, // Enable the periodic update feature. + "scheduledUpdateTime": "10:30", // Set the scheduled time to update the widget. + "updateDuration": 0, + "defaultDimension": "2*2", + "supportDimensions": ["2*2"] + } + ] + } + ``` + +- Set the next update time: The widget will be updated next time at the specified time. You can specify the time by calling the [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime) API. The minimum update interval is 5 minutes. For example, you can configure the widget to update within 5 minutes after the API is called. + + ```ts + import formProvider from '@ohos.app.form.formProvider'; + + let formId = '123456789'; // Use the actual widget ID in real-world scenarios. + try { + // Configure the widget to update in 5 minutes. + formProvider.setFormNextRefreshTime(formId, 5, (err, data) => { + if (err) { + console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`); + return; + } else { + console.info('Succeeded in setFormNextRefreshTimeing.'); + } + }); + } catch (err) { + console.error(`Failed to setFormNextRefreshTime. Code: ${err.code}, message: ${err.message}`); + } + ``` + + +When periodic update is triggered, the system calls the [onUpdateForm()](../reference/apis/js-apis-app-form-formExtensionAbility.md#onupdateform) lifecycle callback of the FormExtensionAbility. In the callback, [updateForm()](../reference/apis/js-apis-app-form-formProvider.md#updateform) can be used to update the widget by the provider. For details about how to use **onUpdateForm()**, see [Updating Widget Content Through FormExtensionAbility](arkts-ui-widget-event-formextensionability.md). + + +> **NOTE** +> 1. Each widget can be updated at the specified interval for a maximum of 50 times every day, including updates triggered by setting [updateDuration](arkts-ui-widget-configuration.md) or calling [setFormNextRefreshTime()](../reference/apis/js-apis-app-form-formProvider.md#setformnextrefreshtime). When the limit is reached, the widget cannot be updated in this mode again. The number of update times is reset at 00:00 every day. +> +> 2. The same timer is used for timing updates at the specified interval. Therefore, the first scheduled update of widgets may have a maximum deviation of 30 minutes. For example, the first widget A (updated every half an hour) is added at 03:20. The timer starts and triggers an update every half an hour. The second widget B (updated every half an hour) is added at 03:40. When the timer event is triggered at 03:50, widget A is updated, and widget B will be updated at 04:20 next time. +> +> 3. Updates at the specified interval and updates at the scheduled time are triggered only when the screen is on. When the screen is off, the update action is merely recorded. When the screen is on, the update action is performed. 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 new file mode 100644 index 0000000000000000000000000000000000000000..b1b09dc409380da8e530f571b2e5711ec63edd10 --- /dev/null +++ b/en/application-dev/application-models/arkts-ui-widget-working-principles.md @@ -0,0 +1,62 @@ +# ArkTS Widget Working Principles + + +## Implementation Principles + +**Figure 1** ArkTS widget implementation principles + +![WidgetPrinciple](figures/WidgetPrinciple.png) + +- Widget host: an application that displays the widget content and controls the widget location. Only the system application can function as a widget host. + +- Widget provider: an application that provides the widget content to display and controls how widget components are laid out and how they interact with users. + +- Widget Manager: a resident agent that manages widgets in the system. It provides the [formProvider](../reference/apis/js-apis-app-form-formProvider.md) and [formHost](../reference/apis/js-apis-app-form-formHost.md) APIs as well as the APIs for widget management, usage, and periodic updates. + +- Widget rendering service: a service that manages widget rendering instances. Widget rendering instances are bound to the [widget components](../reference/arkui-ts/ts-basic-components-formcomponent.md) on the widget host on a one-to-one basis. The widget rendering service runs the widget page code **widgets.abc** for rendering, and sends the rendered data to the corresponding widget component on the widget host. + + **Figure 2** Working principles of the ArkTS widget rendering service +![WidgetRender](figures/WidgetRender.png) + +Unlike JS widgets, ArkTS widgets support logic code running. The widget page code **widgets.abc** is executed by the widget rendering service, which is managed by the Widget Manager. Each widget component of a widget host corresponds to a rendering instance in the widget rendering service. Rendering instances of a widget provider run in the same virtual machine operating environment, and rendering instances of different widget providers run in different virtual machine operating environments. In this way, the resources and state data are isolated between widgets of different widget providers. During development, pay attention to the use of the [globalThis](uiability-data-sync-with-ui.md#using-globalthis-between-uiability-and-page) object. Use one **globalThis** object for widgets from the same widget provider, and different **globalThis** objects for widgets from different widget providers. + + +## Advantages of ArkTS Widgets + +As a quick entry to applications, ArkTS widgets outperform JS widgets in the following aspects: + +- Improved development experience and efficiency, thanks to the unified development paradigm + + ArkTS widgets share the same declarative UI development framework as application pages. This means that the page layouts can be directly reused in widgets, improving development experience and efficiency. + + **Figure 3** Comparison of widget project structures + + ![WidgetProject](figures/WidgetProject.png) + +- More widget features + - Animation: ArkTS widgets support the [attribute animation](../reference/arkui-ts/ts-animatorproperty.md) and [explicit animation](../reference/arkui-ts/ts-explicit-animation.md) capabilities, which can be leveraged to deliver a more engaging experience. + - Custom drawing: ArkTS widgets allow you to draw graphics with the [\](../reference/arkui-ts/ts-components-canvas-canvas.md) component to present information more vividly. + - Logic code execution: The capability to run logic code in widgets means that service logic can be self-closed in widgets, expanding the use cases of widgets. + + +## Constraints on ArkTS Widgets + +Compared with JS widgets, ArkTS widgets provide more capabilities, but they are also more prone to malicious behavior. To account for the impact on the widget host – typically the home screen, ArkTS widgets are subject to the following restrictions: + +- The .so file cannot be loaded. + +- The native programming language cannot be used for development. + +- Only [partial](arkts-ui-widget-page-overview.md) components, events, animations, data management, state management, and API capabilities of the declarative paradigm are supported. + +- The event processing of the widget is independent of that of the widget host. To prevent gesture conflicts, avoid using swipers in the widget when the widget host supports left and right swipes. + +In addition, ArkTS widgets do not support the following features: + +- Importing modules + +- Instant preview + +- Breakpoint debugging. + +- Hot reload diff --git a/en/application-dev/application-models/common-event-overview.md b/en/application-dev/application-models/common-event-overview.md index e8be9abaa3015a5512c47af55d2f364be0de79ad..3f532865e686592282b9080f234c088ee24a64f8 100644 --- a/en/application-dev/application-models/common-event-overview.md +++ b/en/application-dev/application-models/common-event-overview.md @@ -7,8 +7,7 @@ OpenHarmony provides Common Event Service (CES) for applications to subscribe to Common events are classified into system common events and custom common events. -- System common events: defined in CES. Only system applications and system services can publish system common events, such as HAP installation, update, and uninstall. For details about the supported system common events, see [Support](../reference/apis/js-apis-commonEventManager.md#support). - +- System common events: defined in CES. Currently, only system applications and system services can publish system common events, such as HAP installation, update, and uninstall. For details about the supported system common events, see [System Common Events](../reference/apis/commonEventManager-definitions.md). - Custom common events: customized by applications to implement cross-process event communication. @@ -16,9 +15,7 @@ Common events are also classified into unordered, ordered, and sticky common eve - Unordered common events: common events that CES forwards regardless of whether subscribers receive the events and when they subscribe to the events. - -- Ordered common events: common events that CES forwards based on the subscriber priority. CES forwards common events to the subscriber with lower priority only after receiving a reply from the previous subscriber with higher priority. Subscribers with the same priority receive common events in a random order. - +- Ordered common events: common events that CES forwards based on the subscriber priority. CES preferentially forwards an ordered common event to the subscriber with higher priority, waits until the subscriber receives the event, and then forwards the events to the subscriber with lower priority. Subscribers with the same priority receive common events in a random order. - Sticky common events: common events that can be sent to a subscriber before or after they initiate a subscription. Only system applications and system services can send sticky common events, which remain in the system after being sent. The sends must first request the **ohos.permission.COMMONEVENT_STICKY** permission. For details about the configuration, see [Permission Application Guide](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). diff --git a/en/application-dev/application-models/common-event-static-subscription.md b/en/application-dev/application-models/common-event-static-subscription.md index 013d7709e8c1588571e5c53f2a60ea012ee4f1fa..86a3b593d63e49c4b3bd8f66f49b3e39d156d535 100644 --- a/en/application-dev/application-models/common-event-static-subscription.md +++ b/en/application-dev/application-models/common-event-static-subscription.md @@ -2,45 +2,51 @@ ## When to Use -A static subscriber is started once it receives a target event published by the system or application. At the same time, the **onReceiveEvent** callback is triggered, in which you can implement the service logic. For example, if an application needs to execute some initialization tasks during device power-on, the application can subscribe to the power-on event in static mode. After receiving the power-on event, the application is started to execute the initialization tasks. Subscribing to a common event in static mode is achieved by configuring a declaration file and implementing a class that inherits from **StaticSubscriberExtensionAbility**. Note that this subscribing mode has negative impact on system power consumption. Therefore, exercise caution when using this mode. +A static subscriber is started once it receives a target event published by the system or application. At the same time, the [onReceiveEvent()](../reference/apis/js-apis-application-staticSubscriberExtensionAbility.md#staticsubscriberextensionabilityonreceiveevent) callback is triggered. + +You can implement the service logic in the [onReceiveEvent()](../reference/apis/js-apis-application-staticSubscriberExtensionAbility.md#staticsubscriberextensionabilityonreceiveevent) callback. For example, if an application needs to execute some initialization tasks during device power-on, the application can subscribe to the power-on event in static mode. After receiving the power-on event, the application is started to execute the initialization tasks. + +Subscribing to a common event in static mode is achieved by configuring a declaration file and implementing a class that inherits from [StaticSubscriberExtensionAbility](../reference/apis/js-apis-application-staticSubscriberExtensionAbility.md). + +> **NOTE** +> +> The static subscription mode has negative impact on system power consumption. Therefore, exercise caution when using this mode. ## How to Develop -1. Declaring a Static Subscriber +1. Declaring a static subscriber. - To declare a static subscriber, create an ExtensionAbility, which is derived from the **StaticSubscriberExtensionAbility** class, in the project. The sample code is as follows: + To declare a static subscriber, create an ExtensionAbility, which is derived from the **StaticSubscriberExtensionAbility** class, in the project. + + You can implement service logic in the **onReceiveEvent()** callback. ```ts import StaticSubscriberExtensionAbility from '@ohos.application.StaticSubscriberExtensionAbility' export default class StaticSubscriber extends StaticSubscriberExtensionAbility { - onReceiveEvent(event) { - console.log('onReceiveEvent, event:' + event.event); - } + onReceiveEvent(event) { + console.info('onReceiveEvent, event: ' + event.event); + } } ``` - You can implement service logic in the **onReceiveEvent** callback. +2. Configure static subscriber settings. - - -2. Project Configuration for a Static Subscriber - - After writing the static subscriber code, configure the subscriber in the **module.json5** file. The configuration format is as follows: + After writing the static subscriber code, configure the subscriber in the [module.json5](../quick-start/module-configuration-file.md) file. ```ts { "module": { - ...... + ... "extensionAbilities": [ { "name": "StaticSubscriber", - "srcEntrance": "./ets/StaticSubscriber/StaticSubscriber.ts", + "srcEntry": "./ets/staticsubscriber/StaticSubscriber.ts", "description": "$string:StaticSubscriber_desc", "icon": "$media:icon", "label": "$string:StaticSubscriber_label", "type": "staticSubscriber", - "visible": true, + "exported": true, "metadata": [ { "name": "ohos.extension.staticSubscriber", @@ -49,14 +55,14 @@ A static subscriber is started once it receives a target event published by the ] } ] - ...... + ... } } ``` - Pay attention to the following fields in the JSON file: + Some fields in the file are described as follows: - - **srcEntrance**: entry file path of the ExtensionAbility, that is, the file path of the static subscriber declared in Step 2. + - **srcEntry **: entry file path of the ExtensionAbility, that is, the file path of the static subscriber declared in Step 2. - **type**: ExtensionAbility type. For a static subscriber, set this field to **staticSubscriber**. @@ -64,39 +70,49 @@ A static subscriber is started once it receives a target event published by the - **name**: name of the ExtensionAbility. For a static subscriber, declare the name as **ohos.extension.staticSubscriber** for successful identification. - **resource**: path that stores the ExtensionAbility configuration, which is customizable. In this example, the path is **resources/base/profile/subscribe.json**. - A level-2 configuration file pointed to by **metadata** must be in the following format: - ```ts - { - "commonEvents": [ - { - "name": "xxx", - "permission": "xxx", - "events":[ - "xxx" - ] - } - ] - } - ``` +3. Configure the level-2 configuration file to which the metadata points. - If the level-2 configuration file is not declared in this format, the file cannot be identified. The fields are described as follows: + ```json + { + "commonEvents": [ + { + "name": "xxx", + "permission": "xxx", + "events":[ + "xxx" + ] + } + ] + } + ``` - - **name**: name of the ExtensionAbility, which must be the same as the name of **extensionAbility** declared in **module.json5**. + If the level-2 configuration file is not declared in this format, the file cannot be identified. Some fields in the file are described as follows: - - **permission**: permission required for the publisher. If a publisher without the required permission attempts to publish an event, the event is regarded as invalid and will not be published. + - **name**: name of the ExtensionAbility, which must be the same as the name of **extensionAbility** declared in **module.json5**. + - **permission**: permission required for the publisher. If a publisher without the required permission attempts to publish an event, the event is regarded as invalid and will not be published. + - **events**: list of target events to subscribe to. - - **events**: list of target events to subscribe to. +4. Modify the [preset configuration file](https://gitee.com/openharmony/vendor_hihope/blob/master/rk3568/preinstall-config/install_list_permissions.json) of the device, that is, the **/system/etc/app/install_list_permission.json** file on the device. When the device is started, this file is read. During application installation, the common event type specified by **allowCommonEvent** in the file is authorized. The **install_list_permission.json** file contains the following fields: -3. Device System Configuration + - **bundleName**: bundle name of the application. + - **app_signature**: fingerprint information of the application. For details, see [Application Privilege Configuration Guide](../../device-dev/subsystems/subsys-app-privilege-config-guide.md#configuration-in-install_list_capabilityjson). + - **allowCommonEvent**: type of common event that can be started by static broadcast. - In the device system configuration file **/etc/static_subscriber_config.json**, add the bundle name of the static subscriber. + > **NOTE** + > + > The **install_list_permissions.json** file is available only for preinstalled applications. - ```json - { - "xxx", - "ohos.extension.staticSubscriber", - "xxx" - } - ``` + ```json + [ + ... + { + "bundleName": "com.example.myapplication", // Bundle name of the application. + "app_signature": ["****"], // Fingerprint information of the application. + "allowCommonEvent": ["usual.event.A", "usual.event.B"], // Type of common event that can be started by static broadcast. + ] + } + ] + ``` + \ No newline at end of file diff --git a/en/application-dev/application-models/common-event-subscription-overview.md b/en/application-dev/application-models/common-event-subscription-overview.md index 20064af92d3df2e6f7ab7d62c4f71f911848057a..262f30c87e6018fed4e417a196dcaeeb58e42ae2 100644 --- a/en/application-dev/application-models/common-event-subscription-overview.md +++ b/en/application-dev/application-models/common-event-subscription-overview.md @@ -1,7 +1,7 @@ # Common Event Subscription Overview -​The common event service provides two subscription modes: dynamic and static. The biggest difference between these two modes is that dynamic subscription requires the application to be running, while static subscription does not. +The common event service provides two subscription modes: dynamic and static. The biggest difference between these two modes is that dynamic subscription requires the application to be running, while static subscription does not. - In dynamic subscription mode, a subscriber subscribes to common events by calling an API during the running period. For details, see [Subscribing to Common Events in Dynamic Mode](common-event-subscription.md). -- In static subscription mode, a subscriber subscribes to common events by configuring a declaration file and implementing a class that inherits from StaticSubscriberExtensionAbility. For details, see [Subscribing to Common Events in Static Mode](common-event-static-subscription.md). +- In static subscription mode, a subscriber subscribes to common events by configuring a declaration file and implementing a class that inherits from **StaticSubscriberExtensionAbility**. For details, see [Subscribing to Common Events in Static Mode](common-event-static-subscription.md). diff --git a/en/application-dev/application-models/common-event-subscription.md b/en/application-dev/application-models/common-event-subscription.md index 031a9358101e6b1310c0dbeb9f0f7a4c416791e3..c3e3ebfa52415d5e0ebade26973f78a589fb348f 100644 --- a/en/application-dev/application-models/common-event-subscription.md +++ b/en/application-dev/application-models/common-event-subscription.md @@ -8,7 +8,7 @@ In dynamic subscription mode, an application subscribes to a common event when i ## Available APIs -For details about the APIs, see [API Reference](../reference/apis/js-apis-commonEvent.md#commoneventcreatesubscriber). +For details about the APIs, see [API Reference](../reference/apis/js-apis-commonEventManager.md#commoneventmanagersubscribe). | API| Description| | -------- | -------- | @@ -19,10 +19,10 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common ## How to Develop -1. Import the **commonEvent** module. +1. Import the **commonEventManager** module. ```ts - import commonEvent from '@ohos.commonEventManager'; + import commonEventManager from '@ohos.commonEventManager'; ``` 2. Create a **subscribeInfo** object. For details about the data types and parameters of the object, see [CommonEventSubscribeInfo](../reference/apis/js-apis-commonEventManager.md#commoneventsubscribeinfo). @@ -32,7 +32,7 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common let subscriber = null; // Subscriber information. let subscribeInfo = { - events: ["usual.event.SCREEN_OFF"], // Subscribe to the common event screen-off. + events: ["usual.event.SCREEN_OFF"], // Subscribe to the common event screen-off. } ``` @@ -40,14 +40,14 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common ```ts // Callback for subscriber creation. - commonEvent.createSubscriber(subscribeInfo, (err, data) => { - if (err) { - console.error(`[CommonEvent] CreateSubscriberCallBack err=${JSON.stringify(err)}`); - } else { - console.info(`[CommonEvent] CreateSubscriber success`); - subscriber = data; - // Callback for common event subscription. - } + commonEventManager.createSubscriber(subscribeInfo, (err, data) => { + if (err) { + console.error(`Failed to create subscriber. Code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Succeeded in creating subscriber.'); + subscriber = data; + // Callback for common event subscription. }) ``` @@ -56,14 +56,13 @@ For details about the APIs, see [API Reference](../reference/apis/js-apis-common ```ts // Callback for common event subscription. if (subscriber !== null) { - commonEvent.subscribe(subscriber, (err, data) => { - if (err) { - console.error(`[CommonEvent] SubscribeCallBack err=${JSON.stringify(err)}`); - } else { - console.info(`[CommonEvent] SubscribeCallBack data=${JSON.stringify(data)}`); - } - }) + commonEventManager.subscribe(subscriber, (err, data) => { + if (err) { + console.error(`Failed to subscribe common event. Code is ${err.code}, message is ${err.message}`); + return; + } + }) } else { - console.error(`[CommonEvent] Need create subscriber`); + console.error(`Need create subscriber`); } ``` diff --git a/en/application-dev/application-models/component-startup-rules.md b/en/application-dev/application-models/component-startup-rules.md index 0e6c2ce33c68913221c7b09f02e96327b0ea1c30..bddf63dbc69ea243733e6f60f67f92a854833bf7 100644 --- a/en/application-dev/application-models/component-startup-rules.md +++ b/en/application-dev/application-models/component-startup-rules.md @@ -23,22 +23,22 @@ In view of this, OpenHarmony formulates a set of component startup rules, as fol - **Before starting a component of another application, verify the visible field of the target component.** - - If the **visible** field of the target component is **false**, verify the **ohos.permission.START_INVISIBLE_ABILITY** permission. - - For details, see [Component Visible Configuration](../quick-start/module-configuration-file.md#abilities). + - If the **exported** field of the target component is **false**, verify the **ohos.permission.START_INVISIBLE_ABILITY** permission. + - For details, see [Component exported Configuration](../quick-start/module-configuration-file.md#abilities). - **Before starting a component of a background application, verify the BACKGROUND permission.** - An application is considered as a foreground application only when the application process gains focus or its UIAbility component is running in the foreground. - Verify the **ohos.permission.START_ABILITIES_FROM_BACKGROUND** permission. -- **When the startAbilityByCall() method is used, verify the call permission.** For details, see [Using Ability Call to Implement UIAbility Interaction](uiability-intra-device-interaction.md#using-ability-call-to-implement-uiability-interaction) and [Using Cross-Device Ability Call](hop-multi-device-collaboration.md#using-cross-device-ability-call). +- **When the startAbilityByCall() method is used, verify the call permission.** For details, see [Using Call to Implement UIAbility Interaction](uiability-intra-device-interaction.md#using-call-to-implement-uiability-interaction) and [Using Cross-Device Call](hop-multi-device-collaboration.md#using-cross-device-call). - Verify the **ohos.permission.ABILITY_BACKGROUND_COMMUNICATION** permission. > **NOTE** > > - Component startup control has been implemented since OpenHarmony v3.2 Release. -> -> - The new component startup rules are more strict than the original ones. You must be familiar with the new startup rules to prevent service exceptions. +> +> - The new component startup rules are more strict than the original ones. You must be familiar with the new startup rules to prevent service exceptions. ## Intra-Device Component Startup Rules diff --git a/en/application-dev/application-models/config-file-stage.md b/en/application-dev/application-models/config-file-stage.md new file mode 100644 index 0000000000000000000000000000000000000000..66cabf4a99fd4a3f5138c0a8ef1bbf2cefbe15c0 --- /dev/null +++ b/en/application-dev/application-models/config-file-stage.md @@ -0,0 +1,6 @@ +# Application Configuration File (Stage Model) + +The application configuration file contains information about the application configuration, application components, and permissions, as well as custom information. The information will be provided for the compiler, application market, and operating system in the build, distribution, and running phases. + +The application project code developed based on the stage model contains one **app.json5** file and one or more **module.json5** files. For details about common configuration items, see [Application- or Component-Level Configuration (Stage Model)](application-component-configuration-stage.md). For details about the two files, see [Application Configuration File Overview (Stage Model)](../quick-start/application-configuration-file-overview-stage.md). + diff --git a/en/application-dev/application-models/context-switch.md b/en/application-dev/application-models/context-switch.md index 2f52158f5d36be8c59f747376195e9e43078d1f9..e1d155c8a60f6ca3e225174aece28738663b8079 100644 --- a/en/application-dev/application-models/context-switch.md +++ b/en/application-dev/application-models/context-switch.md @@ -5,7 +5,7 @@ | -------- | -------- | -------- | | [getOrCreateLocalDir(callback:AsyncCallback<string>):void;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7)
[getOrCreateLocalDir():Promise<string>;](../reference/apis/js-apis-inner-app-context.md#contextgetorcreatelocaldir7-1) | There is no corresponding API in the stage model.| Applications developed on the stage model do not have the operation permission in the application root directory. Therefore, no corresponding API is provided.| | [verifyPermission(permission:string,options:PermissionOptions,callback:AsyncCallback<number>):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7)
[verifyPermission(permission:string,callback:AsyncCallback<number>):void;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-1)
[verifyPermission(permission:string,options?:PermissionOptions):Promise<number>;](../reference/apis/js-apis-inner-app-context.md#contextverifypermission7-2) | \@ohos.abilityAccessCtrl.d.ts | [verifyAccessTokenSync(tokenID: number, permissionName: Permissions): GrantStatus;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync9)
[verifyAccessToken(tokenID: number, permissionName: Permissions): Promise<GrantStatus>;](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstoken9) | -| [requestPermissionsFromUser(permissions:Array<string>,requestCode:number,resultCallback:AsyncCallback<PermissionRequestResult>):void;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)
[requestPermissionsFromUser(permissions:Array<string>,requestCode:number):Promise<PermissionRequestResult>;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7-1) | application\UIAbilityContext.d.ts | [requestPermissionsFromUser(permissions: Array<string>, requestCallback: AsyncCallback<PermissionRequestResult>) : void;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser)
[requestPermissionsFromUser(permissions: Array<string>) : Promise<PermissionRequestResult>;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextrequestpermissionsfromuser-1) | +| [requestPermissionsFromUser(permissions:Array<string>,requestCode:number,resultCallback:AsyncCallback<PermissionRequestResult>):void;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7)
[requestPermissionsFromUser(permissions:Array<string>,requestCode:number):Promise<PermissionRequestResult>;](../reference/apis/js-apis-inner-app-context.md#contextrequestpermissionsfromuser7-1) | \@ohos.abilityAccessCtrl.d.ts | [requestPermissionsFromUser(context: Context, permissionList: Array<Permissions>, requestCallback: AsyncCallback<PermissionRequestResult>) : void;](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9)
[requestPermissionsFromUser(context: Context, permissionList: Array<Permissions>) : Promise<PermissionRequestResult>;](../reference/apis/js-apis-abilityAccessCtrl.md#requestpermissionsfromuser9-1) | | [getApplicationInfo(callback:AsyncCallback<ApplicationInfo>):void;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7)
[getApplicationInfo():Promise<ApplicationInfo>;](../reference/apis/js-apis-inner-app-context.md#contextgetapplicationinfo7-1) | application\Context.d.ts | [applicationInfo: ApplicationInfo;](../reference/apis/js-apis-inner-application-context.md#attributes)| | [getBundleName(callback : AsyncCallback<string>): void;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7)
[getBundleName(): Promise<string>;](../reference/apis/js-apis-inner-app-context.md#contextgetbundlename7-1) | application\UIAbilityContext.d.ts | [abilityInfo.bundleName: string;](../reference/apis/js-apis-inner-application-uiAbilityContext.md#attributes)| | [getDisplayOrientation(callback : AsyncCallback<bundle.DisplayOrientation>): void;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7)
[getDisplayOrientation(): Promise<bundle.DisplayOrientation>;](../reference/apis/js-apis-inner-app-context.md#contextgetdisplayorientation7-1) | \@ohos.screen.d.ts | [readonly orientation: Orientation;](../reference/apis/js-apis-screen.md#orientation) | diff --git a/en/application-dev/application-models/create-pageability.md b/en/application-dev/application-models/create-pageability.md index 783646ff4cfd5fa2ab193005bfa9d182dc75b70c..d0f308ebc08e035d5568ee0e127a9739e400d124 100644 --- a/en/application-dev/application-models/create-pageability.md +++ b/en/application-dev/application-models/create-pageability.md @@ -76,22 +76,22 @@ In the FA model, you can call **getContext** of **featureAbility** to obtain the The following code snippet shows how to use **getContext()** to obtain the application context and distributed directory: ```ts -import featureAbility from '@ohos.ability.featureAbility' -import fileIo from '@ohos.fileio' +import featureAbility from '@ohos.ability.featureAbility'; +import fs from '@ohos.file.fs'; (async () => { - let dir: string + let dir: string; try { - console.info('Begin to getOrCreateDistributedDir') - dir = await featureAbility.getContext().getOrCreateDistributedDir() + console.info('Begin to getOrCreateDistributedDir'); + dir = await featureAbility.getContext().getOrCreateDistributedDir(); console.info('distribute dir is ' + dir) } catch (error) { - console.error('getOrCreateDistributedDir failed with ' + error) + console.error('getOrCreateDistributedDir failed with ' + error); } let fd: number; let path = dir + "/a.txt"; - fd = fileIo.openSync(path, 0o2 | 0o100, 0o666); - fileIo.close(fd); + fd = fs.openSync(path, fs.OpenMode.READ_WRITE).fd; + fs.close(fd); })() ``` diff --git a/en/application-dev/application-models/data-share-via-want.md b/en/application-dev/application-models/data-share-via-want.md index c04bea2916647804b51022cee1853f3b5d0a7d90..28184edbc9304e7c6fbfbfe673cf1b8deca7d635 100644 --- a/en/application-dev/application-models/data-share-via-want.md +++ b/en/application-dev/application-models/data-share-via-want.md @@ -1,111 +1,132 @@ # Using Want to Share Data Between Applications - Users often need to share data (such as a text or an image) from one application to another. The following uses PDF file sharing as an example to describe how to use Want to share data between applications. +Data sharing requires two UIAbility components (one for the sharing party and the other for the shared party) and one system component (used as the application sharing box). When the sharing party initiates data sharing by calling **startAbility()**, the system implicitly matches and displays all applications that support the type of data to share. After the user selects an application, the system starts the application to complete data sharing. -## Prerequisites - -1. There are two UIAbility components (one for the sharing party and the other for the shared party) and one system component (used as the application selector). When the sharing party initiates data sharing through **startAbility()**, the application selector is started. The system implicitly matches and displays all applications that support the type of data to share. After the user selects an application, the system starts that application to complete data sharing. - -2. In this section, data sharing is triggered by touching a button. You can use other ways to trigger data sharing during application development. This section focuses on the Want configuration used for data sharing. - -3. The following actions are involved in this section: - - **ACTION_SELECT (ohos.want.action.select)**: action of displaying the application selector. - - **ACTION_SEND_DATA (ohos.want.action.sendData)**: action of launching the UI for sending a single data record. It is used to transfer data to the shared party. - - -## How to Develop - -- Sharing party - 1. In the stage mode, the [File Descriptor (FD)](../reference/apis/js-apis-fileio.md#fileioopensync) is used for file transfer. This example assumes that the path of the file to share is obtained. - - ```ts - import fileIO from '@ohos.fileio'; - - // let path = ... - // Open the file whose path is a variable. - let fileFd = fileIO.openSync(path, 0o102, 0o666); - ``` - - 2. As described in the prerequisites, the sharing party starts an application selector and shares the data to the selector, and the selector transfers the data to the shared party. Want of the sharing party must be nested at two layers. At the first layer, implicit Want is used together with the **ohos.want.action.select** action to display the application selector. At the second layer, complete Want is declared in the custom field **parameters** to transfer the data to share. - - ```ts - import wantConstant from '@ohos.app.ability.wantConstant'; - - // let path = ... - // let fileFd = ... - // let fileSize = ... - let want = { - / This action is used to implicitly match the application selector. - action: wantConstant.Action.ACTION_SELECT, - // This is the custom parameter in the first layer of Want, - / which is intended to add information to the application selector. - parameters: { - // MIME type of PDF. - "ability.picker.type": "application/pdf", - "ability.picker.fileNames": [path], - "ability.picker.fileSizes": [fileSize], - // This nested Want ,which will be directly sent to the selected application. - "ability.want.params.INTENT": { - "action": "ohos.want.action.sendData", - "type": "application/pdf", - "parameters": { - "keyFd": {"type": "FD", "value": fileFd} - } - } - } +In this section, data sharing is triggered by touching a button. You can use other ways to trigger data sharing during application development. This section focuses on how to configure Want to implement data sharing. + +The following actions are involved for data sharing: + +- **ohos.want.action.select**: action of starting the application sharing box. +- **ohos.want.action.sendData**: action of sending a single data record, that is, transferring data to the shared party. + +## Sharing Party + +The sharing party starts an application sharing box and transfers the data to the shared party. Therefore, Want of the sharing party must be nested at two layers. In the first layer, implicit Want is used together with the **ohos.want.action.select** action to display the application sharing box. In the second layer, the data to share is declared + +in the custom field **parameters**, and then the Want that includes the **ohos.want.action.sendData** action and the **parameters** field is transferred to the application sharing box. The shared party obtains the shared data from **parameters**. + +```ts +import common from '@ohos.app.ability.common'; + +let fileType = 'application/pdf'; +let fileName = 'TestFile.pdf'; +let fileFd = -1; // Obtain the file descriptor (FD) of the file to share. +let fileSize; // Obtain the size of the file to share. + +function implicitStartAbility() { + let context = getContext(this) as common.UIAbilityContext; + let wantInfo = { + / This action is used to implicitly match the application sharing box. + action: 'ohos.want.action.select', + // This is the custom parameter in the first layer of Want, + / which is intended to add information to the application sharing box. + parameters: { + // MIME type of PDF. + 'ability.picker.type': fileType, + 'ability.picker.fileNames': [fileName], + 'ability.picker.fileSizes': [fileSize], + // This is nested Want ,which will be directly sent to the selected application. + 'ability.want.params.INTENT': { + 'action': 'ohos.want.action.sendData', + 'type': 'application/pdf', + 'parameters': { + 'keyFd': { 'type': 'FD', 'value': fileFd } + } } - ``` - - In the preceding code, the custom field **parameters** is used. The **ability.picker.\*** fields in the first-layer **parameters** are used to pass the information to be displayed on the application selector. The following fields are involved: - - - **"ability.picker.type"**: The application selector renders the file type icon based on this field. - - **"ability.picker.fileNames"**: The application selector displays the file name based on this field. - - **"ability.picker.fileSizes"**: The application selector displays the file size based on this field. The unit is byte. - - **"ability.picker.fileNames"** and **"ability.picker.fileSizes"** are arrays and have a one-to-one mapping. - - For example, when **"ability.picker.type"** is **"application/pdf"**, **"ability.picker.fileNames"** is **"["APIs.pdf"]"**, and **"ability.picker.fileSizes"** is **"[350 \* 1024]"**, the application selector is displayed as follows: - - ![stage-want2](figures/stage-want2.png) - - In the preceding code, the **ability.want.params.INTENT** field is nested Want. In this field, **action** and **type** are used for implicit matching by the application selector. For details about implicit matching, see [Matching Rules of Implicit Want](explicit-implicit-want-mappings.md#matching-rules-of-implicit-want). After the user selects an application, the nested Want of the **ability.want.params.INTENT** field is passed to that application. - -- Shared party - 1. As mentioned above, the application selector performs implicit matching based on the **ability.want.params.INTENT** field. Therefore, you must set **skills** in the ability configuration file (**module.json5** file in the stage model) of the shared party as follows: - - ```ts - "skills": [ - { - "entities": [ - // ... - ], - "actions": [ + } + } + context.startAbility(wantInfo).then(() => { + ... + }).catch((err) => { + ... + }) +} +``` + +> **NOTE** +> +> Data sharing can be implemented only in FD format. For details about how to obtain the FD and file name, see [File Management](../reference/apis/js-apis-file-fs.md). + +In the preceding code, under the custom field **parameters**, the following **ability.picker.*** fields are used to pass the information to be displayed on the application sharing box: + +- **ability.picker.type**: file type icon. +- **ability.picker.fileNames**: file name. +- **ability.picker.fileSizes**: file size, in bytes. +- **ability.picker.fileNames** and **ability.picker.fileSizes** are arrays and have a one-to-one mapping. + +The following figure shows an example. +![](figures/ability-startup-with-implicit-want2.png) + +## Shared Party + +To enable the shared party to identify the shared content, configure **skills** in the [module.json5 file](../quick-start/module-configuration-file.md) of the UIAbility of the shared party. The **actions** and **type** fields in **uris** match the **action** and **type** fields in **ability.want.params.INTENT** of the sharing party, respectively. + +```json +{ + "module": { + ... + "abilities": [ + { + ... + "skills": [ + { + ... + "actions": [ + "action.system.home", "ohos.want.action.sendData" - // ... - ], - "uris": [ - { - "type": "application/pdf" - }, - // ... - ] - }, - ] - ``` - - The **actions** and **type** fields in **uris** match the **action** and **type** fields in **ability.want.params.INTENT**, respectively. - - Files can be transferred in FD mode, but not URI mode. In implicit matching, the **type** field in Want must match the **type** field in **uris** under **skills** of the shared party. Therefore, specify only the **type** field in **uris**. If **host** and **port** are specified, the matching fails. The application selector initiates implicit matching based on **ability.want.params.INTENT**. Therefore, when the **uri** field added to **ability.want.params.INTENT** matches the **uris** field under **skills**, the matching is successful and additional data can be transferred. - 2. After the application selector starts the shared party, the system calls **onCreate** and passes **ability.want.params.INTENT** to the **want** parameter. - - ```ts - onCreate(want, launchParam) { - // When keyFd is undefined, the application crashes. - if (want["parameters"]["keyFd"] !== undefined) { - // Receive the file descriptor. - let fd = want["parameters"]["keyFd"].value; - // ... - } + ... + ], + "uris": [ + { + "type": "application/pdf" + }, + ] + } + ] } - ``` + ] + } +} +``` + +After the user selects an application, the Want nested in the **ability.want.params.INTENT** field is passed to that application. After the UIAbility of the application starts, the application obtains **want** information from [**onCreate()**](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [**onNewWant()**](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonnewwant). + +The following is an example of the Want obtained. You can use the FD of the shared file to perform required operations. + +```json +{ + "deviceId": "", + "bundleName": "com.example.myapplication", + "abilityName": "EntryAbility", + "moduleName": "entry", + "uri": "", + "type": "application/pdf", + "flags": 0, + "action": "ohos.want.action.sendData", + "parameters": { + "component.startup.newRules": true, + "keyFd": { + "type": "FD", + "value": 36 + }, + "mime-type": "application/pdf", + "moduleName": "entry", + "ohos.aafwk.param.callerPid": 3488, + "ohos.aafwk.param.callerToken": 537379209, + "ohos.aafwk.param.callerUid": 20010014 + }, + "entities": [] +} +``` diff --git a/en/application-dev/application-models/datashareextensionability.md b/en/application-dev/application-models/datashareextensionability.md index f671848f890277af92fc23869c5db0d57b02a316..bea3de69c6d7ad375206fb1d53bcc36c2624989d 100644 --- a/en/application-dev/application-models/datashareextensionability.md +++ b/en/application-dev/application-models/datashareextensionability.md @@ -1,4 +1,4 @@ # DataShareExtensionAbility (for System Applications Only) -DataShareExtensionAbility provides the data sharing capability. System applications can implement a DataShareExtensionAbility or access an existing DataShareExtensionAbility in the system. Third-party applications can only access an existing DataShareExtensionAbility. For details, see [DataShare Development](../database/database-datashare-guidelines.md). +DataShareExtensionAbility provides the data sharing capability. System applications can implement a DataShareExtensionAbility or access an existing DataShareExtensionAbility in the system. Third-party applications can only access an existing DataShareExtensionAbility. For details, see [Cross-Application Data Sharing Overview](../database/share-device-data-across-apps-overview.md). diff --git a/en/application-dev/application-models/enterprise-extensionAbility.md b/en/application-dev/application-models/enterprise-extensionAbility.md index 18d5ec828126a2d11b57bf4dd482c4af56fa14bc..da8054e54284cc3a32af5ea6acaafc9eabc01561 100644 --- a/en/application-dev/application-models/enterprise-extensionAbility.md +++ b/en/application-dev/application-models/enterprise-extensionAbility.md @@ -2,12 +2,11 @@ ## Introduction to EnterpriseAdminExtensionAbility -EnterpriseAdminExtensionAbility is a mandatory component for Mobile Device Management (MDM) applications. When developing MDM applications for enterprises, you need to inherit EnterpriseAdminExtensionAbility and implement MDM service logic in the EnterpriseAdminExtensionAbility instance. EnterpriseAdminExtensionAbility implements notifications of system management status changes and defines the callbacks when a device administrator application is enabled or disabled or an application is installed or uninstalled. +EnterpriseAdminExtensionAbility is a mandatory component for Mobile Device Management (MDM) applications. When developing MDM applications for enterprises, you need to inherit EnterpriseAdminExtensionAbility and implement MDM service logic in the EnterpriseAdminExtensionAbility instance. EnterpriseAdminExtensionAbility implements notifications of system management status changes and defines the callbacks for when a device administrator application is enabled or disabled or an application is installed or uninstalled. ## Constraints - EnterpriseAdminExtensionAbility is applicable only to enterprise administrator applications. - +EnterpriseAdminExtensionAbility is applicable only to enterprise administrator applications. ## Observing Activation/Deactivation of a Device Administrator Application and Installation/Removal of an Application @@ -24,59 +23,59 @@ EnterpriseAdminExtensionAbility is a mandatory component for Mobile Device Manag ### Available APIs | Class | API | Description | -| :------------------------------ | ----------------------------------------- | ---------------------------- | +| ------------------------------ | ----------------------------------------- | ---------------------------- | +| EnterpriseAdminExtensionAbility | onAdminEnabled(): void | Called when a device administrator application is activated. | | EnterpriseAdminExtensionAbility | onAdminDisabled(): void | Called when a device administrator application is deactivated.| | EnterpriseAdminExtensionAbility | onBundleAdded(bundleName: string): void | Called when an application is installed on a device. | -| EnterpriseAdminExtensionAbility | onAdminEnabled(): void | Called when a device administrator application is activated. | | EnterpriseAdminExtensionAbility | onBundleRemoved(bundleName: string): void | Called when an application is removed from a device. | ### How to Develop To implement EnterpriseAdminExtensionAbility, you need to activate the device administrator application and create **ExtensionAbility** in the code directory of the device administrator application. The procedure is as follows: -1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **EnterpriseExtAbility**. +1. In the **ets** directory of the target module, right-click and choose **New > Directory** to create a directory named **EnterpriseExtAbility**. 2. Right-click the **EnterpriseExtAbility** directory, and choose **New > TypeScript File** to create a file named **EnterpriseExtAbility.ts**. 3. Open the **EnterpriseExtAbility.ts** file and import the **EnterpriseAdminExtensionAbility** module. Inherit the **EnterpriseAdminExtensionAbility** module to the custom class and add application notification callbacks, such as **onAdminEnabled()** and **onAdminDisabled()**. When the device administrator application is activated or deactivated, the device administrator can receive notifications. -```ts -import EnterpriseAdminExtensionAbility from '@ohos.enterprise.EnterpriseAdminExtensionAbility'; - -export default class EnterpriseAdminAbility extends EnterpriseAdminExtensionAbility { - - onAdminEnabled() { - console.info("onAdminEnabled"); - } - - onAdminDisabled() { - console.info("onAdminDisabled"); - } - - onBundleAdded(bundleName: string) { - console.info("EnterpriseAdminAbility onBundleAdded bundleName:" + bundleName) - } - - onBundleRemoved(bundleName: string) { - console.info("EnterpriseAdminAbility onBundleRemoved bundleName" + bundleName) - } -}; -``` - -​ 4. Register **ServiceExtensionAbility** in the [**module.json5**](../quick-start/module-configuration-file.md) file corresponding to the project module. Set **type** to **enterpriseAdmin** and **srcEntrance** to the path of the ExtensionAbility code. - -```ts -"extensionAbilities": [ - { - "name": "ohos.samples.enterprise_admin_ext_ability", - "type": "enterpriseAdmin", - "visible": true, - "srcEntrance": "./ets/enterpriseextability/EnterpriseAdminAbility.ts" - } - ] -``` + ```ts + import EnterpriseAdminExtensionAbility from '@ohos.enterprise.EnterpriseAdminExtensionAbility'; + + export default class EnterpriseAdminAbility extends EnterpriseAdminExtensionAbility { + + onAdminEnabled() { + console.info("onAdminEnabled"); + } + + onAdminDisabled() { + console.info("onAdminDisabled"); + } + + onBundleAdded(bundleName: string) { + console.info("EnterpriseAdminAbility onBundleAdded bundleName:" + bundleName) + } + + onBundleRemoved(bundleName: string) { + console.info("EnterpriseAdminAbility onBundleRemoved bundleName" + bundleName) + } + }; + ``` + +4. Register **ServiceExtensionAbility** in the [**module.json5**](../quick-start/module-configuration-file.md) file corresponding to the project module. Set **type** to **enterpriseAdmin** and **srcEntry** to the path of the ExtensionAbility code. + + ```ts + "extensionAbilities": [ + { + "name": "ohos.samples.enterprise_admin_ext_ability", + "type": "enterpriseAdmin", + "exported": true, + "srcEntry": "./ets/enterpriseextability/EnterpriseAdminAbility.ts" + } + ] + ``` ## Example -Use **subscribeManagedEvent()** and **unsubscribeManagedEvent()** in the @ohos.enterprise.adminManager module to subscribe to application installation and removal events. When an application is installed or removed, the MDM application is notified of the event. Then, the MDM application reports the event in the callback to notify the enterprise administrator. +Use **subscribeManagedEvent** in the **@ohos.enterprise.adminManager** module to subscribe to application installation and removal events. When an application is installed or removed, the MDM application is notified of the event. Then, the MDM application reports the event in the callback to notify the enterprise administrator. To unsubscribe from events, use **unsubscribeManagedEvent**. ```ts @State managedEvents: Array = [0,1] @@ -105,3 +104,4 @@ Use **subscribeManagedEvent()** and **unsubscribeManagedEvent()** in the @ohos.e }) } ``` + diff --git a/en/application-dev/application-models/explicit-implicit-want-mappings.md b/en/application-dev/application-models/explicit-implicit-want-mappings.md index d2c44bb4b47eff6347d6b4a6c098feadb7a9eead..454fc89d718fb4adfe84c0eab67ebc332117ed26 100644 --- a/en/application-dev/application-models/explicit-implicit-want-mappings.md +++ b/en/application-dev/application-models/explicit-implicit-want-mappings.md @@ -1,28 +1,28 @@ # Matching Rules of Explicit Want and Implicit Want -Both explicit Want and implicit Want can be used to match an ability to start based on certain rules. These rules determine how the parameters set in Want match the configuration file declared by the target ability. +Both explicit [Want](../reference/apis/js-apis-app-ability-want.md) and implicit [Want](../reference/apis/js-apis-app-ability-want.md) can be used to match an application component to start based on certain rules. These rules determine how the parameters set in [want](../reference/apis/js-apis-app-ability-want.md) match the configuration file declared by the target application component. ## Matching Rules of Explicit Want -The table below describes the matching rules of explicit Want. +The table below describes the matching rules of explicit [Want](../reference/apis/js-apis-app-ability-want.md). | Name| Type| Matching Item| Mandatory| Rule Description| | -------- | -------- | -------- | -------- | -------- | -| deviceId | string | Yes| No| If this field is unspecified, only abilities on the local device are matched.| +| deviceId | string | Yes| No| If this field is unspecified, only application components on the local device are matched.| | bundleName | string | Yes| Yes| If **abilityName** is specified but **bundleName** is unspecified, the matching fails.| -| moduleName | string | Yes| No| If this field is unspecified and multiple modules with the same ability name exist in the application, the first ability is matched by default.| +| moduleName | string | Yes| No| If this field is unspecified and multiple modules with the same ability name exist in the application, the first application component is matched by default.| | abilityName | string | Yes| Yes| To use explicit Want, this field must be specified.| -| uri | string | No| No| This field is not used for matching. It is passed to the target ability as a parameter.| -| type | string | No| No| This field is not used for matching. It is passed to the target ability as a parameter.| -| action | string | No| No| This field is not used for matching. It is passed to the target ability as a parameter.| -| entities | Array<string> | No| No| This field is not used for matching. It is passed to the target ability as a parameter.| +| uri | string | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| +| type | string | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| +| action | string | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| +| entities | Array<string> | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| | flags | number | No| No| This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.| -| parameters | {[key: string]: any} | No| No| This field is not used for matching. It is passed to the target ability as a parameter.| +| parameters | {[key: string]: any} | No| No| This field is not used for matching. It is passed to the target application component as a parameter.| ## Matching Rules for Implicit Want -The table below describes the matching rules of implicit Want. +The table below describes the matching rules of implicit [Want](../reference/apis/js-apis-app-ability-want.md). | Name | Type | Matching Item| Mandatory| Rule Description | | ----------- | ------------------------------ | ------ | ---- | ------------------------------------------------------------ | @@ -35,100 +35,107 @@ The table below describes the matching rules of implicit Want. | action | string | Yes | No | | | entities | Array<string> | Yes | No | | | flags | number | No | No | This field is not used for matching and is directly transferred to the system for processing. It is generally used to set runtime information, such as URI data authorization.| -| parameters | {[key: string]: any} | No | No | This field is not used for matching. It is passed to the target ability as a parameter. | +| parameters | {[key: string]: any} | No | No | This field is not used for matching. It is passed to the target application component as a parameter. | Get familiar with the following about implicit Want: - The **want** parameter passed by the caller indicates the operation to be performed by the caller. It also provides data and application type restrictions. -- The **skills** field declares the capabilities of the target ability. For details, see [the skills tag](../quick-start/module-configuration-file.md#skills) in the [module.json5 file](../quick-start/module-configuration-file.md). +- The **skills** field declares the capabilities of the target application component. For details, see [the skills tag](../quick-start/module-configuration-file.md#skills) in the [module.json5 file](../quick-start/module-configuration-file.md). -The system matches the **want** parameter (including the **action**, **entities**, **uri**, and **type** attributes) passed by the caller against the **skills** configuration (including the **actions**, **entities**, **uris**, and **type** attributes) of the abilities one by one. When all the four attributes are matched, a dialog box is displayed for users to select a matched application. +The system matches the **want** parameter (including the **action**, **entities**, **uri**, and **type** attributes) passed by the caller against the **skills** configuration (including the **actions**, **entities**, **uris**, and **type** attributes) of the application components one by one. When all the four attributes are matched, a dialog box is displayed for users to select a matched application. ### Matching Rules of action in the want Parameter -The system matches the [action](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction) attribute in the **want** parameter passed by the caller against **actions** under **skills** of the abilities. +The system matches the **action** attribute in the **want** parameter passed by the caller against **actions** under **skills** of the application components. -- If **action** in the passed **want** parameter is specified but **actions** under **skills** of an ability is unspecified, the matching fails. +- If **action** in the passed **want** parameter is unspecified and **actions** under **skills** of an application component is unspecified, the matching fails. -- If **action** in the passed **want** parameter is unspecified but **actions** under **skills** of an ability is specified, the matching is successful. +- If **action** in the passed **want** parameter is specified but **actions** under **skills** of an application component is unspecified, the matching fails. -- If **action** in the passed **want** parameter is specified, and **actions** under **skills** of an ability is specified and contains **action** in the passed **want** parameter, the matching is successful. +- If **action** in the passed **want** parameter is unspecified but **actions** under **skills** of an application component is specified, the matching is successful. -- If **action** in the passed **want** parameter is specified, and **actions** under **skills** of an ability is specified but does not contain **action** in the passed **want** parameter, the matching fails. +- If **action** in the passed **want** parameter is specified, and **actions** under **skills** of an application component is specified and contains **action** in the passed **want** parameter, the matching is successful. - **Figure 1** Matching rules of action in the want parameter - - ![want-action](figures/want-action.png) +- If **action** in the passed **want** parameter is specified, and **actions** under **skills** of an application component is specified but does not contain **action** in the passed **want** parameter, the matching fails. + **Figure 1** Matching rules of action in the want parameter -### Matching Rules of entities in the want Parameter + ![want-action](figures/want-action.png) -The system matches the [entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity) attribute in the **want** parameter passed by the caller against **entities** under **skills** of the abilities. -- If **entities** in the passed **want** parameter is unspecified but **entities** under **skills** of an ability is specified, the matching is successful. +### Matching Rules of entities in the want Parameter -- If **entities** in the passed **want** parameter is unspecified but **entities** under **skills** of an ability is unspecified, the matching is successful. +The system matches the **entities** attribute in the **want** parameter passed by the caller against **entities** under **skills** of the application components. -- If **entities** in the passed **want** parameter is specified but **entities** under **skills** of an ability is unspecified, the matching fails. +- If **entities** in the passed **want** parameter is unspecified but **entities** under **skills** of an application component is specified, the matching is successful. -- If **entities** in the passed **want** parameter is specified, and **entities** under **skills** of an ability is specified and contains **entities** in the passed **want** parameter, the matching is successful. +- If **entities** in the passed **want** parameter is unspecified but **entities** under **skills** of an application component is unspecified, the matching is successful. -- If **entities** in the passed **want** parameter is specified, and **entities** under **skills** of an ability is specified but does not contain **entities** in the passed **want** parameter, the matching fails. +- If **entities** in the passed **want** parameter is specified but **entities** under **skills** of an application component is unspecified, the matching fails. - Figure 2 Matching rule of entities in the want parameter +- If **entities** in the passed **want** parameter is specified, and **entities** under **skills** of an application component is specified and contains **entities** in the passed **want** parameter, the matching is successful. - ![want-entities](figures/want-entities.png) +- If **entities** in the passed **want** parameter is specified, and **entities** under **skills** of an application component is specified but does not contain **entities** in the passed **want** parameter, the matching fails. + **Figure 2** Matching rules of entities in the want parameter -### Matching Rules of uri and type in the want Parameter + ![want-entities](figures/want-entities.png) -When the **uri** and **type** parameters are specified in the **want** parameter to initiate a component startup request, the system traverses the list of installed components and matches the **uris** array under **skills** of the abilities one by one. If one of the **uris** arrays under **skills** matches the **uri** and **type** in the passed **want**, the matching is successful. -Figure 3 Matching rules when uri and type are specified in the want parameter +### Matching Rules of uri and type in the want Parameter -![want-uri-type1](figures/want-uri-type1.png) +When the **uri** and **type** parameters are specified in the **want** parameter to initiate an application component startup request, the system traverses the list of installed components and matches the **uris** array under **skills** of the application components one by one. If one of the **uris** arrays under **skills** matches the **uri** and **type** in the passed **want**, the matching is successful. There are four combinations of **uri** and **type** settings. The matching rules are as follows: - Neither **uri** or **type** is specified in the **want** parameter. - - If the **uris** array under **skills** of an ability is unspecified, the matching is successful. - - If the **uris** array under **skills** of an ability contains an URI element whose **scheme** and **type** are unspecified, the matching is successful. + - If the **uris** array under **skills** of an application component is unspecified, the matching is successful. + - If the **uris** array under **skills** of an application component contains an URI element whose **scheme** and **type** are unspecified, the matching is successful. - In other cases, the matching fails. - Only **uri** is specified in the **want** parameter. - - If the **uris** array under **skills** of an ability is unspecified, the matching fails. - - If the **uris** array under **skills** of an ability contains an element whose [uri is matched](#matching-rules-of-uri) and **type** is unspecified, the matching is successful. Otherwise, the matching fails. + - If the **uris** array under **skills** of an application component is unspecified, the matching fails. + - If the **uris** array under **skills** of an application component contains an element whose [uri is matched](#matching-rules-of-uri) and **type** is unspecified, the matching is successful. Otherwise, the matching fails. - Only **type** is specified in the **want** parameter. - - If the **uris** array under **skills** of an ability is unspecified, the matching fails. - - If the **uris** array under **skills** of an ability contains an URI element whose **scheme** is unspecified and [type is matched](#matching-rules-of-type), the matching is successful. Otherwise, the matching fails. + - If the **uris** array under **skills** of an application component is unspecified, the matching fails. + - If the **uris** array under **skills** of an application component contains an URI element whose **scheme** is unspecified and [type is matched](#matching-rules-of-type), the matching is successful. Otherwise, the matching fails. + +- Both **uri** and **type** are specified in the **want** parameter, as shown below. + - If the **uris** array under **skills** of an application component is unspecified, the matching fails. + - If the **uris** array under **skills** of an application component contains an element whose [uri is matched](#matching-rules-of-uri) and [type is matched](#matching-rules-of-type), the matching is successful. Otherwise, the matching fails. -- Both **uri** and **type** are specified in the **want** parameter, as shown in Figure 3. - - If the **uris** array under **skills** of an ability is unspecified, the matching fails. - - If the **uris** array under **skills** of an ability contains an element whose [uri is matched](#matching-rules-of-uri) and [type is matched](#matching-rules-of-type), the matching is successful. Otherwise, the matching fails. +Leftmost URI matching: When only **scheme**, a combination of **scheme** and **host**, or a combination of **scheme**, **host**, and **port** is configured in the **uris** array under **skills** of the application component, the matching is successful only if the leftmost URI in the passed **want** parameter matches **scheme**, the combination of **scheme** and **host**, or the combination of **scheme**, **host**, and **port**. +**Figure 3** Matching rules when uri and type are specified in the want parameter -To simplify the description, **uri** and **type** passed in the **want** parameter are called **w_uri** and **w_type**, respectively; the **uris** array under **skills** of an ability to match is called **s_uris**; each element in the array is called **s_uri**. Matching is performed from top to bottom. +![want-uri-type1](figures/want-uri-type1.png) +To simplify the description: -Figure 4 Matching rules of uri and type in the want parameter +- **uri** in the **want** parameter passed in by the caller is called **w_uri**; each element in the **uris** array under **skills** of the application component to match is called **s_uri**. +- **type** in the **want** parameter passed in by the caller is called **w_type**; the type in the **uris** array under **skills** of the application component to match is called **s_type**. -![want-uri-type2](figures/want-uri-type2.png) +**Figure 4** Matching rules of uri and type in the want parameter + +![want-uri-type2](figures/want-uri-type2.png) ### Matching Rules of uri -To simplify the description, **uri** in the passed **want** parameter is called **w_uri**; **uri** under **skills** of an ability to match is called **s_uri**. The matching rules are as follows: +The rules are as follows: - If **scheme** of **s_uri** is unspecified and **w_uri** is unspecified, the matching is successful. Otherwise, the matching fails. - If **host** of **s_uri** is unspecified and **scheme** of **w_uri** and **scheme** of **s_uri** are the same, the matching is successful. Otherwise, the matching fails. -- If **path**, **pathStartWith**, and **pathRegex** of **s_uri** are unspecified and **w_uri** and **s_uri** are the same, the matching is successful. Otherwise, the matching fails. +- If **port** of **s_uri** is unspecified and the combination of **scheme** and **host** of **w_uri** is the same as the combination of **scheme** and **host** of **s_uri**, the matching is successful. Otherwise, the matching fails. + +- If **path**, **pathStartWith**, and **pathRegex** of **s_uri** are unspecified and the combination of **scheme**, **host**, and **port** of **w_uri** is the same as the combination of **scheme**, **host**, and **port** of **s_uri**, the matching is successful. Otherwise, the matching fails. - If **path** of **s_uri** is specified and the **full path expressions** of **w_uri** and **s_uri** are the same, the matching is successful. Otherwise, the matching of **pathStartWith** continues. @@ -138,12 +145,14 @@ To simplify the description, **uri** in the passed **want** parameter is called > **NOTE** > -> The **scheme**, **host**, **port**, **path**, **pathStartWith**, and **pathRegex** attributes of **uris** under **skills** of an ability are concatenated. If **path**, **pathStartWith**, and **pathRegex** are declared in sequence, **uris** can be concatenated into the following expressions: +> The **scheme**, **host**, **port**, **path**, **pathStartWith**, and **pathRegex** attributes of **uris** under **skills** of an application component are concatenated. If **path**, **pathStartWith**, and **pathRegex** are declared in sequence, **uris** can be concatenated into the following expressions: > +> - **Prefix URI expression**: When only **scheme**, a combination of **scheme** and **host**, or a combination of **scheme**, **host**, and **port** is configured in the configuration file, the matching is successful if a URI prefixed with the configuration file is passed in. +> * `scheme://` +> * `scheme://host` +> * `scheme://host:port` > - **Full path expression**: `scheme://host:port/path` -> > - **Prefix expression**: `scheme://host:port/pathStartWith` -> > - **Regular expression**: `scheme://host:port/pathRegex` @@ -153,7 +162,7 @@ To simplify the description, **uri** in the passed **want** parameter is called > > The matching rules of **type** described in this section are based on the fact that **type** in the **want** parameter is specified. If **type** is unspecified, follow the [matching rules of uri and type in the want parameter](#matching-rules-of-uri-and-type-in-the-want-parameter). -To simplify the description, **uri** in the passed **want** parameter is called **w_type**, and **type** of **uris** under **skills** of an ability to match is called **s_type**. The matching rules are as follows: +The matching rules are as follows: - If **s_type** is unspecified, the matching fails. diff --git a/en/application-dev/application-models/extensionability-overview.md b/en/application-dev/application-models/extensionability-overview.md index 5f03c023d20bc16e64a2eb4a28bbe026f0a7feb7..719a451c6b6b468925174d5be187ae3be82e71c5 100644 --- a/en/application-dev/application-models/extensionability-overview.md +++ b/en/application-dev/application-models/extensionability-overview.md @@ -9,9 +9,9 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab - [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md): ExtensionAbility component of the form type, which provides APIs related to widgets. -- [WorkSchedulerExtensionAbility](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md): ExtensionAbility component of the work_scheduler type, which provides APIs for registering, canceling, and querying Work Scheduler tasks. +- [WorkSchedulerExtensionAbility](../reference/apis/js-apis-WorkSchedulerExtensionAbility.md): ExtensionAbility component of the work_scheduler type, which provides callbacks for Work Scheduler tasks. -- [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod.md): ExtensionAbility component of the input_method type, which provides an input method framework that can be used to hide the keyboard, obtain the list of installed input methods, display the dialog box for input method selection, and more. +- [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod.md): ExtensionAbility component of the input_method type, which is used to develop input method applications. - [ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md): ExtensionAbility component of the service type, which provides APIs related to background service scenarios. @@ -25,6 +25,14 @@ An [ExtensionAbilityType](../reference/apis/js-apis-bundleManager.md#extensionab - [EnterpriseAdminExtensionAbility](../reference/apis/js-apis-EnterpriseAdminExtensionAbility.md): ExtensionAbility component of the enterprise_admin type, which provides APIs for processing enterprise management events, such as application installation events on devices and events indicating too many incorrect screen-lock password attempts. +> **NOTE** +> +> 1. Third-party applications cannot implement ServiceExtensionAbility, DataShareExtensionAbility, StaticSubscriberExtensionAbility, or WindowExtensionAbility. +> +> 2. To implement transaction processing in the background for a third-party application, use background tasks rather than ServiceExtensionAbility. For details, see [Background Task](../task-management/background-task-overview.md). +> +> 3. Third-party applications can use other types of ExtensionAbility components that have been defined. + ## Using ExtensionAbility of the Specified Type @@ -39,21 +47,20 @@ The following uses [InputMethodExtensionAbility](../reference/apis/js-apis-input ## Implementing ExtensionAbility of the Specified Type -The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](Widget-development-stage.md). +The following uses [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) as an example. The widget framework provides the base class [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). You derive this base class to create your own class (such as **MyFormExtensionAbility**), implement the callbacks, such as **onCreate()** and **onUpdateForm()**, to provide specific widget functionalities. For details, see [FormExtensionAbility](service-widget-overview.md). You do not need to care when to add or delete a widget. The lifecycle of the FormExtensionAbility instance and the lifecycle of the ExtensionAbility process where the FormExtensionAbility instance is located are scheduled and managed by FormManagerService. - ![form_extension](figures/form_extension.png) > **NOTE** > > For an application, all ExtensionAbility components of the same type run in an independent process, whereas UIAbility, ServiceExtensionAbility, and DataShareExtensionAbility run in another independent process. For details, see [Process Model (Stage Model)](process-model-stage.md). -> +> > For example, an application has one UIAbility component, one ServiceExtensionAbility, one DataShareExtensionAbility, two FormExtensionAbility, and one ImeExtensionAbility. When the application is running, there are three processes: -> +> > - UIAbility, ServiceExtensionAbility, and DataShareExtensionAbility run in an independent process. -> +> > - The two FormExtensionAbility components run in an independent process. -> +> > - The two ImeExtensionAbility components run in an independent process. diff --git a/en/application-dev/application-models/fa-model-development-overview.md b/en/application-dev/application-models/fa-model-development-overview.md index f26280e2cc96cac5601b29db322affcbb323f570..db48ff10046f5277a06c80633153a0efb6124f79 100644 --- a/en/application-dev/application-models/fa-model-development-overview.md +++ b/en/application-dev/application-models/fa-model-development-overview.md @@ -3,12 +3,13 @@ During application development based on the Feature Ability (FA) model, the following tasks are involved in the application model. -**Table 1** FA model development process + + **Table 1** FA model development process | Task| Introduction| Guide| | -------- | -------- | -------- | -| Application component development| Use the PageAbility, ServiceAbility, DataAbility, and widgets of the FA model to develop applications.| - [Application- or Component-Level Configuration](application-component-configuration-fa.md)
- [PageAbility Component](pageability-overview.md)
- [ServiceAbility Component](serviceability-overview.md)
- [DataAbility Component](dataability-overview.md)
- [Widget Development](Widget-development-fa.md)
- [Context](application-context-fa.md)
- [Want](want-fa.md) | -| Inter-process communication (IPC)| Learn the process model and common IPC modes of the FA model.| [Common Events](common-event-fa.md)
[Background Services](rpc.md) | -| Inter-thread communication| Learn the thread model and common inter-thread communication modes of the FA model.| [Inter-Thread Communication](itc-fa-overview.md)| +| Application component development| Use the PageAbility, ServiceAbility, DataAbility, and widgets of the FA model to develop applications.| - [Application- or Component-Level Configuration](application-component-configuration-fa.md)
- [PageAbility Component](pageability-overview.md)
- [ServiceAbility Component](serviceability-overview.md)
- [DataAbility Component](dataability-overview.md)
- [Widget Development](widget-development-fa.md)
- [Context](application-context-fa.md)
- [Want](want-fa.md)| +| Process model| Learn the process model and common IPC modes of the FA model.| [Common Events](common-event-fa.md)
[Background Services](rpc.md)| +| Thread model| Learn the thread model and common inter-thread communication modes of the FA model.| [Inter-Thread Communication](itc-fa-overview.md)| | Mission management| Learn the basic concepts and typical scenarios of mission management in the FA model.| [Mission Management](mission-management-fa.md)| -| Application configuration file| Learn the requirements for developing application configuration files in the FA model.| [Application Configuration File](../quick-start/application-configuration-file-overview-fa.md) | +| Application configuration file| Learn the requirements for developing application configuration files in the FA model.| [Application Configuration File](../quick-start/application-configuration-file-overview-fa.md)| diff --git a/en/application-dev/application-models/figures/AccessibilityFramework.png b/en/application-dev/application-models/figures/AccessibilityFramework.png new file mode 100644 index 0000000000000000000000000000000000000000..786233e6ac160972f62b9786397eb077f7ee767c Binary files /dev/null and b/en/application-dev/application-models/figures/AccessibilityFramework.png differ diff --git a/en/application-dev/application-models/figures/JSCardPrinciple.png b/en/application-dev/application-models/figures/JSCardPrinciple.png new file mode 100644 index 0000000000000000000000000000000000000000..558c2c2a679737eed2bf3b129f632e6300d0d2da Binary files /dev/null and b/en/application-dev/application-models/figures/JSCardPrinciple.png differ diff --git a/en/application-dev/application-models/figures/WidgerCameraCard.png b/en/application-dev/application-models/figures/WidgerCameraCard.png new file mode 100644 index 0000000000000000000000000000000000000000..55e62cdb8791ca6bbc95b7ea4c054e93270ce7a6 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgerCameraCard.png differ diff --git a/en/application-dev/application-models/figures/WidgetAnimation.gif b/en/application-dev/application-models/figures/WidgetAnimation.gif new file mode 100644 index 0000000000000000000000000000000000000000..eaddfb4cf4d0bf9613b3a108c26ea1617d68d3c1 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetAnimation.gif differ diff --git a/en/application-dev/application-models/figures/WidgetArchitecture.png b/en/application-dev/application-models/figures/WidgetArchitecture.png new file mode 100644 index 0000000000000000000000000000000000000000..b97ddda74fb89f32ed248c1d4c6d3e8e6e100a05 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetArchitecture.png differ diff --git a/en/application-dev/application-models/figures/WidgetCanvasDemo.png b/en/application-dev/application-models/figures/WidgetCanvasDemo.png new file mode 100644 index 0000000000000000000000000000000000000000..c09c82dd88cf002020990b5b8327701c445eeaaf Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetCanvasDemo.png differ diff --git a/en/application-dev/application-models/figures/widget-development-stage.png b/en/application-dev/application-models/figures/WidgetCardPage.png similarity index 100% rename from en/application-dev/application-models/figures/widget-development-stage.png rename to en/application-dev/application-models/figures/WidgetCardPage.png diff --git a/en/application-dev/application-models/figures/WidgetLocalStorageProp.png b/en/application-dev/application-models/figures/WidgetLocalStorageProp.png new file mode 100644 index 0000000000000000000000000000000000000000..1a45723865ff9f990c3a4197338e9cbc9eb3b6f4 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetLocalStorageProp.png differ diff --git a/en/application-dev/application-models/figures/WidgetModules.png b/en/application-dev/application-models/figures/WidgetModules.png new file mode 100644 index 0000000000000000000000000000000000000000..6eaac0b6ca404eb9575587add72935e9ce580030 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetModules.png differ diff --git a/en/application-dev/application-models/figures/WidgetPostCardAction.png b/en/application-dev/application-models/figures/WidgetPostCardAction.png new file mode 100644 index 0000000000000000000000000000000000000000..42a07e13036d6252309ca4e4bc857043486269c8 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetPostCardAction.png differ diff --git a/en/application-dev/application-models/figures/WidgetPreviewPage.png b/en/application-dev/application-models/figures/WidgetPreviewPage.png new file mode 100644 index 0000000000000000000000000000000000000000..5f614e3db780f8d83f3c2f0865a9521aacd2b0de Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetPreviewPage.png differ diff --git a/en/application-dev/application-models/figures/WidgetPrinciple.png b/en/application-dev/application-models/figures/WidgetPrinciple.png new file mode 100644 index 0000000000000000000000000000000000000000..68ca315394fe2cb5bd2580ca6df38b9940ac1349 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetPrinciple.png differ diff --git a/en/application-dev/application-models/figures/WidgetProject.png b/en/application-dev/application-models/figures/WidgetProject.png new file mode 100644 index 0000000000000000000000000000000000000000..299eed75fc1edfd9557e0fe743facb0e9c8d94b2 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetProject.png differ diff --git a/en/application-dev/application-models/figures/WidgetProjectCreate1.png b/en/application-dev/application-models/figures/WidgetProjectCreate1.png new file mode 100644 index 0000000000000000000000000000000000000000..5369f48edcee476ae8317b9f0e1fb98b06607e93 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetProjectCreate1.png differ diff --git a/en/application-dev/application-models/figures/WidgetProjectCreate2.png b/en/application-dev/application-models/figures/WidgetProjectCreate2.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf742e04e2f30febb05f2d8638193dc10532863 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetProjectCreate2.png differ diff --git a/en/application-dev/application-models/figures/WidgetProjectCreate3.png b/en/application-dev/application-models/figures/WidgetProjectCreate3.png new file mode 100644 index 0000000000000000000000000000000000000000..98429567ad24b1a83c67118173bf6cb504bea25d Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetProjectCreate3.png differ diff --git a/en/application-dev/application-models/figures/WidgetProjectView.png b/en/application-dev/application-models/figures/WidgetProjectView.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1c06e47502131983b0b7cd56e66269b5be6d88 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetProjectView.png differ diff --git a/en/application-dev/application-models/figures/WidgetRender.png b/en/application-dev/application-models/figures/WidgetRender.png new file mode 100644 index 0000000000000000000000000000000000000000..0f46bd74b0e48ac0c9f947d96d5e147786f547c0 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetRender.png differ diff --git a/en/application-dev/application-models/figures/WidgetSupportApi.png b/en/application-dev/application-models/figures/WidgetSupportApi.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac3d68c19683c69a16f5ebc305f3b79cb8c6566 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetSupportApi.png differ diff --git a/en/application-dev/application-models/figures/WidgetUpdatePage.png b/en/application-dev/application-models/figures/WidgetUpdatePage.png new file mode 100644 index 0000000000000000000000000000000000000000..075c8e97c85386c062a651f3b4f876e6c049171f Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetUpdatePage.png differ diff --git a/en/application-dev/application-models/figures/WidgetUse.png b/en/application-dev/application-models/figures/WidgetUse.png new file mode 100644 index 0000000000000000000000000000000000000000..9bcc46d9044b04633692171f6a553bf43ce147c3 Binary files /dev/null and b/en/application-dev/application-models/figures/WidgetUse.png differ diff --git a/en/application-dev/application-models/figures/ability-startup-with-implicit-want1.png b/en/application-dev/application-models/figures/ability-startup-with-implicit-want1.png new file mode 100644 index 0000000000000000000000000000000000000000..3f871f4816dfcf60a5c30e39b6d0ead2f8eb711e Binary files /dev/null and b/en/application-dev/application-models/figures/ability-startup-with-implicit-want1.png differ diff --git a/en/application-dev/application-models/figures/ability-startup-with-implicit-want2.png b/en/application-dev/application-models/figures/ability-startup-with-implicit-want2.png new file mode 100644 index 0000000000000000000000000000000000000000..4f1656a3c20e472e260e8e125c42b47c11a35abb Binary files /dev/null and b/en/application-dev/application-models/figures/ability-startup-with-implicit-want2.png differ diff --git a/en/application-dev/application-models/figures/application_details.jpg b/en/application-dev/application-models/figures/application_details.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e849a47b3d10faefafbf76a7b6309da305a2e9b0 Binary files /dev/null and b/en/application-dev/application-models/figures/application_details.jpg differ diff --git a/en/application-dev/application-models/figures/mission-and-multiton.png b/en/application-dev/application-models/figures/mission-and-multiton.png new file mode 100644 index 0000000000000000000000000000000000000000..e50f9d44d475711c17bfe56394fddd8a6c7b784c Binary files /dev/null and b/en/application-dev/application-models/figures/mission-and-multiton.png differ diff --git a/en/application-dev/application-models/figures/mission-and-standard.png b/en/application-dev/application-models/figures/mission-and-standard.png deleted file mode 100644 index feeed48f41371bf22abbee8dbae4695b11ed2514..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/mission-and-standard.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/mission-list-recent.png b/en/application-dev/application-models/figures/mission-list-recent.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc35532ad4907fd3a1bfcb61110ed393ea19d1c Binary files /dev/null and b/en/application-dev/application-models/figures/mission-list-recent.png differ diff --git a/en/application-dev/application-models/figures/mission-set-task-snapshot-icon.png b/en/application-dev/application-models/figures/mission-set-task-snapshot-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9d1ba2503f4e1a5d3b2aafdd93923c3f6c411998 Binary files /dev/null and b/en/application-dev/application-models/figures/mission-set-task-snapshot-icon.png differ diff --git a/en/application-dev/application-models/figures/mission-set-task-snapshot-label.png b/en/application-dev/application-models/figures/mission-set-task-snapshot-label.png new file mode 100644 index 0000000000000000000000000000000000000000..c8348685cc0fd521186aa10e8d04495422fc0206 Binary files /dev/null and b/en/application-dev/application-models/figures/mission-set-task-snapshot-label.png differ diff --git a/en/application-dev/application-models/figures/stage-concepts.png b/en/application-dev/application-models/figures/stage-concepts.png index 42e99447a780b167adaf6ddd196bea4437dfa1a7..9d753f27fca4d507da20b5d5060f9b3d28e616b0 100644 Binary files a/en/application-dev/application-models/figures/stage-concepts.png and b/en/application-dev/application-models/figures/stage-concepts.png differ diff --git a/en/application-dev/application-models/figures/stage-want1.png b/en/application-dev/application-models/figures/stage-want1.png deleted file mode 100644 index 558f0a8588d7785eaad1402e68d6ba60c3118f27..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/stage-want1.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/stage-want2.png b/en/application-dev/application-models/figures/stage-want2.png deleted file mode 100644 index 72829adade52ee11419d726f19e218ec4de15220..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/stage-want2.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/standard-mode.png b/en/application-dev/application-models/figures/standard-mode.png deleted file mode 100644 index fc903e6c1be80d3c00281d00d9ab0a1e334d7724..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/standard-mode.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/start-uiability-floating-window.png b/en/application-dev/application-models/figures/start-uiability-floating-window.png new file mode 100644 index 0000000000000000000000000000000000000000..6857c9b31692419dd7937737d53424dd2b79aaed Binary files /dev/null and b/en/application-dev/application-models/figures/start-uiability-floating-window.png differ diff --git a/en/application-dev/application-models/figures/uiability-intra-device-interaction.png b/en/application-dev/application-models/figures/uiability-intra-device-interaction.png index 92292f2c6ef4c9cbd06da2a523f27b571a957e2b..344cf05e96c539ca73fdb9282625a1d1cb8584e7 100644 Binary files a/en/application-dev/application-models/figures/uiability-intra-device-interaction.png and b/en/application-dev/application-models/figures/uiability-intra-device-interaction.png differ diff --git a/en/application-dev/application-models/figures/uiability-launch-type1.gif b/en/application-dev/application-models/figures/uiability-launch-type1.gif new file mode 100644 index 0000000000000000000000000000000000000000..d31a51a1aa97d32ee0ee7df4803378c1b7124119 Binary files /dev/null and b/en/application-dev/application-models/figures/uiability-launch-type1.gif differ diff --git a/en/application-dev/application-models/figures/uiability-launch-type1.png b/en/application-dev/application-models/figures/uiability-launch-type1.png deleted file mode 100644 index c4f5aa4b9a988d8e7148b504c4dcc163961cb103..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/uiability-launch-type1.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/uiability-launch-type2.gif b/en/application-dev/application-models/figures/uiability-launch-type2.gif new file mode 100644 index 0000000000000000000000000000000000000000..ecb3c413e3af2f92ef6834024d0d413e30c2419f Binary files /dev/null and b/en/application-dev/application-models/figures/uiability-launch-type2.gif differ diff --git a/en/application-dev/application-models/figures/uiability-launch-type2.png b/en/application-dev/application-models/figures/uiability-launch-type2.png deleted file mode 100644 index 6f0e43d24f745aee41601cc48f4bc138572fbeb5..0000000000000000000000000000000000000000 Binary files a/en/application-dev/application-models/figures/uiability-launch-type2.png and /dev/null differ diff --git a/en/application-dev/application-models/figures/uiability-launch-type3.gif b/en/application-dev/application-models/figures/uiability-launch-type3.gif new file mode 100644 index 0000000000000000000000000000000000000000..029e8ba7e90eb836f8466c604d4fcf8171ffec6e Binary files /dev/null and b/en/application-dev/application-models/figures/uiability-launch-type3.gif differ diff --git a/en/application-dev/application-models/figures/want-action.png b/en/application-dev/application-models/figures/want-action.png index 0d8e18ce5870bea777c26b832d3f29674c2fa261..b907e8158bd9fd183ceabb181a13aa813f01e77e 100644 Binary files a/en/application-dev/application-models/figures/want-action.png and b/en/application-dev/application-models/figures/want-action.png differ diff --git a/en/application-dev/application-models/figures/want-uri-type1.png b/en/application-dev/application-models/figures/want-uri-type1.png index e0fe40d1a3cd40b72379bd947aaf2e3977021b32..ed53694a9608e8529c5e4633fca42b041bc7ab76 100644 Binary files a/en/application-dev/application-models/figures/want-uri-type1.png and b/en/application-dev/application-models/figures/want-uri-type1.png differ diff --git a/en/application-dev/application-models/hop-cross-device-migration.md b/en/application-dev/application-models/hop-cross-device-migration.md index d90a10995f0aeba773179fc7807ab25711b4594c..418eac519cb62c7339048df4f8663e20df8ef185 100644 --- a/en/application-dev/application-models/hop-cross-device-migration.md +++ b/en/application-dev/application-models/hop-cross-device-migration.md @@ -1,4 +1,4 @@ -# Cross-Device Migration (for System Applications Only)] +# Cross-Device Migration (for System Applications Only) ## When to Use @@ -47,67 +47,42 @@ The table below describes the main APIs used for cross-device migration. For det ## How to Develop -1. Configure the data synchronization permission in the **module.json5** file. The sample code is as follows: - +1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + +2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). + +3. Configure the fields related to cross-device migration in the configuration file. + + Configure the application to support migration. + Set the **continuable** field in the **module.json5** file to **true**. The default value is **false**. If this parameter is set to **false**, the application cannot be continued on the target device. + + ```json { "module": { - "requestPermissions":[ - { - "name" : "ohos.permission.DISTRIBUTED_DATASYNC", + ... + "abilities": [ + { + ... + "continuable": true, } ] } } ``` -2. Configure the fields related to cross-device migration in the configuration file. - - Configure the application to support migration. - - - Set the **continuable** field in the **module.json5** file to **true**. The default value is **false**. If this parameter is set to **false**, the application cannot be continued on the target device. - ```json - { - "module": { - // ... - "abilities": [ - { - // ... - "continuable": true, - } - ] - } - } - ``` - - - Configure the application launch type. For details, see [UIAbility Component Launch Type](uiability-launch-type.md). - -3. Request the data synchronization permission. The sample code for displaying a dialog box to request the permission is as follows: - - ```ts - requestPermission() { - let context = this.context - let permissions: Array = ['ohos.permission.DISTRIBUTED_DATASYNC'] - context.requestPermissionsFromUser(permissions).then((data) => { - console.info("Succeed to request permission from user with data: "+ JSON.stringify(data)) - }).catch((error) => { - console.info("Failed to request permission from user with error: "+ JSON.stringify(error)) - }) - } - ``` + Configure the application launch type. For details, see [UIAbility Component Launch Type](uiability-launch-type.md). 4. Implement [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) in the UIAbility of the initiator. - [onContinue()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) is called on the initiator. You can save the data in this method to implement application compatibility check and migration decision. - - Saving migrated data: You can save the data to be migrated in key-value pairs in **wantParam**. - Checking application compatibility: You can obtain the version number of the target application from **wantParam** and check the compatibility between the target application and the current application. - Making a migration decision: You can determine whether to support the migration based on the return value of **onContinue()**. For details about the return value, see [Available APIs](#available-apis). - The sample code is as follows: - + The sample code is as follows: + ```ts import UIAbility from '@ohos.app.ability.UIAbility'; import AbilityConstant from '@ohos.app.ability.AbilityConstant'; diff --git a/en/application-dev/application-models/hop-multi-device-collaboration.md b/en/application-dev/application-models/hop-multi-device-collaboration.md index e5dfb522fcc8ffa05106e87197de6b276e4c9e00..721e135179810539bf6782489b67d2619536e8d2 100644 --- a/en/application-dev/application-models/hop-multi-device-collaboration.md +++ b/en/application-dev/application-models/hop-multi-device-collaboration.md @@ -11,7 +11,7 @@ Multi-device coordination involves the following scenarios: - [Connecting to ServiceExtensionAbility Across Devices](#connecting-to-serviceextensionability-across-devices) -- [Using Cross-Device Ability Call](#using-cross-device-ability-call) +- [Using Cross-Device Call](#using-cross-device-call) ## Multi-Device Collaboration Process @@ -102,7 +102,7 @@ On device A, touch the **Start** button provided by the initiator application to abilityName: 'FuncAbility', moduleName: 'module1', // moduleName is optional. } - // context is the ability-level context of the initiator UIAbility. + // context is the AbilityContext of the initiator UIAbility. this.context.startAbility(want).then(() => { // ... }).catch((err) => { @@ -118,7 +118,7 @@ On device A, touch the **Start** button provided by the initiator application to ### Available APIs -**Table 2** APIs for starting an ability across devices and returning the result data +**Table 2** APIs for starting a UIAbility across devices and returning the result data | API| Description| | -------- | -------- | @@ -154,7 +154,7 @@ On device A, touch the **Start** button provided by the initiator application to abilityName: 'FuncAbility', moduleName: 'module1', // moduleName is optional. } - // context is the ability-level context of the initiator UIAbility. + // context is the AbilityContext of the initiator UIAbility. this.context.startAbilityForResult(want).then((data) => { // ... }).catch((err) => { @@ -174,7 +174,7 @@ On device A, touch the **Start** button provided by the initiator application to moduleName: 'module1', }, } - // context is the ability-level context of the target UIAbility. + // context is the AbilityContext of the target UIAbility. this.context.terminateSelfWithResult(abilityResult, (err) => { // ... }); @@ -187,7 +187,7 @@ On device A, touch the **Start** button provided by the initiator application to // ... - // context is the ability-level context of the initiator UIAbility. + // context is the AbilityContext of the initiator UIAbility. this.context.startAbilityForResult(want).then((data) => { if (data?.resultCode === RESULT_CODE) { // Parse the information returned by the target UIAbility. @@ -315,24 +315,24 @@ A system application can connect to a service on another device by calling [conn ``` -## Using Cross-Device Ability Call +## Using Cross-Device Call -The basic principle of cross-device ability call is the same as that of intra-device ability call. For details, see [Using Ability Call to Implement UIAbility Interaction (for System Applications Only)](uiability-intra-device-interaction.md#using-ability-call-to-implement-uiability-interaction-for-system-applications-only). +The basic principle of cross-device call is the same as that of intra-device call. For details, see [Using Call to Implement UIAbility Interaction (for System Applications Only)](uiability-intra-device-interaction.md#using-call-to-implement-uiability-interaction-for-system-applications-only). -The following describes how to implement multi-device collaboration through cross-device ability call. +The following describes how to implement multi-device collaboration through cross-device call. ### Available APIs -**Table 4** Ability call APIs +**Table 4** Call APIs | API| Description| | -------- | -------- | | startAbilityByCall(want: Want): Promise<Caller>; | Starts a UIAbility in the foreground or background and obtains the caller object for communicating with the UIAbility.| -| on(method: string, callback: CalleeCallBack): void | Callback invoked when the callee ability registers a method.| -| off(method: string): void | Callback invoked when the callee ability deregisters a method.| -| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the callee ability.| -| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | Sends agreed parcelable data to the callee ability and obtains the agreed parcelable data returned by the callee ability.| +| on(method: string, callback: CalleeCallBack): void | Callback invoked when the CalleeAbility registers a method.| +| off(method: string): void | Callback invoked when the CalleeAbility deregisters a method.| +| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the CalleeAbility.| +| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | Sends agreed parcelable data to the CalleeAbility and obtains the agreed parcelable data returned by the CalleeAbility.| | release(): void | Releases the caller object.| | on(type: "release", callback: OnReleaseCallback): void | Callback invoked when the caller object is released.| @@ -367,29 +367,29 @@ The following describes how to implement multi-device collaboration through cros } ``` -3. Create the callee ability. +3. Create the CalleeAbility. - For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. + For the CalleeAbility, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. 1. Configure the launch type of the UIAbility. - Set **launchType** of the callee ability to **singleton** in the **module.json5** file. + Set **launchType** of the CalleeAbility to **singleton** in the **module.json5** file. | JSON Field| Description| | -------- | -------- | - | "launchType"| Ability launch type. Set this parameter to **singleton**.| + | "launchType"| UIAbility launch type. Set this parameter to **singleton**.| An example of the UIAbility configuration is as follows: ```json "abilities":[{ "name": ".CalleeAbility", - "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", + "srcEnty": "./ets/CalleeAbility/CalleeAbility.ts", "launchType": "singleton", "description": "$string:CalleeAbility_desc", "icon": "$media:icon", "label": "$string:CalleeAbility_label", - "visible": true + "exported": true }] ``` @@ -401,7 +401,7 @@ The following describes how to implement multi-device collaboration through cros 3. Define the agreed parcelable data. - The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. + The data formats sent and received by the CallerAbility and CalleeAbility must be consistent. In the following example, the data formats are number and string. ```ts export default class MyParcelable { @@ -438,13 +438,13 @@ The following describes how to implement multi-device collaboration through cros function sendMsgCallback(data) { console.info('CalleeSortFunc called') - // Obtain the parcelable data sent by the caller ability. + // Obtain the parcelable data sent by the CallerAbility. let receivedData = new MyParcelable(0, '') data.readParcelable(receivedData) console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`) // Process the data. - // Return the parcelable data result to the caller ability. + // Return the parcelable data result to the CallerAbility. return new MyParcelable(receivedData.num + 1, `send ${receivedData.str} succeed`) } @@ -459,7 +459,7 @@ The following describes how to implement multi-device collaboration through cros onDestroy() { try { - this.callee.off(MSG_SEND_METHOD) + this.callee.off(MSG_SEND_METHOD) } catch (error) { console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`) } @@ -467,7 +467,7 @@ The following describes how to implement multi-device collaboration through cros } ``` -4. Obtain the caller object and access the callee ability. +4. Obtain the caller object and access the CalleeAbility. 1. Import the **UIAbility** module. ```ts @@ -476,7 +476,7 @@ The following describes how to implement multi-device collaboration through cros 2. Obtain the caller object. - The **context** attribute of the ability implements **startAbilityByCall** to obtain the caller object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the caller object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. + The **context** attribute of the UIAbility implements **startAbilityByCall** to obtain the caller object for communication. The following example uses **this.context** to obtain the **context** attribute of the UIAbility, uses **startAbilityByCall** to start the CalleeAbility, obtain the caller object, and register the **onRelease** listener of the CallerAbility. You need to implement processing based on service requirements. ```ts async onButtonGetRemoteCaller() { @@ -491,7 +491,7 @@ The following describes how to implement multi-device collaboration through cros if (data != null) { caller = data console.info('get remote caller success') - // 注册caller的release监听 + // Register the onRelease() listener of the CallerAbility. caller.onRelease((msg) => { console.info(`remote caller onRelease is called ${msg}`) }) @@ -505,8 +505,8 @@ The following describes how to implement multi-device collaboration through cros For details about how to implement **getRemoteDeviceId()**, see [Starting UIAbility and ServiceExtensionAbility Across Devices (No Data Returned)](#starting-uiability-and-serviceextensionability-across-devices-no-data-returned). -5. Sends agreed parcelable data to the callee ability. - 1. The parcelable data can be sent to the callee ability with or without a return value. The method and parcelable data must be consistent with those of the callee ability. The following example describes how to send data to the callee ability. +5. Sends agreed parcelable data to the CalleeAbility. + 1. The parcelable data can be sent to the CalleeAbility with or without a return value. The method and parcelable data must be consistent with those of the CalleeAbility. The following example describes how to send data to the CalleeAbility. ```ts const MSG_SEND_METHOD: string = 'CallSendMsg' @@ -520,7 +520,7 @@ The following describes how to implement multi-device collaboration through cros } ``` - 2. In the following, **CallWithResult** is used to send data **originMsg** to the callee ability and assign the data processed by the **CallSendMsg** method to **backMsg**. + 2. In the following, **CallWithResult** is used to send data **originMsg** to the CalleeAbility and assign the data processed by the **CallSendMsg** method to **backMsg**. ```ts const MSG_SEND_METHOD: string = 'CallSendMsg' diff --git a/en/application-dev/application-models/inputmethodextentionability.md b/en/application-dev/application-models/inputmethodextentionability.md index 9a025339cab6a5f555fd61b15597400b31affeb7..eea85014d86310fffd2d22119f3ae68cb3a29493 100644 --- a/en/application-dev/application-models/inputmethodextentionability.md +++ b/en/application-dev/application-models/inputmethodextentionability.md @@ -1,11 +1,11 @@ -# InputMethodExtensionAbility Development +# InputMethodExtensionAbility -[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) is an ExtensionAbility component of the inputMethod type that provides extension capabilities for the input method framework. +## When to Use +[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md), inherited from [ExtensionAbility](extensionability-overview.md), is used for developing input method applications. -InputMethodExtensionAbility can be started or connected by other application components to process transactions in the background based on the request of the caller. +The entire lifecycle of the [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) instance and the owning ExtensionAbility process is scheduled and managed by the input method framework. The input method framework provides the [InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) base class. Derive this base class to implement initialization and resource clearing. - -InputMethodExtensionAbility provides related capabilities through the [InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md). +[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) provides related capabilities through [InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md). ## Implementing an Input Method Application @@ -13,15 +13,13 @@ InputMethodExtensionAbility provides related capabilities through the [InputMeth InputMethodExtensionAbility provides the **onCreate()** and **onDestory()** callbacks, as described below. Override them as required. - **onCreate** - This callback is triggered when a service is created for the first time. You can perform initialization operations, for example, registering a common event listener. - + > **NOTE** > > If a service has been created, starting it again does not trigger the **onCreate()** callback. - + - **onDestroy** - This callback is triggered when the service is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregister the listener. @@ -29,11 +27,11 @@ InputMethodExtensionAbility provides the **onCreate()** and **onDestory()** call To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows: -In the **ets** directory of the target module, right-click and choose **New** > **Extention Ability** > **InputMethod** to a minimum template of InputMethodExtensionAbility. +In the **ets** directory of the target module, right-click and choose **New** > **Extension Ability** > **InputMethod** to a minimum template of InputMethodExtensionAbility. > **NOTE** > -> When compiling the input method application, use the signature at the system_core level. Otherwise, the application will not be able to start the keyboard. +> When compiling the input method application, use the signature at the system_basic level. Otherwise, the application will not be able to start the keyboard. The minimum template implements an input method application with the most basic features, such as starting the keyboard, entering text, and deleting input. You can diversify the feature set of the application by, for example, adding the feature to hide the keyboard. @@ -54,7 +52,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth 1. **InputMethodService.ts** file: - In this file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks. + In the **InputMethodService.ts** file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks. ```ts import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility'; @@ -70,7 +68,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth onDestroy() { console.log("onDestroy."); - this.context.destroy(); + this.keyboardController.onDestroy(); // Destroy the window and deregister the event listener. } } ``` @@ -109,7 +107,6 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth this.unRegisterListener(); // Deregister the event listener. let win = windowManager.findWindow(this.windowName); win.destroyWindow(); // Destroy the window. - this.mContext.terminateSelf(); // Terminate the InputMethodExtensionAbility service. } private initWindow(): void // Initialize the window. @@ -159,7 +156,7 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth }) globalThis.inputAbility.on('inputStop', (imeId) => { if (imeId == "Bundle name/Ability name") { - this.onDestroy(); + this.mContext.destroy(); // Destroy the InputMethodExtensionAbility service. } }); } @@ -342,20 +339,20 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth } ``` - Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the target module. Set **type** to **"inputMethod"** and **srcEntrance** to the code path of the InputMethodExtensionAbility component. +5. Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"inputMethod"** and **srcEntry** to the code path of the InputMethodExtensionAbility component. ```ts { "module": { - // ... + ... "extensionAbilities": [ { "description": "inputMethod", "icon": "$media:icon", "name": "InputMethodExtAbility", - "srcEntrance": "./ets/inputmethodextability/InputMethodService.ts", + "srcEntry": "./ets/inputmethodextability/InputMethodService.ts", "type": "inputMethod", - "visible": true, + "exported": true, } ] } @@ -363,4 +360,3 @@ The minimum template contains four files: **KeyboardController.ts**, **InputMeth ``` - diff --git a/en/application-dev/application-models/itc-with-emitter.md b/en/application-dev/application-models/itc-with-emitter.md index 2966bd8eea41e04893814f20a3c5b2f9e4e456c9..36a259d52b691459e55be502d3071135be67eb00 100644 --- a/en/application-dev/application-models/itc-with-emitter.md +++ b/en/application-dev/application-models/itc-with-emitter.md @@ -1,5 +1,6 @@ # Using Emitter for Inter-Thread Communication + [Emitter](../reference/apis/js-apis-emitter.md) provides APIs for sending and processing events between threads, including the APIs for processing events that are subscribed to in persistent or one-shot manner, unsubscribing from events, and emitting events to the event queue. @@ -13,12 +14,12 @@ To develop the Emitter mode, perform the following steps: // Define an event with eventId 1. let event = { - eventId: 1 + eventId: 1 }; // Trigger the callback after the event with eventId 1 is received. let callback = (eventData) => { - console.info('event callback'); + console.info('event callback'); }; // Subscribe to the event with eventId 1. @@ -29,21 +30,21 @@ To develop the Emitter mode, perform the following steps: ```ts import emitter from "@ohos.events.emitter"; - + // Define an event with eventId 1 and priority Low. let event = { - eventId: 1, - priority: emitter.EventPriority.LOW + eventId: 1, + priority: emitter.EventPriority.LOW }; - + let eventData = { - data: { - "content": "c", - "id": 1, - "isEmpty": false, - } + data: { + "content": "c", + "id": 1, + "isEmpty": false, + } }; - + // Emit the event with eventId 1 and event content eventData. emitter.emit(event, eventData); ``` diff --git a/en/application-dev/application-models/itc-with-worker.md b/en/application-dev/application-models/itc-with-worker.md index 996ab941b0244571dff6116a45ab5e2165cf1184..677483dbb2f4d4a6925995da680c0c18c80b8efc 100644 --- a/en/application-dev/application-models/itc-with-worker.md +++ b/en/application-dev/application-models/itc-with-worker.md @@ -1,5 +1,6 @@ # Using Worker for Inter-Thread Communication + [Worker](../reference/apis/js-apis-worker.md) is an independent thread running in parallel with the main thread. The thread that creates the worker thread is referred to as the host thread. The script file passed in during worker creation is executed in the worker thread. Generally, time-consuming operations are processed in the worker thread. However, pages cannot be directly updated in the worker thread. @@ -9,13 +10,13 @@ To develop the Worker mode, perform the following steps: 1. Configure the **buildOption** field in the [module-level build-profile.json5](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-building-configuration-0000001218440654#section6887184182020) file of the project. ```ts - "buildOption": { - "sourceOption": { - "workers": [ - "./src/main/ets/workers/worker.ts" - ] - } + "buildOption": { + "sourceOption": { + "workers": [ + "./src/main/ets/workers/worker.ts" + ] } + } ``` 2. Create the **worker.ts** file based on the configuration in **build-profile.json5**. @@ -27,9 +28,9 @@ To develop the Worker mode, perform the following steps: // Process messages from the main thread. parent.onmessage = function(message) { - console.info("onmessage: " + message) - // Send a message to the main thread. - parent.postMessage("message from worker thread.") + console.info("onmessage: " + message) + // Send a message to the main thread. + parent.postMessage("message from worker thread.") } ``` @@ -46,10 +47,10 @@ To develop the Worker mode, perform the following steps: // Process messages from the worker thread. wk.onmessage = function(message) { - console.info("message from worker: " + message) + console.info("message from worker: " + message) - // Stop the worker thread based on service requirements. - wk.terminate() + // Stop the worker thread based on service requirements. + wk.terminate(); } ``` @@ -57,23 +58,22 @@ To develop the Worker mode, perform the following steps: ```ts import worker from '@ohos.worker'; - + let wk = new worker.ThreadWorker("../workers/worker.ts"); - + // Send a message to the worker thread. wk.postMessage("message from main thread.") - + // Process messages from the worker thread. wk.onmessage = function(message) { - console.info("message from worker: " + message) - - // Stop the worker thread based on service requirements. - wk.terminate() + console.info("message from worker: " + message) + + // Stop the worker thread based on service requirements. + wk.terminate(); } ``` > **NOTE** -> +> > - If the relative path of **worker.ts** configured in **build-profile.json5** is **./src/main/ets/workers/worker.ts**, pass in the path **entry/ets/workers/worker.ts** when creating a worker thread in the stage model, and pass in the path **../workers/worker.ts** when creating a worker thread in the FA model. -> > - For details about the data types supported between the main thread and worker thread, see [Sequenceable Data Types](../reference/apis/js-apis-worker.md#sequenceable-data-types). diff --git a/en/application-dev/application-models/js-ui-widget-development.md b/en/application-dev/application-models/js-ui-widget-development.md new file mode 100644 index 0000000000000000000000000000000000000000..1c0cd275c9192e6d5723e04241f71560427cbb15 --- /dev/null +++ b/en/application-dev/application-models/js-ui-widget-development.md @@ -0,0 +1,590 @@ +# Developing a JS Widget + + +The following describes how to develop JS widgets based on the web-like development paradigm. + + +## Working Principles + +Below shows the working principles of the widget framework. + +**Figure 1** Widget framework working principles in the stage model + +![JSCardPrinciple](figures/JSCardPrinciple.png) + +The widget host consists of the following modules: + +- Widget usage: provides operations such as creating, deleting, or updating a widget. + +- Communication adapter: provided by the OpenHarmony SDK for communication with the Widget Manager. It sends widget-related operations to the Widget Manager. + +The Widget Manager consists of the following modules: + +- Periodic updater: starts a scheduled task based on the update policy to periodically update a widget after it is added to the Widget Manager. + +- Cache manager: caches view information of a widget after it is added to the Widget Manager to directly return the cached data when the widget is obtained next time. This reduces the latency greatly. + +- Lifecycle manager: suspends update when a widget is switched to the background or is blocked, and updates and/or clears widget data during upgrade and deletion. + +- Object manager: manages RPC objects of the widget host. It is used to verify requests from the widget host and process callbacks after the widget update. + +- Communication adapter: communicates with the widget host and provider through RPCs. + +The widget provider consists of the following modules: + +- Widget service: implemented by the widget provider developer to process requests on widget creation, update, and deletion, and to provide corresponding widget services. + +- Instance manager: implemented by the widget provider developer for persistent management of widget instances allocated by the Widget Manager. + +- Communication adapter: provided by the OpenHarmony SDK for communication with the Widget Manager. It pushes update data to the Widget Manager. + +> **NOTE** +> You only need to develop the widget provider. The system automatically handles the work of the widget host and Widget Manager. + + +## Available APIs + +The **FormExtensionAbility** class has the following APIs. For details, see [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). + +| Name| Description| +| -------- | -------- | +| onAddForm(want: Want): formBindingData.FormBindingData | Called to notify the widget provider that a widget has been created.| +| onCastToNormalForm(formId: string): void | Called to notify the widget provider that a temporary widget has been converted to a normal one.| +| onUpdateForm(formId: string): void | Called to notify the widget provider that a widget has been updated.| +| onChangeFormVisibility(newStatus: { [key: string]: number }): void | Called to notify the widget provider of the change in widget visibility.| +| onFormEvent(formId: string, message: string): void | Called to instruct the widget provider to receive and process a widget event.| +| onRemoveForm(formId: string): void | Called to notify the widget provider that a widget has been destroyed.| +| onConfigurationUpdate(config: Configuration): void | Called when the configuration of the environment where the widget is running is updated.| +| onShareForm?(formId: string): { [key: string]: any } | Called by the widget provider to receive shared widget data.| + +The **FormProvider** class has the following APIs. For details, see [FormProvider](../reference/apis/js-apis-app-form-formProvider.md). + +| Name| Description| +| -------- | -------- | +| setFormNextRefreshTime(formId: string, minute: number, callback: AsyncCallback<void>): void; | Sets the next refresh time for a widget. This API uses an asynchronous callback to return the result.| +| setFormNextRefreshTime(formId: string, minute: number): Promise<void>; | Sets the next refresh time for a widget. This API uses a promise to return the result.| +| updateForm(formId: string, formBindingData: FormBindingData, callback: AsyncCallback<void>): void; | Updates a widget. This API uses an asynchronous callback to return the result.| +| updateForm(formId: string, formBindingData: FormBindingData): Promise<void>; | Updates a widget. This API uses a promise to return the result.| + +The **FormBindingData** class has the following APIs. For details, see [FormBindingData](../reference/apis/js-apis-app-form-formBindingData.md). + +| Name| Description| +| -------- | -------- | +| createFormBindingData(obj?: Object \| string): FormBindingData | Creates a **FormBindingData** object.| + + +## How to Develop + +The widget provider development based on the [stage model](stage-model-development-overview.md) involves the following key steps: + +- [Creating a FormExtensionAbility Instance](#creating-a-formextensionability-instance): Develop the lifecycle callback functions of FormExtensionAbility. + +- [Configuring the Widget Configuration Files](#configuring-the-widget-configuration-files): Configure the application configuration file **module.json5** and profile configuration file. + +- [Persistently Storing Widget Data](#persistently-storing-widget-data): This operation is a form of widget data exchange. + +- [Updating Widget Data](#updating-widget-data): Call **updateForm()** to update the information displayed on a widget. + +- [Developing the Widget UI Page](#developing-the-widget-ui-page): Use HML+CSS+JSON to develop a JS widget UI page. + +- [Developing Widget Events](#developing-widget-events): Add the router and message events for a widget. + + +### Creating a FormExtensionAbility Instance + +To create a widget in the stage model, implement the lifecycle callbacks of **FormExtensionAbility**. Generate a widget template by referring to [Developing a Service Widget](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425). + +1. Import related modules to **EntryFormAbility.ts**. + + + ```ts + import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + import formBindingData from '@ohos.app.form.formBindingData'; + import formInfo from '@ohos.app.form.formInfo'; + import formProvider from '@ohos.app.form.formProvider'; + import dataStorage from '@ohos.data.storage'; + ``` + +2. Implement the FormExtension lifecycle callbacks in **EntryFormAbility.ts**. + + + ```ts + export default class EntryFormAbility extends FormExtensionAbility { + onAddForm(want) { + console.info('[EntryFormAbility] onAddForm'); + // Called when the widget is created. The widget provider should return the widget data binding class. + let obj = { + "title": "titleOnCreate", + "detail": "detailOnCreate" + }; + let formData = formBindingData.createFormBindingData(obj); + return formData; + } + onCastToNormalForm(formId) { + // Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion. + console.info('[EntryFormAbility] onCastToNormalForm'); + } + onUpdateForm(formId) { + // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. + console.info('[EntryFormAbility] onUpdateForm'); + let obj = { + "title": "titleOnUpdate", + "detail": "detailOnUpdate" + }; + let formData = formBindingData.createFormBindingData(obj); + formProvider.updateForm(formId, formData).catch((error) => { + console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error)); + }); + } + onChangeFormVisibility(newStatus) { + // Called when the widget host initiates an event about visibility changes. The widget provider should do something to respond to the notification. This callback takes effect only for system applications. + console.info('[EntryFormAbility] onChangeFormVisibility'); + } + onFormEvent(formId, message) { + // If the widget supports event triggering, override this method and implement the trigger. + console.info('[EntryFormAbility] onFormEvent'); + } + onRemoveForm(formId) { + // Delete widget data. + console.info('[EntryFormAbility] onRemoveForm'); + } + onConfigurationUpdate(config) { + console.info('[EntryFormAbility] nConfigurationUpdate, config:' + JSON.stringify(config)); + } + onAcquireFormState(want) { + return formInfo.FormState.READY; + } + } + ``` + +> **NOTE** +> FormExtensionAbility cannot reside in the background. Therefore, continuous tasks cannot be processed in the widget lifecycle callbacks. + + +### Configuring the Widget Configuration Files + +1. Configure ExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information. + Example configuration: + + + ```json + { + "module": { + ... + "extensionAbilities": [ + { + "name": "EntryFormAbility", + "srcEntry": "./ets/entryformability/EntryFormAbility.ts", + "label": "$string:EntryFormAbility_label", + "description": "$string:EntryFormAbility_desc", + "type": "form", + "metadata": [ + { + "name": "ohos.extension.form", + "resource": "$profile:form_config" + } + ] + } + ] + } + } + ``` + +2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if resource is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal field structure. + + **Table 1** Widget profile configuration file + + | Field| Description| Data Type| Default Value Allowed| + | -------- | -------- | -------- | -------- | + | name | Class name of the widget. The value is a string with a maximum of 127 bytes.| String| No| + | description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes.| String| Yes (initial value: left empty)| + | src | Full path of the UI code corresponding to the widget.| String| No| + | window | Window-related configurations.| Object| Yes| + | isDefault | Whether the widget is a default one. Each UIAbility has only one default widget.
- **true**: The widget is the default one.
- **false**: The widget is not the default one.| Boolean| No| + | colorMode | Color mode of the widget.
- **auto**: auto-adaptive color mode
- **dark**: dark color mode
- **light**: light color mode| String| Yes (initial value: **auto**)| + | supportDimensions | Grid styles supported by the widget.
- **1 * 2**: indicates a grid with one row and two columns.
- **2 * 2**: indicates a grid with two rows and two columns.
- **2 * 4**: indicates a grid with two rows and four columns.
- **4 * 4**: indicates a grid with four rows and four columns.| String array| No| + | defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget.| String| No| + | updateEnabled | Whether the widget can be updated periodically.
- **true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.
- **false**: The widget cannot be updated periodically.| Boolean| No| + | scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| String| Yes (initial value: **0:0**)| + | updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.
If the value is **0**, this field does not take effect.
If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| Number| Yes (initial value: **0**)| + | formConfigAbility | Link to a specific page of the application. The value is a URI.| String| Yes (initial value: left empty)| + | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)| + | metaData | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)| + + Example configuration: + + + ```json + { + "forms": [ + { + "name": "widget", + "description": "This is a service widget.", + "src": "./js/widget/pages/index/index", + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "colorMode": "auto", + "isDefault": true, + "updateEnabled": true, + "scheduledUpdateTime": "10:30", + "updateDuration": 1, + "defaultDimension": "2*2", + "supportDimensions": [ + "2*2" + ] + } + ] + } + ``` + + +### Persistently Storing Widget Data + +A widget provider is usually started when it is needed to provide information about a widget. The Widget Manager supports multi-instance management and uses the widget ID to identify an instance. If the widget provider supports widget data modification, it must persistently store the data based on the widget ID, so that it can access the data of the target widget when obtaining, updating, or starting a widget. + + +```ts +const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store"; +async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) { + // Only the widget ID (formId), widget name (formName), and whether the widget is a temporary one (tempFlag) are persistently stored. + let formInfo = { + "formName": formName, + "tempFlag": tempFlag, + "updateCount": 0 + }; + try { + const storage = await dataStorage.getStorage(DATA_STORAGE_PATH); + // put form info + await storage.put(formId, JSON.stringify(formInfo)); + console.info(`[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`); + await storage.flush(); + } catch (err) { + console.error(`[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err)}`); + } +} + +export default class EntryFormAbility extends FormExtension { + ... + onAddForm(want) { + console.info('[EntryFormAbility] onAddForm'); + + let formId = want.parameters["ohos.extra.param.key.form_identity"]; + let formName = want.parameters["ohos.extra.param.key.form_name"]; + let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"]; + // Persistently store widget data for subsequent use, such as instance acquisition and update. + // Implement this API based on project requirements. + storeFormInfo(formId, formName, tempFlag); + + let obj = { + "title": "titleOnCreate", + "detail": "detailOnCreate" + }; + let formData = formBindingData.createFormBindingData(obj); + return formData; + } +} +``` + +You should override **onRemoveForm** to implement widget data deletion. + + +```ts +const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store"; +async function deleteFormInfo(formId: string) { + try { + const storage = await dataStorage.getStorage(DATA_STORAGE_PATH); + // del form info + await storage.delete(formId); + console.info(`[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`); + await storage.flush(); + } catch (err) { + console.error(`[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err)}`); + } +} + +... + +export default class EntryFormAbility extends FormExtension { + ... + onRemoveForm(formId) { + console.info('[EntryFormAbility] onRemoveForm'); + // Delete the persistent widget instance data. + // Implement this API based on project requirements. + deleteFormInfo(formId); + } +} +``` + +For details about persistence, see [Application Data Persistence Overview](../database/app-data-persistence-overview.md). + +The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. + +- Normal widget: a widget persistently used by the widget host + +- Temporary widget: a widget temporarily used by the widget host + +Data of a temporary widget will be deleted on the Widget Manager if the widget framework is killed and restarted. The widget provider, however, is not notified of the deletion and still keeps the data. Therefore, the widget provider needs to clear the data of temporary widgets proactively if the data has been kept for a long period of time. If the widget host has converted a temporary widget into a normal one, the widget provider should change the widget data from temporary storage to persistent storage. Otherwise, the widget data may be deleted by mistake. + + +### Updating Widget Data + +When an application initiates a scheduled or periodic update, the application obtains the latest data and calls **updateForm()** to update the widget. + + +```ts +onUpdateForm(formId) { + // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. + console.info('[EntryFormAbility] onUpdateForm'); + let obj = { + "title": "titleOnUpdate", + "detail": "detailOnUpdate" + }; + let formData = formBindingData.createFormBindingData(obj); + // Call the updateForm() method to update the widget. Only the data passed through the input parameter is updated. Other information remains unchanged. + formProvider.updateForm(formId, formData).catch((error) => { + console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error)); + }); +} +``` + + +### Developing the Widget UI Page + +You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. This section describes how to develop a page shown below. + +![WidgetCardPage](figures/WidgetCardPage.png) + +- HML: uses web-like paradigm components to describe the widget page information. + + + ```html +
+ +
+ +
+
+ {{title}} + {{detail}} +
+
+
+ ``` + +- CSS: defines style information about the web-like paradigm components in HML. + + + ```css + .container { + flex-direction: column; + justify-content: center; + align-items: center; + } + + .bg-img { + flex-shrink: 0; + height: 100%; + } + + .container-inner { + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + height: 100%; + width: 100%; + padding: 12px; + } + + .title { + font-size: 19px; + font-weight: bold; + color: white; + text-overflow: ellipsis; + max-lines: 1; + } + + .detail_text { + font-size: 16px; + color: white; + opacity: 0.66; + text-overflow: ellipsis; + max-lines: 1; + margin-top: 6px; + } + ``` + +- JSON: defines data and event interaction on the widget UI page. + + + ```json + { + "data": { + "title": "TitleDefault", + "detail": "TextDefault" + }, + "actions": { + "routerEvent": { + "action": "router", + "abilityName": "EntryAbility", + "params": { + "message": "add detail" + } + } + } + } + ``` + + +### Developing Widget Events + +You can set router and message events for components on a widget. The router event applies to UIAbility redirection, and the message event applies to custom click events. + +The key steps are as follows: + +1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file. + +2. Set the router event. + + - **action**: **"router"**, which indicates a router event. + - **abilityName**: name of the UIAbility to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default UIAbility name of the stage model created by DevEco Studio is EntryAbility. + - **params**: custom parameters passed to the target UIAbility. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target UIAbility. For example, in the lifecycle function **onCreate** of the main ability in the stage model, you can obtain **want** and its **parameters** field. + +3. Set the message event. + + - **action**: **"message"**, which indicates a message event. + - **params**: custom parameters of the message event. Set them as required. The value can be obtained from **message** in the widget lifecycle function **onFormEvent()**. + +The following is an example: + +- HML file: + + + ```html +
+ +
+ +
+
+ {{title}} + {{detail}} +
+
+
+ ``` + +- CSS file: + + + ```css + .container { + flex-direction: column; + justify-content: center; + align-items: center; + } + + .bg-img { + flex-shrink: 0; + height: 100%; + } + + .container-inner { + flex-direction: column; + justify-content: flex-end; + align-items: flex-start; + height: 100%; + width: 100%; + padding: 12px; + } + + .title { + font-size: 19px; + font-weight: bold; + color: white; + text-overflow: ellipsis; + max-lines: 1; + } + + .detail_text { + font-size: 16px; + color: white; + opacity: 0.66; + text-overflow: ellipsis; + max-lines: 1; + margin-top: 6px; + } + ``` + +- JSON file + + + ```json + { + "data": { + "title": "TitleDefault", + "detail": "TextDefault" + }, + "actions": { + "routerEvent": { + "action": "router", + "abilityName": "EntryAbility", + "params": { + "info": "router info", + "message": "router message" + } + }, + "messageEvent": { + "action": "message", + "params": { + "detail": "message detail" + } + } + } + } + ``` + +- Receive the router event and obtain parameters in UIAbility. + + + ```ts + import UIAbility from '@ohos.app.ability.UIAbility' + + export default class EntryAbility extends UIAbility { + onCreate(want, launchParam) { + let params = JSON.parse(want.parameters.params); + // Obtain the info parameter passed in the router event. + if (params.info === "router info") { + // do something + // console.info("router info:" + params.info) + } + // Obtain the message parameter passed in the router event. + if (params.message === "router message") { + // do something + // console.info("router message:" + params.message) + } + } + ... + }; + ``` + +- Receive the message event in FormExtensionAbility and obtain parameters. + + + ```ts + import FormExtension from '@ohos.app.form.FormExtensionAbility'; + + export default class FormAbility extends FormExtension { + ... + onFormEvent(formId, message) { + // Obtain the detail parameter passed in the message event. + let msg = JSON.parse(message) + if (msg.detail === "message detail") { + // do something + // console.info("message info:" + msg.detail) + } + } + ... + }; + ``` diff --git a/en/application-dev/application-models/lifecycleapp-switch.md b/en/application-dev/application-models/lifecycleapp-switch.md index 892a8915bfed9927c2707364bdaffa1547f71bf6..9d89597ef5a77246ec7450261916061062d24d8d 100644 --- a/en/application-dev/application-models/lifecycleapp-switch.md +++ b/en/application-dev/application-models/lifecycleapp-switch.md @@ -1,17 +1,17 @@ # LifecycleApp Switching - | API in the FA Model| Corresponding d.ts File in the Stage Model| Corresponding API in the Stage Model| +| API in the FA Model| Corresponding d.ts File in the Stage Model| Corresponding API in the Stage Model| | -------- | -------- | -------- | | onShow?(): void; | \@ohos.window.d.ts | [on(eventType: 'windowStageEvent', callback: Callback<WindowStageEventType>): void;](../reference/apis/js-apis-window.md#onwindowstageevent9)
Listens for the switching to the [foreground](../reference/apis/js-apis-window.md#windowstageeventtype9).| | onHide?(): void; | \@ohos.window.d.ts | [on(eventType: 'windowStageEvent', callback: Callback<WindowStageEventType>): void;](../reference/apis/js-apis-window.md#onwindowstageevent9)
Listens for the switching to the [background](../reference/apis/js-apis-window.md#windowstageeventtype9).| -| onDestroy?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onDestroy(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityondestroy) | +| onDestroy?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onDestroy(): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityondestroy) | | onCreate?(): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate) | | onWindowDisplayModeChanged?(isShownInMultiWindow: boolean, newConfig: resourceManager.Configuration): void; | There is no corresponding API in the stage model.| No corresponding API is provided.| | onStartContinuation?(): boolean; | There is no corresponding API in the stage model.| In the stage model, an application does not need to detect whether the continuation is successful (detected when the application initiates the continuation request). Therefore, the **onStartContinuation()** callback is deprecated.| | onSaveData?(data: Object): boolean; | \@ohos.app.ability.UIAbility.d.ts | [onContinue(wantParam : {[key: string]: any}): AbilityConstant.OnContinueResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncontinue) | | onCompleteContinuation?(result: number): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) | -| onRestoreData?(data: Object): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)
[onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant)
In standard or singleton mode, the target ability completes data restoration in the **onCreate()** callback. In the callback, **launchParam.launchReason** is used to determine whether it is a continuation-based launch scenario. If it is, the data saved before continuation can be obtained from the **want** parameter.| +| onRestoreData?(data: Object): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)
[onNewWant(want: Want, launchParams: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant)
In multiton or singleton mode, the target ability completes data restoration in the **onCreate()** callback. In the callback, **launchParam.launchReason** is used to determine whether it is a continuation-based launch scenario. If it is, the data saved before continuation can be obtained from the **want** parameter.| | onRemoteTerminated?(): void; | application\ContinueCallback.d.ts | [onContinueDone(result: number): void;](../reference/apis/js-apis-distributedMissionManager.md#continuecallback) | | onSaveAbilityState?(outState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onSaveState(reason: AbilityConstant.StateType, wantParam : {[key: string]: any}): AbilityConstant.OnSaveResult;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonsavestate) | | onRestoreAbilityState?(inState: PacMap): void; | \@ohos.app.ability.UIAbility.d.ts | [onCreate(want: Want, param: AbilityConstant.LaunchParam): void;](../reference/apis/js-apis-app-ability-uiAbility.md#abilityoncreate)
After the application is restarted, the **onCreate()** callback is triggered. In the callback, **launchParam.launchReason** is used to determine whether it is a self-recovery scenario. If it is, the data saved before the restart can be obtained from the **want** parameter.| diff --git a/en/application-dev/application-models/mission-management-launch-type.md b/en/application-dev/application-models/mission-management-launch-type.md index 72b6dbf80df2628119ebcc29339ed7e4b70e9a50..ac8af200aef03876ee9aa9f0f3219f3a856afbbd 100644 --- a/en/application-dev/application-models/mission-management-launch-type.md +++ b/en/application-dev/application-models/mission-management-launch-type.md @@ -8,16 +8,19 @@ The following describes how the mission list manager manages the UIAbility insta - **singleton**: Only one UIAbility instance exists for an application. **Figure 1** Missions and singleton mode + ![mission-and-singleton](figures/mission-and-singleton.png) -- **standard**: Each time **startAbility()** is called, a UIAbility instance is created in the application process. +- **multiton**: Each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, a UIAbility instance is created in the application process. + + **Figure 2** Missions and multiton mode - **Figure 2** Missions and standard mode - ![mission-and-standard](figures/mission-and-standard.png) + ![mission-and-multiton](figures/mission-and-multiton.png) -- **specified**: The ([onAcceptWant](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant)) method of [AbilityStage](abilitystage.md) determines whether to create an instance. +- **specified**: The ([onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant)) method of [AbilityStage](abilitystage.md) determines whether to create a UIAbility instance. **Figure 3** Missions and specified mode + ![mission-and-specified](figures/mission-and-specified.png) @@ -30,4 +33,3 @@ Every mission retains a snapshot of the UIAbility instance. After the UIAbility > **NOTE** > > The **specified** mode is supported in the stage model only. - diff --git a/en/application-dev/application-models/mission-management-overview.md b/en/application-dev/application-models/mission-management-overview.md index b6f6668f7ce56a9de0b5a3d0182b14ec189703c9..785a9f8291ea43e756ebed07843ceef23570160d 100644 --- a/en/application-dev/application-models/mission-management-overview.md +++ b/en/application-dev/application-models/mission-management-overview.md @@ -4,7 +4,7 @@ Before getting started with the development of mission management, be familiar with the following concepts related to mission management: -- AbilityRecord: minimum unit for the system service to manage a UIAbility instance. It corresponds to a UIAbility component instance of an application. +- AbilityRecord: minimum unit for the system service to manage a UIAbility instance. It corresponds to a UIAbility component instance of an application. A maximum of 512 UIAbility instances can be managed on the system service side. - MissionRecord: minimum unit for mission management. One MissionRecord has only one AbilityRecord. In other words, a UIAbility component instance corresponds to a mission. @@ -28,102 +28,102 @@ Missions are managed by system applications (such as home screen), rather than t - Switch a mission to the foreground. -A UIAbility instance corresponds to an independent mission. Therefore, when an application calls the **startAbility()** method to start a UIAbility, a mission is created. - - -To call [missionManager](../reference/apis/js-apis-application-missionManager.md) to manage missions, the home screen application must request the **ohos.permission.MANAGE_MISSIONS** permission. For details about the configuration, see [Permission Application Guide](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). - - -You can use **missionManager** to manage missions, for example, listening for mission changes, obtaining mission information or snapshots, and clearing, locking, or unlocking missions. The sample code is as follows: - -```ts -import missionManager from '@ohos.app.ability.missionManager' - -let listener = { - // Listen for mission creation. - onMissionCreated: function (mission) { - console.info("--------onMissionCreated-------") - }, - // Listen for mission destruction. - onMissionDestroyed: function (mission) { - console.info("--------onMissionDestroyed-------") - }, - // Listen for mission snapshot changes. - onMissionSnapshotChanged: function (mission) { - console.info("--------onMissionSnapshotChanged-------") - }, - // Listen for switching the mission to the foreground. - onMissionMovedToFront: function (mission) { - console.info("--------onMissionMovedToFront-------") - }, - // Listen for mission icon changes. - onMissionIconUpdated: function (mission, icon) { - console.info("--------onMissionIconUpdated-------") - }, - // Listen for mission name changes. - onMissionLabelUpdated: function (mission) { - console.info("--------onMissionLabelUpdated-------") - }, - // Listen for mission closure events. - onMissionClosed: function (mission) { - console.info("--------onMissionClosed-------") - } -}; - -// 1. Register a mission change listener. -let listenerId = missionManager.on('mission', listener); - -// 2. Obtain the latest 20 missions in the system. -missionManager.getMissionInfos("", 20, (error, missions) => { - console.info("getMissionInfos is called, error.code = " + error.code); - console.info("size = " + missions.length); - console.info("missions = " + JSON.stringify(missions)); -}); - -// 3. Obtain the detailed information about a mission. -let missionId = 11; // The mission ID 11 is only an example. -let mission = missionManager.getMissionInfo("", missionId).catch(function (err) { - console.info(err); -}); - -// 4. Obtain the mission snapshot. -missionManager.getMissionSnapShot("", missionId, (error, snapshot) => { - console.info("getMissionSnapShot is called, error.code = " + error.code); - console.info("bundleName = " + snapshot.ability.bundleName); -}) - -// 5. Obtain the low-resolution mission snapshot. -missionManager.getLowResolutionMissionSnapShot("", missionId, (error, snapshot) => { - console.info("getLowResolutionMissionSnapShot is called, error.code = " + error.code); - console.info("bundleName = " + snapshot.ability.bundleName); -}) - -// 6. Lock or unlock the mission. -missionManager.lockMission(missionId).then(() => { - console.info("lockMission is called "); -}); - -missionManager.unlockMission(missionId).then(() => { - console.info("unlockMission is called "); -}); - -// 7. Switch the mission to the foreground. -missionManager.moveMissionToFront(missionId).then(() => { - console.info("moveMissionToFront is called "); -}); - -// 8. Clear a single mission. -missionManager.clearMission(missionId).then(() => { - console.info("clearMission is called "); -}); - -// 9. Clear all missions. -missionManager.clearAllMissions().catch(function (err) { - console.info(err); -}); - -// 10. Deregister the mission change listener. -missionManager.off('mission', listenerId, (error) => { - console.info("unregisterMissionListener"); -}) -``` +A UIAbility instance corresponds to an independent mission. Therefore, when an application calls [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to start a UIAbility, a mission is created. + +1. To call [missionManager](../reference/apis/js-apis-application-missionManager.md) to manage missions, the home screen application must request the **ohos.permission.MANAGE_MISSIONS** permission. For details about the configuration, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + +2. You can use **missionManager** to manage missions, for example, listening for mission changes, obtaining mission information or snapshots, and clearing, locking, or unlocking missions. + + ```ts + import missionManager from '@ohos.app.ability.missionManager' + + let listener = { + // Listen for mission creation. + onMissionCreated: function (mission) { + console.info("--------onMissionCreated-------") + }, + // Listen for mission destruction. + onMissionDestroyed: function (mission) { + console.info("--------onMissionDestroyed-------") + }, + // Listen for mission snapshot changes. + onMissionSnapshotChanged: function (mission) { + console.info("--------onMissionSnapshotChanged-------") + }, + // Listen for switching the mission to the foreground. + onMissionMovedToFront: function (mission) { + console.info("--------onMissionMovedToFront-------") + }, + // Listen for mission icon changes. + onMissionIconUpdated: function (mission, icon) { + console.info("--------onMissionIconUpdated-------") + }, + // Listen for mission name changes. + onMissionLabelUpdated: function (mission) { + console.info("--------onMissionLabelUpdated-------") + }, + // Listen for mission closure events. + onMissionClosed: function (mission) { + console.info("--------onMissionClosed-------") + } + }; + + // 1. Register a mission change listener. + let listenerId = missionManager.on('mission', listener); + + // 2. Obtain the latest 20 missions in the system. + missionManager.getMissionInfos("", 20, (error, missions) => { + console.info("getMissionInfos is called, error.code = " + error.code); + console.info("size = " + missions.length); + console.info("missions = " + JSON.stringify(missions)); + }); + + // 3. Obtain the detailed information about a mission. + let missionId = 11; // The mission ID 11 is only an example. + let mission = missionManager.getMissionInfo("", missionId).catch(function (err) { + console.info(err); + }); + + // 4. Obtain the mission snapshot. + missionManager.getMissionSnapShot("", missionId, (error, snapshot) => { + console.info("getMissionSnapShot is called, error.code = " + error.code); + console.info("bundleName = " + snapshot.ability.bundleName); + }) + + // 5. Obtain the low-resolution mission snapshot. + missionManager.getLowResolutionMissionSnapShot("", missionId, (error, snapshot) => { + console.info("getLowResolutionMissionSnapShot is called, error.code = " + error.code); + console.info("bundleName = " + snapshot.ability.bundleName); + }) + + // 6. Lock or unlock the mission. + missionManager.lockMission(missionId).then(() => { + console.info("lockMission is called "); + }); + + missionManager.unlockMission(missionId).then(() => { + console.info("unlockMission is called "); + }); + + // 7. Switch the mission to the foreground. + missionManager.moveMissionToFront(missionId).then(() => { + console.info("moveMissionToFront is called "); + }); + + // 8. Clear a single mission. + missionManager.clearMission(missionId).then(() => { + console.info("clearMission is called "); + }); + + // 9. Clear all missions. + missionManager.clearAllMissions().catch(function (err) { + console.info(err); + }); + + // 10. Deregister the mission change listener. + missionManager.off('mission', listenerId, (error) => { + console.info("unregisterMissionListener"); + }) + ``` + + diff --git a/en/application-dev/application-models/mission-set-icon-name-for-task-snapshot.md b/en/application-dev/application-models/mission-set-icon-name-for-task-snapshot.md new file mode 100644 index 0000000000000000000000000000000000000000..51e2c75c72caa10f56cc21ad2057f9f7a94a8c12 --- /dev/null +++ b/en/application-dev/application-models/mission-set-icon-name-for-task-snapshot.md @@ -0,0 +1,50 @@ +# Setting the Icon and Name of a Mission Snapshot + +Setting a unique icon and name for each mission snapshot of an application helps you better manage the missions and functions of the application. + +By default, the **icon** and **label** fields in the [abilities tag](../quick-start/module-configuration-file.md#abilities) of the [module.json5 file](../quick-start/module-configuration-file.md) are used to set the icon and label. + +Figure 1 Mission snapshot of a UIAbility +![](figures/mission-list-recent.png) + +You can also use [UIAbilityContext.setMissionIcon()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissionicon) and [UIAbilityContext.setMissionLabel()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissionlabel) to customize the icon and name for a mission snapshot. For example, for a UIAbility instance in multiton mode, you can configure the icon and name for each mission snapshot based on different functions. + +This document describes the following operations: + +- [Setting a Mission Snapshot Icon (for System Applications Only)](#setting-a-mission-snapshot-icon-for-system-applications-only) +- [Setting a Mission Snapshot Name](#setting-a-mission-snapshot-name) + +## Setting a Mission Snapshot Icon (for System Applications Only) + +Call [UIAbilityContext.setMissionIcon()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissionicon) to set the icon of a mission snapshot. The icon is an object of the [PixelMap](../reference/apis/js-apis-image.md#pixelmap7) type. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). +```ts +let imagePixelMap: PixelMap = undefined; // Obtain the PixelMap information. + +context.setMissionIcon(imagePixelMap, (err) => { + if (err.code) { + console.error(`Failed to set mission icon. Code is ${err.code}, message is ${err.message}`); + } +}) +``` + +The display effect is shown below. + +Figure 2 Mission snapshot icon +![](figures/mission-set-task-snapshot-icon.png) + +## Setting a Mission Snapshot Name + +Call [UIAbilityContext.setMissionLabel()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextsetmissionlabel) to set the name of a mission snapshot. + +```ts +this.context.setMissionLabel('test').then(() => { + console.info('Succeeded in seting mission label.'); +}).catch((err) => { + console.error(`Failed to set mission label. Code is ${err.code}, message is ${err.message}`); +}); +``` + +The display effect is shown below. + +Figure 3 Mission snapshot name +![](figures/mission-set-task-snapshot-label.png) diff --git a/en/application-dev/application-models/module-switch.md b/en/application-dev/application-models/module-switch.md index c5948c5ac1c4eebef5eb5e6bf65dd6625c021313..8adf2dbb7608da48131c9402f4ea3b0ac09a525d 100644 --- a/en/application-dev/application-models/module-switch.md +++ b/en/application-dev/application-models/module-switch.md @@ -8,39 +8,41 @@ When switching an application from the FA model to the stage model, you must mig | Field Name in the FA Model| Field Description| Field Name in the Stage Model| Difference| | -------- | -------- | -------- | -------- | | mainAbility | Ability displayed on the Service Center icon. When the resident process is started, the **mainAbility** is started.| mainElement | The field name is changed, and the stage mode does not use the period (.) in ability names.| -| package | Package name of the HAP file, which must be unique in the application.| / | The stage model uses **name** to ensure application uniqueness. To ensure a successful switching from the FA model to the stage model, **name** in the stage model must be the same as **package** in the FA model.| +| package | Package name of the HAP file, which must be unique in the application.| / | The stage model uses **name** to ensure application uniqueness. | | name | Class name of the HAP file.| / | This configuration is not enabled in the FA model, and the stage model does not have such a field.| | supportedModes | Modes supported by the application. Currently, only the **drive** mode is defined.| / | This configuration is deprecated in the stage model.| | moduleName in the distro object| Name of the current HAP file.
moduleName in the distro object.| name | The field name is changed.| | moduleType in the distro object| Type of the HAP file. The value can be **entry** or **feature**. For the HAR type, set this field to **har**.| type | The field name is changed.| | installationFree in the distro object| Whether the HAP file supports the installation-free feature.| installationFree | The field name is changed.| | deliveryWithInstall in the distro object| Whether the HAP file is installed with the application.| deliveryWithInstall | The field name is changed.| -| metaData | Metadata of the HAP file.| metadata | See [Table 2](#table-2-metadata-comparison).| -| abilities | All abilities in the current module.| abilities | See [Table 5](#table-5-abilities-comparison).| +| metaData | Metadata of the HAP file.| metadata | For details, see Table 2.| +| abilities | All abilities in the current module.| abilities | For details, see Table 5.| | js | A set of JS modules developed using ArkUI. Each element in the set represents the information about a JS module.| pages | The stage model retains **pages** under the **module** tag. The window configuration is the lower level of **pages**.| | shortcuts | Shortcuts of the application.| shortcut_config.json| In the stage model, the **shortcut_config.json** file is defined in **resources/base/profile** in the development view.| | reqPermissions | Permissions that the application requests from the system when it is running.| requestPermissions | The field name is changed.| | colorMode | Color mode of the application.| / | This configuration is not supported in the stage model.| -| distroFilter | Distribution rules of the application.| distroFilter_config.json| In the stage model, the **distroFilter_config.json** file is defined in **resources/base/profile** in the development view.| +| distributionFilter | Distribution rules of the application.| distroFilter_config.json| In the stage model, the **distroFilter_config.json** file is defined in **resources/base/profile** in the development view.| | reqCapabilities | Device capabilities required for running the application.| / | This configuration is not supported in the stage model.| | commonEvents | Common events.| common_event_config.json| In the stage model, the **common_event_config.json** file is defined in **resources/base/profile** in the development view.| | entryTheme | Keyword of an OpenHarmony internal theme.| / | This configuration is not supported in the stage model.| + **Table 2** metaData comparison -| Field Name Under metaData in the FA Model| Field Description| Field Name Under metaData in the Stage Model| Difference| +| Field Name in the FA Model| Field Description| Field Name in the Stage Model| Difference| | -------- | -------- | -------- | -------- | | parameters | Metadata of the parameters to be passed for calling the ability.| / | This configuration is not supported in the stage model.| | results | Metadata of the ability return value.| / | This configuration is not supported in the stage model.| -| customizeData | Custom metadata of the parent component. **parameters** and **results** cannot be configured in **application**.| metadata | See [Table 3](#table-3-comparison-between-customizedata-under-metadata-in-the-fa-model-and-metadata-in-the-stage-model).| +| customizeData | Custom metadata of the parent component. **parameters** and **results** cannot be configured in **application**.| metadata | **For details**, see Table 3.| -**Table 3** Comparison between customizeData under metaData in the FA model and metadata in the stage model +**Table 3** Comparison between customizeData under metaData in the FA model and metadata in the stage Model -| Field Name Under customizeData in metaData in the FA Model| Field Description| Field Name Under metaData in the Stage Model| Difference| +| Field Name in the FA Model| Field Description| Field Name in the Stage Model| Difference| | -------- | -------- | -------- | -------- | | name | Key name that identifies a data item. The value is a string with a maximum of 255 bytes.| name | None.| | value | Value of the data item. The value is a string with a maximum of 255 bytes.| value | None.| -| extra | Format of the current custom data. The value is the resource value of **extra**.| resource | The field name is changed. For details, see [Table 4](#table-4-metadata-examples).| +| extra | Format of the current custom data. The value is the resource value of **extra**.| resource | The field name is changed. For details, see Table 4.| + **Table 4** metaData examples @@ -56,7 +58,7 @@ When switching an application from the FA model to the stage model, you must mig | process | Name of the process running the application or ability.| / | The stage model does not support configuration of **process** under **abilities**. The configuration of **process** is available under the **module** tag.| | uri | URI of the ability.| / | This configuration is not supported in the stage model.| | deviceCapability | Device capabilities required to run the ability.| / | This configuration is not supported in the stage model.| -| metaData | Metadata of the ability.| metadata | See [Table 2](#table-2-metadata-comparison).| +| metaData | Metadata of the ability.| metadata | For details, see Table 2.| | type | Ability type.| / | This configuration is not supported in the stage model.| | grantPermission | Whether permissions can be granted for any data in the ability.| / | The stage model does not support such a configuration under **abilities**.| | readPermission | Permission required for reading data in the ability. This field applies only to the ability using the Data template.| / | In the stage model, this configuration is available under **extensionAbilities**, but not **abilities**.| @@ -69,5 +71,5 @@ When switching an application from the FA model to the stage model, you must mig | formsEnabled | Whether the ability can provide widgets.| / | This configuration is not supported in the stage model.| | forms | Information about the widgets used by the ability. This field is valid only when **formsEnabled** is set to **true**.| form_config.json| In the stage model, the **form_config.json** file is defined in **resources/base/profile** in the development view.| | srcLanguage | Programming language used to develop the ability.| / | This configuration is not supported in the stage model.| -| srcPath | Path of the JS component code corresponding to the ability.| srcEntrance | Path of the JS code corresponding to the ability.| +| srcPath | Path of the JS component code corresponding to the ability.| srcEntry | Path of the JS code corresponding to the ability.| | uriPermission | Application data that the ability can access.| / | This configuration is not supported in the stage model.| diff --git a/en/application-dev/application-models/page-mission-stack.md b/en/application-dev/application-models/page-mission-stack.md index 702cb9ba928d5266ce6720d10538df6109b0cbeb..cdb7ce724f2a29c711d4d056bcda5716f265ba48 100644 --- a/en/application-dev/application-models/page-mission-stack.md +++ b/en/application-dev/application-models/page-mission-stack.md @@ -5,7 +5,8 @@ A single UIAbility component can implement multiple pages and redirection between these pages. The redirection relationship inside the UIAbility component is called page stack, which is managed by the ArkUI framework. For example, Page1 -> Page2 -> Page3 of UIAbility1 and PageA -> PageB -> PageC of UIAbility2 in the figure below are two page stacks. - **Figure 1** Page stack +**Figure 1** Page stack + ![mission-record](figures/mission-record.png) - A page stack is formed as follows (Steps 2, 3, 5, and 6 are page redirection and managed by ArkUI): diff --git a/en/application-dev/application-models/pageability-launch-type.md b/en/application-dev/application-models/pageability-launch-type.md index 5241a7cabefbf3e68e6a3f413b8892ef5f6ff8d3..524f693c3d8c5586217d3264dedd9bde0daa16ff 100644 --- a/en/application-dev/application-models/pageability-launch-type.md +++ b/en/application-dev/application-models/pageability-launch-type.md @@ -5,10 +5,10 @@ Depending on the launch type, the action performed when the PageAbility starts d **Table 1** PageAbility launch types -| Launch Type| Description| -| -------- | -------- | -| singleton | Each time **startAbility()** is called, if an ability instance of this type already exists in the application process, the instance is reused. There is only one ability instance of this type in **Recents**.
A typical scenario is as follows: When a user opens a video playback application and watches a video, returns to the home screen, and opens the video playback application again, the video that the user watched before returning to the home screen is still played.| -| standard | Default type. Each time **startAbility()** is called, a new ability instance is created in the application process. Multiple ability instances of this type are displayed in **Recents**.
A typical scenario is as follows: When a user opens a document application and touches **New**, a new document task is created. Multiple new document missions are displayed in **Recents**.| +| Launch Type| Meaning| Description | +| -------- | -------- | -------- | +| singleton | Singleton mode| Each time **startAbility()** is called, if an ability instance of this type already exists in the application process, the instance is reused. There is only one ability instance of this type in **Recents**.
A typical scenario is as follows: When a user opens a video playback application and watches a video, returns to the home screen, and opens the video playback application again, the video that the user watched before returning to the home screen is still played.| +| standard | Multiton mode| Default type. Each time **startAbility()** is called, a new ability instance is created in the application process. Multiple ability instances of this type are displayed in **Recents**.
A typical scenario is as follows: When a user opens a document application and touches **New**, a new document task is created. Multiple new document missions are displayed in **Recents**.| You can set **launchType** in the **config.json** file to configure the launch type. The sample code is as follows: @@ -16,13 +16,13 @@ You can set **launchType** in the **config.json** file to configure the launch t ```json { "module": { - // ... + ... "abilities": [ { - // singleton mode. - // standard mode. + // singleton means the singleton mode. + // standard means the multiton mode. "launchType": "standard", - // ... + ... } ] } @@ -30,7 +30,7 @@ You can set **launchType** in the **config.json** file to configure the launch t ``` -When the PageAbility is started for the first time (either in standard or singleton mode), the [PageAbility lifecycle callbacks](pageability-lifecycle.md#table13118194914476) are triggered. When it is not started for the first time in singleton mode, the **onNewWant()** callback (as described in the table below) is triggered, but the **onCreate()** callback is not. +When the PageAbility is started in multiton mode or it is started in singleton mode for the first time, the [PageAbility lifecycle callbacks](pageability-lifecycle.md#table13118194914476) are triggered. When it is not started for the first time in singleton mode, the **onNewWant()** callback (as described in the table below) is triggered, but the **onCreate()** callback is not. **Table 2** Callbacks specific to the singleton mode diff --git a/en/application-dev/application-models/process-model-fa.md b/en/application-dev/application-models/process-model-fa.md index 699643031121521fbf95d26a949df906fa175a18..ce4c9778d3bf678c7ecb8094477050a42eebb7d7 100644 --- a/en/application-dev/application-models/process-model-fa.md +++ b/en/application-dev/application-models/process-model-fa.md @@ -1,4 +1,4 @@ -# Process Model (FA Model) +# Process Model Overview (FA Model) The OpenHarmony process model is shown below. diff --git a/en/application-dev/application-models/process-model-stage.md b/en/application-dev/application-models/process-model-stage.md index bbfa0602aecb127c5e484f0ebbdcb166f81310f7..cf758d94636773dfd190366d0e215de655902abd 100644 --- a/en/application-dev/application-models/process-model-stage.md +++ b/en/application-dev/application-models/process-model-stage.md @@ -1,4 +1,4 @@ -# Process Model (Stage Model) +# Process Model Overview (Stage Model) The OpenHarmony process model is shown below. @@ -15,7 +15,8 @@ The OpenHarmony process model is shown below. > NOTE > -> You can create ServiceExtensionAbility and DataShareExtensionAbility only for system applications. +> - You can create ServiceExtensionAbility and DataShareExtensionAbility only for system applications. +> - To view information about all running processes, run the **hdc shell** command to enter the shell CLI of the device, and run the **ps -ef** command. A system application can apply for multi-process permissions (as shown in the following figure) and configure a custom process for an HAP. UIAbility, DataShareExtensionAbility, and ServiceExtensionAbility in the HAP run in the custom process. Different HAPs run in different processes by configuring different process names. diff --git a/en/application-dev/application-models/redirection-rules.md b/en/application-dev/application-models/redirection-rules.md index d7456653640942bca333a28f7f6d5262ec4d63f3..19c74c605cd7ca1451cef6809f6ec5238cec36df 100644 --- a/en/application-dev/application-models/redirection-rules.md +++ b/en/application-dev/application-models/redirection-rules.md @@ -21,11 +21,11 @@ To enable an ability to be called by any application, configure the **config.jso ```ts { "module": { - // ... + ... "abilities": [ { "visible": "true", - // ... + ... } ] } @@ -33,4 +33,4 @@ To enable an ability to be called by any application, configure the **config.jso ``` -If the ability contains **skills**, you are advised to set **visible** to **true** so that the ability can be [implicitly started](explicit-implicit-want-mappings.md#matching-rules-of-implicit-want) by other applications. If this attribute is set to **false**, the system returns **PERMISSION_DENIED** when other applications attempt to start the ability. In this case, a system application can request the [START_INVISIBLE_ABILITY](../security/permission-list.md) permission to start the ability. Example abilities with **visible** set to **false** are home screen, voice assistant, or search assistant. +If the ability contains **skills**, you are advised to set **visible** to **true** so that the ability can be [implicitly started](explicit-implicit-want-mappings.md) by other applications. If this attribute is set to **false**, the system returns **PERMISSION_DENIED** when other applications attempt to start the ability. In this case, a system application can request the [START_INVISIBLE_ABILITY](../security/permission-list.md) permission to start the ability. Example abilities with **visible** set to **false** are home screen, voice assistant, or search assistant. diff --git a/en/application-dev/application-models/request-permissions.md b/en/application-dev/application-models/request-permissions.md index 670860d87dbb56adceb02f4ca350c24b61260d30..a29b793c9949d1080c8322681ba27cf6495f29ac 100644 --- a/en/application-dev/application-models/request-permissions.md +++ b/en/application-dev/application-models/request-permissions.md @@ -9,37 +9,7 @@ During application development, you must declare the required permission in the To declare a permission in **config.json**, add **reqPermissions** under **module** and list the permission. +For example, to request the permission to access the calendar, perform the following steps: -For example, to declare the permission to access the calendar, request the **ohos.permission.READ_CALENDAR** permission. For details, see [Permission Application Guide](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). - - -The sample code in the **config.json** file is as follows: - -```json -{ - "module": { - // ... - "reqPermissions": [ - { - "name": "ohos.permission.READ_CALENDAR" - // ... - } - ] - } -} -``` - - -Request the permission from uses in the form of a dialog box: - -```ts -import featureAbility from '@ohos.ability.featureAbility'; - -let context = featureAbility.getContext(); -let permissions: Array = ['ohos.permission.READ_CALENDAR'] -context.requestPermissionsFromUser(permissions, 1).then((data) => { - console.info("Succeed to request permission from user with data: " + JSON.stringify(data)) -}).catch((error) => { - console.info("Failed to request permission from user with error: " + JSON.stringify(error)) -}) -``` +1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). +2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). diff --git a/en/application-dev/application-models/service-widget-overview.md b/en/application-dev/application-models/service-widget-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..528d9d4c134107c30de75f7f9e84ab42be514224 --- /dev/null +++ b/en/application-dev/application-models/service-widget-overview.md @@ -0,0 +1,58 @@ +# Service Widget Overview + + +A service widget (also called widget) is a set of UI components that display important information or operations specific to an application. It provides users with direct access to a desired application service, without the need to open the application first. A widget usually appears as a part of the UI of another application (which currently can only be a system application, such as the home screen) and provides basic interactive features such as opening a UI page or sending a message. + + +## Service Widget Architecture + +**Figure 1** Service widget architecture + +![WidgetArchitecture](figures/WidgetArchitecture.png) + +Before you get started, it would be helpful if you have a basic understanding of the following concepts: + +- Widget host: an application that displays the widget content and controls the widget location. An example is the home screen in the preceding figure. + + - Application icon: an application entry icon, clicking which starts the application process. The icon content does not support interactions. + - Widget: an interactive UI in various sizes. It may provide buttons to implement different functions, such as the button to [update the widget content](arkts-ui-widget-event-formextensionability.md) or [switch to an application](arkts-ui-widget-event-router.md). + +- Card provider: an application that provides service widget content to be displayed. It controls the display content, display logic, and component click events triggered on a service widget. + + - FormExtensionAbility: widget service logic module, which provides lifecycle callbacks invoked when a widget is created, destroyed, or updated. + - Widget page: widget UI module, which contains display and interaction information such as components, layouts, and events. + +Below is the typical procedure of using the widget: + +**Figure 2** Typical procedure of using the widget + +![WidgetUse](figures/WidgetUse.png) + +1. Touch and hold an application icon on the home screen to display the shortcut menu. + +2. Touch **Service widget** to access the preview screen. + +3. Touch the **Add to home** button. The widget is then added to the home screen. + + +## Widget UI Development Mode + +In the stage model, the UI of a widget can be developed in [ArkTS](arkts-ui-widget-working-principles.md) or [JS](js-ui-widget-development.md). + +- A widget developed in the ArkTS-based declarative development paradigm is called ArkTS widget. + +- A widget developed in the JS-compatible web-like development paradigm is called JS widget. + +ArkTS widgets and JS widgets have different implementation principles and features. The following table lists the differences in capabilities. + +| Category| JS widget| ArkTS widget| +| -------- | -------- | -------- | +| Development paradigm| Web-like paradigm| Declarative paradigm| +| Component capability| Supported| Supported| +| Layout capability| Supported| Supported| +| Event capability| Supported| Supported| +| Custom animation| Not supported| Supported| +| Custom drawing| Not supported| Supported| +| Logic code execution (excluding the import capability)| Not supported| Supported| + +As can be seen above, ArkTS widgets provide more capabilities and use cases than JS widgets. Therefore, ArkTS widgets are always recommended, except for the case where the widget consists of only static pages. diff --git a/en/application-dev/application-models/serviceextensionability.md b/en/application-dev/application-models/serviceextensionability.md index 9ff7a0ae5d6df7574da19565c81411236dba3dda..f2546aed3e2436b574690ef281347a5715b79dad 100644 --- a/en/application-dev/application-models/serviceextensionability.md +++ b/en/application-dev/application-models/serviceextensionability.md @@ -1,125 +1,152 @@ # ServiceExtensionAbility +## Overview -[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md) is an ExtensionAbility component of the service type that provides extension capabilities related to background services. +[ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md) is an ExtensionAbility component of the SERVICE type that provides capabilities related to background services. It holds an internal [ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md), through which a variety of APIs are provided for external systems. +In this document, the started ServiceExtensionAbility component is called the server, and the component that starts ServiceExtensionAbility is called the client. -ServiceExtensionAbility can be started or connected by other application components to process transactions in the background based on the request of the caller. System applications can call the [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartserviceextensionability) method to start background services or call the [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability) method to connect to background services. Third-party applications can call only **connectServiceExtensionAbility()** to connect to background services. The differences between starting and connecting to background services are as follows: +A ServiceExtensionAbility can be started or connected by other components to process transactions in the background based on the request of the caller. System applications can call the [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability) method to start background services or call the [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method to connect to background services. Third-party applications can call only **connectServiceExtensionAbility()** to connect to background services. The differences between starting and connecting to background services are as follows: +- **Starting**: In the case that AbilityA starts ServiceB, they are weakly associated. After AbilityA exits, ServiceB can still exist. -- In the case that AbilityA starts ServiceB, they are weakly associated. After AbilityA exits, ServiceB can still exist. +- **Connecting**: In the case that AbilityA connects to ServiceB, they are strongly associated. After AbilityA exits, ServiceB also exits. -- In the case that AbilityA connects to ServiceB, they are strongly associated. After AbilityA exits, ServiceB also exits. +Note the following: +- If a ServiceExtensionAbility is started only by means of connecting, its lifecycle is controlled by the client. A new connection is set up each time the client calls the [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method. When the client exits or calls the [disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) method, the connection is disconnected. After all connections are disconnected, the ServiceExtensionAbility automatically exits. -Each type of ExtensionAbility has its own context. ServiceExtensionAbility has [ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md). In this document, the started ServiceExtensionAbility component is called the server, and the component that starts ServiceExtensionAbility is called the client. - - -This topic describes how to use ServiceExtensionAbility in the following scenarios: - - -- [Implementing a Background Service (for System Applications Only)](#implementing-a-background-service-for-system-applications-only) - -- [Starting a Background Service (for System Applications Only)](#starting-a-background-service-for-system-applications-only) - -- [Connecting to a Background Service](#connecting-to-a-background-service) - +- Once a ServiceExtensionAbility is started by means of starting, it will not exit automatically. System applications can call the [stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstopserviceextensionability) method to stop it. > **NOTE** -> - OpenHarmony does not support third-party applications in implementing ServiceExtensionAbility. If you want to implement transaction processing in the background, use background tasks. For details, see [Background Task](../task-management/background-task-overview.md). -> +> +> - Currently, third-party applications cannot implement ServiceExtensionAbility. If you want to implement transaction processing in the background, use background tasks. For details, see [Background Task](../task-management/background-task-overview.md). > - UIAbility of a third-party application can connect to ServiceExtensionAbility provided by the system through the context. -> > - Third-party applications can connect to ServiceExtensionAbility provided by the system only when they gain focus in the foreground. - -## Implementing a Background Service (for System Applications Only) +## Lifecycle [ServiceExtensionAbility](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md) provides the callbacks **onCreate()**, **onRequest()**, **onConnect()**, **onDisconnect()**, and **onDestory()**. Override them as required. The following figure shows the lifecycle of ServiceExtensionAbility. - **Figure 1** ServiceExtensionAbility lifecycle +**Figure 1** ServiceExtensionAbility lifecycle + ![ServiceExtensionAbility-lifecycle](figures/ServiceExtensionAbility-lifecycle.png) - **onCreate** - - This callback is triggered when a service is created for the first time. You can perform initialization operations, for example, registering a common event listener. + + This callback is triggered when a ServiceExtensionAbility is created for the first time. You can perform initialization operations, for example, registering a common event listener. > **NOTE** > - > If a service has been created, starting it again does not trigger the **onCreate()** callback. + > If a ServiceExtensionAbility has been created, starting it again does not trigger the **onCreate()** callback. - **onRequest** - - This callback is triggered when another component calls the **startServiceExtensionAbility()** method to start the service. After being started, the service runs in the background. + + This callback is triggered when another component calls the [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability) method to start a ServiceExtensionAbility. After being started, the ServiceExtensionAbility runs in the background. This callback is triggered each time the [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartserviceextensionability) method is called. - **onConnect** - - This callback is triggered when another component calls the **connectServiceExtensionAbility()** method to connect to the service. In this method, a remote proxy object (IRemoteObject) is returned, through which the client communicates with the server by means of RPC. + + This callback is triggered when another component calls the [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method to connect to a ServiceExtensionAbility. In this method, a remote proxy object (IRemoteObject) is returned, through which the client communicates with the server by means of RPC. At the same time, the system stores the remote proxy object (IRemoteObject). If another component calls the [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextconnectserviceextensionability) method to connect to this ServiceExtensionAbility, the system directly returns the saved remote proxy object (IRemoteObject) and does not trigger the callback. - **onDisconnect** - - This callback is triggered when a component calls the **disconnectServiceExtensionAbility()** method to disconnect from the service. + + This callback is triggered when the last connection is disconnected. A connection is disconnected when the client exits or the [disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextdisconnectserviceextensionability) method is called. - **onDestroy** - This callback is triggered when the service is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregister the listener. + This callback is triggered when ServiceExtensionAbility is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregister the listener. +## Implementing a Background Service (for System Applications Only) -## How to Develop +### Preparations -To implement a background service, manually create a ServiceExtensionAbility component in DevEco Studio. The procedure is as follows: +Only system applications can implement ServiceExtensionAbility. You must make the following preparations before development: -1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **serviceextability**. +- **Switching to the full SDK**: All APIs related to ServiceExtensionAbility are marked as system APIs and hidden by default. Therefore, you must manually obtain the full SDK from the mirror and switch to it in DevEco Studio. For details, see [Guide to Switching to Full SDK](../quick-start/full-sdk-switch-guide.md). -2. In the **serviceextability** directory, right-click and choose **New > TypeScript File** to create a file named **ServiceExtAbility.ts**. +- **Requesting the AllowAppUsePrivilegeExtension privilege**: Only applications with the **AllowAppUsePrivilegeExtension** privilege can develop ServiceExtensionAbility. For details about how to request the privilege, see [Application Privilege Configuration Guide](../../device-dev/subsystems/subsys-app-privilege-config-guide.md). -3. Open the **ServiceExtAbility.ts** file, import the [RPC module](../reference/apis/js-apis-rpc.md), and reload the **onRemoteMessageRequest()** method to receive messages from the client and return the processing result to the client. **REQUEST_VALUE** is used to verify the service request code sent by the client. - - ```ts - import rpc from '@ohos.rpc'; - - const REQUEST_CODE = 99; - - class StubTest extends rpc.RemoteObject { - constructor(des) { - super(des); - } - - // Receive a message from the client and return the processing result to the client. - onRemoteMessageRequest(code, data, reply, option) { - if (code === REQUEST_CODE) { - // Receive data from the client. - // If the client calls data.writeInt() multiple times to write multiple pieces of data, the server can call data.readInt() multiple times to receive all the data. - let optFir = data.readInt(); - let optSec = data.readInt(); - // The server returns the data processing result to the client. - // In the example, the server receives two pieces of data and returns the sum of the two pieces of data to the client. - reply.writeInt(optFir + optSec); - } - return true; - } - - // Send messages to the client in synchronous or asynchronous mode. - sendRequest(code, data, reply, options) { - return null; - } - } - ``` +### Defining IDL Interfaces + +As a background service, ServiceExtensionAbility needs to provide interfaces that can be called by external systems. You can define the interfaces in IDL files and use the [IDL tool](../IDL/idl-guidelines.md) to generate proxy and stub files. The following demonstrates how to define a file named **IIdlServiceExt.idl**: + +```cpp +interface OHOS.IIdlServiceExt { + int ProcessData([in] int data); + void InsertDataToMap([in] String key, [in] int val); +} +``` + +Create the **IdlServiceExt** directory in the **ets** directory corresponding to the module of the DevEco Studio project, and copy the files generated by the [IDL tool](../IDL/idl-guidelines.md) to this directory. Then create a file named **idl_service_ext_impl.ts** to implement the IDL interfaces. + +``` +├── ets +│ ├── IdlServiceExt +│ │ ├── i_idl_service_ext.ts # File generated. +│ │ ├── idl_service_ext_proxy.ts # File generated. +│ │ ├── idl_service_ext_stub.ts # File generated. +│ │ ├── idl_service_ext_impl.ts # Custom file used to implement IDL interfaces. +│ └ +└ +``` + +An example of **idl_service_ext_impl.ts** is as follows: + +```ts +import {processDataCallback} from './i_idl_service_ext'; +import {insertDataToMapCallback} from './i_idl_service_ext'; +import IdlServiceExtStub from './idl_service_ext_stub'; + +const ERR_OK = 0; +const TAG: string = "[IdlServiceExtImpl]"; + +// You need to implement interfaces in this type. +export default class ServiceExtImpl extends IdlServiceExtStub { + processData(data: number, callback: processDataCallback): void { + // Implement service logic. + console.info(TAG, `processData: ${data}`); + callback(ERR_OK, data + 1); + } + + insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void { + // Implement service logic. + console.info(TAG, `insertDataToMap, key: ${key} val: ${val}`); + callback(ERR_OK); + } +} +``` + +### Creating a ServiceExtensionAbility + +To manually create a ServiceExtensionAbility in the DevEco Studio project, perform the following steps: + +1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **ServiceExtAbility**. + +2. In the **ServiceExtAbility** directory, right-click and choose **New > TypeScript File** to create a file named **ServiceExtAbility.ts**. + + ``` + ├── ets + │ ├── IdlServiceExt + │ │ ├── i_idl_service_ext.ts # File generated. + │ │ ├── idl_service_ext_proxy.ts # File generated. + │ │ ├── idl_service_ext_stub.ts # File generated. + │ │ ├── idl_service_ext_impl.ts # Custom file used to implement IDL interfaces. + │ ├── ServiceExtAbility + │ │ ├── ServiceExtAbility.ts + └ + ``` + +3. In the **ServiceExtAbility.ts** file, add the dependency package for importing ServiceExtensionAbility. Customize a class that inherits from ServiceExtensionAbility and implement the lifecycle callbacks, and return the previously defined **ServiceExtImpl** object in the **onConnect** lifecycle callback. -4. In the **ServiceExtAbility.ts** file, add the dependency package for importing ServiceExtensionAbility. Customize a class that inherits from ServiceExtensionAbility and add the required lifecycle callbacks. - ```ts import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility'; - import rpc from '@ohos.rpc'; + import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl'; - const TAG: string = "[Example].[Entry].[ServiceExtAbility]"; - const REQUEST_CODE = 99; - - class StubTest extends rpc.RemoteObject { - // ... - } + const TAG: string = "[ServiceExtAbility]"; export default class ServiceExtAbility extends ServiceExtensionAbility { + serviceExtImpl = new ServiceExtImpl("ExtImpl"); + onCreate(want) { console.info(TAG, `onCreate, want: ${want.abilityName}`); } @@ -130,7 +157,8 @@ To implement a background service, manually create a ServiceExtensionAbility com onConnect(want) { console.info(TAG, `onConnect, want: ${want.abilityName}`); - return new StubTest("test"); + // Return the ServiceExtImpl object, through which the client can communicate with the ServiceExtensionAbility. + return this.serviceExtImpl; } onDisconnect(want) { @@ -143,27 +171,26 @@ To implement a background service, manually create a ServiceExtensionAbility com } ``` -5. Register ServiceExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"service"** and **srcEntrance** to the code path of the ExtensionAbility component. - +4. Register ServiceExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"service"** and **srcEntry** to the code path of the ServiceExtensionAbility component. + ```json { "module": { - // ... + ... "extensionAbilities": [ { "name": "ServiceExtAbility", "icon": "$media:icon", "description": "service", "type": "service", - "visible": true, - "srcEntrance": "./ets/serviceextability/ServiceExtAbility.ts" + "exported": true, + "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ts" } ] } } ``` - ## Starting a Background Service (for System Applications Only) A system application uses the [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartserviceextensionability) method to start a background service. The [onRequest()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonrequest) callback is invoked, and the **Want** object passed by the caller is received through the callback. After the background service is started, its lifecycle is independent of that of the client. In other words, even if the client is destroyed, the background service can still run. Therefore, the background service must be stopped by calling [terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself) when its work is complete. Alternatively, another component can call [stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability) to stop the background service. @@ -173,107 +200,87 @@ A system application uses the [startServiceExtensionAbility()](../reference/apis > [startServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartserviceextensionability), [stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability), and [terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself) of ServiceExtensionContext are system APIs and cannot be called by third-party applications. 1. Start a new ServiceExtensionAbility in a system application. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). - + ```ts + let context = ...; // UIAbilityContext let want = { - "deviceId": "", - "bundleName": "com.example.myapplication", - "abilityName": "ServiceExtAbility" + "deviceId": "", + "bundleName": "com.example.myapplication", + "abilityName": "ServiceExtAbility" }; - this.context.startServiceExtensionAbility(want).then(() => { - console.info('startServiceExtensionAbility success'); - }).catch((error) => { - console.info('startServiceExtensionAbility failed'); + context.startServiceExtensionAbility(want).then(() => { + console.info('Succeeded in starting ServiceExtensionAbility.'); + }).catch((err) => { + console.error(`Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); }) ``` 2. Stop ServiceExtensionAbility in the system application. - + ```ts + let context = ...; // UIAbilityContext let want = { - "deviceId": "", - "bundleName": "com.example.myapplication", - "abilityName": "ServiceExtAbility" + "deviceId": "", + "bundleName": "com.example.myapplication", + "abilityName": "ServiceExtAbility" }; - this.context.stopServiceExtensionAbility(want).then(() => { - console.info('stopServiceExtensionAbility success'); - }).catch((error) => { - console.info('stopServiceExtensionAbility failed'); + context.stopServiceExtensionAbility(want).then(() => { + console.info('Succeeded in stoping ServiceExtensionAbility.'); + }).catch((err) => { + console.error(`Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`); }) ``` 3. ServiceExtensionAbility stops itself. - + ```ts - // this is the current ServiceExtensionAbility component. - this.context.terminateSelf().then(() => { - console.info('terminateSelf success'); - }).catch((error) => { - console.info('terminateSelf failed'); + let context = ...; // ServiceExtensionContext + context.terminateSelf().then(() => { + console.info('Succeeded in terminating self.'); + }).catch((err) => { + console.error(`Failed to terminate self. Code is ${err.code}, message is ${err.message}`); }) ``` - > **NOTE** > > Background services can run in the background for a long time. To minimize resource usage, destroy it in time when a background service finishes the task of the requester. A background service can be stopped in either of the following ways: > > - The background service calls the [terminateSelf()](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextterminateself) method to automatically stop itself. -> > - Another component calls the [stopServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstopserviceextensionability) method to stop the background service. -> > After either method is called, the system destroys the background service. - ## Connecting to a Background Service -Either a system application or a third-party application can connect to a service (service specified in the **Want** object) through [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability). The [onConnect()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonconnect) callback is invoked, and the **Want** object passed by the caller is received through the callback. In this way, a persistent connection is established. +Either a system application or a third-party application can connect to a ServiceExtensionAbility (specified in the **Want** object) through [connectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextconnectserviceextensionability). The [onConnect()](../reference/apis/js-apis-app-ability-serviceExtensionAbility.md#serviceextensionabilityonconnect) callback is invoked, and the **Want** object passed by the caller is received through the callback. In this way, a persistent connection is established. The ServiceExtensionAbility component returns an IRemoteObject in the **onConnect()** callback. Through this IRemoteObject, you can define communication interfaces for RPC interaction between the client and server. Multiple clients can connect to the same background service at the same time. After a client finishes the interaction, it must call [disconnectServiceExtensionAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextdisconnectserviceextensionability) to disconnect from the service. If all clients connected to a background service are disconnected, the system destroys the service. - Call **connectServiceExtensionAbility()** to establish a connection to a background service. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). ```ts - import rpc from '@ohos.rpc'; - - const REQUEST_CODE = 99; let want = { - "deviceId": "", - "bundleName": "com.example.myapplication", - "abilityName": "ServiceExtAbility" + "deviceId": "", + "bundleName": "com.example.myapplication", + "abilityName": "ServiceExtAbility" }; let options = { - onConnect(elementName, remote) { - console.info('onConnect callback'); - if (remote === null) { - console.info(`onConnect remote is null`); - return; - } - let option = new rpc.MessageOption(); - let data = new rpc.MessageParcel(); - let reply = new rpc.MessageParcel(); - data.writeInt(100); - data.writeInt(200); - - // @param code Indicates the service request code sent by the client. - // @param data Indicates the {@link MessageParcel} object sent by the client. - // @param reply Indicates the response message object sent by the remote service. - // @param options Specifies whether the operation is synchronous or asynchronous. - // - // @return Returns {@code true} if the operation is successful; returns {@code false} otherwise. - remote.sendRequest(REQUEST_CODE, data, reply, option).then((ret) => { - let msg = reply.readInt(); - console.info(`sendRequest ret:${ret} msg:${msg}`); - }).catch((error) => { - console.info('sendRequest failed'); - }); - }, - onDisconnect(elementName) { - console.info('onDisconnect callback') - }, - onFailed(code) { - console.info('onFailed callback') + onConnect(elementName, remote) { + /* The input parameter remote is the object returned by ServiceExtensionAbility in the onConnect lifecycle callback. + * This object is used for communication with ServiceExtensionAbility. For details, see the section below. + */ + console.info('onConnect callback'); + if (remote === null) { + console.info(`onConnect remote is null`); + return; } + }, + onDisconnect(elementName) { + console.info('onDisconnect callback') + }, + onFailed(code) { + console.info('onFailed callback') + } } // The ID returned after the connection is set up must be saved. The ID will be passed for service disconnection. let connectionId = this.context.connectServiceExtensionAbility(want, options); @@ -282,11 +289,172 @@ The ServiceExtensionAbility component returns an IRemoteObject in the **onConnec - Use **disconnectServiceExtensionAbility()** to disconnect from the background service. ```ts - let connectionId = 1 // ID returned when the service is connected through connectServiceExtensionAbility. + // connectionId is returned when connectServiceExtensionAbility is called and needs to be manually maintained. this.context.disconnectServiceExtensionAbility(connectionId).then((data) => { - console.info('disconnectServiceExtensionAbility success'); + console.info('disconnectServiceExtensionAbility success'); }).catch((error) => { - console.error('disconnectServiceExtensionAbility failed'); + console.error('disconnectServiceExtensionAbility failed'); }) ``` +## Communication Between the Client and Server + +After obtaining the [rpc.RemoteObject](../reference/apis/js-apis-rpc.md#iremoteobject) object from the **onConnect()** callback, the client can communicate with ServiceExtensionAbility in either of the following ways: + +- Using the IDL interface provided by the server for communication (recommended) + + ```ts + // The client needs to import idl_service_ext_proxy.ts provided by the server for external systems to the local project. + import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy'; + + let options = { + onConnect(elementName, remote) { + console.info('onConnect callback'); + if (remote === null) { + console.info(`onConnect remote is null`); + return; + } + let serviceExtProxy = new IdlServiceExtProxy(remote); + // Communication is carried out by interface calling, without exposing RPC details. + serviceExtProxy.processData(1, (errorCode, retVal) => { + console.info(`processData, errorCode: ${errorCode}, retVal: ${retVal}`); + }); + serviceExtProxy.insertDataToMap('theKey', 1, (errorCode) => { + console.info(`insertDataToMap, errorCode: ${errorCode}`); + }) + }, + onDisconnect(elementName) { + console.info('onDisconnect callback') + }, + onFailed(code) { + console.info('onFailed callback') + } + } + ``` + +- Calling [sendMessageRequest](../reference/apis/js-apis-rpc.md#sendmessagerequest9) to send messages to the server (not recommended) + + ```ts + import rpc from '@ohos.rpc'; + + const REQUEST_CODE = 1; + let options = { + onConnect(elementName, remote) { + console.info('onConnect callback'); + if (remote === null) { + console.info(`onConnect remote is null`); + return; + } + // Directly call the RPC interface to send messages to the server. The client needs to serialize the input parameters and deserialize the return values. The process is complex. + let option = new rpc.MessageOption(); + let data = new rpc.MessageSequence(); + let reply = new rpc.MessageSequence(); + data.writeInt(100); + + // @param code Indicates the service request code sent by the client. + // @param data Indicates the {@link MessageSequence} object sent by the client. + // @param reply Indicates the response message object sent by the remote service. + // @param options Specifies whether the operation is synchronous or asynchronous. + // + // @return Returns {@code true} if the operation is successful; returns {@code false} otherwise. + remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret) => { + let msg = reply.readInt(); + console.info(`sendMessageRequest ret:${ret} msg:${msg}`); + }).catch((error) => { + console.info('sendMessageRequest failed'); + }); + }, + onDisconnect(elementName) { + console.info('onDisconnect callback') + }, + onFailed(code) { + console.info('onFailed callback') + } + } + ``` + +## Client Identity Verification by the Server + +When ServiceExtensionAbility is used to provide sensitive services, the client identity must be verified. You can implement the verification on the stub of the IDL interface. For details about the IDL interface implementation, see [Defining IDL Interfaces](#defining-idl-interfaces). Two verification modes are recommended: + +- **Verifying the client identity based on callerUid** + + Call the [getCallingUid()](../reference/apis/js-apis-rpc.md#getcallinguid) method to obtain the UID of the client, and then call the [getBundleNameByUid()](../reference/apis/js-apis-bundleManager.md#bundlemanagergetbundlenamebyuid) method to obtain the corresponding bundle name. In this way, the client identify is verified. Note that [getBundleNameByUid()](../reference/apis/js-apis-bundleManager.md#bundlemanagergetbundlenamebyuid) is asynchronous, and therefore the server cannot return the verification result to the client. This verification mode applies when the client sends an asynchronous task execution request to the server. The sample code is as follows: + + ```ts + import rpc from '@ohos.rpc'; + import bundleManager from '@ohos.bundle.bundleManager'; + import { processDataCallback } from './i_idl_service_ext'; + import { insertDataToMapCallback } from './i_idl_service_ext'; + import IdlServiceExtStub from './idl_service_ext_stub'; + + const ERR_OK = 0; + const ERR_DENY = -1; + const TAG: string = "[IdlServiceExtImpl]"; + + export default class ServiceExtImpl extends IdlServiceExtStub { + processData(data: number, callback: processDataCallback): void { + console.info(TAG, `processData: ${data}`); + + let callerUid = rpc.IPCSkeleton.getCallingUid(); + bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => { + console.info(TAG, 'getBundleNameByUid: ' + callerBundleName); + // Identify the bundle name of the client. + if (callerBundleName != 'com.example.connectextapp') { // The verification fails. + console.info(TAG, 'The caller bundle is not in trustlist, reject'); + return; + } + // The verification is successful, and service logic is executed normally. + }).catch(err => { + console.info(TAG, 'getBundleNameByUid failed: ' + err.message); + }); + } + + insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void { + // Implement service logic. + console.info(TAG, `insertDataToMap, key: ${key} val: ${val}`); + callback(ERR_OK); + } + } + ``` + +- **Verifying the client identity based on callerTokenId** + + Call the [getCallingTokenId()](../reference/apis/js-apis-rpc.md#getcallingtokenid) method to obtain the token ID of the client, and then call the [verifyAccessTokenSync()](../reference/apis/js-apis-abilityAccessCtrl.md#verifyaccesstokensync) method to check whether the client has a specific permission. Currently, OpenHarmony does not support permission customization. Therefore, only [system-defined permissions](../security/permission-list.md) can be verified. The sample code is as follows: + + ```ts + import rpc from '@ohos.rpc'; + import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; + import {processDataCallback} from './i_idl_service_ext'; + import {insertDataToMapCallback} from './i_idl_service_ext'; + import IdlServiceExtStub from './idl_service_ext_stub'; + + const ERR_OK = 0; + const ERR_DENY = -1; + const TAG: string = "[IdlServiceExtImpl]"; + + export default class ServiceExtImpl extends IdlServiceExtStub { + processData(data: number, callback: processDataCallback): void { + console.info(TAG, `processData: ${data}`); + + let callerTokenId = rpc.IPCSkeleton.getCallingTokenId(); + let accessManger = abilityAccessCtrl.createAtManager(); + // The permission to be verified varies depending on the service requirements. ohos.permission.SET_WALLPAPER is only an example. + let grantStatus = + accessManger.verifyAccessTokenSync(callerTokenId, "ohos.permission.SET_WALLPAPER"); + if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { + console.info(TAG, `PERMISSION_DENIED`); + callback(ERR_DENY, data); // The verification fails and an error is returned. + return; + } + callback(ERR_OK, data + 1); // The verification is successful, and service logic is executed normally. + } + + insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void { + // Implement service logic. + console.info(TAG, `insertDataToMap, key: ${key} val: ${val}`); + callback(ERR_OK); + } + } + ``` + diff --git a/en/application-dev/application-models/stage-model-development-overview.md b/en/application-dev/application-models/stage-model-development-overview.md index 3436d29e5816cbe5b1ed30eba10e2435c34777e2..d4ad1d87c602c169e13b9ff99b8b491a33babd0a 100644 --- a/en/application-dev/application-models/stage-model-development-overview.md +++ b/en/application-dev/application-models/stage-model-development-overview.md @@ -10,9 +10,9 @@ The following figure shows the basic concepts used in the stage model. - [UIAbility component](uiability-overview.md) and [ExtensionAbility component](extensionability-overview.md) - The stage model provides two types of application components: UIAbility and ExtensionAbility. Both have specific classes and support object-oriented development. They are the specific implementation of the abstract ability concept on the stage model. They are also units scheduled by the Ability Manager Service (AMS), which schedules their lifecycles as well. + The stage model provides two types of application components: UIAbility and ExtensionAbility. Both have specific classes and support object-oriented development. - - UIAbility has the UI and is mainly used for user interaction. For example, with UIAbility, the Gallery application can display images in the liquid layout. After a user selects an image, it uses a new UI to display the image details. The user can touch the **Back** button to return to the liquid layout. The lifecycle of the UIAbility component contains the creation, destruction, foreground, and background states. Display-related states are exposed through WindowStage events. + - UIAbility is a type of application component that provides the UI for user interaction. For example, with UIAbility, the Gallery application can display images in the liquid layout. After a user selects an image, it uses a new UI to display the image details. The user can touch the **Back** button to return to the liquid layout. The lifecycle of the UIAbility component contains the creation, destruction, foreground, and background states. Display-related states are exposed through WindowStage events. - ExtensionAbility is oriented to specific scenarios. You cannot derive directly from ExtensionAbility. Instead, use the derived classes of ExtensionAbility for your scenarios, such as FormExtensionAbility for widget scenarios, InputMethodExtensionAbility for input method scenarios, and WorkSchedulerExtensionAbility for Work Scheduled task scenarios. For example, to enable a user to create an application widget on the home screen, you must derive FormExtensionAbility, implement the callback functions, and configure the capability in the configuration file. The derived class instances are created by developers and their lifecycles are managed by the system. In the stage model, you must use the derived classes of ExtensionAbility to develop custom services based on your service scenarios. - [WindowStage](../windowmanager/application-window-stage.md) @@ -21,7 +21,7 @@ The following figure shows the basic concepts used in the stage model. - [Context](application-context-stage.md) - In the stage model, Context and its derived classes provide a variety of capabilities that can be called during the runtime. The UIAbility component and ExtensionAbility derived classes have different Context classes. These classes, which all inherit from the base class Context, provide different capabilities. + In the stage model, Context and its derived classes provide a variety of resources and capabilities that can be called during the runtime. The UIAbility component and ExtensionAbility derived classes have different Context classes. These classes, which all inherit from the base class Context, provide different capabilities. - [AbilityStage](abilitystage.md) @@ -37,7 +37,7 @@ During application development based on the stage model, the following tasks are | Task| Introduction| Guide| | -------- | -------- | -------- | | Application component development| Use the UIAbility and ExtensionAbility components of the stage model to develop applications.| - [Application- or Component-Level Configuration](application-component-configuration-stage.md)
- [UIAbility Component](uiability-overview.md)
- [ExtensionAbility Component](extensionability-overview.md)
- [AbilityStage Container Component](abilitystage.md)
- [Context](application-context-stage.md)
- [Component Startup Rules](component-startup-rules.md)| -| Inter-process communication (IPC)| Learn the process model and common IPC modes of the stage model.| - [Common Events](common-event-overview.md)
- [Background Services](background-services.md)| -| Inter-thread communication| Learn the thread model and common inter-thread communication modes of the stage model.| - [Emitter](itc-with-emitter.md)
- [Worker](itc-with-worker.md)| +| Process model| Learn the process model and common IPC modes of the stage model.| - [Common Events](common-event-overview.md)
- [Background Services](background-services.md)| +| Thread model| Learn the thread model and common inter-thread communication modes of the stage model.| - [Emitter](itc-with-emitter.md)
- [Worker](itc-with-worker.md)| | Mission management| Learn the basic concepts and typical scenarios of mission management in the stage model.| - [Mission Management Scenarios](mission-management-overview.md)
- [Mission Management and Launch Type](mission-management-launch-type.md)
- [Page Stack and Mission List](page-mission-stack.md)| -| Application configuration file| Learn the requirements for developing application configuration files in the stage model.| [Application Configuration File](../quick-start/application-configuration-file-overview-stage.md)| +| Application configuration file| Learn the requirements for developing application configuration files in the stage model.| [Application Configuration File](config-file-stage.md)| diff --git a/en/application-dev/application-models/start-page.md b/en/application-dev/application-models/start-page.md index 58966d93cba037eaad141caaed0feaaaa672cde1..1def472cd5fa2567fda123fc272542787b1f985c 100644 --- a/en/application-dev/application-models/start-page.md +++ b/en/application-dev/application-models/start-page.md @@ -70,7 +70,7 @@ struct Index { ``` -When the launch type of a PageAbility is set to **standard** or when the PageAbility with the launch type set to **singleton** is started for the first time, you can use the **parameters** parameter in **want** to transfer the pages information and use the **startAbility()** method to start the PageAbility. For details about the launch type, see [PageAbility Launch Type](pageability-launch-type.md). The target PageAbility can use the **featureAbility.getWant()** method in **onCreate** to obtain the **want** parameter, and then call **router.push** to start a specified page. +When a PageAbility in multiton mode is started or when the PageAbility in singleton mode is started for the first time, you can use the **parameters** parameter in **want** to transfer the pages information and use the **startAbility()** method to start the PageAbility. For details about the launch type, see [PageAbility Launch Type](pageability-launch-type.md). The target PageAbility can use the **featureAbility.getWant()** method in **onCreate** to obtain the **want** parameter, and then call **router.push** to start a specified page. When a user touches the button on the page of the caller PageAbility, the **startAbility()** method is called to start the target PageAbility. The **want** parameter in **startAbility()** carries the specified page information. @@ -83,7 +83,7 @@ struct Index { @State message: string = 'Hello World' build() { - // ... + ... Button("startAbility") .onClick(() => { featureAbility.startAbility({ @@ -98,7 +98,7 @@ struct Index { console.info("startAbility failed errcode:" + err.code) }) }) - // ... + ... Button("page2") .onClick(() => { featureAbility.startAbility({ @@ -113,7 +113,7 @@ struct Index { console.info("startAbility failed errcode:" + err.code) }) }) - // ... + ... } } ``` @@ -136,7 +136,7 @@ export default { }) }, onDestroy() { - // ... + ... }, } ``` diff --git a/en/application-dev/application-models/start-pageability-from-stage.md b/en/application-dev/application-models/start-pageability-from-stage.md index 9d1b7ed27f6780ce56d1e90b3be5d196cf3b1187..bd6a11187fdfbc81c63bcc6601f8a8e82b0dbe4c 100644 --- a/en/application-dev/application-models/start-pageability-from-stage.md +++ b/en/application-dev/application-models/start-pageability-from-stage.md @@ -21,7 +21,7 @@ export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage) { console.info("EntryAbility onWindowStageCreate") windowStage.loadContent('pages/Index', (err, data) => { - // ... + ... }); let want = { bundleName: "com.ohos.fa", @@ -66,7 +66,7 @@ export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage) { console.info("EntryAbility onWindowStageCreate") windowStage.loadContent('pages/Index', (err, data) => { - // ... + ... }); let want = { bundleName: "com.ohos.fa", diff --git a/en/application-dev/application-models/start-remote-pageability.md b/en/application-dev/application-models/start-remote-pageability.md index 36ee305b49698c1f6e6cf216174f77212f1d53e4..a52378af306c9719b086b23f0909f87d7bf376b3 100644 --- a/en/application-dev/application-models/start-remote-pageability.md +++ b/en/application-dev/application-models/start-remote-pageability.md @@ -83,28 +83,31 @@ After obtaining the data synchronization permission, obtain the trusted device l The following sample code shows how to use **getTrustedDeviceListSync()** to obtain the trusted device list. ```ts -import deviceManager from '@ohos.distributedHardware.deviceManager'; -let dmClass; +import deviceManager from '@ohos.distributedHardware.deviceManager'; + +let dmClass; + function getDeviceManager() { - deviceManager.createDeviceManager('ohos.example.distributedService', (error, dm) => { - if (error) { - console.info('create device manager failed with ' + error) - } - dmClass = dm; - }) + deviceManager.createDeviceManager('ohos.example.distributedService', (error, dm) => { + if (error) { + console.info('create device manager failed with ' + error) + } + dmClass = dm; + }) } -function getRemoteDeviceId() { - if (typeof dmClass === 'object' && dmClass != null) { - let list = dmClass.getTrustedDeviceListSync(); - if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { - console.info("EntryAbility onButtonClick getRemoteDeviceId err: list is null"); - return; - } - console.info("EntryAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); - return list[0].deviceId; - } else { - console.info("EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null"); - } + +function getRemoteDeviceId() { + if (typeof dmClass === 'object' && dmClass != null) { + let list = dmClass.getTrustedDeviceListSync(); + if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { + console.info("EntryAbility onButtonClick getRemoteDeviceId err: list is null"); + return; + } + console.info("EntryAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); + return list[0].deviceId; + } else { + console.info("EntryAbility onButtonClick getRemoteDeviceId err: dmClass is null"); + } } ``` @@ -116,21 +119,22 @@ The following sample code shows how to explicitly start a remote PageAbility thr ```ts import featureAbility from '@ohos.ability.featureAbility'; -function onStartRemoteAbility() { - console.info('onStartRemoteAbility begin'); - let params; - let wantValue = { - bundleName: 'ohos.samples.etsDemo', - abilityName: 'ohos.samples.etsDemo.RemoteAbility', - deviceId: getRemoteDeviceId(), // getRemoteDeviceId is defined in the preceding sample code. - parameters: params - }; - console.info('onStartRemoteAbility want=' + JSON.stringify(wantValue)); - featureAbility.startAbility({ - want: wantValue - }).then((data) => { - console.info('onStartRemoteAbility finished, ' + JSON.stringify(data)); - }); - console.info('onStartRemoteAbility end'); + +function onStartRemoteAbility() { + console.info('onStartRemoteAbility begin'); + let params; + let wantValue = { + bundleName: 'ohos.samples.etsDemo', + abilityName: 'ohos.samples.etsDemo.RemoteAbility', + deviceId: getRemoteDeviceId(), // getRemoteDeviceId is defined in the preceding sample code. + parameters: params + }; + console.info('onStartRemoteAbility want=' + JSON.stringify(wantValue)); + featureAbility.startAbility({ + want: wantValue + }).then((data) => { + console.info('onStartRemoteAbility finished, ' + JSON.stringify(data)); + }); + console.info('onStartRemoteAbility end'); } ``` diff --git a/en/application-dev/application-models/start-serviceability.md b/en/application-dev/application-models/start-serviceability.md index f3b0f6aeabc8a3ea35c1f6c390ec53239730443c..e07428f13a5ce3a3981b7881387dc8498f1380d1 100644 --- a/en/application-dev/application-models/start-serviceability.md +++ b/en/application-dev/application-models/start-serviceability.md @@ -27,7 +27,7 @@ async function startServiceAbility() { ``` -In the preceding code, **startAbility()** is used to start the ServiceAbility. +In the preceding code, [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is used to start the ServiceAbility. - If the ServiceAbility is not running, the system calls **onStart()** to initialize the ServiceAbility, and then calls **onCommand()** on the ServiceAbility. diff --git a/en/application-dev/application-models/subscribe-system-environment-variable-changes.md b/en/application-dev/application-models/subscribe-system-environment-variable-changes.md new file mode 100644 index 0000000000000000000000000000000000000000..4eecf15808da492ca69d933fdabf39aa82347ce5 --- /dev/null +++ b/en/application-dev/application-models/subscribe-system-environment-variable-changes.md @@ -0,0 +1,172 @@ +# Subscribing to System Environment Variable Changes + +System environment variables are system settings (for example, the system language or screen direction) of a device that may change during the running of an application. + +By subscribing to the changes of system environment variables, the application can detect the changes in a timely manner and process the changes accordingly, providing better user experience. For example, when the system language changes, the application can display the UI in the new language; when the user rotates the device to landscape or portrait mode, the application can re-arrange the UI to adapt to the new screen orientation and size. + +The system environment variable changes are usually triggered by options in **Settings** or icons in **Control Panel**. For details about the system environment variables that support subscription, see [Configuration](../reference/apis/js-apis-app-ability-configuration.md). + +In OpenHarmony, you can subscribe to system environment variable changes in the following ways: + +- [Using ApplicationContext for Subscription](#using-applicationcontext-for-subscription) +- [Using AbilityStage for Subscription](#using-abilitystage-for-subscription) +- [Using UIAbility for Subscription](#using-uiability-for-subscription) +- [Using ExtensionAbility for Subscription](#using-extensionability-for-subscription) + +## Using ApplicationContext for Subscription + +[ApplicationContext](../reference/apis/js-apis-inner-application-applicationContext.md) provides an API for registering a callback function to subscribe to the system environment variable changes. It also provides an API for deregistration so you can release related resources when they are no longer needed. + +1. Call **ApplicationContext.on(type: 'environment', callback: EnvironmentCallback)** to subscribe to changes in system environment variables. The code snippet below is used to subscribe to system language changes on a page. + + ```ts + import common from '@ohos.app.ability.common'; + + @Entry + @Component + struct Index { + private context = getContext(this) as common.UIAbilityContext; + private callbackId: number; // ID of the subscription for system environment variable changes. + + subscribeConfigurationUpdate() { + let systemLanguage: string = this.context.config.language; // Obtain the system language in use. + + // 1. Obtain an ApplicationContext object. + let applicationContext = this.context.getApplicationContext(); + + // 2. Subscribe to system environment variable changes through ApplicationContext. + let environmentCallback = { + onConfigurationUpdated(newConfig) { + console.info(`onConfigurationUpdated systemLanguage is ${systemLanguage}, newConfig: ${JSON.stringify(newConfig)}`); + + if (this.systemLanguage !== newConfig.language) { + console.info(`systemLanguage from ${systemLanguage} changed to ${newConfig.language}`); + systemLanguage = newConfig.language; // Save the new system language as the system language in use, which will be used for comparison. + } + }, + onMemoryLevel(level) { + console.info(`onMemoryLevel level: ${level}`); + } + } + + this.callbackId = applicationContext.on('environment', environmentCallback); + } + + // Page display. + build() { + ... + } + } + ``` + +2. Call **ApplicationContext.off(type: 'environment', callbackId: number)** to release the resources. + + ```ts + import common from '@ohos.app.ability.common'; + + @Entry + @Component + struct Index { + private context = getContext(this) as common.UIAbilityContext; + private callbackId: number; // ID of the subscription for system environment variable changes. + + unsubscribeConfigurationUpdate() { + let applicationContext = this.context.getApplicationContext(); + applicationContext.off('environment', this.callbackId); + } + + // Page display. + build() { + ... + } + } + ``` + +## Using AbilityStage for Subscription + +The AbilityStage component provides the [AbilityStage.onConfigurationUpdate()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonconfigurationupdate) callback for subscribing to system environment variable changes. This callback is invoked when a system environment variable changes. In this callback, the latest system environment configuration is obtained through the [Configuration](../reference/apis/js-apis-app-ability-configuration.md) object. + +> **NOTE** +> +> - AbilityStage is not automatically generated in the default project of DevEco Studio. For details about how to create an AbilityStage file, see [AbilityStage Component Container](abilitystage.md). +> - The callback used to subscribe to system environment variable changes has the same lifecycle as the [AbilityStage](../reference/apis/js-apis-app-ability-abilityStage.md) instance and will be destroyed when the instance is destroyed. + +The code snippet below uses the [AbilityStage.onConfigurationUpdate()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonconfigurationupdate) callback to subscribe to the system language changes. + +```ts +import AbilityStage from '@ohos.app.ability.AbilityStage'; + +let systemLanguage: string; // System language in use. + +export default class MyAbilityStage extends AbilityStage { + onCreate() { + systemLanguage = this.context.config.language; // Obtain the system language in use when the AbilityStage instance is loaded for the first time. + console.info(`systemLanguage is ${systemLanguage} `); + } + + onConfigurationUpdate(newConfig) { + console.info(`onConfigurationUpdated systemLanguage is ${systemLanguage}, newConfig: ${JSON.stringify(newConfig)}`); + + if (systemLanguage !== newConfig.language) { + console.info(`systemLanguage from ${systemLanguage} changed to ${newConfig.language}`); + systemLanguage = newConfig.language; // Save the new system language as the system language in use, which will be used for comparison. + } + } +} +``` + +## Using UIAbility for Subscription + +The UIAbility component provides the **UIAbility.onConfigurationUpdate()** callback for subscribing to system environment variable changes. This callback is invoked when a system environment variable changes. In this callback, the latest system environment configuration is obtained through the [Configuration](../reference/apis/js-apis-app-ability-configuration.md) object, without restarting the UIAbility. + +> **NOTE** +> +> The callback used to subscribe to system environment variable changes has the same lifecycle as the UIAbility instance and will be destroyed when the instance is destroyed. + +The code snippet below uses the **onConfigurationUpdate()** callback to subscribe to the system language changes. + +```ts +import UIAbility from '@ohos.app.ability.UIAbility'; + +let systemLanguage: string; // System language in use. + +export default class EntryAbility extends UIAbility { + onCreate(want, launchParam) { + systemLanguage = this.context.config.language; // Obtain the system language in use when the UIAbility instance is loaded for the first time. + console.info(`systemLanguage is ${systemLanguage} `); + } + + onConfigurationUpdate(newConfig) { + console.info(`onConfigurationUpdated systemLanguage is ${systemLanguage}, newConfig: ${JSON.stringify(newConfig)}`); + + if (systemLanguage !== newConfig.language) { + console.info(`systemLanguage from ${systemLanguage} changed to ${newConfig.language}`); + systemLanguage = newConfig.language; // Save the new system language as the system language in use, which will be used for comparison. + } + } + + ... +} +``` + +## Using ExtensionAbility for Subscription + +The ExtensionAbility component provides the **onConfigurationUpdate()** callback for subscribing system environment variable changes. This callback is invoked when a system environment variable changes. In this callback, the latest system environment configuration is obtained through the [Configuration](../reference/apis/js-apis-app-ability-configuration.md) object. + +> **NOTE** +> +> The callback used to subscribe to system environment variable changes has the same lifecycle as the ExtensionAbility instance and will be destroyed when the instance is destroyed. + +The code snippet below uses FormExtensionAbility as an example to describe how to use the **onConfigurationUpdate()** callback to subscribe to system environment variable changes. + +```ts +import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'; + +export default class EntryFormAbility extends FormExtensionAbility { + onConfigurationUpdate(newConfig) { + console.info(`newConfig is ${JSON.stringify(newConfig)}`); + } + + ... +} +``` diff --git a/en/application-dev/application-models/thread-model-fa.md b/en/application-dev/application-models/thread-model-fa.md index 56cf8c94732acede5ee03cf9f9b79553e0183b49..f6b335f8932ee1ebd5bb9bdf11db99ff354a1470 100644 --- a/en/application-dev/application-models/thread-model-fa.md +++ b/en/application-dev/application-models/thread-model-fa.md @@ -1,13 +1,11 @@ -# Thread Model (FA Model) - +# Thread Model Overview (FA Model) There are three types of threads in the FA model: - - Main thread - + Manages other threads. - + - Ability thread - One ability thread for each ability. - Distributes input events. @@ -16,13 +14,11 @@ There are three types of threads in the FA model: - Receives messages sent by the worker thread. - Worker thread - + Performs time-consuming operations - Based on the OpenHarmony thread model, different services run on different threads. Service interaction requires inter-thread communication. Threads can communicate with each other in Emitter or Worker mode. Emitter is mainly used for event synchronization between threads, and Worker is mainly used to execute time-consuming tasks. - > **NOTE** > > The FA model provides an independent thread for each ability. Emitter is mainly used for event synchronization within the ability thread, between a pair of ability threads, or between the ability thread and worker thread. diff --git a/en/application-dev/application-models/thread-model-stage.md b/en/application-dev/application-models/thread-model-stage.md index deaab60b7bd7549dcb96bc00d7896d5c67e5c5d2..2b1f855980a14eeba89a18184c69d46eebaea6ac 100644 --- a/en/application-dev/application-models/thread-model-stage.md +++ b/en/application-dev/application-models/thread-model-stage.md @@ -1,20 +1,15 @@ -# Thread Model (Stage Model) +# Thread Model Overview (Stage Model) For an OpenHarmony application, each process has a main thread to provide the following functionalities: -- Manage other threads. - -- Enable multiple UIAbility components of the same application to share the same main thread. - -- Distribute input events. - - Draw the UI. - -- Invoke application code callbacks (event processing and lifecycle callbacks). - +- Manage the ArkTS engine instance of the main thread so that multiple UIAbility components can run on it. +- Manage ArkTS engine instances of other threads (such as the worker thread), for example, starting and terminating other threads. +- Distribute interaction events. +- Process application code callbacks (event processing and lifecycle management). - Receive messages sent by the worker thread. -In addition to the main thread, there is an independent thread, named worker. The worker thread is mainly used to perform time-consuming operations. It cannot directly operate the UI. The worker thread is created in the main thread and is independent of the main thread. A maximum of seven worker threads can be created. +In addition to the main thread, there is an independent thread, named worker. The worker thread is mainly used to perform time-consuming operations. The worker thread is created in the main thread and is independent from the main thread. It cannot directly operate the UI. A maximum of seven worker threads can be created. ![thread-model-stage](figures/thread-model-stage.png) @@ -22,4 +17,6 @@ Based on the OpenHarmony thread model, different services run on different threa > **NOTE** > -> The stage model provides only the main thread and worker thread. Emitter is mainly used for event synchronization within the main thread or between the main thread and worker thread. +> - The stage model provides only the main thread and worker thread. Emitter is mainly used for event synchronization within the worker thread or between the main thread and worker thread. +> - The UIAbility and UI are in the main thread. For details about data synchronization between them, see [Data Synchronization Between UIAbility and UI](uiability-data-sync-with-ui.md). +> - To view thread information about an application process, run the **hdc shell** command to enter the shell CLI of the device, and then run the **ps -p ** -T command**, where ** indicates the [process ID](process-model-stage.md) of the application. diff --git a/en/application-dev/application-models/uiability-data-sync-with-ui.md b/en/application-dev/application-models/uiability-data-sync-with-ui.md index f53627e974a0f9b4b1323d7694f94e07ebf51753..6998001c763a464e51f8cef4a0c64aec6f1db5e8 100644 --- a/en/application-dev/application-models/uiability-data-sync-with-ui.md +++ b/en/application-dev/application-models/uiability-data-sync-with-ui.md @@ -3,17 +3,16 @@ Based on the OpenHarmony application model, you can use any of the following ways to implement data synchronization between the UIAbility component and UI: -- EventHub: The [base class Context](application-context-stage.md) provides the EventHub capability. It is implemented based on the publish/subscribe (pub/sub) pattern. Your application subscribes to an event and when the event occurs, receives a notification. - -- globalThis: It is a global object accessible in the ArkTS engine instance. -- LocalStorage/AppStorage: See [State Management of Application-Level Variables](../quick-start/arkts-state-mgmt-application-level.md). +- [Using EventHub for Data Synchronization](#using-eventhub-for-data-synchronization): The **EventHub** object is provided by the base class **Context**. Events are transferred using the publish/subscribe (pub/sub) pattern. Specifically, after subscribing to an event, your application will receive the event and process it accordingly when the event is published. +- [Using globalThis for Data Synchronization](#using-globalthis-for-data-synchronization): **globalThis** is a global object inside the ArkTS engine instance and can be accessed by components such as UIAbility, ExtensionAbility, and Page. +- [Using AppStorage or LocalStorage for Data Synchronization](#using-appstorage-or-localstorage-for-data-synchronization): ArkUI provides two application-level state management solutions: AppStorage and LocalStorage, which implement application- and UIAbility-level data synchronization, respectively. ## Using EventHub for Data Synchronization -[EventHub](../reference/apis/js-apis-inner-application-eventHub.md) provides an event mechanism at the UIAbility or ExtensionAbility component level. Centered on the UIAbility or ExtensionAbility component, EventHub provides data communication capabilities for subscribing to, unsubscribing from, and triggering events. +[EventHub](../reference/apis/js-apis-inner-application-eventHub.md) provides an event mechanism for the UIAbility or ExtensionAbility component so that they can subscribe to, unsubscribe from, and trigger events. -Before using EventHub, you must obtain an EventHub object, which is provided by the [base class Context](application-context-stage.md). This section uses EventHub as an example to describe how to implement data synchronization between the UIAbility component and the UI. +Before using the APIs provided by **EventHub**, you must obtain an **EventHub** object, which is provided by the [base class Context](application-context-stage.md). This section uses EventHub as an example to describe how to implement data synchronization between the UIAbility component and the UI. 1. Call [eventHub.on()](../reference/apis/js-apis-inner-application-eventHub.md#eventhubon) in the UIAbility in either of the following ways to register a custom event **event1**. @@ -23,21 +22,21 @@ Before using EventHub, you must obtain an EventHub object, which is provided by const TAG: string = '[Example].[Entry].[EntryAbility]'; export default class EntryAbility extends UIAbility { - func1(...data) { - // Trigger the event to complete the service operation. - console.info(TAG, '1. ' + JSON.stringify(data)); - } - - onCreate(want, launch) { - // Obtain an eventHub object. - let eventhub = this.context.eventHub; - // Subscribe to the event. - eventhub.on('event1', this.func1); - eventhub.on('event1', (...data) => { - // Trigger the event to complete the service operation. - console.info(TAG, '2. ' + JSON.stringify(data)); - }); - } + func1(...data) { + // Trigger the event to complete the service operation. + console.info(TAG, '1. ' + JSON.stringify(data)); + } + + onCreate(want, launch) { + // Obtain an eventHub object. + let eventhub = this.context.eventHub; + // Subscribe to the event. + eventhub.on('event1', this.func1); + eventhub.on('event1', (...data) => { + // Trigger the event to complete the service operation. + console.info(TAG, '2. ' + JSON.stringify(data)); + }); + } } ``` @@ -63,7 +62,7 @@ Before using EventHub, you must obtain an EventHub object, which is provided by // Page display. build() { - // ... + ... } } ``` @@ -81,44 +80,42 @@ Before using EventHub, you must obtain an EventHub object, which is provided by 4. After **event1** is used, you can call [eventHub.off()](../reference/apis/js-apis-inner-application-eventHub.md#eventhuboff) to unsubscribe from the event. ```ts - // context is the ability context of the UIAbility instance. + // context is the AbilityContext of the UIAbility instance. this.context.eventHub.off('event1'); ``` ## Using globalThis for Data Synchronization - **globalThis** is a global object inside the [ArkTS engine instance](thread-model-stage.md) and can be used by UIAbility, ExtensionAbility, and Page inside the engine. Therefore, you can use **globalThis** for data synchronization. - **Figure 1** Using globalThis for data synchronization - - ![globalThis1](figures/globalThis1.png) +**Figure 1** Using globalThis for data synchronization +![globalThis1](figures/globalThis1.png) The following describes how to use **globalThis** in three scenarios. Precautions are provided as well. - [Using globalThis Between UIAbility and Page](#using-globalthis-between-uiability-and-page) -- [Using globalThis Between UIAbility and UIAbility](##using-globalthis-between-uiability-and-uiability) +- [Using globalThis Between UIAbility and UIAbility](#using-globalthis-between-uiability-and-uiability) - [Use globalThis Between UIAbility and ExtensionAbility](#using-globalthis-between-uiability-and-extensionability) - [Precautions for Using globalThis](#precautions-for-using-globalthis) ### Using globalThis Between UIAbility and Page -You can use **globalThis** to bind attributes or methods to implement data synchronization between the UIAbility component and UI. For example, if you bind the **want** parameter in the UIAbility component, you can use the **want** parameter information on the UI corresponding to the UIAbility component. +By binding attributes or methods to **globalThis**, you can implement data synchronization between the UIAbility component and UI. For example, if you bind the **want** parameter in the UIAbility component, you can use the **want** parameter information on the UI corresponding to the UIAbility component. -1. When **startAbility()** is called to start a UIAbility instance, the **onCreate()** callback is invoked, and the **want** parameter can be passed in the callback. Therefore, you can bind the **want** parameter to **globalThis**. +1. When [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called to start a UIAbility instance, the [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) callback is invoked, and the **want** parameter can be passed in the callback. Therefore, you can bind the **want** parameter to **globalThis**. ```ts - import UIAbility from '@ohos.app.ability.UIAbility' + import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launch) { - globalThis.entryAbilityWant = want; - // ... - } + onCreate(want, launch) { + globalThis.entryAbilityWant = want; + ... + } - // ... + ... } ``` @@ -136,7 +133,7 @@ You can use **globalThis** to bind attributes or methods to implement data synch // Page display. build() { - // ... + ... } } ``` @@ -144,51 +141,51 @@ You can use **globalThis** to bind attributes or methods to implement data synch ### Using globalThis Between UIAbility and UIAbility -To implement data synchronization between two UIAbility components in the same application, you can bind data to **globalThis**. For example, you can save data in **globalThis** in AbilityA and obtain the data from AbilityB. +To implement data synchronization between two UIAbility components in the same application, you can bind data to **globalThis**. For example, you can save data in **globalThis** in UIAbilityA and obtain the data from UIAbilityB. -1. AbilityA stores a string and binds it to globalThis. +1. UIAbilityA stores a string and binds it to globalThis. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityA extends UIAbility { - onCreate(want, launch) { - globalThis.entryAbilityStr = 'AbilityA'; // AbilityA stores the string "AbilityA" to globalThis. - // ... - } + export default class UIAbilityA extends UIAbility { + onCreate(want, launch) { + globalThis.entryAbilityStr = 'UIAbilityA'; // UIAbilityA stores the string "UIAbilityA" to globalThis. + ... + } } ``` -2. Obtain the data from AbilityB. +2. Obtain the data from UIAbilityB. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityB extends UIAbility { - onCreate(want, launch) { - // AbilityB reads the name from globalThis and outputs it. - console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr); - // ... - } + export default class UIAbilityB extends UIAbility { + onCreate(want, launch) { + // UIAbilityB reads name from globalThis and outputs it. + console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr); + ... + } } ``` ### Using globalThis Between UIAbility and ExtensionAbility -To implement data synchronization between the UIAbility and ExtensionAbility components in the same application, you can bind data to **globalThis**. For example, you can save data in **globalThis** in AbilityA and obtain the data from ServiceExtensionAbility. +To implement data synchronization between the UIAbility and ExtensionAbility components in the same application, you can bind data to **globalThis**. For example, you can save data in **globalThis** in UIAbilityA and obtain the data from ServiceExtensionAbility. -1. AbilityA stores a string and binds it to globalThis. +1. UIAbilityA stores a string and binds it to globalThis. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityA extends UIAbility { - onCreate(want, launch) { - // AbilityA stores the string "AbilityA" to globalThis. - globalThis.entryAbilityStr = 'AbilityA'; - // ... - } + export default class UIAbilityA extends UIAbility { + onCreate(want, launch) { + // UIAbilityA stores the string "UIAbilityA" to globalThis. + globalThis.entryAbilityStr = 'UIAbilityA'; + ... + } } ``` @@ -198,22 +195,21 @@ To implement data synchronization between the UIAbility and ExtensionAbility com import Extension from '@ohos.app.ability.ServiceExtensionAbility' export default class ServiceExtAbility extends Extension { - onCreate(want) { - / / ServiceExtAbility reads name from globalThis and outputs it. - console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr); - // ... - } + onCreate(want) { + / / ServiceExtAbility reads name from globalThis and outputs it. + console.info('name from entryAbilityStr: ' + globalThis.entryAbilityStr); + ... + } } ``` ### Precautions for Using globalThis - **Figure 2** Precautions for globalThis - +**Figure 2** Precautions for globalThis ![globalThis2](figures/globalThis2.png) -- In the stage model, all the UIAbility components in a process share one ArkTS engine instance. When using **globalThis**, do not store objects with the same name. For example, if AbilityA and AbilityB use **globalThis** to store two objects with the same name, the object stored earlier will be overwritten. +- In the stage model, all the UIAbility components in a process share one ArkTS engine instance. When using **globalThis**, do not store objects with the same name. For example, if UIAbilityA and UIAbilityB use **globalThis** to store two objects with the same name, the object stored earlier will be overwritten. - This problem does not occur in the FA model because each UIAbility component uses an independent engine. @@ -221,20 +217,20 @@ To implement data synchronization between the UIAbility and ExtensionAbility com The following provides an example to describe the object overwritten problem in the stage model. -1. In the AbilityA file, [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) is stored in **globalThis**. +1. In the UIAbilityA file, [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) is stored in **globalThis**. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityA extends UIAbility { - onCreate(want, launch) { - globalThis.context = this.context; // AbilityA stores the context in globalThis. - // ... - } + export default class UIAbilityA extends UIAbility { + onCreate(want, launch) { + globalThis.context = this.context; // UIAbilityA stores the context in globalThis. + ... + } } ``` -2. Obtain and use [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) on the page of Ability A. After the AbilityA instance is used, switch it to the background. +2. Obtain and use [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) on the page of UIAbilityA. After the UIAbilityA instance is used, switch it to the background. ```ts @Entry @@ -242,33 +238,29 @@ The following provides an example to describe the object overwritten problem in struct Index { onPageShow() { let ctx = globalThis.context; // Obtain the context from globalThis and use it. - let permissions = ['com.example.permission'] - ctx.requestPermissionsFromUser(permissions,(result) => { - // ... - }); } // Page display. build() { - // ... + ... } } ``` -3. In the AbilityB file, [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) is stored in **globalThis** and has the same name as that in the AbilityA file. +3. In the UIAbilityB file, [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) is stored in **globalThis** and has the same name as that in the UIAbilityA file. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityB extends UIAbility { - onCreate(want, launch) { - // AbilityB overwrites the context stored by AbilityA in globalThis. - globalThis.context = this.context; - // ... - } + export default class UIAbilityB extends UIAbility { + onCreate(want, launch) { + // UIAbilityB overwrites the context stored by UIAbilityA in globalThis. + globalThis.context = this.context; + ... + } } ``` -4. Obtain and use [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) on the page of Ability B. The obtained **globalThis.context** is the value of [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) in AbilityB. +4. Obtain and use [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) on the page of UIAbilityB. The obtained **globalThis.context** is the value of [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) in UIAbilityB. ```ts @Entry @@ -276,47 +268,43 @@ The following provides an example to describe the object overwritten problem in struct Index { onPageShow() { let ctx = globalThis.context; // Obtain the context from globalThis and use it. - let permissions = ['com.example.permission'] - ctx.requestPermissionsFromUser(permissions,(result) => { - console.info('requestPermissionsFromUser result:' + JSON.stringify(result)); - }); } // Page display. build() { - // ... + ... } } ``` -5. Switch the AbilityB instance to the background and switch the AbilityA instance to the foreground. In this case, AbilityA will not enter the **onCreate()** lifecycle again. +5. Switch the UIAbilityB instance to the background and switch the UIAbilityA instance to the foreground. In this case, UIAbilityA will not enter the **onCreate()** lifecycle again. ```ts import UIAbility from '@ohos.app.ability.UIAbility' - export default class AbilityA extends UIAbility { - onCreate(want, launch) { // AbilityA will not enter this lifecycle. - globalThis.context = this.context; - // ... - } + export default class UIAbilityA extends UIAbility { + onCreate(want, launch) { // UIAbilityA will not enter this lifecycle. + globalThis.context = this.context; + ... + } } ``` -6. When the page of AbilityA is displayed, the obtained **globalThis.context** is [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) of AbilityB instead of AbilityA. An error occurs. +6. When the page of UIAbilityA is displayed, the obtained **globalThis.context** is [UIAbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md) of UIAbilityB instead of UIAbilityA. An error occurs. ```ts @Entry @Component struct Index { onPageShow() { - let ctx = globalThis.context; // The context in globalThis is the context of AbilityB. - let permissions=['com.example.permission']; - ctx.requestPermissionsFromUser(permissions,(result) => { // Using this object causes a process breakdown. - console.info('requestPermissionsFromUser result:' + JSON.stringify(result)); - }); + let ctx = globalThis.context; // The context in globalThis is the context of UIAbilityB. } // Page display. build() { - // ... + ... } } ``` + +## Using AppStorage or LocalStorage for Data Synchronization + +ArkUI provides AppStorage and LocalStorage to implement application- and UIAbility-level data synchronization, respectively. Both solutions can be used to manage the application state, enhance application performance, and improve user experience. The AppStorage is a global state manager and is applicable when multiple UIAbilities share the same state data. The LocalStorage is a local state manager that manages state data used inside a single UIAbility. They help you control the application state more flexibly and improve the maintainability and scalability of applications. For details, see [State Management of Application-Level Variables](../quick-start/arkts-application-state-management-overview.md). diff --git a/en/application-dev/application-models/uiability-intra-device-interaction.md b/en/application-dev/application-models/uiability-intra-device-interaction.md index 58cea75a988647cbbc3e53e3918990812cfdc702..8e8bbb6535e1d2e0342c6e362b581ff556267ad5 100644 --- a/en/application-dev/application-models/uiability-intra-device-interaction.md +++ b/en/application-dev/application-models/uiability-intra-device-interaction.md @@ -15,9 +15,11 @@ This topic describes the UIAbility interaction modes in the following scenarios. - [Starting UIAbility of Another Application and Obtaining the Return Result](#starting-uiability-of-another-application-and-obtaining-the-return-result) +- [Starting UIAbility with Window Mode Specified (for System Applications Only)](#starting-uiability-with-window-mode-specified-for-system-applications-only) + - [Starting a Specified Page of UIAbility](#starting-a-specified-page-of-uiability) -- [Using Ability Call to Implement UIAbility Interaction (for System Applications Only)](#using-ability-call-to-implement-uiability-interaction-for-system-applications-only) +- [Using Call to Implement UIAbility Interaction (for System Applications Only)](#using-call-to-implement-uiability-interaction-for-system-applications-only) ## Starting UIAbility in the Same Application @@ -26,145 +28,166 @@ This scenario is possible when an application contains multiple UIAbility compon Assume that your application has two UIAbility components: EntryAbility and FuncAbility, either in the same module or different modules. You are required to start FuncAbility from EntryAbility. -1. In EntryAbility, call **startAbility()** to start UIAbility. The [want](../reference/apis/js-apis-app-ability-want.md) parameter is the entry parameter for starting the UIAbility instance. In the **want** parameter, **bundleName** indicates the bundle name of the application to start; **abilityName** indicates the name of the UIAbility to start; **moduleName** is required only when the target UIAbility belongs to a different module; **parameters** is used to carry custom information. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). +1. In EntryAbility, call [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to start UIAbility. The [want](../reference/apis/js-apis-app-ability-want.md) parameter is the entry parameter for starting the UIAbility instance. In the **want** parameter, **bundleName** indicates the bundle name of the application to start; **abilityName** indicates the name of the UIAbility to start; **moduleName** is required only when the target UIAbility belongs to a different module; **parameters** is used to carry custom information. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). ```ts - let wantInfo = { - deviceId: '', // An empty deviceId indicates the local device. - bundleName: 'com.example.myapplication', - abilityName: 'FuncAbility', - moduleName: 'module1', // moduleName is optional. - parameters: {// Custom information. - info: 'From the Index page of EntryAbility', - }, + let context = ...; // UIAbilityContext + let want = { + deviceId: '', // An empty deviceId indicates the local device. + bundleName: 'com.example.myapplication', + abilityName: 'FuncAbility', + moduleName: 'func', // moduleName is optional. + parameters: {// Custom information. + info: 'From the Index page of EntryAbility', + }, } - // context is the ability-level context of the initiator UIAbility. - this.context.startAbility(wantInfo).then(() => { - // ... + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbility(want).then(() => { + console.info('Succeeded in starting ability.'); }).catch((err) => { - // ... + console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); }) ``` - -2. Use the FuncAbility lifecycle callback to receive the parameters passed from EntryAbility. + +2. In FuncAbility, use [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) or [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonnewwant) to receive the parameters passed in by EntryAbility. ```ts import UIAbility from '@ohos.app.ability.UIAbility'; - import Window from '@ohos.window'; export default class FuncAbility extends UIAbility { - onCreate(want, launchParam) { - // Receive the parameters passed by the caller UIAbility. - let funcAbilityWant = want; - let info = funcAbilityWant?.parameters?.info; - // ... - } + onCreate(want, launchParam) { + // Receive the parameters passed by the initiator UIAbility. + let funcAbilityWant = want; + let info = funcAbilityWant?.parameters?.info; + ... + } } ``` - -3. To stop the **UIAbility** instance after the FuncAbility service is complete, call **terminateSelf()** in FuncAbility. + + > **NOTE** + > + > In FuncAbility started, you can obtain the PID and bundle name of the UIAbility through **parameters** in the passed **want** parameter. + +3. To stop the **UIAbility** instance after the FuncAbility service is complete, call [terminateSelf()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself) in FuncAbility. ```ts - // context is the ability-level context of the UIAbility instance to stop. - this.context.terminateSelf((err) => { - // ... + let context = ...; // UIAbilityContext + + // context is the UIAbilityContext of the UIAbility instance to stop. + context.terminateSelf((err) => { + if (err.code) { + console.error(`Failed to terminate Self. Code is ${err.code}, message is ${err.message}`); + return; + } }); ``` + + > **NOTE** + > + > When [terminateSelf()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself) is called to stop the **UIAbility** instance, the snapshot of the instance is retained by default. That is, the mission corresponding to the instance is still displayed in Recents. If you do not want to retain the snapshot, set **removeMissionAfterTerminate** under the [abilities](../quick-start/module-configuration-file.md#abilities) tag to **true** in the [module.json5 file](../quick-start/module-configuration-file.md) of the corresponding UIAbility. + +4. To stop all UIAbility instances of the application, call **killProcessBySelf()** of [ApplicationContext](../reference/apis/js-apis-inner-application-applicationContext.md) to stop all processes of the application. ## Starting UIAbility in the Same Application and Obtaining the Return Result When starting FuncAbility from EntryAbility, you want the result to be returned after the FuncAbility service is finished. For example, your application uses two independent UIAbility components to carry the entry and sign-in functionalities. After the sign-in operation is finished in the sign-in UIAbility, the sign-in result needs to be returned to the entry UIAbility. -1. In EntryAbility, call **startAbilityForResult()** to start FuncAbility. Use **data** in the asynchronous callback to receive information returned after FuncAbility stops itself. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). +1. In EntryAbility, call [startAbilityForResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to start FuncAbility. Use **data** in the asynchronous callback to receive information returned after FuncAbility stops itself. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). ```ts - let wantInfo = { - deviceId: '', // An empty deviceId indicates the local device. - bundleName: 'com.example.myapplication', - abilityName: 'FuncAbility', - moduleName: 'module1', // moduleName is optional. - parameters: {// Custom information. - info: 'From the Index page of EntryAbility', - }, + let context = ...; // UIAbilityContext + let want = { + deviceId: '', // An empty deviceId indicates the local device. + bundleName: 'com.example.myapplication', + abilityName: 'FuncAbility', + moduleName: 'func', // moduleName is optional. + parameters: {// Custom information. + info: 'From the Index page of EntryAbility', + }, } - // context is the ability-level context of the initiator UIAbility. - this.context.startAbilityForResult(wantInfo).then((data) => { - // ... + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbilityForResult(want).then((data) => { + ... }).catch((err) => { - // ... + console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }) ``` - -2. Call **terminateSelfWithResult()** to stop FuncAbility. Use the input parameter **abilityResult** to carry the information that FuncAbility needs to return to EntryAbility. + +2. Call [terminateSelfWithResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to stop FuncAbility. Use the input parameter **abilityResult** to carry the information that FuncAbility needs to return to EntryAbility. ```ts + let context = ...; // UIAbilityContext const RESULT_CODE: number = 1001; let abilityResult = { - resultCode: RESULT_CODE, - want: { - bundleName: 'com.example.myapplication', - abilityName: 'FuncAbility', - moduleName: 'module1', - parameters: { - info: 'From the Index page of FuncAbility', - }, + resultCode: RESULT_CODE, + want: { + bundleName: 'com.example.myapplication', + abilityName: 'FuncAbility', + moduleName: 'func', + parameters: { + info: 'From the Index page of FuncAbility', }, + }, } - // context is the ability-level context of the callee UIAbility. - this.context.terminateSelfWithResult(abilityResult, (err) => { - // ... + // context is the AbilityContext of the target UIAbility. + context.terminateSelfWithResult(abilityResult, (err) => { + if (err.code) { + console.error(`Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`); + return; + } }); ``` - -3. After FuncAbility stops itself, EntryAbility uses the **startAbilityForResult()** method to receive the information returned by FuncAbility. The value of **RESULT_CODE** must be the same as the preceding value. + +3. After FuncAbility stops itself, EntryAbility uses [startAbilityForResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to receive the information returned by FuncAbility. The value of **RESULT_CODE** must be the same as the preceding value. ```ts + let context = ...; // UIAbilityContext const RESULT_CODE: number = 1001; - // ... + ... - // context is the ability-level context of the initiator UIAbility. - this.context.startAbilityForResult(want).then((data) => { - if (data?.resultCode === RESULT_CODE) { - // Parse the information returned by the callee UIAbility. - let info = data.want?.parameters?.info; - // ... - } + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbilityForResult(want).then((data) => { + if (data?.resultCode === RESULT_CODE) { + // Parse the information returned by the target UIAbility. + let info = data.want?.parameters?.info; + ... + } }).catch((err) => { - // ... + console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }) ``` ## Starting UIAbility of Another Application -Generally, the user only needs to do a common operation (for example, selecting a document application to view the document content) to start the UIAbility of another application. The [implicit Want launch mode](want-overview.md#types-of-want) is recommended. The system identifies a matched UIAbility and starts it based on the **want** parameter of the caller. +Generally, the user only needs to do a common operation (for example, selecting a document application to view the document content) to start the UIAbility of another application. The [implicit Want launch mode](want-overview.md#types-of-want) is recommended. The system identifies a matched UIAbility and starts it based on the **want** parameter of the initiator UIAbility. There are two ways to start **UIAbility**: [explicit and implicit](want-overview.md). - Explicit Want launch: This mode is used to start a determined UIAbility component of an application. You need to set **bundleName** and **abilityName** of the target application in the **want** parameter. -- Implicit Want launch: The user selects a UIAbility to start based on the matching conditions. That is, the UIAbility to start is not determined (the **abilityName** parameter is not specified). When the **startAbility()** method is called, the **want** parameter specifies a series of parameters such as [entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity) and [actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction). **entities** provides additional type information of the target UIAbility, such as the browser or video player. **actions** specifies the common operations to perform, such as viewing, sharing, and application details. Then the system analyzes the **want** parameter to find the right UIAbility to start. You usually do not know whether the target application is installed and what **bundleName** and **abilityName** of the target application are. Therefore, implicit Want launch is usually used to start the UIAbility of another application. +- Implicit Want launch: The user selects a UIAbility to start based on the matching conditions. That is, the UIAbility to start is not determined (the **abilityName** parameter is not specified). When [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, the want parameter specifies a series of parameters such as **entities** and **actions**. **entities** provides category information of the target UIAbility, such as the browser or video player. **actions** specifies the common operations to perform, such as viewing, sharing, and application details. Then the system analyzes the **want** parameter to find the right UIAbility to start. You usually do not know whether the target application is installed and what **bundleName** and **abilityName** of the target application are. Therefore, implicit Want launch is usually used to start the UIAbility of another application. This section describes how to start the UIAbility of another application through implicit Want. -1. Install multiple document applications on your device. In the **module.json5** file of each UIAbility component, configure [entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity) and [actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction) under **skills**. +1. Install multiple document applications on your device. In the [module.json5 file](../quick-start/module-configuration-file.md) of each UIAbility component, configure **entities** and **actions** under **skills**. ```json { "module": { "abilities": [ { - // ... + ... "skills": [ { "entities": [ - // ... + ... "entity.system.default" ], "actions": [ - // ... + ... "ohos.want.action.viewData" ] } @@ -175,60 +198,65 @@ This section describes how to start the UIAbility of another application through } ``` -2. Include **entities** and **actions** of the caller's **want** parameter into **entities** and **actions** under **skills** of the target UIAbility. After the system matches the UIAbility that meets the **entities** and **actions** information, a dialog box is displayed, showing the list of matched UIAbility instances for users to select. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). +2. Include **entities** and **actions** of the initiator UIAbility's **want** parameter into **entities** and **actions** under **skills** of the target UIAbility. After the system matches the UIAbility that meets the **entities** and **actions** information, a dialog box is displayed, showing the list of matched UIAbility instances for users to select. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). ```ts - let wantInfo = { - deviceId: '', // An empty deviceId indicates the local device. - // Uncomment the line below if you want to implicitly query data only in the specific bundle. - // bundleName: 'com.example.myapplication', - action: 'ohos.want.action.viewData', - // entities can be omitted. - entities: ['entity.system.default'], + let context = ...; // UIAbilityContext + let want = { + deviceId: '', // An empty deviceId indicates the local device. + // Uncomment the line below if you want to implicitly query data only in the specific bundle. + // bundleName: 'com.example.myapplication', + action: 'ohos.want.action.viewData', + // entities can be omitted. + entities: ['entity.system.default'], } - // context is the ability-level context of the initiator UIAbility. - this.context.startAbility(wantInfo).then(() => { - // ... + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbility(want).then(() => { + console.info('Succeeded in starting ability.'); }).catch((err) => { - // ... + console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); }) ``` - - The following figure shows the effect. When you click **Open PDF**, a dialog box is displayed for you to select. - - ![uiability-intra-device-interaction](figures/uiability-intra-device-interaction.png) -3. To stop the **UIAbility** instance after the document application is used, call **terminateSelf()**. + The following figure shows the effect. When you click **Open PDF**, a dialog box is displayed for you to select. + ![](figures/uiability-intra-device-interaction.png) + +3. To stop the **UIAbility** instance after the document application is used, call [terminateSelf()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateself). ```ts - // context is the ability-level context of the UIAbility instance to stop. - this.context.terminateSelf((err) => { - // ... + let context = ...; // UIAbilityContext + + // context is the UIAbilityContext of the UIAbility instance to stop. + context.terminateSelf((err) => { + if (err.code) { + console.error(`Failed to terminate self. Code is ${err.code}, message is ${err.message}`); + return; + } }); ``` ## Starting UIAbility of Another Application and Obtaining the Return Result -If you want to obtain the return result when using implicit Want to start the UIAbility of another application, use the **startAbilityForResult()** method. An example scenario is that the main application needs to start a third-party payment application and obtain the payment result. +If you want to obtain the return result when using implicit Want to start the UIAbility of another application, use [startAbilityForResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult). An example scenario is that the main application needs to start a third-party payment application and obtain the payment result. -1. In the **module.json5** file of the UIAbility corresponding to the payment application, set [entities](../reference/apis/js-apis-ability-wantConstant.md#wantconstantentity) and [actions](../reference/apis/js-apis-ability-wantConstant.md#wantconstantaction) under **skills**. +1. In the [module.json5 file](../quick-start/module-configuration-file.md) of the UIAbility corresponding to the payment application, set **entities** and **actions** under **skills**. ```json { "module": { "abilities": [ { - // ... + ... "skills": [ { "entities": [ - // ... + ... "entity.system.default" ], "actions": [ - // ... + ... "ohos.want.action.editData" ] } @@ -239,68 +267,125 @@ If you want to obtain the return result when using implicit Want to start the UI } ``` -2. Call the **startAbilityForResult()** method to start the UIAbility of the payment application. Include **entities** and **actions** of the caller's **want** parameter into **entities** and **actions** under **skills** of the target UIAbility. Use **data** in the asynchronous callback to receive the information returned to the caller after the payment UIAbility stops itself. After the system matches the UIAbility that meets the **entities** and **actions** information, a dialog box is displayed, showing the list of matched UIAbility instances for users to select. +2. Call [startAbilityForResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to start the UIAbility of the payment application. Include **entities** and **actions** of the initiator UIAbility's **want** parameter into **entities** and **actions** under **skills** of the target UIAbility. Use **data** in the asynchronous callback to receive the information returned to the initiator UIAbility after the payment UIAbility stops itself. After the system matches the UIAbility that meets the **entities** and **actions** information, a dialog box is displayed, showing the list of matched UIAbility instances for users to select. ```ts - let wantInfo = { - deviceId: '', // An empty deviceId indicates the local device. - // Uncomment the line below if you want to implicitly query data only in the specific bundle. - // bundleName: 'com.example.myapplication', - action: 'ohos.want.action.editData', - // entities can be omitted. - entities: ['entity.system.default'], + let context = ...; // UIAbilityContext + let want = { + deviceId: '', // An empty deviceId indicates the local device. + // Uncomment the line below if you want to implicitly query data only in the specific bundle. + // bundleName: 'com.example.myapplication', + action: 'ohos.want.action.editData', + // entities can be omitted. + entities: ['entity.system.default'] } - // context is the ability-level context of the initiator UIAbility. - this.context.startAbilityForResult(wantInfo).then((data) => { - // ... + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbilityForResult(want).then((data) => { + ... }).catch((err) => { - // ... + console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }) ``` - -3. After the payment is finished, call the **terminateSelfWithResult()** method to stop the payment UIAbility and return the **abilityResult** parameter. + +3. After the payment is finished, call [terminateSelfWithResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) to stop the payment UIAbility and return the **abilityResult** parameter. ```ts + let context = ...; // UIAbilityContext const RESULT_CODE: number = 1001; let abilityResult = { - resultCode: RESULT_CODE, - want: { - bundleName: 'com.example.myapplication', - abilityName: 'EntryAbility', - moduleName: 'entry', - parameters: { - payResult: 'OKay', - }, + resultCode: RESULT_CODE, + want: { + bundleName: 'com.example.myapplication', + abilityName: 'EntryAbility', + moduleName: 'entry', + parameters: { + payResult: 'OKay', }, + }, } - // context is the ability-level context of the callee UIAbility. - this.context.terminateSelfWithResult(abilityResult, (err) => { - // ... + // context is the AbilityContext of the target UIAbility. + context.terminateSelfWithResult(abilityResult, (err) => { + if (err.code) { + console.error(`Failed to terminate self with result. Code is ${err.code}, message is ${err.message}`); + return; + } }); ``` - -4. Receive the information returned by the payment application in the callback of the **startAbilityForResult()** method. The value of **RESULT_CODE** must be the same as that returned by **terminateSelfWithResult()**. + +4. Receive the information returned by the payment application in the callback of the [startAbilityForResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult) method. The value of **RESULT_CODE** must be the same as that returned by [terminateSelfWithResult()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextterminateselfwithresult). ```ts + let context = ...; // UIAbilityContext const RESULT_CODE: number = 1001; let want = { // Want parameter information. }; - // context is the ability-level context of the initiator UIAbility. - this.context.startAbilityForResult(want).then((data) => { - if (data?.resultCode === RESULT_CODE) { - // Parse the information returned by the callee UIAbility. - let payResult = data.want?.parameters?.payResult; - // ... - } + // context is the UIAbilityContext of the initiator UIAbility. + context.startAbilityForResult(want).then((data) => { + if (data?.resultCode === RESULT_CODE) { + // Parse the information returned by the target UIAbility. + let payResult = data.want?.parameters?.payResult; + ... + } }).catch((err) => { - // ... + console.error(`Failed to start ability for result. Code is ${err.code}, message is ${err.message}`); }) ``` +## Starting UIAbility with Window Mode Specified (for System Applications Only) + +By specifying the window mode when starting the UIAbility of an application, the application can be displayed in different window modes, which can be full-screen, floating window, or split-screen. + +In full-screen mode, an application occupies the entire screen after being started. Users cannot view other windows or applications. This mode is suitable for an application that requires users to focus on a specific task or UI. + +In floating window mode, an application is displayed on the screen as a floating window after being started. Users can easily switch to other windows or applications. The mode is suitable for an application that requires users to process multiple tasks at the same time. + +In split-screen mode, two applications occupy the entire screen, with one on the left or in the upper part of the screen and the other on the right or in the lower part. This mode helps users improve multi-task processing efficiency. + +The window mode is specified by the **windowMode** field in the [StartOptions](../reference/apis/js-apis-app-ability-startOptions.md) parameter of [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability). + +> **NOTE** +> +> 1. If the **windowMode** field is not specified, the UIAbility is started in the default window mode. +> 2. To ensure that the application can be displayed in the required window mode, check the **supportWindowMode** field in the [abilities](../quick-start/module-configuration-file.md#abilities) tag in the [module.json5 file](../quick-start/module-configuration-file.md) of the UIAbility and make sure the specified window mode is supported. + +The following uses the floating window mode as an example to describe how to start the FuncAbility from the EntryAbility page. + +1. Add the [StartOptions](../reference/apis/js-apis-app-ability-startOptions.md) parameter in [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability). +2. Set the **windowMode** field in the [StartOptions](../reference/apis/js-apis-app-ability-startOptions.md) parameter to **WINDOW_MODE_FLOATING**, indicating that the UIAbility will be displayed in a floating window. + +For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). + +```ts +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + +let context = ...; // UIAbilityContext +let want = { + deviceId: '', // An empty deviceId indicates the local device. + bundleName: 'com.example.myapplication', + abilityName: 'FuncAbility', + moduleName: 'func', // moduleName is optional. + parameters: {// Custom information. + info: 'From the Index page of EntryAbility', + }, +} +let options = { + windowMode: AbilityConstant.WindowMode.WINDOW_MODE_FLOATING +}; +// context is the UIAbilityContext of the initiator UIAbility. +context.startAbility(want, options).then(() => { + console.info('Succeeded in starting ability.'); +}).catch((err) => { + console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); +}) +``` + +The display effect is shown below. + +![](figures/start-uiability-floating-window.png) ## Starting a Specified Page of UIAbility @@ -309,24 +394,25 @@ A UIAbility component can have multiple pages. When it is started in different s ### Specifying a Startup Page -When the caller UIAbility starts another UIAbility, it usually needs to redirect to a specified page. For example, FuncAbility contains two pages: Index (corresponding to the home page) and Second (corresponding to function A page). You can configure the specified page URL in the **want** parameter by adding a custom parameter to **parameters** in **want**. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). +When the initiator UIAbility starts another UIAbility, it usually needs to redirect to a specified page. For example, FuncAbility contains two pages: Index (corresponding to the home page) and Second (corresponding to function A page). You can configure the specified page URL in the **want** parameter by adding a custom parameter to **parameters** in **want**. For details about how to obtain the context, see [Obtaining the Context of UIAbility](uiability-usage.md#obtaining-the-context-of-uiability). ```ts -let wantInfo = { +let context = ...; // UIAbilityContext +let want = { deviceId: '', // An empty deviceId indicates the local device. bundleName: 'com.example.myapplication', abilityName: 'FuncAbility', - moduleName: 'module1', // moduleName is optional. + moduleName: 'func', // moduleName is optional. parameters: {// Custom parameter used to pass the page information. router: 'funcA', }, } -// context is the ability-level context of the initiator UIAbility. -this.context.startAbility(wantInfo).then(() => { - // ... +// context is the UIAbilityContext of the initiator UIAbility. +context.startAbility(want).then(() => { + console.info('Succeeded in starting ability.'); }).catch((err) => { - // ... + console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); }) ``` @@ -341,25 +427,25 @@ import UIAbility from '@ohos.app.ability.UIAbility' import Window from '@ohos.window' export default class FuncAbility extends UIAbility { - funcAbilityWant; - - onCreate(want, launchParam) { - // Receive the parameters passed by the caller UIAbility. - this.funcAbilityWant = want; - } - - onWindowStageCreate(windowStage: Window.WindowStage) { - // Main window is created. Set a main page for this ability. - let url = 'pages/Index'; - if (this.funcAbilityWant?.parameters?.router) { - if (this.funcAbilityWant.parameters.router === 'funA') { - url = 'pages/Second'; - } - } - windowStage.loadContent(url, (err, data) => { - // ... - }); + funcAbilityWant; + + onCreate(want, launchParam) { + // Receive the parameters passed by the initiator UIAbility. + this.funcAbilityWant = want; + } + + onWindowStageCreate(windowStage: Window.WindowStage) { + // Main window is created. Set a main page for this UIAbility. + let url = 'pages/Index'; + if (this.funcAbilityWant?.parameters?.router) { + if (this.funcAbilityWant.parameters.router === 'funA') { + url = 'pages/Second'; + } } + windowStage.loadContent(url, (err, data) => { + ... + }); + } } ``` @@ -378,11 +464,11 @@ In summary, when a UIAbility instance of application A has been created and the import UIAbility from '@ohos.app.ability.UIAbility' export default class FuncAbility extends UIAbility { - onNewWant(want, launchParam) { - // Receive the parameters passed by the caller UIAbility. - globalThis.funcAbilityWant = want; - // ... - } + onNewWant(want, launchParam) { + // Receive the parameters passed by the initiator UIAbility. + globalThis.funcAbilityWant = want; + ... + } } ``` @@ -406,221 +492,208 @@ In summary, when a UIAbility instance of application A has been created and the // Page display. build() { - // ... + ... } } ``` > **NOTE** -> When the [launch type of the callee UIAbility](uiability-launch-type.md) is set to **standard**, a new instance is created each time the callee UIAbility is started. In this case, the [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback will not be invoked. +> +> When the [launch type of the target UIAbility](uiability-launch-type.md) is set to **multiton**, a new instance is created each time the target UIAbility is started. In this case, the [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback will not be invoked. -## Using Ability Call to Implement UIAbility Interaction (for System Applications Only) +## Using Call to Implement UIAbility Interaction (for System Applications Only) -Ability call is an extension of the UIAbility capability. It enables the UIAbility to be invoked by and communicate with external systems. The UIAbility invoked can be either started in the foreground or created and run in the background. You can use the ability call to implement data sharing between two UIAbility instances (caller ability and callee ability) through IPC. +Call is an extension of the UIAbility capability. It enables the UIAbility to be invoked by and communicate with external systems. The UIAbility invoked can be either started in the foreground or created and run in the background. You can use the call to implement data sharing between two UIAbility instances (CallerAbility and CalleeAbility) through IPC. -The core API used for the ability call is **startAbilityByCall**, which differs from **startAbility** in the following ways: +The core API used for the call is **startAbilityByCall()**, which differs from **startAbility()** in the following ways: -- **startAbilityByCall** supports ability launch in the foreground and background, whereas **startAbility** supports ability launch in the foreground only. +- **startAbilityByCall()** supports UIAbility launch in the foreground and background, whereas **startAbility()** supports UIAbility launch in the foreground only. -- The caller ability can use the caller object returned by **startAbilityByCall** to communicate with the callee ability, but **startAbility** does not provide the communication capability. +- The CallerAbility can use the caller object returned by **startAbilityByCall()** to communicate with the CalleeAbility, but **startAbility()** does not provide the communication capability. -Ability call is usually used in the following scenarios: +Call is usually used in the following scenarios: -- Communicating with the callee ability +- Communicating with the CalleeAbility -- Starting the callee ability in the background +- Starting the CalleeAbility in the background -**Table 1** Terms used in the ability call + +**Table 1** Terms used in the call | **Term**| Description| | -------- | -------- | -| CallerAbility | UIAbility that triggers the ability call.| -| CalleeAbility | UIAbility invoked by the ability call.| -| Caller | Object returned by **startAbilityByCall** and used by the caller ability to communicate with the callee ability.| -| Callee | Object held by the callee ability to communicate with the caller ability.| +| CallerAbility| UIAbility that triggers the call.| +| CalleeAbility | UIAbility invoked by the call.| +| Caller | Object returned by **startAbilityByCall** and used by the CallerAbility to communicate with the CalleeAbility.| +| Callee | Object held by the CalleeAbility to communicate with the CallerAbility.| -The following figure shows the ability call process. +The following figure shows the call process. - Figure 1 Ability call process +Figure 1 Call process - ![call](figures/call.png) +![call](figures/call.png) -- The caller ability uses **startAbilityByCall** to obtain a caller object and uses **call()** of the caller object to send data to the callee ability. +- The CallerAbility uses **startAbilityByCall** to obtain a caller object and uses **call()** of the caller object to send data to the CalleeAbility. -- The callee ability, which holds a **Callee** object, uses **on()** of the **Callee** object to register a callback. This callback is invoked when the callee ability receives data from the caller ability. +- The CalleeAbility, which holds a **Callee** object, uses **on()** of the **Callee** object to register a callback. This callback is invoked when the CalleeAbility receives data from the CallerAbility. > **NOTE** -> 1. Currently, only system applications can use the ability call. +> 1. Currently, only system applications can use the call. > -> 2. The launch type of the callee ability must be **singleton**. +> 2. The launch type of the CalleeAbility must be **singleton**. > -> 3. Both local (intra-device) and cross-device ability calls are supported. The following describes how to initiate a local call. For details about how to initiate a cross-device ability call, see [Using Cross-Device Ability Call](hop-multi-device-collaboration.md#using-cross-device-ability-call). +> 3. Both local (intra-device) and cross-device calls are supported. The following describes how to initiate a local call. For details about how to initiate a cross-device call, see [Using Cross-Device Call](hop-multi-device-collaboration.md#using-cross-device-call). ### Available APIs -The following table describes the main APIs used for the ability call. For details, see [AbilityContext](../reference/apis/js-apis-app-ability-uiAbility.md#caller). +The following table describes the main APIs used for the call. For details, see [AbilityContext](../reference/apis/js-apis-app-ability-uiAbility.md#caller). - **Table 2** Ability call APIs +**Table 2** Call APIs | API| Description| | -------- | -------- | | startAbilityByCall(want: Want): Promise<Caller> | Starts a UIAbility in the foreground (through the **want** configuration) or background (default) and obtains the caller object for communication with the UIAbility. For details, see [AbilityContext](../reference/apis/js-apis-inner-application-uiAbilityContext.md#abilitycontextstartabilitybycall) or [ServiceExtensionContext](../reference/apis/js-apis-inner-application-serviceExtensionContext.md#serviceextensioncontextstartabilitybycall).| -| on(method: string, callback: CalleeCallBack): void | Callback invoked when the callee ability registers a method.| -| off(method: string): void | Callback invoked when the callee ability deregisters a method.| -| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the callee ability.| -| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | Sends agreed parcelable data to the callee ability and obtains the agreed parcelable data returned by the callee ability.| +| on(method: string, callback: CalleeCallBack): void | Callback invoked when the CalleeAbility registers a method.| +| off(method: string): void | Callback invoked when the CalleeAbility deregisters a method.| +| call(method: string, data: rpc.Parcelable): Promise<void> | Sends agreed parcelable data to the CalleeAbility.| +| callWithResult(method: string, data: rpc.Parcelable): Promise<rpc.MessageSequence> | Sends agreed parcelable data to the CalleeAbility and obtains the agreed parcelable data returned by the CalleeAbility.| | release(): void | Releases the caller object.| | on(type: "release", callback: OnReleaseCallback): void | Callback invoked when the caller object is released.| -The implementation of using the ability call for UIAbility interaction involves two parts. - -- [Creating a Callee Ability](#creating-a-callee-ability) - -- [Accessing the Callee Ability](#accessing-the-callee-ability) +The implementation of using the call for UIAbility interaction involves two parts. +- [Creating a CalleeAbility](#creating-a-calleeability) -### Creating a Callee Ability +- [Accessing the CalleeAbility](#accessing-the-calleeability) -For the callee ability, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. -1. Configure the ability launch type. - Set **launchType** of the callee ability to **singleton** in the **module.json5** file. +### Creating a CalleeAbility - | JSON Field| Description| - | -------- | -------- | - | "launchType" | Ability launch type. Set this parameter to **singleton**.| - - An example of the ability configuration is as follows: +For the CalleeAbility, implement the callback to receive data and the methods to marshal and unmarshal data. When data needs to be received, use **on()** to register a listener. When data does not need to be received, use **off()** to deregister the listener. +1. Configure the launch type of the UIAbility. + + For example, set the launch type of the CalleeAbility to **singleton**. For details, see [UIAbility Component Launch Type](uiability-launch-type.md). - ```json - "abilities":[{ - "name": ".CalleeAbility", - "srcEntrance": "./ets/CalleeAbility/CalleeAbility.ts", - "launchType": "singleton", - "description": "$string:CalleeAbility_desc", - "icon": "$media:icon", - "label": "$string:CalleeAbility_label", - "visible": true - }] - ``` - 2. Import the **UIAbility** module. ```ts - import Ability from '@ohos.app.ability.UIAbility'; + import UIAbility from '@ohos.app.ability.UIAbility'; ``` 3. Define the agreed parcelable data. - The data formats sent and received by the caller and callee abilities must be consistent. In the following example, the data formats are number and string. + + The data formats sent and received by the CallerAbility and CalleeAbility must be consistent. In the following example, the data formats are number and string. ```ts export default class MyParcelable { - num: number = 0 - str: string = "" + num: number = 0; + str: string = ''; - constructor(num, string) { - this.num = num - this.str = string - } + constructor(num, string) { + this.num = num; + this.str = string; + } - marshalling(messageSequence) { - messageSequence.writeInt(this.num) - messageSequence.writeString(this.str) - return true - } + marshalling(messageSequence) { + messageSequence.writeInt(this.num); + messageSequence.writeString(this.str); + return true; + } - unmarshalling(messageSequence) { - this.num = messageSequence.readInt() - this.str = messageSequence.readString() - return true - } + unmarshalling(messageSequence) { + this.num = messageSequence.readInt(); + this.str = messageSequence.readString(); + return true; + } } ``` 4. Implement **Callee.on** and **Callee.off**. - The time to register a listener for the callee ability depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the ability and deregistered in **onDestroy**. After receiving parcelable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The sample code is as follows: - + + The time to register a listener for the CalleeAbility depends on your application. The data sent and received before the listener is registered and that after the listener is deregistered are not processed. In the following example, the **MSG_SEND_METHOD** listener is registered in **onCreate** of the UIAbility and deregistered in **onDestroy**. After receiving parcelable data, the application processes the data and returns the data result. You need to implement processing based on service requirements. The sample code is as follows: + ```ts const TAG: string = '[CalleeAbility]'; const MSG_SEND_METHOD: string = 'CallSendMsg'; function sendMsgCallback(data) { - console.info('CalleeSortFunc called'); + console.info('CalleeSortFunc called'); - // Obtain the parcelable data sent by the caller ability. - let receivedData = new MyParcelable(0, ''); - data.readParcelable(receivedData); - console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`); + // Obtain the parcelable data sent by the CallerAbility. + let receivedData = new MyParcelable(0, ''); + data.readParcelable(receivedData); + console.info(`receiveData[${receivedData.num}, ${receivedData.str}]`); - // Process the data. - // Return the parcelable data result to the caller ability. - return new MyParcelable(receivedData.num + 1, `send ${receivedData.str} succeed`); + // Process the data. + // Return the parcelable data result to the CallerAbility. + return new MyParcelable(receivedData.num + 1, `send ${receivedData.str} succeed`); } - export default class CalleeAbility extends Ability { - onCreate(want, launchParam) { - try { - this.callee.on(MSG_SEND_METHOD, sendMsgCallback); - } catch (error) { - console.info(`${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`); - } + export default class CalleeAbility extends UIAbility { + onCreate(want, launchParam) { + try { + this.callee.on(MSG_SEND_METHOD, sendMsgCallback); + } catch (err) { + console.error(`Failed to register. Code is ${err.code}, message is ${err.message}`); } + } - onDestroy() { - try { - this.callee.off(MSG_SEND_METHOD); - } catch (error) { - console.error(TAG, `${MSG_SEND_METHOD} unregister failed with error ${JSON.stringify(error)}`); - } + onDestroy() { + try { + this.callee.off(MSG_SEND_METHOD); + } catch (err) { + console.error(`Failed to unregister. Code is ${err.code}, message is ${err.message}`); } + } } ``` -### Accessing the Callee Ability +### Accessing the CalleeAbility 1. Import the **UIAbility** module. ```ts - import Ability from '@ohos.app.ability.UIAbility'; + import UIAbility from '@ohos.app.ability.UIAbility'; ``` 2. Obtain the caller interface. - The **context** attribute of the ability implements **startAbilityByCall** to obtain the caller object for communication. The following example uses **this.context** to obtain the **context** attribute of the ability, uses **startAbilityByCall** to start the callee ability, obtain the caller object, and register the **onRelease** listener of the caller ability. You need to implement processing based on service requirements. + + The **UIAbilityContext** attribute implements **startAbilityByCall** to obtain the caller object for communication. The following example uses **this.context** to obtain the **UIAbilityContext**, uses **startAbilityByCall** to start the CalleeAbility, obtain the caller object, and register the **onRelease** listener of the CallerAbility. You need to implement processing based on service requirements. ```ts - // Register the onRelease() listener of the caller ability. + // Register the onRelease() listener of the CallerAbility. private regOnRelease(caller) { - try { - caller.on("release", (msg) => { - console.info(`caller onRelease is called ${msg}`); - }) - console.info('caller register OnRelease succeed'); - } catch (error) { - console.info(`caller register OnRelease failed with ${error}`); - } + try { + caller.on('release', (msg) => { + console.info(`caller onRelease is called ${msg}`); + }) + console.info('Succeeded in registering on release.'); + } catch (err) { + console.err(`Failed to caller register on release. Code is ${err.code}, message is ${err.message}`); + } } async onButtonGetCaller() { - try { - this.caller = await context.startAbilityByCall({ - bundleName: 'com.samples.CallApplication', - abilityName: 'CalleeAbility' - }) - if (this.caller === undefined) { - console.info('get caller failed') - return - } - console.info('get caller success') - this.regOnRelease(this.caller) - } catch (error) { - console.info(`get caller failed with ${error}`) + try { + this.caller = await context.startAbilityByCall({ + bundleName: 'com.samples.CallApplication', + abilityName: 'CalleeAbility' + }); + if (this.caller === undefined) { + console.info('get caller failed') + return; } + console.info('get caller success') + this.regOnRelease(this.caller) + } (err) { + console.err(`Failed to get caller. Code is ${err.code}, message is ${err.message}`); + } } ``` diff --git a/en/application-dev/application-models/uiability-launch-type.md b/en/application-dev/application-models/uiability-launch-type.md index 19d7b73402e354027c1ff72ff4c2432dc56f8862..c7aac3d4bc3acdb2601580646024ec1f8117473f 100644 --- a/en/application-dev/application-models/uiability-launch-type.md +++ b/en/application-dev/application-models/uiability-launch-type.md @@ -6,7 +6,7 @@ The launch type of the UIAbility component refers to the state of the UIAbility - [Singleton](#singleton) -- [Standard](#standard) +- [Multiton](#multiton) - [Specified](#specified) @@ -17,9 +17,9 @@ The launch type of the UIAbility component refers to the state of the UIAbility Each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, if a UIAbility instance of this type already exists in the application process, the instance is reused. Therefore, only one UIAbility instance of this type exists in the system, that is, displayed in **Recents**. -**Figure 1** Demonstration effect in singleton mode +**Figure 1** Demonstration effect in singleton mode -![uiability-launch-type1](figures/uiability-launch-type1.png) +![uiability-launch-type1](figures/uiability-launch-type1.gif) > **NOTE** > @@ -31,11 +31,11 @@ To use the singleton mode, set **launchType** in the [module.json5 configuration ```json { "module": { - // ... + ... "abilities": [ { "launchType": "singleton", - // ... + ... } ] } @@ -43,25 +43,25 @@ To use the singleton mode, set **launchType** in the [module.json5 configuration ``` -## Standard +## Multiton -In standard mode, each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, a new UIAbility instance of this type is created in the application process. Multiple UIAbility instances of this type are displayed in **Recents**. +In multiton mode, each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, a new UIAbility instance of this type is created in the application process. Multiple UIAbility instances of this type are displayed in **Recents**. -**Figure 2** Demonstration effect in standard mode +**Figure 2** Demonstration effect in multiton mode -![standard-mode](figures/standard-mode.png) +![uiability-launch-type2](figures/uiability-launch-type2.gif) -To use the standard mode, set **launchType** in the [module.json5 configuration file](../quick-start/module-configuration-file.md) to **standard**. +To use the multiton mode, set **launchType** in the [module.json5 file](../quick-start/module-configuration-file.md) to **multiton**. ```json { "module": { - // ... + ... "abilities": [ { - "launchType": "standard", - // ... + "launchType": "multiton", + ... } ] } @@ -73,90 +73,87 @@ To use the standard mode, set **launchType** in the [module.json5 configuration The **specified** mode is used in some special scenarios. For example, in a document application, you want a document instance to be created each time you create a document, but you want to use the same document instance when you repeatedly open an existing document. -**Figure 3** Demonstration effect in specified mode +**Figure 3** Demonstration effect in specified mode -![uiability-launch-type2](figures/uiability-launch-type2.png) +![uiability-launch-type3](figures/uiability-launch-type3.gif) -For example, there are EntryAbility and SpecifiedAbility, and the launch type of SpecifiedAbility is set to **specified**. You are required to start SpecifiedAbility from EntryAbility. +For example, there are two UIAbility components: EntryAbility and SpecifiedAbility (with the launch type **specified**). You are required to start SpecifiedAbility from EntryAbility. + +1. In SpecifiedAbility, set the **launchType** field in the [module.json5 file](../quick-start/module-configuration-file.md) to **specified**. -1. In SpecifiedAbility, set the **launchType** field in the [module.json5 configuration file](../quick-start/module-configuration-file.md) to **specified**. - ```json { "module": { - // ... + ... "abilities": [ { "launchType": "specified", - // ... + ... } ] } } ``` -2. Before a UIAbility instance is created, you can create a unique string key for the instance. The key is bound to the UIAbility instance when it is created. Each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, the application is asked which UIAbility instance is used to respond to the [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) request. - In EntryAbility, add a custom parameter, for example, **instanceKey**, to the [want](want-overview.md) parameter in [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to distinguish the UIAbility instances. - +2. Create a unique string key for the instance. Each time [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called, the application, based on the key, identifies the UIAbility instance used to respond to the request. In EntryAbility, add a custom parameter, for example, **instanceKey**, to the **want** parameter in [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) to distinguish the UIAbility instance. + ```ts // Configure an independent key for each UIAbility instance. // For example, in the document usage scenario, use the document path as the key. function getInstance() { - // ... + ... } + let context =...; // context is the UIAbilityContext of the initiator UIAbility. let want = { - deviceId: '', // An empty deviceId indicates the local device. - bundleName: 'com.example.myapplication', - abilityName: 'SpecifiedAbility', - moduleName: 'module1', // moduleName is optional. - parameters: {// Custom information. - instanceKey: getInstance(), - }, + deviceId: '', // An empty deviceId indicates the local device. + bundleName: 'com.example.myapplication', + abilityName: 'SpecifiedAbility', + moduleName: 'specified', // moduleName is optional. + parameters: {// Custom information. + instanceKey: getInstance(), + }, } - // context is the ability-level context of the initiator UIAbility. - this.context.startAbility(want).then(() => { - // ... + + context.startAbility(want).then(() => { + console.info('Succeeded in starting ability.'); }).catch((err) => { - // ... + console.error(`Failed to start ability. Code is ${err.code}, message is ${err.message}`); }) ``` - -3. During running, the internal service of UIAbility determines whether to create multiple instances. If the key is matched, the UIAbility instance bound to the key is started. Otherwise, a new UIAbility instance is created. - The launch type of SpecifiedAbility is set to **specified**. Before SpecifiedAbility is started, the [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant) callback of the corresponding AbilityStage instance is invoked to parse the input **want** parameter and obtain the custom parameter **instanceKey**. A string key identifier is returned through the [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant) callback of the AbilityStage instance. [If the returned key corresponds to a started UIAbility instance](mission-management-launch-type.md#fig14520125175314), that UIAbility instance is switched to the foreground and gains focus again. Otherwise, a new instance is created and started. - + +3. Before SpecifiedAbility is started, the [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant) callback of the corresponding AbilityStage instance is invoked to obtain the key of the UIAbility, because the launch type of SpecifiedAbility is set to **specified**. If a UIAbility instance matching the key exists, the system starts the UIAbility instance and invokes its [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback. Otherwise, the system creates a new UIAbility instance and invokes its [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) and [onWindowStageCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate) callbacks. + + In the sample code, the [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant) callback parses the **want** parameter to obtain the custom parameter **instanceKey**. The service logic returns a key string based on **instanceKey** parameter to identify the UIAbility instance. If the returned key maps to a started UIAbility instance, the system pulls the UIAbility instance back to the foreground and obtains the focus. If the returned key does not map to a started UIAbility instance, the system creates a new UIAbility instance and starts it. + ```ts import AbilityStage from '@ohos.app.ability.AbilityStage'; export default class MyAbilityStage extends AbilityStage { - onAcceptWant(want): string { - // In the AbilityStage instance of the callee, a key value corresponding to a UIAbility instance is returned for UIAbility whose launch type is specified. - // In this example, SpecifiedAbility of module1 is returned. - if (want.abilityName === 'SpecifiedAbility') { - // The returned string key is a custom string. - return `SpecifiedAbilityInstance_${want.parameters.instanceKey}`; - } - - return ''; + onAcceptWant(want): string { + // In the AbilityStage instance of the callee, a key value corresponding to a UIAbility instance is returned for UIAbility whose launch type is specified. + // In this example, SpecifiedAbility of module1 is returned. + if (want.abilityName === 'SpecifiedAbility') { + // The returned key string is a custom string. + return `SpecifiedAbilityInstance_${want.parameters.instanceKey}`; } + + return ''; + } } ``` - + > **NOTE** > > 1. Assume that the application already has a UIAbility instance created, and the launch type of the UIAbility instance is set to **specified**. If [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability) is called again to start the UIAbility instance, and the [onAcceptWant()](../reference/apis/js-apis-app-ability-abilityStage.md#abilitystageonacceptwant) callback of [AbilityStage](../reference/apis/js-apis-app-ability-abilityStage.md) matches a created UIAbility instance, the original UIAbility instance is started, and no new UIAbility instance is created. In this case, the [onNewWant()](../reference/apis/js-apis-app-ability-uiAbility.md#abilityonnewwant) callback is invoked, but the [onCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityoncreate) and [onWindowStageCreate()](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonwindowstagecreate) callbacks are not. > 2. AbilityStage is not automatically generated in the default project of DevEco Studio. For details about how to create an AbilityStage file, see [AbilityStage Component Container](abilitystage.md). - - For example, in the document application, different key values are bound to different document instances. Each time a document is created, a new key value (for example, file path) is passed, and a new UIAbility instance is created when UIAbility is started in AbilityStage. However, when you open an existing document, the same UIAbility instance is started again in AbilityStage. - -The following steps are used as an example. + For example, in the document application, different keys are bound to different document instances. Each time a document is created, a new key (for example, file path) is passed, and a new UIAbility instance is created when UIAbility is started in AbilityStage. However, when you open an existing document, the same UIAbility instance is started again in AbilityStage. + + The following steps are used as an example. + 1. Open file A. A UIAbility instance, for example, UIAbility instance 1, is started. - 2. Close the process of file A in **Recents**. UIAbility instance 1 is destroyed. Return to the home screen and open file A again. A new UIAbility instance is started, for example, UIAbility instance 2. - 3. Return to the home screen and open file B. A new UIAbility instance is started, for example, UIAbility instance 3. - - 4. Return to the home screen and open file A again. UIAbility instance 2 is started. + 4. Return to the home screen and open file A again. UIAbility instance 2 is started. This is because the system automatically matches the key of the UIAbility instance and starts the UIAbility instance that has a matching key. In this example, UIAbility instance 2 has the same key as file A. Therefore, the system pulls back UIAbility instance 2 and focuses it without creating a new instance. - \ No newline at end of file diff --git a/en/application-dev/application-models/uiability-lifecycle.md b/en/application-dev/application-models/uiability-lifecycle.md index 57200abb8fbfb625e2e6c8999da5ad817e2e85b4..a61b9ae836904a420877d4d36ad011f82cf5202b 100644 --- a/en/application-dev/application-models/uiability-lifecycle.md +++ b/en/application-dev/application-models/uiability-lifecycle.md @@ -8,8 +8,7 @@ When a user opens, switches, and returns to an application, the UIAbility instan The lifecycle of UIAbility has four states: **Create**, **Foreground**, **Background**, and **Destroy**, as shown in the figure below. **Figure 1** UIAbility lifecycle states - -![Ability-Life-Cycle](figures/Ability-Life-Cycle.png) +![Ability-Life-Cycle](figures/Ability-Life-Cycle.png) ## Description of Lifecycle States @@ -22,24 +21,25 @@ The **Create** state is triggered when the UIAbility instance is created during ```ts import UIAbility from '@ohos.app.ability.UIAbility'; -import window from '@ohos.window'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - // Initialize the application. - } - // ... + onCreate(want, launchParam) { + // Initialize the application. + } + ... } ``` +> **NOTE** +> +> [Want](../reference/apis/js-apis-app-ability-want.md) is used as the carrier to transfer information between application components. For details, see [Want](want-overview.md). ### WindowStageCreate and WindowStageDestory After the UIAbility instance is created but before it enters the **Foreground** state, the system creates a WindowStage instance and triggers the **onWindowStageCreate()** callback. You can set UI loading and WindowStage event subscription in the callback. -**Figure 2** WindowStageCreate and WindowStageDestory - -![Ability-Life-Cycle-WindowStage](figures/Ability-Life-Cycle-WindowStage.png) +**Figure 2** WindowStageCreate and WindowStageDestory +![Ability-Life-Cycle-WindowStage](figures/Ability-Life-Cycle-WindowStage.png) In the **onWindowStageCreate()** callback, use [loadContent()](../reference/apis/js-apis-window.md#loadcontent9-2) to set the page to be loaded, and call [on('windowStageEvent')](../reference/apis/js-apis-window.md#onwindowstageevent9) to subscribe to [WindowStage events](../reference/apis/js-apis-window.md#windowstageeventtype9), for example, having or losing focus, or becoming visible or invisible. @@ -48,25 +48,40 @@ import UIAbility from '@ohos.app.ability.UIAbility'; import window from '@ohos.window'; export default class EntryAbility extends UIAbility { - // ... - - onWindowStageCreate(windowStage: Window.WindowStage) { - // Subscribe to the WindowStage events (having or losing focus, or becoming visible or invisible). - try { - windowStage.on('windowStageEvent', (data) => { - console.info('Succeeded in enabling the listener for window stage event changes. Data: ' + - JSON.stringify(data)); - }); - } catch (exception) { - console.error('Failed to enable the listener for window stage event changes. Cause:' + - JSON.stringify(exception)); - }; - - // Set the UI loading. - windowStage.loadContent('pages/Index', (err, data) => { - // ... - }); + ... + + onWindowStageCreate(windowStage: window.WindowStage) { + // Subscribe to the WindowStage events (having or losing focus, or becoming visible or invisible). + try { + windowStage.on('windowStageEvent', (data) => { + let stageEventType: window.WindowStageEventType = data; + switch (stageEventType) { + case window.WindowStageEventType.SHOWN: // Switch to the foreground. + console.info('windowStage foreground.'); + break; + case window.WindowStageEventType.ACTIVE: // Gain focus. + console.info('windowStage active.'); + break; + case window.WindowStageEventType.INACTIVE: // Lose focus. + console.info('windowStage inactive.'); + break; + case window.WindowStageEventType.HIDDEN: // Switch to the background. + console.info('windowStage background.'); + break; + default: + break; + } + }); + } catch (exception) { + console.error('Failed to enable the listener for window stage event changes. Cause:' + + JSON.stringify(exception)); } + + // Set UI loading. + windowStage.loadContent('pages/Index', (err, data) => { + ... + }); + } } ``` @@ -82,18 +97,23 @@ import UIAbility from '@ohos.app.ability.UIAbility'; import window from '@ohos.window'; export default class EntryAbility extends UIAbility { - // ... - - onWindowStageDestroy() { - // Release UI resources. - // Unsubscribe from the WindowStage events such as having or losing focus in the onWindowStageDestroy() callback. - try { - windowStage.off('windowStageEvent'); - } catch (exception) { - console.error('Failed to disable the listener for window stage event changes. Cause:' + - JSON.stringify(exception)); - }; - } + windowStage: window.WindowStage; + ... + + onWindowStageCreate(windowStage: window.WindowStage) { + this.windowStage = windowStage; + ... + } + + onWindowStageDestroy() { + // Release UIresources. + // Unsubscribe from the WindowStage events such as having or losing focus in the onWindowStageDestroy() callback. + try { + this.windowStage.off('windowStageEvent'); + } catch (err) { + console.error(`Failed to disable the listener for window stage event changes. Code is ${err.code}, message is ${err.message}`); + }; + } } ``` @@ -115,16 +135,16 @@ When the application is switched to the background, you can disable positioning import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - // ... + ... - onForeground() { - // Apply for the resources required by the system or re-apply for the resources released in onBackground(). - } + onForeground() { + // Apply for the resources required by the system or re-apply for the resources released in onBackground(). + } - onBackground() { - // Release useless resources when the UI is invisible, or perform time-consuming operations in this callback, - // for example, saving the status. - } + onBackground() { + // Release useless resources when the UI is invisible, or perform time-consuming operations in this callback, + // for example, saving the status. + } } ``` @@ -137,13 +157,12 @@ The UIAbility instance is destroyed when **terminateSelf()** is called or the us ```ts import UIAbility from '@ohos.app.ability.UIAbility'; -import window from '@ohos.window'; export default class EntryAbility extends UIAbility { - // ... + ... - onDestroy() { - // Release system resources and save data. - } + onDestroy() { + // Release system resources and save data. + } } ``` diff --git a/en/application-dev/application-models/uiability-overview.md b/en/application-dev/application-models/uiability-overview.md index 6767d667e441f6baee987d175c30052bac0afc01..5aa0ca79c49bd9f7566e7e0cc1ea977cea8c83d5 100644 --- a/en/application-dev/application-models/uiability-overview.md +++ b/en/application-dev/application-models/uiability-overview.md @@ -5,8 +5,27 @@ UIAbility is a type of application component that provides the UI for user interaction. -UIAbility is the basic unit scheduled by the system and provides a window for applications to draw UIs. A UIAbility component can implement a functional module through multiple pages. Each UIAbility component instance corresponds to a mission in **Recents**. +The following design philosophy is behind UIAbility: +1. Native support for [cross-device migration](hop-cross-device-migration.md) and [multi-device collaboration](hop-multi-device-collaboration.md) at the application component level + +2. Support for multiple device types and window forms + +> **NOTE** +> +> For details, see [Interpretation of the Application Model](application-model-description.md). + +The UIAbility division principles and suggestions are as follows: + +UIAbility is the basic unit scheduled by the system and provides a window for applications to draw UIs. An application can contain one or more UIAbility components. For example, for a payment application, you can use two UIAbility components to carry the entry and payment functionalities. + +Each UIAbility component instance is displayed as a mission in Recents. + +You can develop a single UIAbility or multiple UIAbilities for your application based on service requirements. + +- If you want only one mission to be displayed in Recents, use one UIAbility and multiple pages. + +- If you want multiple missions to be displayed in Recents or multiple windows to be opened simultaneously, use multiple UIAbilities. ## Privacy Statement Configuration @@ -16,17 +35,17 @@ To enable an application to properly use a UIAbility component, declare the UIAb ```json { "module": { - // ... + ... "abilities": [ { "name": "EntryAbility", // Name of the UIAbility component. - "srcEntrance": "./ets/entryability/EntryAbility.ts", // Code path of the UIAbility component. + "srcEntry": "./ets/entryability/EntryAbility.ts", // Code path of the UIAbility component. "description": "$string:EntryAbility_desc", // Description of the UIAbility component. "icon": "$media:icon", // Icon of the UIAbility component. "label": "$string:EntryAbility_label", // Label of the UIAbility component. "startWindowIcon": "$media:icon", // Index of the icon resource file. "startWindowBackground": "$color:start_window_background", // Index of the background color resource file. - // ... + ... } ] } diff --git a/en/application-dev/application-models/uiability-usage.md b/en/application-dev/application-models/uiability-usage.md index fa8badc6d48c7e550922cb60a15d02eab9cc80b6..46959284ef3bfe179d465f5dae92b6f8538f680a 100644 --- a/en/application-dev/application-models/uiability-usage.md +++ b/en/application-dev/application-models/uiability-usage.md @@ -14,14 +14,14 @@ import UIAbility from '@ohos.app.ability.UIAbility'; import window from '@ohos.window'; export default class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage: window.WindowStage) { - // Main window is created. Set a main page for this ability. - windowStage.loadContent('pages/Index', (err, data) => { - // ... - }); - } + onWindowStageCreate(windowStage: window.WindowStage) { + // Main window is created. Set a main page for this ability. + windowStage.loadContent('pages/Index', (err, data) => { + ... + }); + } - // ... + ... } ``` @@ -40,15 +40,14 @@ The UIAbility class has its own context, which is an instance of the [UIAbilityC import UIAbility from '@ohos.app.ability.UIAbility'; export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - // Obtain the context of the UIAbility instance. - let context = this.context; - - // ... - } + onCreate(want, launchParam) { + // Obtain the context of the UIAbility instance. + let context = this.context; + ... + } } ``` - + - Import the context module and define the **context** variable in the component. ```ts @@ -68,7 +67,7 @@ The UIAbility class has its own context, which is an instance of the [UIAbilityC // Page display. build() { - // ... + ... } } ``` @@ -93,7 +92,7 @@ The UIAbility class has its own context, which is an instance of the [UIAbilityC // Page display. build() { - // ... + ... } } ``` diff --git a/en/application-dev/application-models/want-overview.md b/en/application-dev/application-models/want-overview.md index 21c79d517fc237cae9b9bab7d5347ffc82766d5a..cf5cac43999a5efbe59659252b6b3db325cacd8a 100644 --- a/en/application-dev/application-models/want-overview.md +++ b/en/application-dev/application-models/want-overview.md @@ -3,47 +3,49 @@ ## Definition and Usage of Want -[Want](../reference/apis/js-apis-app-ability-want.md) is used as the carrier to transfer information between application components. It is used as a parameter of **startAbility()** to specify the startup target and information that needs to be carried during startup, for example, **bundleName** and **abilityName**, which respectively indicate the bundle name of the target ability and the ability name in the bundle. For example, when UIAbilityA starts UIAbilityB and needs to transfer some data to UIAbilityB, it can use Want to transfer the data. +[Want](../reference/apis/js-apis-app-ability-want.md) is an object that transfers information between application components. It is often used as a parameter of [startAbility()](../reference/apis/js-apis-inner-application-uiAbilityContext.md#uiabilitycontextstartability). For example, when UIAbilityA needs to start UIAbilityB and transfer some data to UIAbilityB, it can use the **want** parameter in **startAbility()** to transfer the data. -**Figure 1** Want usage +**Figure 1** Want usage -![usage-of-want](figures/usage-of-want.png) +![usage-of-want](figures/usage-of-want.png) ## Types of Want -- **Explicit Want**: A type of Want with **abilityName** and **bundleName** specified when starting an ability. - When there is an explicit object to process the request, the target ability can be started by specifying the bundle name and ability name in Want. Explicit Want is usually used to start a known ability. - +- **Explicit Want**: If **abilityName** and **bundleName** are specified in the **want** parameter when starting an an application component, explicit Want is used. + + Explicit Want is usually used to start a known target application component in the same application. The target application component is started by specifying **bundleName** of the application where the target application component is located and **abilityName** in the **Want** object. When there is an explicit object to process the request, explicit Want is a simple and effective way to start the target application component. + ```ts let wantInfo = { - deviceId: '', // An empty deviceId indicates the local device. - bundleName: 'com.example.myapplication', - abilityName: 'FuncAbility', + deviceId: '', // An empty deviceId indicates the local device. + bundleName: 'com.example.myapplication', + abilityName: 'FuncAbility', } ``` - -- **Implicit Want**: A type of Want with **abilityName** unspecified when starting the ability. - Implicit Want can be used when the object used to process the request is unclear and the current application wants to use a capability (defined by the [skills tag](../quick-start/module-configuration-file.md#skills)) provided by another application. For example, you can use implicit Want to describe a request for opening a link, since you do not care which application is used to open the link. The system matches all applications that support the request. - + +- **Implicit Want**: If **abilityName** is not specified in the **want** parameter when starting the an application component, implicit Want is used. + + Implicit Want can be used when the object used to process the request is unclear and the current application wants to use a capability (defined by the [skills tag](../quick-start/module-configuration-file.md#skills)) provided by another application. The system matches all applications that declare to support the capability. For example, for a link open request, the system matches all applications that support the request and provides the available ones for users to select. + ```ts let wantInfo = { - // Uncomment the line below if you want to implicitly query data only in the specific bundle. - // bundleName: 'com.example.myapplication', - action: 'ohos.want.action.search', - // entities can be omitted. - entities: [ 'entity.system.browsable' ], - uri: 'https://www.test.com:8080/query/student', - type: 'text/plain', + // Uncomment the line below if you want to implicitly query data only in the specific bundle. + // bundleName: 'com.example.myapplication', + action: 'ohos.want.action.search', + // entities can be omitted. + entities: [ 'entity.system.browsable' ], + uri: 'https://www.test.com:8080/query/student', + type: 'text/plain', }; ``` - + > **NOTE** - > - Depending on the ability matching result, the following cases may be possible when you attempt to use implicit Want to start the ability. - > - No ability is matched. The startup fails. - > - An ability that meets the conditions is matched. That ability is started. - > - Multiple abilities that meet the conditions are matched. A dialog box is displayed for users to select one of them. + > - Depending on the application component matching result, the following cases may be possible when you attempt to use implicit Want to start the application component. + > - No application component is matched. The startup fails. + > - An application component that meets the conditions is matched. That application component is started. + > - Multiple application components that meet the conditions are matched. A dialog box is displayed for users to select one of them. > > - If the **want** parameter passed does not contain **abilityName** or **bundleName**, the ServiceExtensionAbility components of all applications cannot be started through implicit Want. > diff --git a/en/application-dev/application-models/widget-development-fa.md b/en/application-dev/application-models/widget-development-fa.md index 17f9ee7234865b5d01e2a5f68e52cf7928739db7..3aa1a9fd29495b36a02d155e5d2ad48e94bce5ad 100644 --- a/en/application-dev/application-models/widget-development-fa.md +++ b/en/application-dev/application-models/widget-development-fa.md @@ -20,7 +20,7 @@ Before you get started, it would be helpful if you have a basic understanding of Figure 1 shows the working principles of the widget framework. -**Figure 1** Widget framework working principles in the FA model +**Figure 1** Widget framework working principles in the FA model ![form-extension](figures/form-extension.png) The widget host consists of the following modules: @@ -122,48 +122,48 @@ To create a widget in the FA model, implement the widget lifecycle callbacks. Ge ```ts export default { - onCreate(want) { - console.info('FormAbility onCreate'); - // Called when the widget is created. The widget provider should return the widget data binding class. - let obj = { - "title": "titleOnCreate", - "detail": "detailOnCreate" - }; - let formData = formBindingData.createFormBindingData(obj); - return formData; - }, - onCastToNormal(formId) { - // Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion. - console.info('FormAbility onCastToNormal'); - }, - onUpdate(formId) { - // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. - console.info('FormAbility onUpdate'); - let obj = { - "title": "titleOnUpdate", - "detail": "detailOnUpdate" - }; - let formData = formBindingData.createFormBindingData(obj); - formProvider.updateForm(formId, formData).catch((error) => { - console.info('FormAbility updateForm, error:' + JSON.stringify(error)); - }); - }, - onVisibilityChange(newStatus) { - // Called when the widget host initiates an event about visibility changes. The widget provider should do something to respond to the notification. This callback takes effect only for system applications. - console.info('FormAbility onVisibilityChange'); - }, - onEvent(formId, message) { - // If the widget supports event triggering, override this method and implement the trigger. - console.info('FormAbility onEvent'); - }, - onDestroy(formId) { - // Delete widget data. - console.info('FormAbility onDestroy'); - }, - onAcquireFormState(want) { - console.info('FormAbility onAcquireFormState'); - return formInfo.FormState.READY; - }, + onCreate(want) { + console.info('FormAbility onCreate'); + // Called when the widget is created. The widget provider should return the widget data binding class. + let obj = { + "title": "titleOnCreate", + "detail": "detailOnCreate" + }; + let formData = formBindingData.createFormBindingData(obj); + return formData; + }, + onCastToNormal(formId) { + // Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion. + console.info('FormAbility onCastToNormal'); + }, + onUpdate(formId) { + // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. + console.info('FormAbility onUpdate'); + let obj = { + "title": "titleOnUpdate", + "detail": "detailOnUpdate" + }; + let formData = formBindingData.createFormBindingData(obj); + formProvider.updateForm(formId, formData).catch((error) => { + console.info('FormAbility updateForm, error:' + JSON.stringify(error)); + }); + }, + onVisibilityChange(newStatus) { + // Called when the widget host initiates an event about visibility changes. The widget provider should do something to respond to the notification. This callback takes effect only for system applications. + console.info('FormAbility onVisibilityChange'); + }, + onEvent(formId, message) { + // If the widget supports event triggering, override this method and implement the trigger. + console.info('FormAbility onEvent'); + }, + onDestroy(formId) { + // Delete widget data. + console.info('FormAbility onDestroy'); + }, + onAcquireFormState(want) { + console.info('FormAbility onAcquireFormState'); + return formInfo.FormState.READY; + }, } ``` @@ -188,15 +188,15 @@ The widget configuration file is named **config.json**. Find the **config.json** ```json - "js": [{ - "name": "widget", - "pages": ["pages/index/index"], - "window": { - "designWidth": 720, - "autoDesignWidth": true - }, - "type": "form" - }] + "js": [{ + "name": "widget", + "pages": ["pages/index/index"], + "window": { + "designWidth": 720, + "autoDesignWidth": true + }, + "type": "form" + }] ``` - The **abilities** module in the **config.json** file corresponds to **FormAbility** of the widget. The internal structure is described as follows: @@ -275,7 +275,7 @@ async function storeFormInfo(formId: string, formName: string, tempFlag: boolean } } -// ... +... onCreate(want) { console.info('FormAbility onCreate'); @@ -293,7 +293,7 @@ async function storeFormInfo(formId: string, formName: string, tempFlag: boolean let formData = formBindingData.createFormBindingData(obj); return formData; } -// ... +... ``` You should override **onDestroy** to implement widget data deletion. @@ -313,17 +313,17 @@ async function deleteFormInfo(formId: string) { } } -// ... +... onDestroy(formId) { console.info('FormAbility onDestroy'); // Delete the persistent widget instance data. // Implement this API based on project requirements. deleteFormInfo(formId); } -// ... +... ``` -For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). +For details about how to implement persistent data storage, see [Application Data Persistence Overview](../database/app-data-persistence-overview.md). The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. @@ -364,7 +364,7 @@ You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. Thi > **NOTE** > -> Only the JavaScript-based web-like development paradigm is supported when developing the widget UI. +> In the FA model, only the JavaScript-based web-like development paradigm is supported when developing the widget UI. - HML: uses web-like paradigm components to describe the widget page information. @@ -543,4 +543,3 @@ The following is an example: } } ``` - diff --git a/en/application-dev/application-models/widget-development-stage.md b/en/application-dev/application-models/widget-development-stage.md deleted file mode 100644 index 3e542956072a31fbc8dbca097ae264dfe8ebfc5f..0000000000000000000000000000000000000000 --- a/en/application-dev/application-models/widget-development-stage.md +++ /dev/null @@ -1,599 +0,0 @@ -# FormExtensionAbility (Widget) - - -## Widget Overview - -FormExtensionAbility provides a service widget (also called widget), which is a set of UI components that display important information or operations specific to an application. It provides users with direct access to a desired application service, without the need to open the application first. - -A widget usually appears as a part of the UI of another application (which currently can only be a system application) and provides basic interactive features such as opening a UI page or sending a message. - -Before you get started, it would be helpful if you have a basic understanding of the following concepts: - -- Widget host: an application that displays the widget content and controls the widget location. - -- Widget Manager: a resident agent that provides widget management features such as periodic widget updates. - -- Widget provider: an atomic service that provides the widget content to display and controls how widget components are laid out and how they interact with users. - - -## Working Principles - -Figure 1 shows the working principles of the widget framework. - -**Figure 1** Widget framework working principles in the stage model -![form-extension](figures/form-extension.png) - -The widget host consists of the following modules: - -- Widget usage: provides operations such as creating, deleting, or updating a widget. - -- Communication adapter: provided by the OpenHarmony SDK for communication with the Widget Manager. It sends widget-related operations to the Widget Manager. - -The Widget Manager consists of the following modules: - -- Periodic updater: starts a scheduled task based on the update policy to periodically update a widget after it is added to the Widget Manager. - -- Cache manager: caches view information of a widget after it is added to the Widget Manager to directly return the cached data when the widget is obtained next time. This reduces the latency greatly. - -- Lifecycle manager: suspends update when a widget is switched to the background or is blocked, and updates and/or clears widget data during upgrade and deletion. - -- Object manager: manages RPC objects of the widget host. It is used to verify requests from the widget host and process callbacks after the widget update. - -- Communication adapter: communicates with the widget host and provider through RPCs. - -The widget provider consists of the following modules: - -- Widget service: implemented by the widget provider developer to process requests on widget creation, update, and deletion, and to provide corresponding widget services. - -- Instance manager: implemented by the widget provider developer for persistent management of widget instances allocated by the Widget Manager. - -- Communication adapter: provided by the OpenHarmony SDK for communication with the Widget Manager. It pushes update data to the Widget Manager. - -> **NOTE** -> -> You only need to develop the widget provider. The system automatically handles the work of the widget host and Widget Manager. - - -## Available APIs - -The **FormExtensionAbility** class has the following APIs. For details, see [FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md). - -| API| Description| -| -------- | -------- | -| onAddForm(want: Want): formBindingData.FormBindingData | Called to notify the widget provider that a widget has been created.| -| onCastToNormalForm(formId: string): void | Called to notify the widget provider that a temporary widget has been converted to a normal one.| -| onUpdateForm(formId: string): void | Called to notify the widget provider that a widget has been updated.| -| onChangeFormVisibility(newStatus: { [key: string]: number }): void | Called to notify the widget provider of the change in widget visibility.| -| onFormEvent(formId: string, message: string): void | Called to instruct the widget provider to receive and process a widget event.| -| onRemoveForm(formId: string): void| Called to notify the widget provider that a widget has been destroyed.| -| onConfigurationUpdate(config: Configuration): void | Called when the configuration of the environment where the widget is running is updated.| -| onShareForm?(formId: string): { [key: string]: any }| Called by the widget provider to receive shared widget data.| - -The **FormExtensionAbility** class also has a member context, that is, the FormExtensionContext class. For details, see [FormExtensionContext](../reference/apis/js-apis-inner-application-formExtensionContext.md). - -| API| Description| -| -------- | -------- | -| startAbility(want: Want, callback: AsyncCallback<void>): void | Starts UIAbility of the application to which a widget belongs. This API uses an asynchronous callback to return the result. (This is a system API and cannot be called by third-party applications. You must apply for the permission to use the API.)| -| startAbility(want: Want): Promise<void> | Starts UIAbility of the application to which a widget belongs. This API uses a promise to return the result. (This is a system API and cannot be called by third-party applications. You must apply for the permission to use the API.)| - -The **FormProvider** class has the following APIs. For details, see [FormProvider](../reference/apis/js-apis-app-form-formProvider.md). - -| API| Description| -| -------- | -------- | -| setFormNextRefreshTime(formId: string, minute: number, callback: AsyncCallback<void>): void; | Sets the next refresh time for a widget. This API uses an asynchronous callback to return the result.| -| setFormNextRefreshTime(formId: string, minute: number): Promise<void>; | Sets the next refresh time for a widget. This API uses a promise to return the result.| -| updateForm(formId: string, formBindingData: FormBindingData, callback: AsyncCallback<void>): void; | Updates a widget. This API uses an asynchronous callback to return the result.| -| updateForm(formId: string, formBindingData: FormBindingData): Promise<void>;| Updates a widget. This API uses a promise to return the result.| - -The **FormBindingData** class has the following APIs. For details, see [FormBindingData](../reference/apis/js-apis-app-form-formBindingData.md). - -| API| Description| -| -------- | -------- | -| createFormBindingData(obj?: Object \ string): FormBindingData| | Creates a **FormBindingData** object.| - - -## How to Develop - -The widget provider development based on the [stage model](stage-model-development-overview.md) involves the following key steps: - -- [Creating a FormExtensionAbility Instance](#creating-a-formextensionability-instance): Develop the lifecycle callback functions of FormExtensionAbility. - -- [Configuring the Widget Configuration File](#configuring-the-widget-configuration-file): Configure the application configuration file **module.json5** and profile configuration file. - -- [Persistently Storing Widget Data](#persistently-storing-widget-data): Perform persistent management on widget information. - -- [Updating Widget Data](#updating-widget-data): Call **updateForm()** to update the information displayed on a widget. - -- [Developing the Widget UI Page](#developing-the-widget-ui-page): Use HML+CSS+JSON to develop a JS widget UI page. - -- [Developing Widget Events](#developing-widget-events): Add the router and message events for a widget. - - -### Creating a FormExtensionAbility Instance - -To create a widget in the stage model, implement the lifecycle callbacks of **FormExtensionAbility**. Generate a widget template by referring to [Developing a Service Widget](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-development-service-widget-0000001263280425). - -1. Import related modules to **EntryFormAbility.ts**. - - ```ts - import FormExtension from '@ohos.app.form.FormExtensionAbility'; - import formBindingData from '@ohos.app.form.formBindingData'; - import formInfo from '@ohos.app.form.formInfo'; - import formProvider from '@ohos.app.form.formProvider'; - import dataStorage from '@ohos.data.storage'; - ``` - -2. Implement the FormExtension lifecycle callbacks in **EntryFormAbility.ts**. - - ```ts - export default class EntryFormAbility extends FormExtension { - onAddForm(want) { - console.info('[EntryFormAbility] onAddForm'); - // Called when the widget is created. The widget provider should return the widget data binding class. - let obj = { - "title": "titleOnCreate", - "detail": "detailOnCreate" - }; - let formData = formBindingData.createFormBindingData(obj); - return formData; - } - onCastToNormalForm(formId) { - // Called when the widget host converts the temporary widget into a normal one. The widget provider should do something to respond to the conversion. - console.info('[EntryFormAbility] onCastToNormalForm'); - } - onUpdateForm(formId) { - // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. - console.info('[EntryFormAbility] onUpdateForm'); - let obj = { - "title": "titleOnUpdate", - "detail": "detailOnUpdate" - }; - let formData = formBindingData.createFormBindingData(obj); - formProvider.updateForm(formId, formData).catch((error) => { - console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error)); - }); - } - onChangeFormVisibility(newStatus) { - // Called when the widget host initiates an event about visibility changes. The widget provider should do something to respond to the notification. This callback takes effect only for system applications. - console.info('[EntryFormAbility] onChangeFormVisibility'); - } - onFormEvent(formId, message) { - // If the widget supports event triggering, override this method and implement the trigger. - console.info('[EntryFormAbility] onFormEvent'); - } - onRemoveForm(formId) { - // Delete widget data. - console.info('[EntryFormAbility] onRemoveForm'); - } - onConfigurationUpdate(config) { - console.info('[EntryFormAbility] nConfigurationUpdate, config:' + JSON.stringify(config)); - } - onAcquireFormState(want) { - return formInfo.FormState.READY; - } - } - ``` - -> **NOTE** -> -> FormExtensionAbility cannot reside in the background. Therefore, continuous tasks cannot be processed in the widget lifecycle callbacks. - -### Configuring the Widget Configuration File - -1. Configure ExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information. - Example configuration: - - - ```json - { - "module": { - // ... - "extensionAbilities": [ - { - "name": "EntryFormAbility", - "srcEntrance": "./ets/entryformability/EntryFormAbility.ts", - "label": "$string:EntryFormAbility_label", - "description": "$string:EntryFormAbility_desc", - "type": "form", - "metadata": [ - { - "name": "ohos.extension.form", - "resource": "$profile:form_config" - } - ] - } - ] - } - } - ``` - -2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if resource is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal field structure. - **Table 1** Widget profile configuration file - - | Field| Description| Data Type| Initial Value Allowed| - | -------- | -------- | -------- | -------- | - | name | Class name of a widget. The value is a string with a maximum of 127 bytes.| String| No| - | description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes.| String| Yes (initial value: left empty)| - | src | Full path of the UI code corresponding to the widget.| String| No| - | window | Window-related configurations.| Object| Yes| - | isDefault | Whether the widget is a default one. Each ability has only one default widget.
**true**: The widget is the default one.
**false**: The widget is not the default one.| Boolean| No| - | colorMode | Color mode of the widget.
**auto**: The widget adopts the auto-adaptive color mode.
**dark**: The widget adopts the dark color mode.
**light**: The widget adopts the light color mode.| String| Yes (initial value: **auto**)| - | supportDimensions | Grid styles supported by the widget.
**1 * 2**: indicates a grid with one row and two columns.
**2 * 2**: indicates a grid with two rows and two columns.
**2 * 4**: indicates a grid with two rows and four columns.
**4 * 4**: indicates a grid with four rows and four columns.| String array| No| - | defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget.| String| No| - | updateEnabled | Whether the widget can be updated periodically.
**true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.
**false**: The widget cannot be updated periodically.| Boolean| No| - | scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| String| Yes (initial value: **0:0**)| - | updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.
If the value is **0**, this field does not take effect.
If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.
**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used.| Number| Yes (initial value: **0**)| - | formConfigAbility | Link to a specific page of the application. The value is a URI.| String| Yes (initial value: left empty)| - | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification.| String| Yes (initial value: left empty)| - | metaData | Metadata of the widget. This field contains the array of the **customizeData** field.| Object| Yes (initial value: left empty)| - - Example configuration: - - ```json - { - "forms": [ - { - "name": "widget", - "description": "This is a widget.", - "src": "./js/widget/pages/index/index", - "window": { - "designWidth": 720, - "autoDesignWidth": true - }, - "colorMode": "auto", - "isDefault": true, - "updateEnabled": true, - "scheduledUpdateTime": "10:30", - "updateDuration": 1, - "defaultDimension": "2*2", - "supportDimensions": [ - "2*2" - ] - } - ] - } - ``` - - -### Persistently Storing Widget Data - -A widget provider is usually started when it is needed to provide information about a widget. The Widget Manager supports multi-instance management and uses the widget ID to identify an instance. If the widget provider supports widget data modification, it must persistently store the data based on the widget ID, so that it can access the data of the target widget when obtaining, updating, or starting a widget. - - -```ts -const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store"; -async function storeFormInfo(formId: string, formName: string, tempFlag: boolean) { - // Only the widget ID (formId), widget name (formName), and whether the widget is a temporary one (tempFlag) are persistently stored. - let formInfo = { - "formName": formName, - "tempFlag": tempFlag, - "updateCount": 0 - }; - try { - const storage = await dataStorage.getStorage(DATA_STORAGE_PATH); - // Put the widget information. - await storage.put(formId, JSON.stringify(formInfo)); - console.info(`[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`); - await storage.flush(); - } catch (err) { - console.error(`[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err)}`); - } -} - -export default class EntryFormAbility extends FormExtension { - // ... - onAddForm(want) { - console.info('[EntryFormAbility] onAddForm'); - - let formId = want.parameters["ohos.extra.param.key.form_identity"]; - let formName = want.parameters["ohos.extra.param.key.form_name"]; - let tempFlag = want.parameters["ohos.extra.param.key.form_temporary"]; - // Persistently store widget data for subsequent use, such as instance acquisition and update. - // Implement this API based on project requirements. - storeFormInfo(formId, formName, tempFlag); - - let obj = { - "title": "titleOnCreate", - "detail": "detailOnCreate" - }; - let formData = formBindingData.createFormBindingData(obj); - return formData; - } -} -``` - -You should override **onRemoveForm** to implement widget data deletion. - - -```ts -const DATA_STORAGE_PATH = "/data/storage/el2/base/haps/form_store"; -async function deleteFormInfo(formId: string) { - try { - const storage = await dataStorage.getStorage(DATA_STORAGE_PATH); - // Delete the widget information. - await storage.delete(formId); - console.info(`[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`); - await storage.flush(); - } catch (err) { - console.error(`[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err)}`); - } -} - -// ... - -export default class EntryFormAbility extends FormExtension { - // ... - onRemoveForm(formId) { - console.info('[EntryFormAbility] onRemoveForm'); - // Delete the persistent widget instance data. - // Implement this API based on project requirements. - deleteFormInfo(formId); - } -} -``` - -For details about how to implement persistent data storage, see [Lightweight Data Store Development](../database/database-preference-guidelines.md). - -The **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary. - -- Normal widget: a widget persistently used by the widget host - -- Temporary widget: a widget temporarily used by the widget host - -Data of a temporary widget will be deleted on the Widget Manager if the widget framework is killed and restarted. The widget provider, however, is not notified of the deletion and still keeps the data. Therefore, the widget provider needs to clear the data of temporary widgets proactively if the data has been kept for a long period of time. If the widget host has converted a temporary widget into a normal one, the widget provider should change the widget data from temporary storage to persistent storage. Otherwise, the widget data may be deleted by mistake. - - -### Updating Widget Data - -When an application initiates a scheduled or periodic update, the application obtains the latest data and calls **updateForm()** to update the widget. - - -```ts -onUpdateForm(formId) { - // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host. - console.info('[EntryFormAbility] onUpdateForm'); - let obj = { - "title": "titleOnUpdate", - "detail": "detailOnUpdate" - }; - let formData = formBindingData.createFormBindingData(obj); - // Call the updateForm() method to update the widget. Only the data passed through the input parameter is updated. Other information remains unchanged. - formProvider.updateForm(formId, formData).catch((error) => { - console.info('[EntryFormAbility] updateForm, error:' + JSON.stringify(error)); - }); -} -``` - - -### Developing the Widget UI Page - -You can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. This section describes how to develop a page shown below. - -![widget-development-stage](figures/widget-development-stage.png) - -> **NOTE** -> -> Only the JavaScript-based web-like development paradigm is supported when developing the widget UI. - -- HML: uses web-like paradigm components to describe the widget page information. - - ```html -
- -
- -
-
- {{title}} - {{detail}} -
-
-
- ``` - -- CSS: defines style information about the web-like paradigm components in HML. - - ```css - .container { - flex-direction: column; - justify-content: center; - align-items: center; - } - - .bg-img { - flex-shrink: 0; - height: 100%; - } - - .container-inner { - flex-direction: column; - justify-content: flex-end; - align-items: flex-start; - height: 100%; - width: 100%; - padding: 12px; - } - - .title { - font-size: 19px; - font-weight: bold; - color: white; - text-overflow: ellipsis; - max-lines: 1; - } - - .detail_text { - font-size: 16px; - color: white; - opacity: 0.66; - text-overflow: ellipsis; - max-lines: 1; - margin-top: 6px; - } - ``` - -- JSON: defines data and event interaction on the widget UI page. - - ```json - { - "data": { - "title": "TitleDefault", - "detail": "TextDefault" - }, - "actions": { - "routerEvent": { - "action": "router", - "abilityName": "EntryAbility", - "params": { - "message": "add detail" - } - } - } - } - ``` - - -### Developing Widget Events - -You can set router and message events for components on a widget. The router event applies to ability redirection, and the message event applies to custom click events. - -The key steps are as follows: - -1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file. - -2. Set the router event. - - **action**: **"router"**, which indicates a router event. - - **abilityName**: name of the ability to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default UIAbility name of the stage model created by DevEco Studio is EntryAbility. - - **params**: custom parameters passed to the target ability. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target ability. For example, in the lifecycle function **onCreate** of the main ability in the stage model, you can obtain **want** and its **parameters** field. - -3. Set the message event. - - **action**: **"message"**, which indicates a message event. - - **params**: custom parameters of the message event. Set them as required. The value can be obtained from **message** in the widget lifecycle function **onFormEvent()**. - -The following is an example: - -- HML file: - - ```html -
- -
- -
-
- {{title}} - {{detail}} -
-
-
- ``` - -- CSS file: - - ```css - .container { - flex-direction: column; - justify-content: center; - align-items: center; - } - - .bg-img { - flex-shrink: 0; - height: 100%; - } - - .container-inner { - flex-direction: column; - justify-content: flex-end; - align-items: flex-start; - height: 100%; - width: 100%; - padding: 12px; - } - - .title { - font-size: 19px; - font-weight: bold; - color: white; - text-overflow: ellipsis; - max-lines: 1; - } - - .detail_text { - font-size: 16px; - color: white; - opacity: 0.66; - text-overflow: ellipsis; - max-lines: 1; - margin-top: 6px; - } - ``` - -- JSON file: - - ```json - { - "data": { - "title": "TitleDefault", - "detail": "TextDefault" - }, - "actions": { - "routerEvent": { - "action": "router", - "abilityName": "EntryAbility", - "params": { - "info": "router info", - "message": "router message" - } - }, - "messageEvent": { - "action": "message", - "params": { - "detail": "message detail" - } - } - } - } - ``` - -- Receive the router event and obtain parameters in UIAbility. - - ```ts - import UIAbility from '@ohos.app.ability.UIAbility' - - export default class EntryAbility extends UIAbility { - onCreate(want, launchParam) { - // Obtain the info parameter passed in the router event. - if (want.parameters.info === "router info") { - // Do something. - // console.log("router info:" + want.parameters.info) - } - // Obtain the message parameter passed in the router event. - if (want.parameters.message === "router message") { - // Do something. - // console.log("router message:" + want.parameters.message) - } - } - // ... - }; - ``` - -- Receive the message event in FormExtensionAbility and obtain parameters. - - ```ts - import FormExtension from '@ohos.app.form.FormExtensionAbility'; - - export default class FormAbility extends FormExtension { - // ... - onFormEvent(formId, message) { - // Obtain the detail parameter passed in the message event. - let msg = JSON.parse(message) - if (msg.params.detail === "message detail") { - // Do something. - // console.log("message info:" + msg.params.detail) - } - } - // ... - }; - ``` - diff --git a/en/application-dev/application-models/widget-switch.md b/en/application-dev/application-models/widget-switch.md index 8d9823385a8a05f71c742327dc966054427a6718..2f8a0a41b2e1b20824a19d08c98f67e39cd05df6 100644 --- a/en/application-dev/application-models/widget-switch.md +++ b/en/application-dev/application-models/widget-switch.md @@ -11,7 +11,7 @@ Widget switching involves the following parts: | Configuration Item | FA Model | Stage Model | | ---------------- | ------------------------------------------- | ------------------------------------------------------------ | | Configuration item location | **formAbility** and **forms** are in the **config.json** file.| **extensionAbilities** (configuration for **formExtensionAbility**) is in the **module.json5** file in the level-1 directory, and **forms** (configuration for **forms** contained in **formExtensionAbility**) is in the **form_config.json** file in the level-2 directory.| -| Widget code path | Specified by **srcPath**, without the file name. | Specified by **srcEntrance**, with the file name. | +| Widget code path | Specified by **srcPath**, without the file name. | Specified by **srcEntry**, with the file name. | | Programming language | **srcLanguage** can be set to **js** or **ets**. | This configuration item is unavailable. Only ets is supported. | | Whether to enable widgets | formsEnabled | This configuration item is unavailable. The setting of **type** set to **form** means that the widgets are enabled. | | Ability type | type: service | type: form | @@ -32,7 +32,7 @@ Figure 2 Widget configuration differences | Item| FA Model| Stage Model| | -------- | -------- | -------- | -| Entry file| **form.ts** in the directory pointed to by **srcPath**| File pointed to by **srcEntrance**| +| Entry file| **form.ts** in the directory pointed to by **srcPath**| File pointed to by **srcEntry**| | Lifecycle| export default| import FormExtension from '\@ohos.app.form.FormExtensionAbility';
export default class FormAbility extends FormExtension| diff --git a/en/application-dev/application-models/windowextensionability.md b/en/application-dev/application-models/windowextensionability.md index bdbcb65ffca3aa635146cdb8dfa97763ce99d652..3f07cb8e46f1289c70144981e5250b165d84cf7d 100644 --- a/en/application-dev/application-models/windowextensionability.md +++ b/en/application-dev/application-models/windowextensionability.md @@ -1,4 +1,5 @@ -# WindowExtensionAbility +# WindowExtensionAbility (for System Applications Only) + [WindowExtensionAbility](../reference/apis/js-apis-application-windowExtensionAbility.md) is a type of ExtensionAbility component that allows a system application to be embedded in and displayed over another application. @@ -6,15 +7,14 @@ The WindowExtensionAbility component must be used together with the [AbilityComponent](../reference/arkui-ts/ts-container-ability-component.md) to process services of the started application. WindowExtensionAbility is run in connection mode. A system application must use the AbilityComponent to start the WindowExtensionAbility component. Each ExtensionAbility has its own context. For WindowExtensionAbility, -the context is [WindowExtensionContext](../reference/apis/js-apis-inner-application-windowExtensionContext.md). +the context is [WindowExtensionContext](../reference/apis/js-apis-inner-application-windowExtensionContext.md). > **NOTE** > > **WindowExtensionAbility** is a system API. To embed a third-party application in another application and display it over the application, switch to the full SDK by following the instructions provided in [Guide to Switching to Full SDK](../../application-dev/quick-start/full-sdk-switch-guide.md). -> -## Setting an Embedded Ability (for System Applications Only) +## Setting an Embedded UIAbility The **WindowExtensionAbility** class provides **onConnect()**, **onDisconnect()**, and **onWindowReady()** lifecycle callbacks, which can be overridden. @@ -42,7 +42,7 @@ To implement an embedded application, manually create a WindowExtensionAbility i onWindowReady(window) { window.loadContent('WindowExtAbility/pages/index1').then(() => { window.getProperties().then((pro) => { - console.log("WindowExtension " + JSON.stringify(pro)); + console.info("WindowExtension " + JSON.stringify(pro)); }) window.show(); }) @@ -58,7 +58,7 @@ To implement an embedded application, manually create a WindowExtensionAbility i } ``` -4. Register the WindowExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"window"** and **srcEntrance** to the code path of the ExtensionAbility component. +4. Register the WindowExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"window"** and **srcEntry** to the code path of the ExtensionAbility component. ```json { @@ -66,11 +66,11 @@ To implement an embedded application, manually create a WindowExtensionAbility i "extensionAbilities": [ { "name": "WindowExtAbility", - "srcEntrance": "./ets/WindowExtAbility/WindowExtAbility.ts", + "srcEntry": "./ets/WindowExtAbility/WindowExtAbility.ts", "icon": "$media:icon", "description": "WindowExtension", "type": "window", - "visible": true, + "exported": true, } ], } @@ -78,7 +78,7 @@ To implement an embedded application, manually create a WindowExtensionAbility i ``` -## Starting an Embedded Ability (for System Applications Only) +## Starting an Embedded UIAbility System applications can load the created WindowExtensionAbility through the AbilityComponent. @@ -109,4 +109,5 @@ System applications can load the created WindowExtensionAbility through the Abil .backgroundColor(0x64BB5c) } } - ``` \ No newline at end of file + ``` + diff --git a/en/application-dev/application-test/arkxtest-guidelines.md b/en/application-dev/application-test/arkxtest-guidelines.md index 5f4f36e78850272bc5101fa1b86e76f6e91d5f37..cccf0b6049941195a787cba716946299c0d3f4ae 100644 --- a/en/application-dev/application-test/arkxtest-guidelines.md +++ b/en/application-dev/application-test/arkxtest-guidelines.md @@ -13,7 +13,7 @@ In this document you will learn about the key functions of arkXtest and how to u 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. -### Principles +### Implementation arkXtest is divided into two parts: unit test framework and UI test framework. @@ -27,28 +27,28 @@ arkXtest is divided into two parts: unit test framework and UI test framework. ![](figures/TestFlow.PNG) -- UI Testing Framework +- UI Test Framework - The UI test framework provides [UiTest APIs](../reference/apis/js-apis-uitest.md) for you to call in different test scenarios. The test scripts are executed on top of the unit test framework mentioned above. + 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. The figure below shows the main functions of the UI test framework. ![](figures/Uitest.PNG) -### Limitations and Constraints +### Constraints - The features of the UI test framework are available only in OpenHarmony 3.1 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). -## Environment preparations +## Preparing the Environment ### Environment Requirements -Software for writing test scripts: DevEco Studio 3.0 or later +Software: DevEco Studio 3.0 or later -Hardware for running test scripts: PC connected to a OpenHarmony device, such as the RK3568 development board +Hardware: PC connected to an OpenHarmony device, such as the RK3568 development board ### Setting Up the Environment @@ -57,7 +57,7 @@ Hardware for running test scripts: PC connected to a OpenHarmony device, such as ## Creating a Test Script -1. Open DevEco Studio and create a project, where the **ohos** directory is where the test script is located. +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 @@ -95,7 +95,7 @@ export default function abilityTest() { The unit test script must contain the following basic elements: -1. Import of the dependency package so that the dependent test APIs can be used. +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. @@ -105,7 +105,7 @@ The unit test script must contain the following basic elements: 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, add the dependency package, as shown below: +In this example, the UI test script is written based on the preceding unit test script. First, import the dependency, as shown below: ```js import {Driver,ON,Component,MatchPattern} from '@ohos.uitest' @@ -180,11 +180,11 @@ The logs added to the test case are displayed after the test case execution, rat **Possible Causes** -More than one asynchronous interface is called in the test case.
In principle, logs in the test case are printed before the test case execution is complete. +More than one asynchronous API is called in the test case.
In principle, logs in the test case are printed before the test case execution is complete. - **Solution** +**Solution** -If more than one asynchronous interface is called, you are advised to encapsulate the interface invoking into the promise mode +If more than one asynchronous API is called, you are advised to encapsulate the API invoking into the promise mode. #### Error "fail to start ability" is reported during test case execution @@ -227,7 +227,7 @@ The UI test case fails to be executed. The HiLog file contains the error message **Possible Causes** -The ArkUI feature is disabled. As a result, the control tree information on the test page is not generated. +The ArkUI feature is disabled. As a result, the component tree information is not generated on the test page. **Solution** @@ -237,33 +237,33 @@ Run the following command, restart the device, and execute the test case again: hdc shell param set persist.ace.testmode.enabled 1 ``` -#### The failure log contains "uitest-api dose not allow calling concurrently" +#### The failure log contains "uitest-api does not allow calling concurrently" **Problem** -The UI test case fails to be executed. The HiLog file contains the error message "uitest-api dose 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** -1. In the test case, the **await** operator is not added to the asynchronous interface provided by the UI test framework. +1. In the test case, the **await** operator is not added to the asynchronous API provided by the UI test framework. 2. The UI test case is executed in multiple processes. As a result, multiple UI test processes are started. The framework does not support multi-process invoking. **Solution** -1. Check the case implementation and add the **await** operator to the asynchronous interface invoking. +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 "dose not exist on current UI! Check if the UI has changed after you got the widget object" +#### The failure log contains "does not exist on current UI! Check if the UI has changed after you got the widget object" **Problem** -The UI test case fails to be executed. The HiLog file contains the error message "dose not exist on current UI! Check if the UI has changed after you got the widget object". +The UI test case fails to be executed. The HiLog file contains the error message "does not exist on current UI! Check if the UI has changed after you got the widget object." **Possible Causes** -After the target component is found in the code of the test case, the device UI changes. As a result, the found component is lost and the emulation operation cannot be performed. +After the target component is found in the code of the test case, the device UI changes, resulting in loss of the found component and inability to perform emulation. **Solution** diff --git a/en/application-dev/connectivity/http-request.md b/en/application-dev/connectivity/http-request.md index 010e37d468a672b2e48a55b60ca8ee8eb905ce1d..39ed52f818f0d904099bf13a581b58a84d61adae 100644 --- a/en/application-dev/connectivity/http-request.md +++ b/en/application-dev/connectivity/http-request.md @@ -23,7 +23,7 @@ The following table provides only a simple description of the related APIs. For | off(type: 'headersReceive') | Unregisters the observer for HTTP Response Header events.| | once\('headersReceive'\)8+ | Registers a one-time observer for HTTP Response Header events.| -## How to Develop +## How to Develop request APIs 1. Import the **http** namespace from **@ohos.net.http.d.ts**. 2. Call **createHttp()** to create an **HttpRequest** object. @@ -42,42 +42,44 @@ let httpRequest = http.createHttp(); // This API is used to listen for the HTTP Response Header event, which is returned earlier than the result of the HTTP request. It is up to you whether to listen for HTTP Response Header events. // on('headerReceive', AsyncCallback) is replaced by on('headersReceive', Callback) since API version 8. httpRequest.on('headersReceive', (header) => { - console.info('header: ' + JSON.stringify(header)); + console.info('header: ' + JSON.stringify(header)); }); httpRequest.request( - // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. - "EXAMPLE_URL", - { - method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET. - // You can add header fields based on service requirements. - header: { - 'Content-Type': 'application/json' - }, - // This field is used to transfer data when the POST request is used. - extraData: { - "data": "data to send", - }, - expectDataType: http.HttpDataType.STRING, // Optional. This field specifies the type of the return data. - usingCache: true, // Optional. The default value is true. - priority: 1, // Optional. The default value is 1. - connectTimeout: 60000 // Optional. The default value is 60000, in ms. - readTimeout: 60000, // Optional. The default value is 60000, in ms. - usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system. - }, (err, data) => { - if (!err) { - // data.result carries the HTTP response. Parse the response based on service requirements. - console.info('Result:' + JSON.stringify(data.result)); - console.info('code:' + JSON.stringify(data.responseCode)); - // data.header carries the HTTP response header. Parse the content based on service requirements. - console.info('header:' + JSON.stringify(data.header)); - console.info('cookies:' + JSON.stringify(data.cookies)); // 8+ - } else { - console.info('error:' + JSON.stringify(err)); - // Unsubscribe from HTTP Response Header events. - httpRequest.off('headersReceive'); - // Call the destroy() method to release resources after HttpRequest is complete. - httpRequest.destroy(); - } + // Customize EXAMPLE_URL in extraData on your own. It is up to you whether to add parameters to the URL. + { + method: http.RequestMethod.POST, // Optional. The default value is http.RequestMethod.GET. + // You can add header fields based on service requirements. + header: { + 'Content-Type': 'application/json' + }, + // This field is used to transfer data when the POST request is used. + extraData: { + "data": "data to send", + }, + expectDataType: http.HttpDataType.STRING, // Optional. This field specifies the type of the return data. + usingCache: true, // Optional. The default value is true. + priority: 1, // Optional. The default value is 1. + connectTimeout: 60000 // Optional. The default value is 60000, in ms. + readTimeout: 60000, // Optional. The default value is 60000, in ms. + usingProtocol: http.HttpProtocol.HTTP1_1, // Optional. The default protocol type is automatically specified by the system. + }, (err, data) => { + if (!err) { + // data.result carries the HTTP response. Parse the response based on service requirements. + console.info('Result:' + JSON.stringify(data.result)); + console.info('code:' + JSON.stringify(data.responseCode)); + // data.header carries the HTTP response header. Parse the content based on service requirements. + console.info('header:' + JSON.stringify(data.header)); + console.info('cookies:' + JSON.stringify(data.cookies)); // 8+ + // Call the destroy() method to release resources after the HttpRequest is complete. + httpRequest.destroy(); + } else { + console.info('error:' + JSON.stringify(err)); + // Unsubscribe from HTTP Response Header events. + httpRequest.off('headersReceive'); + // Call the destroy() method to release resources after HttpRequest is complete. + httpRequest.destroy(); } + } ); ``` + diff --git a/en/application-dev/connectivity/net-sharing.md b/en/application-dev/connectivity/net-sharing.md index d5bc9cf2f8817723f0f23d666c45997a6735f706..1fb8a1dc42a24702680e285ea221fabbca54be05 100644 --- a/en/application-dev/connectivity/net-sharing.md +++ b/en/application-dev/connectivity/net-sharing.md @@ -1,29 +1,36 @@ # Network Sharing ## Introduction + The Network Sharing module allows you to share your device's Internet connection with other connected devices by means of Wi-Fi hotspot, Bluetooth, and USB sharing. It also allows you to query the network sharing state and shared mobile data volume. -> **NOTE** +> **Note:** > To maximize the application running efficiency, most API calls are called asynchronously in callback or promise mode. The following code examples use the callback mode. For details about the APIs, see [sms API Reference](../reference/apis/js-apis-net-sharing.md). ## Basic Concepts -- Wi-Fi sharing: Shares the network through a Wi-Fi hotspot. -- Bluetooth sharing: Shares the network through Bluetooth. -- USB tethering: Shares the network using a USB flash drive. + +- Wi-Fi sharing: Shares the network through a Wi-Fi hotspot. +- Bluetooth sharing: Shares the network through Bluetooth. +- USB tethering: Shares the network using a USB flash drive. ## **Constraints** -- Programming language: C++ and JS -- System: Linux kernel -- The initial APIs of this module are supported since API version 9. Newly added APIs will be marked with a superscript to indicate their earliest API version. + +- Programming language: C++ and JS +- System: Linux kernel +- The initial APIs of this module are supported since API version 9. Newly added APIs will be marked with a superscript to indicate their earliest API version. ## When to Use + Typical network sharing scenarios are as follows: -- Enabling network sharing -- Disabling network sharing -- Obtaining the data traffic of the shared network + +- Enabling Network Sharing +- Disabling network sharing +- Obtaining the data traffic of the shared network The following describes the development procedure specific to each application scenario. + ## Available APIs + For the complete list of APIs and example code, see [Network Sharing](../reference/apis/js-apis-net-sharing.md). | Type| API| Description| @@ -53,19 +60,19 @@ For the complete list of APIs and example code, see [Network Sharing](../referen 4. Return the callback for successfully starting network sharing. ```js - // Import the sharing namespace from @ohos.net.sharing. - import sharing from '@ohos.net.sharing' - - // Subscribe to network sharing state changes. - sharing.on('sharingStateChange', (error, data) => { - console.log(JSON.stringify(error)); - console.log(JSON.stringify(data)); - }); - - // Call startSharing to start network sharing of the specified type. - sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { - console.log(JSON.stringify(error)); - }); +// Import the sharing namespace from @ohos.net.sharing. +import sharing from '@ohos.net.sharing' + +// Subscribe to network sharing state changes. +sharing.on('sharingStateChange', (error, data) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); +}); + +// Call startSharing to start network sharing of the specified type. +sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { + console.log(JSON.stringify(error)); +}); ``` ## Disabling network sharing @@ -78,19 +85,19 @@ For the complete list of APIs and example code, see [Network Sharing](../referen 4. Return the callback for successfully stopping network sharing. ```js - // Import the sharing namespace from @ohos.net.sharing. - import sharing from '@ohos.net.sharing' - - // Subscribe to network sharing state changes. - sharing.on('sharingStateChange', (error, data) => { - console.log(JSON.stringify(error)); - console.log(JSON.stringify(data)); - }); - - // Call stopSharing to stop network sharing of the specified type. - sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { - console.log(JSON.stringify(error)); - }); +// Import the sharing namespace from @ohos.net.sharing. +import sharing from '@ohos.net.sharing' + +// Subscribe to network sharing state changes. +sharing.on('sharingStateChange', (error, data) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); +}); + +// Call stopSharing to stop network sharing of the specified type. +sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { + console.log(JSON.stringify(error)); +}); ``` ## Obtaining the data traffic of the shared network @@ -103,28 +110,28 @@ For the complete list of APIs and example code, see [Network Sharing](../referen 4. Call **stopSharing** to stop network sharing of the specified type and clear the data volume of network sharing. ```js - // Import the sharing namespace from @ohos.net.sharing. - import sharing from '@ohos.net.sharing' - - // Call startSharing to start network sharing of the specified type. - sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { - console.log(JSON.stringify(error)); - }); - - // Call getStatsTotalBytes to obtain the data traffic generated during data sharing. - sharing.getStatsTotalBytes((error, data) => { - console.log(JSON.stringify(error)); - console.log(JSON.stringify(data)); - }); - - // Call stopSharing to stop network sharing of the specified type and clear the data volume of network sharing. - sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { - console.log(JSON.stringify(error)); - }); - - // Call getStatsTotalBytes again. The data volume of network sharing has been cleared. - sharing.getStatsTotalBytes((error, data) => { - console.log(JSON.stringify(error)); - console.log(JSON.stringify(data)); - }); +// Import the sharing namespace from @ohos.net.sharing. +import sharing from '@ohos.net.sharing' + +// Call startSharing to start network sharing of the specified type. +sharing.startSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { + console.log(JSON.stringify(error)); +}); + +// Call getStatsTotalBytes to obtain the data traffic generated during data sharing. +sharing.getStatsTotalBytes((error, data) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); +}); + +// Call stopSharing to stop network sharing of the specified type and clear the data volume of network sharing. +sharing.stopSharing(sharing.SharingIfaceType.SHARING_WIFI, (error) => { + console.log(JSON.stringify(error)); +}); + +// Call getStatsTotalBytes again. The data volume of network sharing has been cleared. +sharing.getStatsTotalBytes((error, data) => { + console.log(JSON.stringify(error)); + console.log(JSON.stringify(data)); +}); ``` diff --git a/en/application-dev/connectivity/socket-connection.md b/en/application-dev/connectivity/socket-connection.md index 5cae73b2a5c84f280aea80e299605ee80ac2553a..6f656ecccea384730223113ae87f5a1132355575 100644 --- a/en/application-dev/connectivity/socket-connection.md +++ b/en/application-dev/connectivity/socket-connection.md @@ -13,10 +13,10 @@ The Socket Connection module allows an application to transmit data over a Socke ## When to Use -Applications transmit data over TCP, UDP, or TLS Socket connections. The main application scenarios are as follows: +Applications transmit data over TCP, UDP, or TLSSocket connections. The main application scenarios are as follows: -- Implementing data transmission over TCP/UDP Socket connections -- Implementing encrypted data transmission over TLS Socket connections +- Implementing data transmission over TCP/UDPSocket connections +- Implementing encrypted data transmission over TLSSocket connections ## Available APIs @@ -40,12 +40,12 @@ Socket connection functions are mainly implemented by the **socket** module. The | off(type: 'close') | Unsubscribes from **close** events of the Socket connection.| | on(type: 'error') | Subscribes to **error** events of the Socket connection.| | off(type: 'error') | Unsubscribes from **error** events of the Socket connection.| -| on(type: 'listening') | Subscribes to **listening** events of the UDP Socket connection. | -| off(type: 'listening') | Unsubscribes from **listening** events of the UDP Socket connection. | -| on(type: 'connect') | Subscribes to **connect** events of the TCP Socket connection. | -| off(type: 'connect') | Unsubscribes from **connect** events of the TCP Socket connection.| +| on(type: 'listening') | Subscribes to **listening** events of the UDPSocket connection. | +| off(type: 'listening') | Unsubscribes from **listening** events of the UDPSocket connection. | +| on(type: 'connect') | Subscribes to **connect** events of the TCPSocket connection. | +| off(type: 'connect') | Unsubscribes from **connect** events of the TCPSocket connection.| -TLS Socket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs. +TLSSocket connection functions are mainly provided by the **tls_socket** module. The following table describes the related APIs. | API| Description| | -------- | -------- | @@ -56,28 +56,28 @@ TLS Socket connection functions are mainly provided by the **tls_socket** module | getCertificate() | Obtains an object representing the local certificate.| | getCipherSuite() | Obtains a list containing information about the negotiated cipher suite.| | getProtocol() | Obtains a string containing the SSL/TLS protocol version negotiated for the current connection.| -| getRemoteAddress() | Obtains the peer address of the TLS Socket connection.| +| getRemoteAddress() | Obtains the peer address of the TLSSocket connection.| | getRemoteCertificate() | Obtains an object representing a peer certificate.| | getSignatureAlgorithms() | Obtains a list containing signature algorithms shared between the server and client, in descending order of priority.| -| getState() | Obtains the TLS Socket connection status.| -| off(type: 'close') | Unsubscribes from **close** events of the TLS Socket connection.| -| off(type: 'error') | Unsubscribes from **error** events of the TLS Socket connection.| -| off(type: 'message') | Unsubscribes from **message** events of the TLS Socket connection.| -| on(type: 'close') | Subscribes to **close** events of the TLS Socket connection.| -| on(type: 'error') | Subscribes to **error** events of the TLS Socket connection.| -| on(type: 'message') | Subscribes to **message** events of the TLS Socket connection.| +| getState() | Obtains the TLSSocket connection status.| +| off(type: 'close') | Unsubscribes from **close** events of the TLSSocket connection.| +| off(type: 'error') | Unsubscribes from **error** events of the TLSSocket connection.| +| off(type: 'message') | Unsubscribes from **message** events of the TLSSocket connection.| +| on(type: 'close') | Subscribes to **close** events of the TLSSocket connection.| +| on(type: 'error') | Subscribes to **error** events of the TLSSocket connection.| +| on(type: 'message') | Subscribes to **message** events of the TLSSocket connection.| | send() | Sends data.| -| setExtraOptions() | Sets other properties of the TLS Socket connection.| +| setExtraOptions() | Sets other properties of the TLSSocket connection.| -## Transmitting Data over TCP/UDP Socket Connections +## Transmitting Data over TCP/UDPSocket Connections -The implementation is similar for UDP Socket and TCP Socket connections. The following uses data transmission over a TCP Socket connection as an example. +The implementation is similar for UDPSocket and TCPSocket connections. The following uses data transmission over a TCPSocket connection as an example. 1. Import the required **socket** module. 2. Create a **TCPSocket** object. -3. (Optional) Subscribe to TCP Socket connection events. +3. (Optional) Subscribe to TCPSocket connection events. 4. Bind the IP address and port number. The port number can be specified or randomly allocated by the system. @@ -85,89 +85,89 @@ The implementation is similar for UDP Socket and TCP Socket connections. The fol 6. Send data. -7. Enable the TCP Socket connection to be automatically closed after use. - - ```js - import socket from '@ohos.net.socket' - - // Create a TCPSocket object. - let tcp = socket.constructTCPSocketInstance(); - - // Subscribe to TCP Socket connection events. - tcp.on('message', value => { - console.log("on message") - let buffer = value.message - let dataView = new DataView(buffer) - let str = "" - for (let i = 0;i < dataView.byteLength; ++i) { - str += String.fromCharCode(dataView.getUint8(i)) - } - console.log("on connect received:" + str) - }); - tcp.on('connect', () => { - console.log("on connect") - }); - tcp.on('close', () => { - console.log("on close") - }); - - // Bind the local IP address and port number. - let bindAddress = { - address: '192.168.xx.xx', - port: 1234, // Bound port, for example, 1234. - family: 1 - }; - tcp.bind(bindAddress, err => { - if (err) { - console.log('bind fail'); - return; - } - console.log('bind success'); - - // Set up a connection to the specified IP address and port number. - let connectAddress = { - address: '192.168.xx.xx', - port: 5678, // Connection port, for example, 5678. - family: 1 - }; - tcp.connect({ - address: connectAddress, timeout: 6000 - }, err => { - if (err) { - console.log('connect fail'); - return; - } - console.log('connect success'); - - // Send data. - tcp.send({ - data: 'Hello, server!' - }, err => { - if (err) { - console.log('send fail'); - return; - } - console.log('send success'); - }) - }); - }); - - // Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. - setTimeout(() => { - tcp.close((err) => { - console.log('close socket.') - }); - tcp.off('message'); - tcp.off('connect'); - tcp.off('close'); - }, 30 * 1000); - ``` - -## Implementing Encrypted Data Transmission over TLS Socket Connections +7. Enable the TCPSocket connection to be automatically closed after use. + +```js +import socket from '@ohos.net.socket' + +// Create a TCPSocket object. +let tcp = socket.constructTCPSocketInstance(); + +// Subscribe to TCPSocket connection events. +tcp.on('message', value => { + console.log("on message") + let buffer = value.message + let dataView = new DataView(buffer) + let str = "" + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)) + } + console.log("on connect received:" + str) +}); +tcp.on('connect', () => { + console.log("on connect") +}); +tcp.on('close', () => { + console.log("on close") +}); + +// Bind the local IP address and port number. +let bindAddress = { + address: '192.168.xx.xx', + port: 1234, // Bound port, for example, 1234. + family: 1 +}; +tcp.bind(bindAddress, err => { + if (err) { + console.log('bind fail'); + return; + } + console.log('bind success'); + + // Set up a connection to the specified IP address and port number. + let connectAddress = { + address: '192.168.xx.xx', + port: 5678, // Connection port, for example, 5678. + family: 1 + }; + tcp.connect({ + address: connectAddress, timeout: 6000 + }, err => { + if (err) { + console.log('connect fail'); + return; + } + console.log('connect success'); + + // Send data. + tcp.send({ + data: 'Hello, server!' + }, err => { + if (err) { + console.log('send fail'); + return; + } + console.log('send success'); + }) + }); +}); + +// Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events. +setTimeout(() => { + tcp.close((err) => { + console.log('close socket.') + }); + tcp.off('message'); + tcp.off('connect'); + tcp.off('close'); +}, 30 * 1000); +``` + +## Implementing encrypted data transmission over TLSSocket connections ### How to Develop -TLS Socket connection process on the client: +TLSSocket connection process on the client: 1. Import the required **socket** module. @@ -177,145 +177,145 @@ TLS Socket connection process on the client: 4. Create a **TLSSocket** object. -5. (Optional) Subscribe to TLS Socket connection events. +5. (Optional) Subscribe to TLSSocket connection events. 6. Send data. -7. Enable the TLS Socket connection to be automatically closed after use. +7. Enable the TLSSocket connection to be automatically closed after use. ```js - import socket from '@ohos.net.socket' - - // Create a TLS Socket connection (for two-way authentication). - let tlsTwoWay = socket.constructTLSSocketInstance(); - - // Subscribe to TLS Socket connection events. - tlsTwoWay.on('message', value => { - console.log("on message") - let buffer = value.message - let dataView = new DataView(buffer) - let str = "" - for (let i = 0; i < dataView.byteLength; ++i) { - str += String.fromCharCode(dataView.getUint8(i)) - } - console.log("on connect received:" + str) - }); - tlsTwoWay.on('connect', () => { - console.log("on connect") - }); - tlsTwoWay.on('close', () => { - console.log("on close") - }); - - // Bind the local IP address and port number. - tlsTwoWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => { - if (err) { - console.log('bind fail'); - return; - } - console.log('bind success'); - }); - - // Set the communication parameters. - let options = { - ALPNProtocols: ["spdy/1", "http/1.1"], - - // Set up a connection to the specified IP address and port number. - address: { - address: "192.168.xx.xxx", - port: xxxx, // Port - family: 1, - }, - - // Set the parameters used for authentication during communication. - secureOptions: { - key: "xxxx", // Key - cert: "xxxx", // Digital certificate - ca: ["xxxx"], // CA certificate - passwd: "xxxx", // Password for generating the key - protocols: [socket.Protocol.TLSv12], // Communication protocol - useRemoteCipherPrefer: true, // Whether to preferentially use the peer cipher suite - signatureAlgorithms: "rsa_pss_rsae_sha256:ECDSA+SHA256", // Signature algorithm - cipherSuite: "AES256-SHA256", // Cipher suite - }, - }; - - // Set up a connection. - tlsTwoWay.connect(options, (err, data) => { - console.error(err); - console.log(data); - }); - - // Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. - tlsTwoWay.close((err) => { - if (err) { - console.log("close callback error = " + err); - } else { - console.log("close success"); - } - tlsTwoWay.off('message'); - tlsTwoWay.off('connect'); - tlsTwoWay.off('close'); - }); - - // Create a TLS Socket connection (for one-way authentication). - let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication - - // Subscribe to TLS Socket connection events. - tlsTwoWay.on('message', value => { - console.log("on message") - let buffer = value.message - let dataView = new DataView(buffer) - let str = "" - for (let i = 0;i < dataView.byteLength; ++i) { - str += String.fromCharCode(dataView.getUint8(i)) - } - console.log("on connect received:" + str) - }); - tlsTwoWay.on('connect', () => { - console.log("on connect") - }); - tlsTwoWay.on('close', () => { - console.log("on close") - }); - - // Bind the local IP address and port number. - tlsOneWay.bind({address: '192.168.xxx.xxx', port: xxxx, family: 1}, err => { - if (err) { - console.log('bind fail'); - return; - } - console.log('bind success'); - }); - - // Set the communication parameters. - let oneWayOptions = { - address: { - address: "192.168.xxx.xxx", - port: xxxx, - family: 1, - }, - secureOptions: { - ca: ["xxxx","xxxx"], // CA certificate - cipherSuite: "AES256-SHA256", // Cipher suite - }, - }; - - // Set up a connection. - tlsOneWay.connect(oneWayOptions, (err, data) => { - console.error(err); - console.log(data); - }); - - // Enable the TCP Socket connection to be automatically closed after use. Then, disable listening for TCP Socket connection events. - tlsTwoWay.close((err) => { - if (err) { - console.log("close callback error = " + err); - } else { - console.log("close success"); - } - tlsTwoWay.off('message'); - tlsTwoWay.off('connect'); - tlsTwoWay.off('close'); - }); +import socket from '@ohos.net.socket' + +// Create a TLSSocket connection (for two-way authentication). +let tlsTwoWay = socket.constructTLSSocketInstance(); + +// Subscribe to TLSSocket connection events. +tlsTwoWay.on('message', value => { + console.log("on message") + let buffer = value.message + let dataView = new DataView(buffer) + let str = "" + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)) + } + console.log("on connect received:" + str) +}); +tlsTwoWay.on('connect', () => { + console.log("on connect") +}); +tlsTwoWay.on('close', () => { + console.log("on close") +}); + +// Bind the local IP address and port number. +tlsTwoWay.bind({ address: '192.168.xxx.xxx', port: xxxx, family: 1 }, err => { + if (err) { + console.log('bind fail'); + return; + } + console.log('bind success'); +}); + +// Set the communication parameters. +let options = { + ALPNProtocols: ["spdy/1", "http/1.1"], + + // Set up a connection to the specified IP address and port number. + address: { + address: "192.168.xx.xxx", + port: xxxx, // Port + family: 1, + }, + + // Set the parameters used for authentication during communication. + secureOptions: { + key: "xxxx", // Key + cert: "xxxx", // Digital certificate + ca: ["xxxx"], // CA certificate + passwd: "xxxx", // Password for generating the key + protocols: [socket.Protocol.TLSv12], // Communication protocol + useRemoteCipherPrefer: true, // Whether to preferentially use the peer cipher suite + signatureAlgorithms: "rsa_pss_rsae_sha256:ECDSA+SHA256", // Signature algorithm + cipherSuite: "AES256-SHA256", // Cipher suite + }, +}; + +// Set up a connection. +tlsTwoWay.connect(options, (err, data) => { + console.error(err); + console.log(data); +}); + +// Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events. +tlsTwoWay.close((err) => { + if (err) { + console.log("close callback error = " + err); + } else { + console.log("close success"); + } + tlsTwoWay.off('message'); + tlsTwoWay.off('connect'); + tlsTwoWay.off('close'); +}); + +// Create a TLSSocket connection (for one-way authentication). +let tlsOneWay = socket.constructTLSSocketInstance(); // One way authentication + +// Subscribe to TLSSocket connection events. +tlsTwoWay.on('message', value => { + console.log("on message") + let buffer = value.message + let dataView = new DataView(buffer) + let str = "" + for (let i = 0; i < dataView.byteLength; ++i) { + str += String.fromCharCode(dataView.getUint8(i)) + } + console.log("on connect received:" + str) +}); +tlsTwoWay.on('connect', () => { + console.log("on connect") +}); +tlsTwoWay.on('close', () => { + console.log("on close") +}); + +// Bind the local IP address and port number. +tlsOneWay.bind({ address: '192.168.xxx.xxx', port: xxxx, family: 1 }, err => { + if (err) { + console.log('bind fail'); + return; + } + console.log('bind success'); +}); + +// Set the communication parameters. +let oneWayOptions = { + address: { + address: "192.168.xxx.xxx", + port: xxxx, + family: 1, + }, + secureOptions: { + ca: ["xxxx","xxxx"], // CA certificate + cipherSuite: "AES256-SHA256", // Cipher suite + }, +}; + +// Set up a connection. +tlsOneWay.connect(oneWayOptions, (err, data) => { + console.error(err); + console.log(data); +}); + +// Enable the TCPSocket connection to be automatically closed after use. Then, disable listening for TCPSocket connection events. +tlsTwoWay.close((err) => { + if (err) { + console.log("close callback error = " + err); + } else { + console.log("close success"); + } + tlsTwoWay.off('message'); + tlsTwoWay.off('connect'); + tlsTwoWay.off('close'); +}); ``` diff --git a/en/application-dev/connectivity/websocket-connection.md b/en/application-dev/connectivity/websocket-connection.md index b68d537fc5ad96f3ab60b53e2e75a96d7b555f76..0484cdd64ae04d1af5997cc5fd28607a6d6f5e24 100644 --- a/en/application-dev/connectivity/websocket-connection.md +++ b/en/application-dev/connectivity/websocket-connection.md @@ -1,85 +1,84 @@ # WebSocket Connection +## When to Use -## Use Cases - -You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event. +You can use WebSocket to establish a bidirectional connection between a server and a client. Before doing this, you need to use the **createWebSocket()** API to create a **WebSocket** object and then use the **connect()** API to connect to the server. +If the connection is successful, the client will receive a callback of the **open** event. Then, the client can communicate with the server using the **send()** API. +When the server sends a message to the client, the client will receive a callback of the **message** event. If the client no longer needs this connection, it can call the **close()** API to disconnect from the server. Then, the client will receive a callback of the **close** event. If an error occurs in any of the preceding processes, the client will receive a callback of the **error** event. - ## Available APIs The WebSocket connection function is mainly implemented by the WebSocket module. To use related APIs, you must declare the **ohos.permission.INTERNET** permission. The following table describes the related APIs. -| API | Description | +| API| Description| | -------- | -------- | -| createWebSocket() | Creates a WebSocket connection. | -| connect() | Establishes a WebSocket connection to a given URL. | -| send() | Sends data through the WebSocket connection. | -| close() | Closes a WebSocket connection. | -| on(type: 'open') | Enables listening for **open** events of a WebSocket connection. | -| off(type: 'open') | Disables listening for **open** events of a WebSocket connection. | -| on(type: 'message') | Enables listening for **message** events of a WebSocket connection. | -| off(type: 'message') | Disables listening for **message** events of a WebSocket connection. | -| on(type: 'close') | Enables listening for **close** events of a WebSocket connection. | -| off(type: 'close') | Disables listening for **close** events of a WebSocket connection. | -| on(type: 'error') | Enables listening for **error** events of a WebSocket connection. | -| off(type: 'error') | Disables listening for **error** events of a WebSocket connection. | - +| createWebSocket() | Creates a WebSocket connection.| +| connect() | Establishes a WebSocket connection to a given URL.| +| send() | Sends data through the WebSocket connection.| +| close() | Closes a WebSocket connection.| +| on(type: 'open') | Enables listening for **open** events of a WebSocket connection.| +| off(type: 'open') | Disables listening for **open** events of a WebSocket connection.| +| on(type: 'message') | Enables listening for **message** events of a WebSocket connection.| +| off(type: 'message') | Disables listening for **message** events of a WebSocket connection.| +| on(type: 'close') | Enables listening for **close** events of a WebSocket connection.| +| off(type: 'close') | Disables listening for **close** events of a WebSocket connection.| +| on(type: 'error') | Enables listening for **error** events of a WebSocket connection.| +| off(type: 'error') | Disables listening for **error** events of a WebSocket connection.| ## How to Develop -1. Import the required WebSocket module. +1. Import the required webSocket module. 2. Create a **WebSocket** object. -3. (Optional) Subscribe to WebSocket open, message, close, and error events. +3. (Optional) Subscribe to WebSocket **open**, **message**, **close**, and **error** events. 4. Establish a WebSocket connection to a given URL. 5. Close the WebSocket connection if it is no longer needed. - - ```js - import webSocket from '@ohos.net.webSocket'; - - var defaultIpAddress = "ws://"; - let ws = webSocket.createWebSocket(); - ws.on('open', (err, value) => { - console.log("on open, status:" + JSON.stringify(value)); - // When receiving the on('open') event, the client can use the send() API to communicate with the server. - ws.send("Hello, server!", (err, value) => { - if (!err) { - console.log("Message sent successfully"); - } else { - console.log("Failed to send the message. Err:" + JSON.stringify(err)); - } - }); - }); - ws.on('message', (err, value) => { - console.log("on message, message:" + value); - // When receiving the `bye` message (the actual message name may differ) from the server, the client proactively disconnects from the server. - if (value === 'bye') { - ws.close((err, value) => { - if (!err) { - console.log("Connection closed successfully"); - } else { - console.log("Failed to close the connection. Err: " + JSON.stringify(err)); - } - }); - } - }); - ws.on('close', (err, value) => { - console.log("on close, code is " + value.code + ", reason is " + value.reason); - }); - ws.on('error', (err) => { - console.log("on error, error:" + JSON.stringify(err)); - }); - ws.connect(defaultIpAddress, (err, value) => { - if (!err) { - console.log("Connected successfully"); - } else { - console.log("Connection failed. Err:" + JSON.stringify(err)); - } - }); - ``` + +```js +import webSocket from '@ohos.net.webSocket'; + +var defaultIpAddress = "ws://"; +let ws = webSocket.createWebSocket(); +ws.on('open', (err, value) => { + console.log("on open, status:" + JSON.stringify(value)); + // When receiving the on('open') event, the client can use the send() API to communicate with the server. + ws.send("Hello, server!", (err, value) => { + if (!err) { + console.log("Message sent successfully"); + } else { + console.log("Failed to send the message. Err:" + JSON.stringify(err)); + } + }); +}); +ws.on('message', (err, value) => { + console.log("on message, message:" + value); + // When receiving the `bye` message (the actual message name may differ) from the server, the client proactively disconnects from the server. + if (value === 'bye') { + ws.close((err, value) => { + if (!err) { + console.log("Connection closed successfully"); + } else { + console.log("Failed to close the connection. Err: " + JSON.stringify(err)); + } + }); + } +}); +ws.on('close', (err, value) => { + console.log("on close, code is " + value.code + ", reason is " + value.reason); +}); +ws.on('error', (err) => { + console.log("on error, error:" + JSON.stringify(err)); +}); +ws.connect(defaultIpAddress, (err, value) => { + if (!err) { + console.log("Connected successfully"); + } else { + console.log("Connection failed. Err:" + JSON.stringify(err)); + } +}); +``` diff --git a/en/application-dev/database/Readme-EN.md b/en/application-dev/database/Readme-EN.md index f7ec17755105ef29b58f7ebba41393f07e74fca5..77e1d8f9738d949ce9b0f0396bf66f99b9bf924e 100644 --- a/en/application-dev/database/Readme-EN.md +++ b/en/application-dev/database/Readme-EN.md @@ -1,17 +1,22 @@ # Data Management -- Distributed Data Service - - [Distributed Data Service Overview](database-mdds-overview.md) - - [Distributed Data Service Development](database-mdds-guidelines.md) -- Relational Database - - [RDB Overview](database-relational-overview.md) - - [RDB Development](database-relational-guidelines.md) -- Preferences - - [Preferences Overview](database-preference-overview.md) - - [Preferences Development](database-preference-guidelines.md) -- Distributed Data Object - - [Distributed Data Object Overview](database-distributedobject-overview.md) - - [Distributed Data Object Development](database-distributedobject-guidelines.md) -- Data Share - - [DataShare Overview](database-datashare-overview.md) - - [DataShare Development](database-datashare-guidelines.md) +- [Data Management Overview](data-mgmt-overview.md) +- Application Data Persistence + - [Overview of Application Data Persistence](app-data-persistence-overview.md) + - [Persisting Preferences Data](data-persistence-by-preferences.md) + - [Persisting KV Store Data](data-persistence-by-kv-store.md) + - [Persisting RDB Store Data](data-persistence-by-rdb-store.md) +- Distributed Application Data Synchronization + - [Distributed Application Data Synchronization Overview](sync-app-data-across-devices-overview.md) + - [Cross-Device Synchronization of KV Stores](data-sync-of-kv-store.md) + - [Cross-Device Synchronization of RDB Stores](data-sync-of-rdb-store.md) + - [Cross-Device Synchronization of Distributed Data Objects](data-sync-of-distributed-data-object.md) +- Data Reliability and Security + - [Data Reliability and Security Overview](data-reliability-security-overview.md) + - [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) diff --git a/en/application-dev/database/access-control-by-device-and-data-level.md b/en/application-dev/database/access-control-by-device-and-data-level.md new file mode 100644 index 0000000000000000000000000000000000000000..16ed5d5988636fc7e669cb1cba5a14d56ef7d67c --- /dev/null +++ b/en/application-dev/database/access-control-by-device-and-data-level.md @@ -0,0 +1,122 @@ +# Access Control by Device and Data Level + + +## Basic Concepts + +Distributed data management implements access control based on data security labels and device security levels. + +A higher data security label and device security level indicate stricter encryption and access control measures and higher data security. + + +### Data Security Labels + +The data can be rated into four security levels: S1, S2, S3, and S4. + + | Risk Level| Security Level| Definition| Example| +| -------- | -------- | -------- | -------- | +| Critical| S4 | Special data types defined by industry laws and regulations, involving the most private individual information or data that may cause significant adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Political opinions, religious and philosophical belief, trade union membership, genetic data, biological information, health and sexual life status, sexual orientation, device authentication, and personal credit card information| +| High| S3 | Data that may cause critical adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Individual real-time precise positioning information and movement trajectory| +| Moderate| S2 | Data that may cause major adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Detailed addresses and nicknames of individuals| +| Low| S1 | Data that may cause minor adverse impact on an individual or group once disclosed, tampered with, corrupted, or destroyed.| Gender, nationality, and user application records| + + +### Device Security Levels + +Device security levels are classified into SL1 to SL5 based on devices' security capabilities, for example, whether a Trusted Execution Environment (TEE) or a secure storage chip is available. For example, the development boards RK3568 and Hi3516 are SL1 (lower security) devices, and tablets are SL4 (higher security) devices. + +During device networking, you can run the **hidumper -s 3511** command to query the device security level. The following example shows how to query the security level of the RK3568 board: + +![en-us_image_0000001542496993](figures/en-us_image_0000001542496993.png) + + +## Access Control Mechanism in Cross-Device Synchronization + +In cross-device data synchronization, data access is controlled based on the device security level and data security labels. In principle, data can be synchronized only to the devices whose data security labels are not higher than the device's security level. The access control matrix is as follows: + +|Device Security Level|Data Security Labels of the Synchornizable Device| +|---|---| +|SL1|S1| +|SL2|S1 to S2| +|SL3|S1 to S3| +|SL4|S1 to S4| +|SL5|S1 to S4| + +For example, the security level of development boards RK3568 and Hi3516 is SL1. The database with data security label S1 can be synchronized with RK3568 and Hi3516, but the databases with database labels S2-S4 cannot. + + +## When to Use + +The access control mechanism ensures secure data storage and synchronization across devices. When creating a database, you need to correctly set the security level for the database. + + +## Setting the Security Level for a KV Store + +When a KV store is created, the **securityLevel** parameter specifies the security level of the KV store. The following example shows how to create a KV store with security level of S1. + +For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). + + + +```js +import distributedKVStore from '@ohos.data.distributedKVStore'; + +let kvManager; +let context = getContext(this); +const kvManagerConfig = { + context: context, + bundleName: 'com.example.datamanagertest' +} +try { + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); +} catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); +} +let kvStore; +try { + const options = { + createIfMissing: true, + encrypt: true, + backup: false, + autoSync: true, + kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, + securityLevel: distributedKVStore.SecurityLevel.S1 + }; + kvManager.getKVStore('storeId', options, (err, store) => { + if (err) { + console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting KVStore.'); + kvStore = store; + }); +} catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); +} +``` + + +## Setting the Security Level for an RDB Store + +When an RDB store is created, the **securityLevel** parameter specifies the security level of the RDB store. The following example shows how to create an RDB store with security level of S1. + +For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). + + + +```js +import relationalStore from '@ohos.data.relationalStore'; + +let store; +const STORE_CONFIG = { + name: 'RdbTest.db', + securityLevel: relationalStore.SecurityLevel.S1 +}; +let promise = relationalStore.getRdbStore(this.context, STORE_CONFIG); +promise.then(async (rdbStore) => { + store = rdbStore; + console.info('Succeeded in getting RdbStore.') +}).catch((err) => { + console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`); +}) +``` diff --git a/en/application-dev/database/app-data-persistence-overview.md b/en/application-dev/database/app-data-persistence-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..99690708daa2e14ae66566907ff28aa086e0cc9b --- /dev/null +++ b/en/application-dev/database/app-data-persistence-overview.md @@ -0,0 +1,17 @@ +# Application Data Persistence Overview + + +Application data persistence means to save the application data in the memory to a file or database on a device. The data in the memory is usually saved in the forms of data structs or data objects, and the data in storage media can be saved in the forms of text, databases, or binary files. + + +The OpenHarmony standard system supports typical data storage forms, including user preferences (**Preferences**), key-value databases (**KV-Store**), and relational databases (**RelationalStore**). + + +You can use proper data storage forms to implement data persistence: + + +- **Preferences**: used to store application configuration data. Data is stored as text files on a device. When the application is used, it loads all the data from the text file to the memory. **Preferences** allow fast and efficient data access, but are not suitable when a large amount of data needs to be stored. + +- **KV-Store**: used to store data in KV pairs, in which the key uniquely identifies the data. A KV store is a kind of non-relational database. It is ideal for storing service data with few data and service relationships. It has been widely used because it poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data synchronization. KV databases feature higher cross-device and cross-version compatibility than relational databases. + +- **RelationalStore**: used to store data in rows and columns. It is widely used to process relational data in applications. RelationalStore provides a set of APIs for adding, deleting, modifying, and querying data. You can also define and use SQL statements for complex service scenarios. diff --git a/en/application-dev/database/data-backup-and-restore.md b/en/application-dev/database/data-backup-and-restore.md new file mode 100644 index 0000000000000000000000000000000000000000..0f95a70dbbf782a438368e5afd8d84581127fcc7 --- /dev/null +++ b/en/application-dev/database/data-backup-and-restore.md @@ -0,0 +1,231 @@ +# Database Backup and Restoration + + +## When to Use + +You may need to restore a database in any of the following cases: + +An important operation being performed by an application is interrupted. + +The database is unavailable due to data loss or corruption, or dirty data. + + +Both KV stores and RDB stores support database backup and restoration. In addition, KV stores allow you to delete database backups to release local storage space. + + +## Backing Up, Restoring, and Deleting a KV Store + +You can use **backup()** to back up a KV store, use **restore()** to restore a KV store, and use **deletebackup()** to delete a KV store backup file. For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). + +1. Create a KV store. + + (1) Create a **kvManager** instance. + + (2) Set database parameters. + + (3) Create a **kvStore** instance. + + + ```js + import distributedKVStore from '@ohos.data.distributedKVStore'; + + let kvManager; + let context = getContext(this); + const kvManagerConfig = { + context: context, + bundleName: 'com.example.datamanagertest' + } + try { + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); + } catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); + } + let kvStore; + try { + const options = { + createIfMissing: true, + encrypt: false, + backup: false, + autoSync: true, + kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, + securityLevel: distributedKVStore.SecurityLevel.S2 + }; + kvManager.getKVStore('storeId', options, (err, store) => { + if (err) { + console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting KVStore.'); + kvStore = store; + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +2. Use **put()** to insert data to the KV store. + + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Fail to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +3. Use **backup()** to back up the KV store. + + ```js + let file = 'BK001'; + try { + kvStore.backup(file, (err) => { + if (err) { + console.error(`Fail to backup data.code:${err.code},message:${err.message}`); + } else { + console.info('Succeeded in backupping data.'); + } + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +4. Use **delete()** to delete data to simulate unexpected deletion or data tampering. + + ```js + try { + kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Fail to delete data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in deleting data.'); + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +5. Use **restore()** to restore the KV store. + + ```js + let file = 'BK001'; + try { + kvStore.restore(file, (err) => { + if (err) { + console.error(`Fail to restore data. Code:${err.code},message:${err.message}`); + } else { + console.info('Succeeded in restoring data.'); + } + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +6. Use **deleteBackup()** to delete the backup file to release storage space. + + ```js + let kvStore; + let files = ['BK001']; + try { + kvStore.deleteBackup(files).then((data) => { + console.info(`Succeed in deleting Backup. Data:filename is ${data[0]},result is ${data[1]}.`); + }).catch((err) => { + console.error(`Fail to delete Backup. Code:${err.code},message:${err.message}`); + }) + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + + +## Backing Up and Restoring an RDB Store + +You can use **backup()** to back up an RDB store, and use **restore()** to restore an RDB store. For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). + +1. Use **getRdbStore()** to create an RDB store. + + ```js + import relationalStore from '@ohos.data.relationalStore'; + + let store; + let context = getContext(this); + const STORE_CONFIG = { + name: 'RdbTest.db', + securityLevel: relationalStore.SecurityLevel.S1 + }; + relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => { + store = rdbStore; + if (err) { + console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`); + return; + } + store.executeSql("CREATE TABLE IF NOT EXISTS EMPLOYEE (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, salary INTEGER, codes Uint8Array);", null); + console.info('Succeeded in getting RdbStore.'); + }) + ``` + +2. Use **insert()** to insert data to the RDB store. + + ```js + const valueBucket = { + 'NAME': 'Lisa', + 'AGE': 18, + 'SALARY': 100.5, + 'CODES': new Uint8Array([1, 2, 3, 4, 5]) + }; + store.insert('EMPLOYEE', valueBucket, relationalStore.ConflictResolution.ON_CONFLICT_REPLACE, (err, rowId) => { + if (err) { + console.error(`Failed to insert data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in inserting data. rowId:${rowId}`); + }) + ``` + +3. Use **backup()** to back up the RDB store. + + ```js + store.backup('dbBackup.db', (err) => { + if (err) { + console.error(`Failed to backup data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in backuping data.`); + }) + ``` + +4. Use **delete()** to delete data to simulate unexpected deletion or data tampering. + + ```js + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); + predicates.equalTo('NAME', 'Lisa'); + let promise = store.delete(predicates); + promise.then((rows) => { + console.info(`Delete rows: ${rows}`); + }).catch((err) => { + console.error(`Failed to delete data. Code:${err.code},message:${err.message}`); + }) + ``` + +5. Use **restore()** to restore the RDB store. + + ```js + store.restore('dbBackup.db', (err) => { + if (err) { + console.error(`Failed to restore data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in restoring data.`); + }) + ``` diff --git a/en/application-dev/database/data-encryption.md b/en/application-dev/database/data-encryption.md new file mode 100644 index 0000000000000000000000000000000000000000..d9672ce7fdc54b93d736f4684ea1b454e512c207 --- /dev/null +++ b/en/application-dev/database/data-encryption.md @@ -0,0 +1,85 @@ +# Database Encryption + + +## When to Use + +OpenHarmony provides the database encryption capability to effectively protect the data stored in a database. Database encryption allows data to be stored and used in ciphertext, ensuring data confidentiality and integrity. + +The encrypted database can be accessed only using an API, and the database file cannot be opened in other ways. Whether a database is encrypted is set when the database is created, and the setting cannot be changed. + +Both KV stores and RDB stores support database encryption. + + +## Encrypting a KV Store + +When a KV store is created, the **encrypt** parameter in **options** specifies whether to encrypt the KV store. The value **true** means to encrypt the KV store, and the value **false** (default) means the opposite. + +For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). + + +```js +import distributedKVStore from '@ohos.data.distributedKVStore'; + +let kvManager; +let context = getContext(this); +const kvManagerConfig = { + context: context, + bundleName: 'com.example.datamanagertest', +} +try { + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); +} catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); +} +let kvStore; +try { + const options = { + createIfMissing: true, + // Whether to encrypt the KV store. + encrypt: true, + backup: false, + autoSync: true, + kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, + securityLevel: distributedKVStore.SecurityLevel.S2 + }; + kvManager.getKVStore('storeId', options, (err, store) => { + if (err) { + console.error(`Fail to get KVStore. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting KVStore.'); + kvStore = store; + }); +} catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); +} +``` + + +## Encrypting an RDB Store + +When an RDB store is created, the **encrypt** parameter in **options** specifies whether to encrypt the RDB store. The value **true** means to encrypt the RDB store, and the value **false** (default) means the opposite. + +For details about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). + + +```js +import relationalStore from '@ohos.data.relationalStore'; + +let store; +let context = getContext(this); +const STORE_CONFIG = { + name: 'RdbTest.db', + securityLevel: relationalStore.SecurityLevel.S1, + encrypt: true +}; +relationalStore.getRdbStore(context, STORE_CONFIG, (err, rdbStore) => { + store = rdbStore; + if (err) { + console.error(`Failed to get RdbStore. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in getting RdbStore.`); +}) +``` diff --git a/en/application-dev/database/data-mgmt-overview.md b/en/application-dev/database/data-mgmt-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..aa98d97da5acdce3a382a70d383e140463a5399a --- /dev/null +++ b/en/application-dev/database/data-mgmt-overview.md @@ -0,0 +1,36 @@ +# Data Management Overview + + +## Function + +Data management provides data storage, management, and synchronization capabilities. For example, you can store the Contacts application data in database for secure management and shared access, and synchronize the contacts information with a smart watch. + +- Data storage: provides data persistence capabilities, which can be classified into user preferences, key-value (KV) stores, and relational database (RDB) stores by data characteristics. + +- Data management: provides efficient data management capabilities, including permission management, data backup and restoration, and dataShare framework. + +- Data synchronization: provides data synchronization across devices. For example, distributed data objects support sharing of memory objects across devices, and distributed databases support database access across devices. + +The database stores created by an application are saved to the application sandbox. When the application is uninstalled, the database stores are automatically deleted. + + +## Working Principles + +The data management module includes user preferences (**Preferences**), KV data management (**KV-Store**), RDB data management (**RelationalStore**), distributed data object (**DataObject**), and cross-application data management (**DataShare**). The interface layer provides standard JavaScript APIs for application development. The Frameworks&System service layer implements storage and synchronization of component data, and provides dependencies for SQLite and other subsystems. + + **Figure 1** Data management architecture + +![dataManagement](figures/dataManagement.jpg) + + +- **Preferences**: implements persistence of lightweight configuration data and supports subscription of data change notifications. Preferences are used to store application configuration information and user preference settings and do not support distributed synchronization. + +- **KV-Store**: implements read, write, encryption, and manual backup of data in KV stores and notification subscription. When an application needs to use the distributed capabilities of KV stores, KV-Store sends a synchronization request to DatamgrService to implement data synchronization across devices. + +- **RelationalStore**: implements addition, deletion, modification, query, encryption, manually backup of data in RDB stores, and notification subscription. When an application needs to use the distributed capabilities of an RDB store, RelationalStore sends a synchronization request to DatamgrService to implement data synchronization across devices. + +- **DataObject**: independently provides distributed capabilities for the data of object structs. For the object data that is still required after the restart of an application (either the cross-device application or local device application), the **DatamgrService** implements temporary storage of the object data. + +- **DataShare**: provides the data provider-consumer mode to implement addition, deletion, modification, and query of cross-application data on a device, and notification subscription. **DataShare** is not bound to any database and can interact with RDB and KV stores. You can also encapsulate your own databases for C/C++ applications.
In addition to the provider-consumer mode, **DataShare** provides silent access, which allows direct access to the provider's data via the DatamgrService proxy instead of starting the provider. Currently, only the RDB stores support silent access. + +- **DatamgrService**: implements synchronization and cross-application sharing for other components, including cross-device synchronization of **RelationalStore** and **KV-Store**, silent access to provider data of **DataShare**, and temporary storage of **DataObject** synchronization object data. diff --git a/en/application-dev/database/data-persistence-by-kv-store.md b/en/application-dev/database/data-persistence-by-kv-store.md new file mode 100644 index 0000000000000000000000000000000000000000..2337702fb79b1d2c53154181362b393211f90f50 --- /dev/null +++ b/en/application-dev/database/data-persistence-by-kv-store.md @@ -0,0 +1,194 @@ +# Persisting KV Store Data + + +## When to Use + +The key-value (KV) database stores data in the form of KV pairs. You can use KV stores to store data organized in a simple model, for example, product names and prices or employee IDs and daily attendance. The simple data structure allows higher compatibility with different database versions and device types. + + +## Constraints + +- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB. + +- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB. + +- A maximum of 16 distributed KV stores can be opened simultaneously for an application. + +- Blocking operations, for example, modifying UI components, are not allowed in the KV store event callbacks. + + +## Available APIs + +The following table lists the APIs used for KV data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). + +| API| Description| +| -------- | -------- | +| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.| +| getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void | Creates and obtains a KV store of the specified type.| +| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback<void>): void | Adds a KV pair of the specified type to this KV store.| +| get(key: string, callback: AsyncCallback<Uint8Array\|string\|boolean\|number>): void | Obtains the value of the specified key.| +| delete(key: string, callback: AsyncCallback<void>): void | Deletes a KV pair based on the specified key.| + + +## How to Develop + +1. Create a **KvManager** instance to manage database objects. + + Example: + Stage model: + + + ```js + // Import the module. + import distributedKVStore from '@ohos.data.distributedKVStore'; + + // Stage model + import UIAbility from '@ohos.app.ability.UIAbility'; + + let kvManager; + + export default class EntryAbility extends UIAbility { + onCreate() { + let context = this.context; + const kvManagerConfig = { + context: context, + bundleName: 'com.example.datamanagertest' + }; + try { + // Create a KVManager instance. + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); + // Create and obtain the database. + } catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); + } + } + } + ``` + + FA model: + + + ```js + // Import the module. + import distributedKVStore from '@ohos.data.distributedKVStore'; + + // FA model + import featureAbility from '@ohos.ability.featureAbility'; + + let kvManager; + let context = featureAbility.getContext(); // Obtain the context. + const kvManagerConfig = { + context: context, + bundleName: 'com.example.datamanagertest' + }; + try { + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); + // Create and obtain the database. + } catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); + } + ``` + +2. Create and obtain a KV store. + + Example: + ```js + try { + const options = { + createIfMissing: true, // Whether to create a KV store when the database file does not exist. By default, a KV store is created. + encrypt: false, // Whether to encrypt the KV store. By default, KV stores are not encrypted. + backup: false, // Whether to back up database files. By default, the database files are backed up. + autoSync: true, // Whether to automatically synchronize database files. The value **true** means to automatically synchronize database files; the value **false** (default) means the opposite. + kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, // Type of the KV store to create. By default, a device KV store is created. + securityLevel: distributedKVStore.SecurityLevel.S2 // Security level of the KV store. + }; + // storeId uniquely identifies a KV store. + kvManager.getKVStore('storeId', options, (err, kvStore) => { + if (err) { + console.error(`Failed to get KVStore. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting KVStore.'); + // Perform related data operations. + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +3. Use **put()** to add data to the KV store. + + Example: + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + + > **NOTE** + > + > The **put()** method adds a KV pair if the specified key does not exists and changes the value if the the specified key already exists. + +4. Use **get()** to obtain the value of a key. + + Example: + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => { + if (err !== undefined) { + console.error(`Failed to get data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in getting data. data:${data}`); + }); + }); + } catch (e) { + console.error(`Failed to get data. Code:${e.code},message:${e.message}`); + } + ``` + +5. Use **delete()** to delete the data of the specified key. + + Example: + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + kvStore.delete(KEY_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to delete data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in deleting data.'); + }); + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` diff --git a/en/application-dev/database/data-persistence-by-preferences.md b/en/application-dev/database/data-persistence-by-preferences.md new file mode 100644 index 0000000000000000000000000000000000000000..87a4fe9c523baffae4b05448fa36eb9d6daf3b99 --- /dev/null +++ b/en/application-dev/database/data-persistence-by-preferences.md @@ -0,0 +1,252 @@ +# Persisting Preferences Data + + +## When to Use + +The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs, and supports persistence of the KV pairs when required, as well as modification and query of the data. You can use **Preferences** when you want a unique storage for global data. **Preferences** caches data in the memory, which allows fast access when the data is required. **Preferences** is recommended for storing small amount of data, such as personalized settings (font size and whether to enable the night mode) of applications. + + +## Working Principles + +User applications call **Preference** through the JS interface to read and write data files. You can load the data of a **Preferences** persistence file to a **Preferences** instance. Each file uniquely corresponds to an instance. The system stores the instance in memory through a static container until the instance is removed from the memory or the file is deleted. The following figure illustrates how **Preference** works. + +The preference persistent file of an application is stored in the application sandbox. You can use **context** to obtain the file path. For details, see [Obtaining the Application Development Path](../application-models/application-context-stage.md#obtaining-the-application-development-path). + +**Figure 1** Preferences working mechanism + +![preferences](figures/preferences.jpg) + + +## Constraints + +- The key in a KV pair must be a string and cannot be empty or exceed 80 bytes. + +- If the value is of the string type, it can be empty or a string not longer than 8192 bytes. + +- The memory usage increases with the amount of **Preferences** data. The maximum number of data records recommended is 10,000. Otherwise, high memory overheads will be caused. + + +## Available APIs + +The following table lists the APIs used for preferences data persistence. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [User Preferences](../reference/apis/js-apis-data-preferences.md). + +| API| Description| +| -------- | -------- | +| getPreferences(context: Context, name: string, callback: AsyncCallback<Preferences>): void | Obtain a **Preferences** instance.| +| put(key: string, value: ValueType, callback: AsyncCallback<void>): void | Writes data to the Preferences instance. You can use **flush()** to persist the **Preferences** instance data.| +| has(key: string, callback: AsyncCallback<boolean>): void | Checks whether the **Preferences** instance contains a KV pair with the given key. The key cannot be empty.| +| get(key: string, defValue: ValueType, callback: AsyncCallback<ValueType>): void | Obtains the value of the specified key. If the value is null or not of the default value type, **defValue** is returned.| +| delete(key: string, callback: AsyncCallback<void>): void | Deletes the KV pair with the given key from the **Preferences** instance.| +| flush(callback: AsyncCallback<void>): void | Flushes the data of this **Preferences** instance to a file for data persistence.| +| on(type: 'change', callback: Callback<{ key : string }>): void | Subscribes to data changes of the specified key. When the value of the specified key is changed and saved by **flush()**, a callback will be invoked to return the new data.| +| off(type: 'change', callback?: Callback<{ key : string }>): void | Unsubscribes from data changes.| +| deletePreferences(context: Context, name: string, callback: AsyncCallback<void>): void | Deletes a **Preferences** instance from memory. If the **Preferences** instance has a persistent file, this API also deletes the persistent file.| + + +## How to Develop + +1. Import the **@ohos.data.preferences** module. + + ```js + import dataPreferences from '@ohos.data.preferences'; + ``` + +2. Obtain a **Preferences** instance. Read data from a file and load the data to a **Preferences** instance for data operations. + + Stage model: + + + ```js + import UIAbility from '@ohos.app.ability.UIAbility'; + + class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + try { + dataPreferences.getPreferences(this.context, 'mystore', (err, preferences) => { + if (err) { + console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting preferences.'); + // Perform related data operations. + }) + } catch (err) { + console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`); + } + } + } + ``` + + FA model: + + + ```js + import featureAbility from '@ohos.ability.featureAbility'; + + // Obtain the context. + let context = featureAbility.getContext(); + + try { + dataPreferences.getPreferences(context, 'mystore', (err, preferences) => { + if (err) { + console.error(`Failed to get preferences. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting preferences.'); + // Perform related data operations. + }) + } catch (err) { + console.error(`Failed to get preferences. Code is ${err.code},message:${err.message}`); + } + ``` + +3. Write data. + + Use **put()** to write data to the **Preferences** instance. After data is written, you can use **flush()** to persist the **Preferences** instance data to a file if necessary. + + > **NOTE** + > + > If the specified key already exists, the **put()** method changes the value. To prevent a value from being changed by mistake, you can use **has()** to check whether the KV pair exists. + + Example: + + + ```js + try { + preferences.has('startup', function (err, val) { + if (err) { + console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`); + return; + } + if (val) { + console.info("The key 'startup' is contained."); + } else { + console.info("The key 'startup' does not contain."); + // Add a KV pair. + try { + preferences.put('startup', 'auto', (err) => { + if (err) { + console.error(`Failed to put data. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + }) + } catch (err) { + console.error(`Failed to put data. Code: ${err.code},message:${err.message}`); + } + } + }) + } catch (err) { + console.error(`Failed to check the key 'startup'. Code:${err.code}, message:${err.message}`); + } + ``` + +4. Read data. + + Use **get()** to obtain the value of the specified key. If the value is null or is not of the default value type, the default data is returned. + + Example: + + ```js + try { + preferences.get('startup', 'default', (err, val) => { + if (err) { + console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Succeeded in getting value of 'startup'. val: ${val}.`); + }) + } catch (err) { + console.error(`Failed to get value of 'startup'. Code:${err.code}, message:${err.message}`); + } + ``` + +5. Delete data. + + Use delete() to delete a KV pair.
Example: + + + ```js + try { + preferences.delete('startup', (err) => { + if (err) { + console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`); + return; + } + console.info("Succeeded in deleting the key 'startup'."); + }) + } catch (err) { + console.error(`Failed to delete the key 'startup'. Code:${err.code}, message:${err.message}`); + } + ``` + +6. Persist data. + + You can use **flush()** to persist the data held in a **Preferences** instance to a file. Example: + + ```js + try { + preferences.flush((err) => { + if (err) { + console.error(`Failed to flush. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in flushing.'); + }) + } catch (err) { + console.error(`Failed to flush. Code:${err.code}, message:${err.message}`); + } + ``` + +7. Subscribe to data changes. + + Specify an observer as the callback to return the data changes for an application. When the value of the subscribed key is changed and saved by **flush()**, the observer callback will be invoked to return the new data. Example: + + ```js + let observer = function (key) { + console.info('The key' + key + 'changed.'); + } + preferences.on('change', observer); + // The data is changed from 'auto' to 'manual'. + preferences.put('startup', 'manual', (err) => { + if (err) { + console.error(`Failed to put the value of 'startup'. Code:${err.code},message:${err.message}`); + return; + } + console.info("Succeeded in putting the value of 'startup'."); + preferences.flush((err) => { + if (err) { + console.error(`Failed to flush. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in flushing.'); + }) + }) + ``` + +8. Delete a **Preferences** instance from the memory. + + Use **deletePreferences()** to delete a **Preferences** instance from the memory. If the **Preferences** instance has a persistent file, the persistent file and its backup and corrupted files will also be deleted. + + > **NOTE** + > + > - The deleted **Preferences** instance cannot be used for data operations. Otherwise, data inconsistency will be caused. + > + > - The deleted data and files cannot be restored. + + Example: + + + ```js + try { + dataPreferences.deletePreferences(this.context, 'mystore', (err, val) => { + if (err) { + console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in deleting preferences.'); + }) + } catch (err) { + console.error(`Failed to delete preferences. Code:${err.code}, message:${err.message}`); + } + ``` \ No newline at end of file diff --git a/en/application-dev/database/data-persistence-by-rdb-store.md b/en/application-dev/database/data-persistence-by-rdb-store.md new file mode 100644 index 0000000000000000000000000000000000000000..e4ee7a87a0fb3166a0f8c4a617d4cb1a823eb743 --- /dev/null +++ b/en/application-dev/database/data-persistence-by-rdb-store.md @@ -0,0 +1,245 @@ +# Persisting RDB Store Data + + +## When to Use + +A relational database (RDB) store is used to store data in complex relational models, such as the student information including names, student IDs, and scores of each subject, or employee information including names, employee IDs, and positions, based on SQLite. The data is more complex than key-value (KV) pairs due to strict mappings. You can use **RelationalStore** to implement persistence of this type of data. + + +## Basic Concepts + +- **Predicates**: A representation of the property or feature of a data entity, or the relationship between data entities. It is used to define operation conditions. + +- **ResultSet**: a set of query results, which allows access to the required data in flexible modes. + + +## Working Principles + +**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) + + +## Constraints + +- The default logging mode is Write Ahead Log (WAL), and the default flushing mode is **FULL** mode. + +- An RDB store can be connected to a maximum of four connection pools for user read operations. + +- To ensure data accuracy, only one write operation is allowed at a time. + +- Once an application is uninstalled, related database files and temporary files on the device are automatically deleted. + + +## Available APIs + +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| +| -------- | -------- | +| 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 + +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'; + + class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + const STORE_CONFIG = { + name: 'RdbTest.db', // Database file name. + securityLevel: relationalStore.SecurityLevel.S1 // Database security level. + }; + + 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) => { + if (err) { + console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Succeeded in getting RdbStore.`); + store.executeSql(SQL_CREATE_TABLE); // Create a data table. + + // Perform operations such as adding, deleting, modifying, and querying data in the RDB store. + + }); + } + } + ``` + + FA model: + + + ```js + import relationalStore from '@ohos.data.relationalStore'; // Import the module. + import featureAbility from '@ohos.ability.featureAbility'; + + // Obtain the context. + let context = featureAbility.getContext(); + + const STORE_CONFIG = { + name: 'RdbTest.db', // Database file name. + securityLevel: relationalStore.SecurityLevel.S1 // Database security level. + }; + + 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(context, STORE_CONFIG, (err, store) => { + if (err) { + console.error(`Failed to get RdbStore. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Succeeded in getting RdbStore.`); + store.executeSql(SQL_CREATE_TABLE); // Create a data table. + + // Perform operations such as adding, deleting, modifying, and querying data in the RDB store. + + }); + ``` + + > **NOTE** + > + > - 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. + +2. Use **insert()** to insert data to the RDB store. + + Example: + + ```js + const valueBucket = { + 'NAME': 'Lisa', + 'AGE': 18, + 'SALARY': 100.5, + 'CODES': new Uint8Array([1, 2, 3, 4, 5]) + }; + store.insert('EMPLOYEE', valueBucket, (err, rowId) => { + if (err) { + console.error(`Failed to insert data. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Succeeded in inserting data. rowId:${rowId}`); +}) + ``` + + > **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. + + Example: + + ```js + // Modify data. + const valueBucket = { + 'NAME': 'Rose', + 'AGE': 22, + 'SALARY': 200.5, + 'CODES': new Uint8Array([1, 2, 3, 4, 5]) + }; + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); // Create predicates for the table named EMPLOYEE. + predicates.equalTo('NAME', 'Lisa'); // Modify the data of Lisa in the EMPLOYEE table to the specified data. + store.update(valueBucket, predicates, (err, rows) => { + if (err) { + console.error(`Failed to update data. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Succeeded in updating data. row count: ${rows}`); + }) + + // Delete data. + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); + predicates.equalTo('NAME', 'Lisa'); + store.delete(predicates, (err, rows) => { + if (err) { + console.error(`Failed to delete data. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`Delete rows: ${rows}`); + }) + ``` + +4. Query data based on the conditions specified by **Predicates**. + + Use **query()** to query data. The data obtained is returned in a **ResultSet** object. + + Example: + + ```js + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); + predicates.equalTo('NAME', 'Rose'); + store.query(predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'], (err, resultSet) => { + if (err) { + console.error(`Failed to query data. Code:${err.code}, message:${err.message}`); + return; + } + console.info(`ResultSet column names: ${resultSet.columnNames}`); + console.info(`ResultSet column count: ${resultSet.columnCount}`); + }) + ``` + + > **NOTE** + > + > Use **close()** to close the **ResultSet** that is no longer used in a timely manner so that the memory allocated can be released. + +5. Delete the RDB store. + + Use **deleteRdbStore()** to delete the RDB store and related database files. + + Example: + + Stage model: + + + ```js + import UIAbility from '@ohos.app.ability.UIAbility'; + + class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + relationalStore.deleteRdbStore(this.context, 'RdbTest.db', (err) => { + if (err) { + console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in deleting RdbStore.'); + }); + } + } + ``` + + FA model: + + + ```js + import featureAbility from '@ohos.ability.featureAbility'; + + // Obtain the context. + let context = featureAbility.getContext(); + + relationalStore.deleteRdbStore(context, 'RdbTest.db', (err) => { + if (err) { + console.error(`Failed to delete RdbStore. Code:${err.code}, message:${err.message}`); + return; + } + console.info('Succeeded in deleting RdbStore.'); + }); + ``` diff --git a/en/application-dev/database/data-reliability-security-overview.md b/en/application-dev/database/data-reliability-security-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..049007876bff8cbf8b0bf6b3cad9cd28a80c92ff --- /dev/null +++ b/en/application-dev/database/data-reliability-security-overview.md @@ -0,0 +1,64 @@ +# Data Reliability and Security Overview + +## Introduction + +During system running, a database fault may occur due to storage damage, insufficient storage space, file system permission, or system power-off. The database fault may cause data loss. For example, the database corruption of Contacts causes the loss of Contacts data. The data management subsystem provides the following solutions and capabilities to ensure data reliability and security: + +- Data backup and restoration: Critical data (such as the bank information) can be backed up and restored from the backup to prevent data loss. + +- Database encryption: The database that stores sensitive information, such as authentication credentials and financial data, can be encrypted to improve data security. + +- Access control by device and data level: The access to data across devices is controlled based on the device security level and data security labels. + +In addition, the backup database is stored in the application sandbox. When the storage space is insufficient, you can delete the local database backup to release space. + + +## Basic Concepts + +Before developing functions related to data reliability and security, understand the following concepts. + + +### Database Backup and Restoration + +- Database backup: OpenHarmony provides full backup of database files. + When backing up a database, you only need to invoke the backup API of the database, without closing the database. + +- Database restoration: You can restore a database from a database backup file. + + +### Database Encryption + +The entire database file can be encrypted to enhance the database security. + + +### Data Rating + +In distributed scenarios, the access to data is controlled based on the device security level and data security labels. + +A higher data security label and device security level indicate stricter encryption and access control measures and higher data security. + + +## Working Principles + + +### Database Backup and Restoration Mechanism + +The data of a database is backed up to the specified file. Subsequent operations on the database do not affect the backup file. The database is overwritten by the specified backup file only when a restoration is performed. + +- KV store backup directory: **/data/service/el1(el2)/public/database/...{appId}/kvdb/backup/...{storeId}** + +- RDB store backup directory: **/data/app/el1(el2)/100/database/...{bundlename}/rdb** + + +### Database Encryption Mechanism + +When encrypting a database, you do not need to pass in the key for encryption. The only thing you need to do is set the database encryption status. The system automatically calls the [HUKS APIs](../reference/apis/js-apis-huks.md) to generate a key and encrypt the database. + + +## Constraints + +- The database encryption key is automatically changed once a year. + +- A maximum of five backup files can be retained for a KV store. + +- Automatic backup of a KV store must be performed when the device is charging and the screen is off. diff --git a/en/application-dev/database/data-sync-of-distributed-data-object.md b/en/application-dev/database/data-sync-of-distributed-data-object.md new file mode 100644 index 0000000000000000000000000000000000000000..d620fae702fd1559e07114e2e650a03248179bcd --- /dev/null +++ b/en/application-dev/database/data-sync-of-distributed-data-object.md @@ -0,0 +1,307 @@ +# Cross-Device Synchronization of Distributed Data Objects + + +## When to Use + +To implement traditional data synchronization between devices, you need to design the message processing logic, including setting up a communication link, sending, receiving, and processing messages, retry mechanism upon errors, and resolving data conflicts. The workload is heavy. In addition, the debugging complexity increases with the number of devices. + +The device status, message sending progress, and data transmitted are variables. If these variables support global access, they can be accessed as local variables on difference devices. This simplifies data synchronization between multiple devices. + +The distributed data object (**DataObject**) implements global access to variables. **DataObject** provides basic data object management capabilities and distributed capabilities. You can use the APIs to create, query, delete, and modify in-memory objects and subscribe to event notifications. OpenHarmony also provides easy-to-use JS APIs for distributed application scenarios to easily implement cross-device data collaboration for the same application. In addition, object status and data changes on different devices can be observed. This feature implements data object collaboration for the same application between multiple devices that form a Super Device. **DataObject** greatly reduces the development workloads compared with the traditional mode. + + +## Basic Concepts + +- Distributed in-memory database
+ The distributed in-memory database caches data in the memory so that applications can quickly access data without persisting data. If the database is closed, the data is not retained. + +- Distributed data object + A distributed data object is an encapsulation of the JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading data from and writing data to a distributed data object are mapped to the **get()** and **put()** operations in the corresponding database, respectively. + + The distributed data object can be in the following states in its lifecycle: + + - **Uninitialized**: The distributed data object is not instantiated or has been destroyed. + - **Local**: The data table is created, but the data cannot be synchronized. + - **Distributed**: The data table is created, and there are at least two online devices with the same session ID. In this case, data can be synchronized across devices. If a device is offline or the session ID is empty, the distributed data object changes to the local state. + + +## Working Principles + +**Figure 1** Working mechanism + +![distributedObject](figures/distributedObject.jpg) + +The distributed data objects are encapsulated into JS objects in distributed in-memory databases. This allows the distributed data objects to be operated in the same way as local variables. The system automatically implements cross-device data synchronization. + + +### JS Object Storage and Encapsulation Mechanism + +- An in-memory database is created for each distributed data object instance and identified by a session ID (**SessionId**). The in-memory databases created for different applications are isolated from each other. + +- When a distributed data object is instantiated, all properties of the object are traversed recursively. **Object.defineProperty** is used to define the **set()** and **get()** methods of all properties. The **set()** and **get()** methods correspond to the **put** and **get** operations of a record in the database, respectively. **Key** specifies the property name, and **Value** specifies the property value. + +- When a distributed data object is read or written, the **set()** and **get()** methods are automatically called to perform the related operations to the database. + +**Table 1** Correspondence between a distributed data object and a distributed database + +| Distributed Data Object Instance| Object Instance| Property Name| Property Value| +| -------- | -------- | -------- | -------- | +| Distributed in-memory database| Database identified by **sessionID**| Key of a record in the database| Value of a record in the database| + + +### Cross-Device Synchronization and Data Change Notification Mechanism + +The distributed data object is used to implement data synchronization between objects. You can create a distributed data object and set **sessionID** for the devices on a trusted network. The distributed data objects with the same **sessionID** on different devices can synchronize data with each other. + +As shown in the following figure, distributed data object 1 on device A and device B have the same session ID **session1**. The synchronization relationship of session1 is established between the two objects. + + **Figure 2** Object synchronization relationship + +![distributedObject_sync](figures/distributedObject_sync.jpg) + +For each device, only one object can be added to a synchronization relationship. As shown in the preceding figure, distributed data object 2 of device A cannot be added to session 1 because distributed data object 1 of device A has been added to session 1. + +After the synchronization relationship is established, each session has a copy of shared object data. The distributed data objects added to the same session support the following operations: + + (1) Reading or modifying the data in the session. + + (2) Listening for data changes made by other devices. + + (3) Listening for status changes, such as the addition and removal of other devices. + + +### Minimum Unit to Synchronize + +Attribute is the minimum unit to synchronize in distributed data objects. For example, object 1 in the following figure has three attributes: name, age, and parents. If one of the attributes is changed, only the changed attribute needs to be synchronized. + +**Figure 3** Synchronization of distributed data objects + + +![distributedObject_syncView](figures/distributedObject_syncView.jpg) + + +### Object Persistence Mechanism + +Distributed data objects run in the process space of applications. When the data of a distributed data object is persisted in the distributed database, the data will not be lost after the application exits. + +You need to persist distributed data objects in the following scenarios: + +- Enable an application to retrieve the exact same data after it is opened again. In this case, you need to persist the distributed data object (for example, object 1). After the application is opened again, create a distributed data object (for example, object 2) and set the session ID of object 1 for object 2. Then, the application can retrieve the data of object 1. + +- Enable an application opened on another device to retrieve the exact same data. In this case, you need to persist the distributed data object (for example, object 1) on device A and synchronize the data to device B. Then, create a distributed data object (for example, object 2) and set the session ID of object 1 for object 2. When the application is opened on device B, it can retrieve the same application data used on device A before the application is closed. + + +## Constraints + +- Data synchronization can be implemented across devices only for the applications with the same **bundleName**. + +- Data can be synchronized only for the distributed data objects with the same **sessionID** of the same application. + +- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects. + +- The maximum size of a distributed data object is 500 KB. + +- It takes about 50 ms from the time when 1 KB of data starts to be modified on a device to the time when another device receives a data change notification. + +- A maximum of 16 distributed data object instances can be created for an application. + +- For optimal performance and user experience, the maximum number of devices for data collaboration is 3. + +- For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified. + +- Only JS APIs are supported. + + +## Available APIs + +The following table lists the APIs for cross-device synchronization of distributed data objects. Most of the interfaces are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed Data Object](../reference/apis/js-apis-data-distributedobject.md). + +| API| Description| +| -------- | -------- | +| create(context: Context, source: object): DataObject | Creates a distributed data object instance.| +| genSessionId(): string | Generates a session ID for distributed data objects.| +| setSessionId(sessionId: string, callback: AsyncCallback<void>): void | Sets a session ID for data synchronization. Automatic synchronization is performed for devices with the same session ID on a trusted network.| +| setSessionId(callback: AsyncCallback<void>): void | Exits all sessions.| +| on(type: 'change', callback: Callback<{ sessionId: string, fields: Array<string> }>): void | Subscribes to data changes of this distributed data object.| +| on(type: 'status', callback: Callback<{ sessionId: string, networkId: string, status: 'online' \| 'offline' }>): void | Subscribes to status changes of this distributed data object.| +| save(deviceId: string, callback: AsyncCallback<SaveSuccessResponse>): void | Saves a distributed data object.| +| revokeSave(callback: AsyncCallback<RevokeSaveSuccessResponse>): void | Revokes the save operation of the distributed data object.| + + +## How to Develop + +The following example demonstrates how to implement a distributed data object synchronization. + +1. Import the **@ohos.data.distributedDataObject** module. + + ```js + import distributedDataObject from '@ohos.data.distributedDataObject'; + ``` + +2. Request permissions. + + 1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + 2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). + +3. Creates a distributed data object instance. + + Stage model: + + ```js + // Import the module. + import distributedDataObject from '@ohos.data.distributedDataObject'; + import UIAbility from '@ohos.app.ability.UIAbility'; + + class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + // Create a distributed data object, which contains attributes of the string, number, boolean, and object types. + let localObject = distributedDataObject.create(this.context, { + name: 'jack', + age: 18, + isVis: false, + parent: { mother: 'jack mom', father: 'jack Dad' }, + list: [{ mother: 'jack mom' }, { father: 'jack Dad' }] + }); + } + } + ``` + + FA model: + + + ```js + // Import the module. + import distributedDataObject from '@ohos.data.distributedDataObject'; + import featureAbility from '@ohos.ability.featureAbility'; + // Obtain the context. + let context = featureAbility.getContext(); + // Create a distributed data object, which contains attributes of the string, number, boolean, and object types. + let localObject = distributedDataObject.create(context, { + name: 'jack', + age: 18, + isVis: false, + parent: { mother: 'jack mom', father: 'jack Dad' }, + list: [{ mother: 'jack mom' }, { father: 'jack Dad' }] + }); + ``` + +4. Set the same session ID for the distributed data objects for data synchronization. The data objects in the synchronization network include the local and remote objects. + + ```js + // Set a session ID, for example, 123456, for device 1. + let sessionId = '123456'; + + localObject.setSessionId(sessionId); + + // Set the same session ID for device 2. + + // Create a distributed data object, which contains attributes of the string, number, boolean, and object types. + let remoteObject = distributedDataObject.create(this.context, { + name: undefined, + age: undefined, // undefined indicates that the data comes from the peer end. + isVis: true, + parent: undefined, + list: undefined + }); + // After learning that the device goes online, the remote object synchronizes data. That is, name changes to jack and age to 18. + remoteObject.setSessionId(sessionId); + ``` + +5. Observe data changes of a distributed data object. You can subscribe to data changes of the remote object. When the data in the remote object changes, a callback will be invoked to return a data change event. + + ```js + function changeCallback(sessionId, changeData) { + console.info(`change: ${sessionId}`); + + if (changeData !== null && changeData !== undefined) { + changeData.forEach(element => { + console.info(`The element ${localObject[element]} changed.`); + }); + } + } + + // To refresh the page in changeCallback, correctly bind (this) to the changeCallback. + localObject.on("change", this.changeCallback.bind(this)); + ``` + +6. Modify attributes of the distributed data object. The object attributes support basic data types (number, Boolean, and string) and complex data types (array and nested basic types). + + ```js + localObject.name = 'jack1'; + localObject.age = 19; + localObject.isVis = false; + localObject.parent = { mother: 'jack1 mom', father: 'jack1 Dad' }; + localObject.list = [{ mother: 'jack1 mom' }, { father: 'jack1 Dad' }]; + ``` + + > **NOTE** + > + > For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified. + + + ```js + // Supported modification. + localObject.parent = { mother: 'mom', father: 'dad' }; + // Modification not supported. + localObject.parent.mother = 'mom'; + ``` + +7. Access a distributed data object. Obtain the distributed data object attributes, which are the latest data on the network. + + ```js + console.info(`name:${localObject['name']}`); + ``` + +8. Unsubscribe from data changes. You can specify the callback to unregister. If you do not specify the callback, all data change callbacks of the distributed data object will be unregistered. + + ```js + // Unregister this.changeCallback. + localObject.off('change', this.changeCallback); + // Unregister all data change callbacks. + localObject.off('change'); + ``` + +9. Subscribes to status changes of a distributed data object. A callback will be invoked to report the status change when the target distributed data object goes online or offline. + + ```js + function statusCallback(sessionId, networkId, status) { + // Service processing. + } + + localObject.on('status', this.statusCallback); + ``` + +10. Save a distributed data object and revoke the data saving operation. + + ```js + // Save the data object if the device on the network needs to retrieve the object data after the application exits. + localObject.save('local').then((result) => { + console.info(`Succeeded in saving. SessionId:${result.sessionId},version:${result.version},deviceId:${result.deviceId}`); + }).catch((err) => { + console.error(`Failed to save. Code:${err.code},message:${err.message}`); + }); + + // Revoke the save of a distributed data object. + localObject.revokeSave().then((result) => { + console.info(`Succeeded in revokeSaving. Session:${result.sessionId}`); + }).catch((err) => { + console.error(`Failed to revokeSave. Code:${err.code},message:${err.message}`); + }); + ``` + +11. Unsubscribe from the status changes of a distributed data object. You can specify the callback to unregister. If you do not specify the callback, this API unregisters all status change callbacks of this distributed data object. + + ```js + // Unregister this.statusCallback. + localObject.off('status', this.statusCallback); + // Unregister all status change callbacks. + localObject.off('status'); + ``` + +12. Remove a distributed data object from the synchronization network. The data of the removed distributed data object will not be synchronized to other devices. + + ```js + localObject.setSessionId(() => { + console.info('leave all lession.'); + }); + ``` diff --git a/en/application-dev/database/data-sync-of-kv-store.md b/en/application-dev/database/data-sync-of-kv-store.md new file mode 100644 index 0000000000000000000000000000000000000000..a32e605f9c358e83b47660d12a32eeb29d677851 --- /dev/null +++ b/en/application-dev/database/data-sync-of-kv-store.md @@ -0,0 +1,278 @@ +# Cross-Device Synchronization of KV Stores + + +## When to Use + +KV Stores are suitable for storing service data with simple relationships. It provides higher read and write performance than the SQL database. KV stores are widely used because the simplicity of the KV data model poses fewer database version compatibility issues in distributed scenarios and simplifies conflict handling in data synchronization. + + +## Basic Concepts + +Before implementing cross-device synchronization of KV stores, understand the following concepts: + + +### Single KV Store + +In a single KV store, data is saved in the unit of a single entry. When data is modified locally, the data entry is updated no matter whether it has been synchronized. Only one copy of data is retained globally for multiple devices. The data of the latest time is kept for the same entry (with the same primary code) of multiple devices. The data in single KV stores is not differentiated by device. If the data modified on multiple devices has the same key, the value will be overwritten. For the data written or modified locally, the data with the latest time is synchronized to other devices. Single KV stores are used to store information, such as the Contacts and weather application data. + +![singleKVStore](figures/singleKVStore.jpg) + + +### Device KV Store + +In a device KV store, the local device ID is added before the key of the KV pair stored by an application. In this way, the data of different devices is isolated. Data is managed by device and can be queried by device. + +The underlying devices manage the data by device. The device KV stores support distributed data query by device, but do not support modification of the data synchronized from peer devices. Device KV stores are used to store the data that needs to be accessed by device, such as the Gallery thumbnails. + +![deviceKVStore](figures/deviceKVStore.jpg) + + +## Synchronization Types + +The **DatamgrService** provides the following synchronization types: + + +- Manual synchronization: The application calls **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). You can use the [**sync()** with the **query** parameter](../reference/apis/js-apis-distributedKVStore.md#sync-1) to synchronize the data that meets the specified conditions. The manual synchronization is available only for system applications. + +- Automatic synchronization: The distributed database automatically pushes local data to the remote end and pulls remote data to the local end. An automatic synchronization is triggered when a device goes online or an application updates data. + + +## Working Principles + +After completing device discovery and authentication, the underlying communication component notifies the application that the device goes online. The **DatamgrService** then establishes an encrypted transmission channel to synchronize data between the two devices. + + +### Cross-Device Data Synchronization Mechanism + +![kvStore](figures/kvStore.jpg) + +When **put()** or **delete()** is called successfully, an automatic synchronization is triggered. The distributed data is sent to the peer device through the communication adaptation layer for synchronization. + +If **sync()** is called successfully, a manual synchronization is triggered to send distributed data to the peer device through the communication adaptation layer. + + +### Data Change Notification Mechanism + +When data is added, deleted, or modified, a notification is sent to the subscriber. The notifications can be classified into the following types: + +- Local data change notification: subscription of the application data changes on the local device. When the data in the local KV store is added, deleted, or modified in the database, a notification is received. + +- Distributed data change notification: subscription of the application data changes of other devices in the network. When the data in the local KV store changes after being synchronized with data from another device in the same network, a notification is received. + + +## Constraints + +- For each record in a device KV store, the key cannot exceed 896 bytes and the value cannot exceed 4 MB. + +- For each record in a single KV store, the key cannot exceed 1 KB and the value cannot exceed 4 MB. + +- The KV stores do not support custom conflict resolution policies for applications. + +- A maximum of 16 KV stores can be opened simultaneously for an application. + +- Each KV store supports a maximum of eight callbacks for subscription of data change notifications. + +- The manual synchronization is available only for system applications. + + +## Available APIs + +The following table lists the APIs for cross-device data synchronization of the single KV store. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). + +| API| Description| +| -------- | -------- | +| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** instance to manage database objects.| +| getKVStore<T>(storeId: string, options: Options, callback: AsyncCallback<T>): void | Creates and obtains a KV store of the specified type.| +| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback<void>): void | Inserts and updates data.| +| on(event: 'dataChange', type: SubscribeType, listener: Callback<ChangeNotification>): void | Subscribes to data changes in the KV store.| +| get(key: string, callback: AsyncCallback<boolean \| string \| number \| Uint8Array>): void | Queries the value of the specified key.| +| sync(deviceIds: string[], mode: SyncMode, delayMs?: number): void | Triggers a manual synchronization of the KV store.| + + +## How to Develop + +The following uses a single KV store as an example to describe how to implement cross-device data synchronization. The following describes the development process. + +![kvStore_development_process](figures/kvStore_development_process.png) + +> **NOTE** +> +> The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization). + +1. Import the module. + + ```js + import distributedKVStore from '@ohos.data.distributedKVStore'; + ``` + +2. Request permissions. + + 1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + 2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). + +3. Create a **KvManager** instance based on the specified **KvManagerConfig** object. + + 1. Create a **kvManagerConfig** object based on the application context. + 2. Create a **KvManager** instance. + + + ```js + // Obtain the context of the stage model. + import UIAbility from '@ohos.app.ability.UIAbility'; + let kvManager; + let context = null; + + class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + context = this.context; + } + } + + // Obtain the context of the FA model. + import featureAbility from '@ohos.ability.featureAbility'; + + let context = featureAbility.getContext(); + + // Construct a kvManager instance. + try { + const kvManagerConfig = { + bundleName: 'com.example.datamanagertest', + context: context + } + kvManager = distributedKVStore.createKVManager(kvManagerConfig); + console.info('Succeeded in creating KVManager.'); + // Create and obtain the KV store. + } catch (e) { + console.error(`Failed to create KVManager. Code:${e.code},message:${e.message}`); + } + ``` + +4. Obtain the KV store of the specified type. + + 1. Declare the ID of the distributed KV store to create. + 2. Disable the auto synchronization function (**autoSync:false**) to facilitate subsequent verification of the synchronization function. If synchronization is required, call the **sync()** interface. + + + ```js + try { + const options = { + createIfMissing: true, + encrypt: false, + backup: false, + autoSync: false, + // If kvStoreType is left empty, a device KV store is created by default. + kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, + // Device KV store: kvStoreType: distributedKVStore.KVStoreType.DEVICE_COLLABORATION, + securityLevel: distributedKVStore.SecurityLevel.S1 + }; + kvManager.getKVStore('storeId', options, (err, kvStore) => { + if (err) { + console.error(`Failed to get KVStore: Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in getting KVStore.'); + // Perform related data operations. + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +5. Subscribe to changes of distributed data. + + ```js + try { + kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => { + console.info(`dataChange callback call data: ${data}`); + }); + } catch (e) { + console.error(`An unexpected error occurred. code:${e.code},message:${e.message}`); + } + ``` + +6. Write data to the single KV store. + + 1. Construct the key and value to be written to the single KV store. + 2. Write KV pairs to the single KV store. + + + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + }); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + ``` + +7. Query data in the single KV store. + + 1. Construct the key to be queried from the single KV store. + 2. Query data from the single KV store. + + + ```js + const KEY_TEST_STRING_ELEMENT = 'key_test_string'; + const VALUE_TEST_STRING_ELEMENT = 'value_test_string'; + try { + kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, (err) => { + if (err !== undefined) { + console.error(`Failed to put data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in putting data.'); + kvStore.get(KEY_TEST_STRING_ELEMENT, (err, data) => { + if (err != undefined) { + console.error(`Failed to get data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`Succeeded in getting data. Data:${data}`); + }); + }); + } catch (e) { + console.error(`Failed to get data. Code:${e.code},message:${e.message}`); + } + ``` + +8. Synchronize data to other devices. + + Select the devices to be synchronized with data and the synchronization mode. The user needs to confirm the synchronization mode when the application is started for the first time. + + > **NOTE** + > + > If manual synchronization is used, **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications. + + + ```js + import deviceManager from '@ohos.distributedHardware.deviceManager'; + + let devManager; + // create deviceManager + deviceManager.createDeviceManager('bundleName', (err, value) => { + if (!err) { + devManager = value; + // deviceIds is obtained by devManager.getTrustedDeviceListSync. + let deviceIds = []; + if (devManager !== null) { + // The ohos.permission.ACCESS_SERVICE_DM permission is required. This permission is available only for system applications. + let devices = devManager.getTrustedDeviceListSync(); + for (let i = 0; i < devices.length; i++) { + deviceIds[i] = devices[i].deviceId; + } + } + try { + // 1000 indicates the maximum delay, in ms. + kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000); + } catch (e) { + console.error(`An unexpected error occurred. Code:${e.code},message:${e.message}`); + } + } + }); + ``` diff --git a/en/application-dev/database/data-sync-of-rdb-store.md b/en/application-dev/database/data-sync-of-rdb-store.md new file mode 100644 index 0000000000000000000000000000000000000000..444c5ab1ffadf09cfea56732e417df660c20ca8d --- /dev/null +++ b/en/application-dev/database/data-sync-of-rdb-store.md @@ -0,0 +1,173 @@ +# Cross-Device Synchronization of RDB Stores + + +## When to Use + +You can synchronize the application data in a local RDB store on a device to other divices that form a Super Device. + + +## Basic Concepts + +OpenHamony supports synchronization of the relational data of an application across multiple devices. + +- Distributed table list
After a table is created for an application in an RDB store, you can set it as a distributed table. When querying the RDB store of a remote device, you can obtain the distributed table name of the remote device based on the local table name. + +- Synchronization mode
Data can be synchronized between devices in either of the following ways:
- Pushing data from a local device to a remote device.
- Pulling data from a remote device to a local device. + + +## Working Principles + +After completing device discovery and authentication, the underlying communication component notifies the application that the device goes online. The **DatamgrService** then establishes an encrypted transmission channel to synchronize data between the two devices. + + +### Cross-Device Data Synchronization Mechanism + +![relationalStore_sync](figures/relationalStore_sync.jpg) + +After writing data to an RDB store, the service sends a synchronization request to the **DatamgrService**. + +The **DatamgrService** reads the data to be synchronized from the application sandbox and sends the data to the **DatamgrService** of the target device based on the **deviceId** of the peer device. Then, the **DatamgrService** writes the data to the RDB of the same application. + + +### Data Change Notification Mechanism + +When data is added, deleted, or modified, a notification is sent to the subscriber. The notifications can be classified into the following types: + +- Local data change notification: subscription of the application data changes on the local device. When the data in the local KV store is added, deleted, or modified in the database, a notification is received. + +- Distributed data change notification: subscription of the application data changes of other devices in the network. When the data in the local RDB store changes after being synchronized with data from another device in the same network, a notification is received. + + +## Constraints + +- A maximum of 16 distributed RDB stores can be opened simultaneously for an application. + +- Each RDB store supports a maximum of eight callbacks for subscription of data change notifications. + +- Third-party applications cannot call the distributed APIs that must be specified with the device. + + +## Available APIs + +The following table lists the APIs for cross-device data synchronization of RDB stores. Most of the APIs are executed asynchronously, using a callback or promise to return the result. The following table uses the callback-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). + +| API| Description| +| -------- | -------- | +| setDistributedTables(tables: Array<string>, callback: AsyncCallback<void>): void | Sets the distributed tables to be synchronized.| +| sync(mode: SyncMode, predicates: RdbPredicates, callback: AsyncCallback<Array<[string, number]>>): void | Synchronizes data across devices.| +| on(event: 'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | Subscribes to changes in the distributed data.| +| off(event:'dataChange', type: SubscribeType, observer: Callback<Array<string>>): void | Unsubscribe from changes in the distributed data.| +| obtainDistributedTableName(device: string, table: string, callback: AsyncCallback<string>): void; | Obtains the table name on the specified device based on the local table name.| +| remoteQuery(device: string, table: string, predicates: RdbPredicates, columns: Array<string> , callback: AsyncCallback<ResultSet>): void | Queries data from the RDB store of a remote device based on specified conditions.| + + +## How to Develop + +> **NOTE** +> +> The data on a device can be synchronized only to the devices whose data security labels are not higher than the security level of the device. For details, see [Access Control Mechanism in Cross-Device Synchronization](sync-app-data-across-devices-overview.md#access-control-mechanism-in-cross-device-synchronization). + +1. Import the module. + + ```js + import relationalStore from '@ohos.data.relationalStore'; + ``` + +2. Request permissions. + + 1. Request the **ohos.permission.DISTRIBUTED_DATASYNC** permission. For details, see [Declaring Permissions in the Configuration File](../security/accesstoken-guidelines.md#declaring-permissions-in-the-configuration-file). + 2. Display a dialog box to ask authorization from the user when the application is started for the first time. For details, see [Requesting User Authorization](../security/accesstoken-guidelines.md#requesting-user-authorization). + +3. Create an RDB store and set a table for distributed synchronization. + + ```js + const STORE_CONFIG = { + name: 'RdbTest.db', // Database file name. + securityLevel: relationalStore.SecurityLevel.S1 // Database security level. + }; + relationalStore.getRdbStore(this.context, STORE_CONFIG, (err, store) => { + store.executeSql('CREATE TABLE IF NOT EXISTS EMPLOYEE (ID INTEGER PRIMARY KEY AUTOINCREMENT, NAME TEXT NOT NULL, AGE INTEGER, SALARY REAL, CODES BLOB)', null, (err) => { + // Set the table for distributed synchronization. + store.setDistributedTables(['EMPLOYEE']); + // Perform related operations. + }) + }) + ``` + +4. Synchronize data across devices. After **sync()** is called to trigger a synchronization, data is synchronized from the local device to all other devices on the network. + + ```js + // Construct the predicate object for synchronizing the distributed table. + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); + // Call sync() to synchronize data. + store.sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicates, (err, result) => { + // Check whether data synchronization is successful. + if (err) { + console.error(`Failed to sync data. Code:${err.code},message:${err.message}`); + return; + } + console.info('Succeeded in syncing data.'); + for (let i = 0; i < result.length; i++) { + console.info(`device:${result[i][0]},status:${result[i][1]}`); + } + }) + ``` + +5. Subscribe to changes in the distributed data. The data synchronization triggers the **observer** callback registered in **on()**. The input parameter of the callback is the ID of the device whose data changes. + + ```js + let observer = function storeObserver(devices) { + for (let i = 0; i < devices.length; i++) { + console.info(`The data of device:${devices[i]} has been changed.`); + } + } + + try { + // Register an observer to listen for the changes of the distributed data. + // When data in the RDB store changes, the registered callback will be invoked to return the data changes. + store.on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, observer); + } catch (err) { + console.error('Failed to register observer. Code:${err.code},message:${err.message}'); + } + + // You can unsubscribe from the data changes if required. + try { + store.off('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, observer); + } catch (err) { + console.error('Failed to register observer. Code:${err.code},message:${err.message}'); + } + ``` + +6. Query data across devices. If data synchronization is not complete or triggered, an application can call **remoteQuery()** to query data from a remote device. + + > **NOTE** + > + > **deviceIds** is obtained by using [devManager.getTrustedDeviceListSync](../reference/apis/js-apis-device-manager.md#gettrusteddevicelistsync). The APIs of the **deviceManager** module are all system interfaces and available only to system applications. + + + ```js + // Obtain device IDs. + import deviceManager from '@ohos.distributedHardware.deviceManager'; + + deviceManager.createDeviceManager("com.example.appdatamgrverify", (err, manager) => { + if (err) { + console.info(`Failed to create device manager. Code:${err.code},message:${err.message}`); + return; + } + let devices = manager.getTrustedDeviceListSync(); + let deviceId = devices[0].deviceId; + + // Construct a predicate object for querying the distributed table. + let predicates = new relationalStore.RdbPredicates('EMPLOYEE'); + // Query data from the specified remote device and return the query result. + store.remoteQuery(deviceId, 'EMPLOYEE', predicates, ['ID', 'NAME', 'AGE', 'SALARY', 'CODES'], + function (err, resultSet) { + if (err) { + console.error(`Failed to remoteQuery data. Code:${err.code},message:${err.message}`); + return; + } + console.info(`ResultSet column names: ${resultSet.columnNames}, column count: ${resultSet.columnCount}`); + } + ) + }) + ``` diff --git a/en/application-dev/database/database-datashare-guidelines.md b/en/application-dev/database/database-datashare-guidelines.md deleted file mode 100644 index 2b99fd72c8482dd9c0c6b8b34e85eba6acf17e98..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-datashare-guidelines.md +++ /dev/null @@ -1,231 +0,0 @@ -# DataShare Development -The **DataShare** module allows an application to manage its own data and share data with other applications. Currently, data can be shared only between applications on the same device. - -## Available APIs - -**Table 1** APIs of the data provider - -|API|Description| -|:------|:------| -|onCreate?(want: Want, callback: AsyncCallback<void>): void|Called to initialize service logic when the data provider application is created, for example, when a database is created.| -|insert?(uri: string, valueBucket: ValuesBucket, callback: AsyncCallback<number>): void|Inserts data into the database.| -|update?(uri: string, predicates: DataSharePredicates, valueBucket: ValuesBucket, callback: AsyncCallback<number>): void|Updates data in the database.| -|query?(uri: string, predicates: DataSharePredicates, columns: Array<string>, callback: AsyncCallback<Object>): void|Queries data from the database.| -|delete?(uri: string, predicates: DataSharePredicates, callback: AsyncCallback<number>): void|Deletes data from the database.| - -For details about the data provider APIs, see [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md). - -**Table 2** APIs of the data consumer - -| API | Description | -| :----------------------------------------------------------- | :--------------------------------- | -| createDataShareHelper(context: Context, uri: string, callback: AsyncCallback<DataShareHelper>): void | Creates a **DataShareHelper** instance. | -| insert(uri: string, value: ValuesBucket, callback: AsyncCallback<number>): void | Inserts a single data record into the database. | -| update(uri: string, predicates: DataSharePredicates, value: ValuesBucket, callback: AsyncCallback<number>): void | Updates data in the database. | -| query(uri: string, predicates: DataSharePredicates, columns: Array<string>, callback: AsyncCallback<DataShareResultSet>): void | Queries data from the database. | -| delete(uri: string, predicates: DataSharePredicates, callback: AsyncCallback<number>): void | Deletes one or more data records from the database.| - -For more information, see [DataShareHelper](../reference/apis/js-apis-data-dataShare.md). - -## When to Use - -There are two roles in **DataShare**: - -- Data provider: adds, deletes, modifies, and queries data, opens files, and shares data. -- Data consumer: accesses the data provided by the provider using **DataShareHelper**. - -### Data Provider Application Development (for System Applications Only) - -[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required. - -- **onCreate** - - Called by the server to initialize service logic when the **DataShare** client connects to the **DataShareExtensionAbility** server. - -- **insert** - - Inserts data. This API is called when the client requests to insert data. - -- **update** - - Updates data. This API is called when the client requests to update data. - -- **delete** - - Deletes data. This API is called when the client requests to delete data. - -- **query** - - Queries data. This API is called when the client requests to query data. - -- **batchInsert** - - Batch inserts data. This API is called when the client requests to batch insert data. - -- **normalizeUri** - - Converts the URI provided by the client to the URI used by the server. - -- **denormalizeUri** - - Converts the URI used by the server to the initial URI passed by the client. - -Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows: - -1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareAbility**. - -2. Right-click the **DataShareAbility** directory, and choose **New > TypeScript File** to create a file named **DataShareAbility.ts**. - -3. In the **DataShareAbility.ts** file, import **DataShareExtensionAbility** and other dependencies. - - ```ts - import Extension from '@ohos.application.DataShareExtensionAbility'; - import rdb from '@ohos.data.relationalStore'; - import fileIo from '@ohos.fileio'; - import dataSharePredicates from '@ohos.data.dataSharePredicates'; - ``` - -4. Override **DataShareExtensionAbility** APIs based on actual requirements. For example, if the data provider provides only data query, override only **query()**. - -5. Implement the data provider services. For example, implement data storage of the data provider by using a database, reading and writing files, or accessing the network. - - ```ts - const DB_NAME = "DB00.db"; - const TBL_NAME = "TBL00"; - const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS " - + TBL_NAME - + " (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)"; - - let rdbStore; - let result; - - export default class DataShareExtAbility extends Extension { - private rdbStore_; - - // Override onCreate(). - onCreate(want, callback) { - result = this.context.cacheDir + '/datashare.txt'; - // Create an RDB store. - rdb.getRdbStore(this.context, { - name: DB_NAME, - securityLevel: rdb.SecurityLevel.S1 - }, function (err, data) { - rdbStore = data; - rdbStore.executeSql(DDL_TBL_CREATE, [], function (err) { - console.log('DataShareExtAbility onCreate, executeSql done err:' + JSON.stringify(err)); - }); - if (callback) { - callback(); - } - }); - } - - // Override query(). - query(uri, predicates, columns, callback) { - if (predicates == null || predicates == undefined) { - console.info('invalid predicates'); - } - try { - rdbStore.query(TBL_NAME, predicates, columns, function (err, resultSet) { - if (resultSet != undefined) { - console.info('resultSet.rowCount: ' + resultSet.rowCount); - } - if (callback != undefined) { - callback(err, resultSet); - } - }); - } catch (err) { - console.error('error' + err); - } - } - // Override other APIs as required. - // ... - }; - ``` - - -6. Define **DataShareExtensionAbility** in **module.json5**. - - | Field | Description | - | --------- | ------------------------------------------------------------ | - | "name" | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**. | - | "type" | Ability type. The value is **dataShare**, indicating the development is based on the **datashare** template. | - | "uri" | URI used for communication. It is the unique identifier for the data consumer to connect to the provider. | - | "visible" | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**. | - - **module.json5 example** - - ```json - "extensionAbilities": [ - { - "srcEntrance": "./ets/DataShareExtAbility/DataShareExtAbility.ts", - "name": "DataShareExtAbility", - "icon": "$media:icon", - "description": "$string:description_datashareextability", - "type": "dataShare", - "uri": "datashare://com.samples.datasharetest.DataShare", - "visible": true - } - ] - ``` - - - -### Data Consumer Application Development - -1. Import dependencies. - - ```ts - import UIAbility from '@ohos.app.ability.UIAbility'; - import dataShare from '@ohos.data.dataShare'; - import dataSharePredicates from '@ohos.data.dataSharePredicates'; - ``` - -2. Define the URI string for communicating with the data provider. - - ```ts - // 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"); - ``` - -3. Create a **DataShareHelper** instance. - - ```ts - let dsHelper; - let abilityContext; - - export default class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage) { - abilityContext = this.context; - dataShare.createDataShareHelper(abilityContext, dseUri, (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. - - ```ts - // 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.log("dsHelper insert result: " + data); - }); - // Update data. - dsHelper.update(dseUri, predicates, updateBucket, (err, data) => { - console.log("dsHelper update result: " + data); - }); - // Query data. - dsHelper.query(dseUri, predicates, valArray, (err, data) => { - console.log("dsHelper query result: " + data); - }); - // Delete data. - dsHelper.delete(dseUri, predicates, (err, data) => { - console.log("dsHelper delete result: " + data); - }); - ``` diff --git a/en/application-dev/database/database-datashare-overview.md b/en/application-dev/database/database-datashare-overview.md deleted file mode 100644 index 53fbd723922ea602e694e0844d2486b13f48538c..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-datashare-overview.md +++ /dev/null @@ -1,56 +0,0 @@ -# DataShare Overview - -## Introduction - -The **DataShare** module allows an application to manage its own data and share data with other applications. Currently, data can be shared only between applications on the same device. - -**DataShare** must be used together with [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md). - -Data needs to be shared in a wealth of scenarios. For example, contacts, short message service (SMS), and media gallery always needs to be shared. 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. **DataShare** provides a secure data sharing mechanism for applications in a variety of scenarios. - -The data provider can directly use the **DataShare** framework to share data with other applications without complex encapsulation. The data consumer only needs to learn and use a set of interfaces because the data access mode does not vary with the data provisioning mode. This greatly reduces the learning time and development difficulty. - -## Basic Concepts - - -Before you get started, familiarize yourself with the following concepts: - - -- Data provider - - The **DataShareExtensionAbility** based on the stage model implements functions, such as selectively adding, deleting, modifying, and querying data, and opening files. It implements services related to cross-application data sharing. - -- Data consumer - - The data consumer uses **DataShareHelper**, a utility class created by [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper), to access the data provided by the data provider. - -- **ValuesBucket** - - One or more data records stored in the form of key-value (KV) pairs. The keys are of the string type. The values can be of the number, string, Boolean, or Unit8Array type. - -- Result set - - A collection of query results. Flexible data access modes are provided for users to obtain data. - -- Predicate - - Conditions specified for updating, deleting, or querying data in the database. - -## Working Principles - -**Figure 1** DataShare mechanism - - -![](figures/en_DataShare.png) - -- The **DataShareExtAbility** module, as the data provider, implements services related to data sharing between applications. -- The **DataShareHelper** module, as the data consumer, provides interfaces for accessing data, including adding, deleting, modifying, and querying data. -- The data consumer communicates with the data provider using inter-process communication (IPC). The data provider can be implemented through a database or other data storage. - -- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets. - -## Constraints - -- **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 result sets are restricted by IPC. diff --git a/en/application-dev/database/database-distributedobject-guidelines.md b/en/application-dev/database/database-distributedobject-guidelines.md deleted file mode 100644 index dcbc34b48912020e0a7c6e0c987ce5de1d0b75c8..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-distributedobject-guidelines.md +++ /dev/null @@ -1,282 +0,0 @@ -# Distributed Data Object Development - -## When to Use - -The **distributedDataObject** module provides APIs to implement data collaboration of the same application across multiple devices. In addition, the devices that form a Super Device can listen for object status and data changes with each other. - -For example, when the data of a distributed data object is added, deleted, or modified for application A on device 1, application A on device 2 can obtain the updated data. In addition, device 2 can listen for data changes and online/offline of the data objects on device 1. - -## Available APIs - -For details about the APIs, see [Distributed Data Object](../reference/apis/js-apis-data-distributedobject.md). - -### Creating a Distributed Data Object Instance - -Call **createDistributedObject()** to create a distributed data object instance. You can specify the attributes of the instance in **source**. - - -**Table 1** API for creating a distributed data object instance - -| Bundle Name| API| Description| -| -------- | -------- | -------- | -| ohos.data.distributedDataObject| createDistributedObject(source: object): DistributedObject | Creates a distributed data object instance for data operations.
- **source**: attributes of the distributed data object to create.
- **DistributedObject**: returns the distributed data object created.| - -### Generating a Session ID - -Call **genSessionId()** to generate a session ID randomly. The generated session ID can be used to set the session ID of a distributed data object. - -**Table 2** API for generating a session ID randomly - -| Bundle Name| API| Description| -| -------- | -------- | -------- | -| ohos.data.distributedDataObject| genSessionId(): string | Generates a session ID, which can be used as the session ID of a distributed data object.| - -### Setting a Session ID for a Distributed Data Object - -Call **setSessionId()** to set a session ID for a distributed data object. The session ID is a unique identifier for one collaboration across devices. The distributed data objects to be synchronized must be associated with the same session ID. - -**Table 3** API for setting a session ID - -| Class| API| Description| -| -------- | -------- | -------- | -| DistributedDataObject | setSessionId(sessionId?: string): boolean | Sets a session ID for this distributed data object.
**sessionId**: ID of the distributed data object on a trusted network. To remove a distributed data object from the network, set this parameter to "" or leave it empty.| - -### Observing Data Changes - -Call **on()** to subscribe to data changes of a distributed data object. When the data changes, a callback will be invoked to return the data changes. You can use **off()** to unsubscribe from the data changes. - -**Table 4** APIs for observing data changes of a distributed data object - -| Class| API| Description| -| -------- | -------- | -------- | -| DistributedDataObject| on(type: 'change', callback: Callback<{ sessionId: string, fields: Array<string> }>): void | Subscribes to data changes.| -| DistributedDataObject| off(type: 'change', callback?: Callback<{ sessionId: string, fields: Array<string> }>): void | Unsubscribes from data changes.
**Callback**: callback to unregister. If this parameter is not specified, all data changes of this distributed data object will be unsubscribed from. | - -### Observing Online or Offline Status - -Call **on()** to subscribe to status changes of a distributed data object. The status can be online or offline. When the status changes, a callback will be invoked to return the status. You can use **off()** to unsubscribe from the status changes. - -**Table 5** APIs for observing status changes of a distributed data object - -| Class| API| Description| -| -------- | -------- | -------- | -| DistributedDataObject| on(type: 'status', callback: Callback<{ sessionId: string, networkId: string, status: 'online' \| 'offline' }>): void | Subscribes to the status changes of a distributed data object.| -| DistributedDataObject| off(type: 'status', callback?: Callback<{ sessionId: string, deviceId: string, status: 'online' \| 'offline' }>): void | Unsubscribes from status changes of a distributed data object.| - -### Saving or Deleting a Distributed Data Object - -Call **save()** to save a distributed data object. When the application is active, the saved data will not be released. When the application exits and restarts, the data saved on the device will be restored. - -Call **revokeSave()** to delete a distributed data object that is no longer required. If the distributed data object is saved on the local device, **revokeSave()** will delete the data from all trusted devices. If the distributed data object is not saved on the local device, **revokeSave()** will delete the data from the local device. - -The saved data will be released in the following cases: - -- The data is stored for more than 24 hours. -- The application has been uninstalled. -- Data is successfully restored. - -**Table 6** APIs for saving and deleting a distributed data object - -| Class| API| Description| -| -------- | -------- | -------- | -| DistributedDataObject | save(deviceId: string): Promise<SaveSuccessResponse> | Saves a distributed data object.| -| DistributedDataObject| revokeSave(): Promise<RevokeSaveSuccessResponse> | Deletes a distributed data object. | - -## How to Develop - -The following example shows how to implement distributed data object synchronization. - -1. Import the @ohos.data.distributedDataObject module to the development environment. - - ```js - import distributedObject from '@ohos.data.distributedDataObject'; - ``` -2. Apply for the permission. - - Add the required permission (FA model) to the **config.json** file. - - ```json - { - "module": { - "reqPermissions": [ - { - "name": "ohos.permission.DISTRIBUTED_DATASYNC" - } - ] - } - } - ``` - For the apps based on the stage model, see [Declaring Permissions](../security/accesstoken-guidelines.md#stage-model). - - This permission must also be granted by the user when the application is started for the first time. - - ```js - // FA model - import featureAbility from '@ohos.ability.featureAbility'; - - function grantPermission() { - console.info('grantPermission'); - let context = featureAbility.getContext(); - context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) { - console.info(`requestPermissionsFromUser CallBack`); - - }) - console.info('end grantPermission'); - } - - grantPermission(); - ``` - - ```ts - // Stage model - import UIAbility from '@ohos.app.ability.UIAbility'; - - let context = null; - - class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage) { - context = this.context; - } - } - - function grantPermission() { - let permissions = ['ohos.permission.DISTRIBUTED_DATASYNC']; - context.requestPermissionsFromUser(permissions).then((data) => { - console.info('success: ${data}'); - }).catch((error) => { - console.error('failed: ${error}'); - }); - } - - grantPermission(); - ``` - -3. Obtain a distributed data object instance. - - ```js - let localObject = distributedObject.createDistributedObject({ - name: undefined, - age: undefined, - isVis: true, - parent: undefined, - list: undefined - }); - let sessionId = distributedObject.genSessionId(); - ``` - -4. Add the distributed data object instance to a network for data synchronization. The data objects in the synchronization network include the local and remote objects. - - ```js - // Local object - let localObject = distributedObject.createDistributedObject({ - name: "jack", - age: 18, - isVis: true, - parent: { mother: "jack mom", father: "jack Dad" }, - list: [{ mother: "jack mom" }, { father: "jack Dad" }] - }); - localObject.setSessionId(sessionId); - - // Remote object - let remoteObject = distributedObject.createDistributedObject({ - name: undefined, - age: undefined, - isVis: true, - parent: undefined, - list: undefined - }); - // After learning that the local device goes online, the remote object synchronizes data. That is, name changes to jack and age to 18. - remoteObject.setSessionId(sessionId); - ``` - -5. Observe the data changes of the distributed data object. You can subscribe to data changes of the remote object. When the data in the remote object changes, a callback will be invoked to return the data changes. - - ```js - function changeCallback(sessionId, changeData) { - console.info("change" + sessionId); - - if (changeData != null && changeData != undefined) { - changeData.forEach(element => { - console.info("changed !" + element + " " + localObject[element]); - }); - } - } - - // To refresh the page in changeCallback, correctly bind (this) to the changeCallback. - localObject.on("change", this.changeCallback.bind(this)); - ``` - -6. Modify attributes of the distributed data object. The object attributes support basic data types (such as number, Boolean, and string) and complex data types (array and nested basic types). - - ```js - localObject.name = "jack"; - localObject.age = 19; - localObject.isVis = false; - localObject.parent = { mother: "jack mom", father: "jack Dad" }; - localObject.list = [{ mother: "jack mom" }, { father: "jack Dad" }]; - ``` - - > **NOTE**
- > For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified. - - ```js - // Supported modification. - localObject.parent = { mother: "mom", father: "dad" }; - // Modification not supported. - localObject.parent.mother = "mom"; - ``` - -7. Access the distributed data object.
Obtain the distributed data object attributes, which are the latest data on the network. - - ```js - console.info("name " + localObject["name"]); - ``` -8. Unsubscribe from data changes. You can specify the callback to unregister. If you do not specify the callback, all data change callbacks of the distributed data object will be unregistered. - - ```js - // Unregister the specified data change callback. - localObject.off("change", changeCallback); - // Unregister all data change callbacks. - localObject.off("change"); - ``` -9. Subscribe to status changes of this distributed data object. A callback will be invoked to report the status change when the target distributed data object goes online or offline. - - ```js - function statusCallback(sessionId, networkId, status) { - this.response += "status changed " + sessionId + " " + status + " " + networkId; - } - - localObject.on("status", this.statusCallback); - ``` - -10. Save a distributed data object and delete it. - - ```js - // Save a distributed data object. - localObject.save("local").then((result) => { - console.info("save sessionId " + result.sessionId); - console.info("save version " + result.version); - console.info("save deviceId " + result.deviceId); - }, (result) => { - console.info("save local failed."); - }); - // Revoke the data saving operation. - localObject.revokeSave().then((result) => { - console.info("revokeSave success."); - }, (result) => { - console.info("revokeSave failed."); - }); - ``` -11. Unsubscribe from the status changes of this distributed data object. You can specify the callback to unregister. If you do not specify the callback, this API unregisters all status change callbacks of this distributed data object. - - ```js - // Unregister the specified status change callback. - localObject.off("status", this.statusCallback); - // Unregister all status change callbacks. - localObject.off("status"); - ``` -12. Remove the distributed data object from the synchronization network. The data changes on the local object will not be synchronized to the removed distributed data object. - - ```js - localObject.setSessionId(""); - ``` diff --git a/en/application-dev/database/database-distributedobject-overview.md b/en/application-dev/database/database-distributedobject-overview.md deleted file mode 100644 index 9fb93eba7c13f85da0cdccb9036df26e4d8d8ce0..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-distributedobject-overview.md +++ /dev/null @@ -1,45 +0,0 @@ -# Distributed Data Object Overview - -The distributed data object management framework provides object-oriented in-memory data management. It provides basic data management capabilities, such as creating, querying, deleting, and modifying distributed data objects, and observing data and status changes of the distributed data objects. This management framework also provides distributed capabilities to implement data object collaboration for the same application between multiple devices that form a Super Device. - - -## Basic Concepts - -- **Distributed in-memory database** - - The distributed in-memory database caches data in the memory so that applications can quickly access data. This database, however, does not store data persistently. If the database is closed, the data is not retained. - - -- **Distributed data object** - - A distributed data object is an encapsulation of the JS object type. Each distributed data object instance creates a data table in the in-memory database. The in-memory databases created for different applications are isolated from each other. Reading data from and writing data to a distributed data object are mapped to the **get** and **put** operations in the corresponding database, respectively. - - The distributed data object can be in the following states in its lifecycle: - - - **Uninitialized**: The distributed data object is not instantiated or has been destroyed. - - **Local**: The data table is created, but the data cannot be synchronized. - - **Distributed**: The data table is created, and there are at least two online devices with the same session ID. In this case, data can be synchronized across devices. If a device is offline or the session ID is empty, the distributed data object changes to the local state. - - -## Working Principles - -The distributed data objects are encapsulated into JS objects in distributed in-memory databases. This allows the distributed data objects to be operated in the same way as local variables. The system automatically implements cross-device data synchronization. - -**Figure 1** Working mechanism - -![how-distributedobject-works](figures/how-distributedobject-works.png) - - - - -## Constraints - -- Data synchronization can be implemented across devices only for the applications with the same **bundleName**. - -- Each distributed data object occupies 100 KB to 150 KB of memory. Therefore, you are advised not to create too many distributed data objects. - -- The maximum size of a distributed data object is 500 KB. - -- For the distributed data object of the complex type, only the root attribute can be modified. The subordinate attributes cannot be modified. - -- Only JS APIs are supported. diff --git a/en/application-dev/database/database-mdds-guidelines.md b/en/application-dev/database/database-mdds-guidelines.md deleted file mode 100644 index 219a2e2493f61f4e20560855871db123a148ae9c..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-mdds-guidelines.md +++ /dev/null @@ -1,263 +0,0 @@ -# Distributed Data Service Development - -## When to Use - -The Distributed Data Service (DDS) implements synchronization of application data across user devices. When data is added, deleted, or modified for an application on a device, the same application on another device can obtain the updated data. The DDS applies to the distributed gallery, messages, contacts, and file manager. - - -## Available APIs - -For details about the APIs, see [Distributed KV Store](../reference/apis/js-apis-distributedKVStore.md). - -**Table 1** APIs provided by the DDS - -| API | Description | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| createKVManager(config: KVManagerConfig): KVManager | Creates a **KvManager** object for database management. | -| getKVStore<T extends KVStore>(storeId: string, options: Options, callback: AsyncCallback<T>): void
getKVStore<T extends KVStore>(storeId: string, options: Options): Promise<T> | Creates and obtains a KV store.| -| put(key: string, value: Uint8Array\|string\|number\|boolean, callback: AsyncCallback<void>): void
put(key: string, value: Uint8Array\|string\|number\|boolean): Promise<void> | Inserts and updates data. | -| delete(key: string, callback: AsyncCallback<void>): void
delete(key: string): Promise<void> | Deletes data. | -| get(key: string, callback: AsyncCallback<Uint8Array\|string\|boolean\|number>): void
get(key: string): Promise<Uint8Array\|string\|boolean\|number> | Obtains data. | -| on(event: 'dataChange', type: SubscribeType, observer: Callback<ChangeNotification>): void
on(event: 'syncComplete', syncCallback: Callback<Array<[string,number]>>): void | Subscribes to data changes in the KV store. | -| sync(deviceIdList: string[], mode: SyncMode, delayMs?: number): void | Triggers database synchronization in manual mode. | - -## How to Develop - -The following uses a single KV store as an example to describe the development procedure. - -1. Import the distributed data module. - - ```js - import distributedKVStore from '@ohos.data.distributedKVStore'; - ``` - -2. Apply for the required permission if data synchronization is required. - - Add the permission required (FA model) in the **config.json** file. The sample code is as follows: - - ```json - { - "module": { - "reqPermissions": [ - { - "name": "ohos.permission.DISTRIBUTED_DATASYNC" - } - ] - } - } - ``` - - For the apps based on the stage model, see [Declaring Permissions](../security/accesstoken-guidelines.md#stage-model). - - This permission must also be granted by the user when the application is started for the first time. The sample code is as follows: - - ```js - // FA model - import featureAbility from '@ohos.ability.featureAbility'; - - function grantPermission() { - console.info('grantPermission'); - let context = featureAbility.getContext(); - context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666).then((data) => { - console.info('success: ${data}'); - }).catch((error) => { - console.error('failed: ${error}'); - }) - } - - grantPermission(); - - // Stage model - import UIAbility from '@ohos.app.ability.UIAbility'; - - let context = null; - - class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage) { - let context = this.context; - } - } - - function grantPermission() { - let permissions = ['ohos.permission.DISTRIBUTED_DATASYNC']; - context.requestPermissionsFromUser(permissions).then((data) => { - console.log('success: ${data}'); - }).catch((error) => { - console.error('failed: ${error}'); - }); - } - - grantPermission(); - ``` - -3. Create a **KvManager** instance based on the specified **KvManagerConfig** object. - - 1. Create a **kvManagerConfig** object based on the application context. - 2. Create a **KvManager** instance. - - The sample code is as follows: - - ```js - // Obtain the context of the FA model. - import featureAbility from '@ohos.ability.featureAbility'; - let context = featureAbility.getContext(); - - // Obtain the context of the stage model. - import UIAbility from '@ohos.app.ability.UIAbility'; - let context = null; - class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage){ - context = this.context; - } - } - - let kvManager; - try { - const kvManagerConfig = { - bundleName: 'com.example.datamanagertest', - context:context, - } - kvManager = distributedKVStore.createKVManager(kvManagerConfig); - console.log("Created KVManager successfully"); - } catch (e) { - console.error(`Failed to create KVManager. Code is ${e.code}, message is ${e.message}`); - } - ``` - -4. Create and obtain a single KV store. - - 1. Declare the ID of the single KV store to create. - 2. Create a single KV store. You are advised to disable automatic synchronization (`autoSync:false`) and call `sync` when a synchronization is required. - - The sample code is as follows: - - ```js - let kvStore; - try { - const options = { - createIfMissing: true, - encrypt: false, - backup: false, - autoSync: false, - kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION, - securityLevel: distributedKVStore.SecurityLevel.S1 - }; - kvManager.getKVStore('storeId', options, function (err, store) { - if (err) { - console.error(`Failed to get KVStore: code is ${err.code}, message is ${err.message}`); - return; - } - console.log('Obtained KVStore successfully'); - kvStore = store; - }); - } catch (e) { - console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`); - } - ``` - - > **NOTE**
- > - > For data synchronization between networked devices, you are advised to open the distributed KV store during application startup to obtain the database handle. With this database handle (`kvStore` in this example), you can perform operations, such as inserting data into the KV store, without creating the KV store repeatedly during the lifecycle of the handle. - -5. Subscribe to changes in the distributed data. - - The following is the sample code for subscribing to the data changes of a single KV store: - - ```js - try{ - kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, function (data) { - console.log(`dataChange callback call data: ${data}`); - }); - }catch(e){ - console.error(`An unexpected error occured. Code is ${e.code}, message is ${e.message}`); - } - ``` - -6. Write data to the single KV store. - - 1. Construct the `Key` and `Value` to be written into the single KV store. - 2. Write key-value pairs into the single KV store. - - The following is the sample code for writing key-value pairs of the string type into the single KV store: - - ```js - const KEY_TEST_STRING_ELEMENT = 'key_test_string'; - const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; - try { - kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { - if (err != undefined) { - console.error(`Failed to put data. Code is ${err.code}, message is ${err.message}`); - return; - } - console.log("Put data successfully"); - }); - }catch (e) { - console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`); - } - ``` - -7. Query data in the single KV store. - - 1. Construct the `Key` to be queried from the single KV store. - 2. Query data from the single KV store. - - The following is the sample code for querying data of the string type from the single KV store: - - ```js - const KEY_TEST_STRING_ELEMENT = 'key_test_string'; - const VALUE_TEST_STRING_ELEMENT = 'value-test-string'; - try { - kvStore.put(KEY_TEST_STRING_ELEMENT, VALUE_TEST_STRING_ELEMENT, function (err,data) { - if (err != undefined) { - console.error(`Failed to put data. Code is ${err.code}, message is ${err.message}`); - return; - } - console.log("Put data successfully"); - kvStore.get(KEY_TEST_STRING_ELEMENT, function (err,data) { - if (err != undefined) { - console.error(`Failed to obtain data. Code is ${err.code}, message is ${err.message}`); - return; - } - console.log(`Obtained data successfully:${data}`); - }); - }); - }catch (e) { - console.error(`Failed to obtain data. Code is ${e.code}, message is ${e.message}`); - } - ``` - -8. Synchronize data to other devices. - - Select the devices in the same network and the synchronization mode to synchronize data. - - > **NOTE**
- > - > The APIs of the `deviceManager` module are system interfaces. - - The following is the example code for synchronizing data in a single KV store: - - ```js - import deviceManager from '@ohos.distributedHardware.deviceManager'; - - let devManager; - // Create deviceManager. - deviceManager.createDeviceManager('bundleName', (err, value) => { - if (!err) { - devManager = value; - // deviceIds is obtained by deviceManager by calling getTrustedDeviceListSync(). - let deviceIds = []; - if (devManager != null) { - var devices = devManager.getTrustedDeviceListSync(); - for (var i = 0; i < devices.length; i++) { - deviceIds[i] = devices[i].deviceId; - } - } - try{ - // 1000 indicates that the maximum delay is 1000 ms. - kvStore.sync(deviceIds, distributedKVStore.SyncMode.PUSH_ONLY, 1000); - } catch (e) { - console.error(`An unexpected error occurred. Code is ${e.code}, message is ${e.message}`); - } - } - }); - ``` diff --git a/en/application-dev/database/database-mdds-overview.md b/en/application-dev/database/database-mdds-overview.md deleted file mode 100644 index cfe264a4f7eb06cd51cb834bc3e38ee27e649a14..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-mdds-overview.md +++ /dev/null @@ -1,103 +0,0 @@ -# Distributed Data Service Overview - -The distributed data service (DDS) implements distributed database collaboration across devices for applications. - -Applications save data to distributed databases by calling the DDS APIs. The DDS isolates data of different applications based on a triplet of account, application, and database to ensure secure data access. The DDS synchronizes application data between trusted devices to provide users with consistent data access experience on different devices. - -You do not need to care about the implementation of the database locking mechanism. - - -## Basic Concepts - -### KV Data Model - -The key-value (KV) data model allows data to be organized, indexed, and stored in KV pairs. - -The KV data model is suitable for storing service data that is not related. It provides better read and write performance than the SQL database. The KV data model is widely used in distributed scenarios because it handles database version compatibility issues and data synchronization conflicts easily. The distributed database is based on the KV data model and provides KV-based access interfaces. - -### Distributed Database Transaction - -Distributed database transactions include local transactions (same as the transactions of traditional databases) and synchronization transactions. Synchronization transactions allow data to be synchronized between devices by local transaction. Synchronization of a local transaction modification either succeeds or fails on all the devices. - -### Distributed Database Consistency - -In a distributed scenario, cross-device collaboration demands consistent data between the devices in the same network. 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 will obtain the latest 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 latest data. The data on these devices may be inconsistent after a certain period of time. -- **Eventual consistency**: When data is added, deleted, or modified on a device, other devices in the same network may not obtain the latest data immediately. However, data on these devices will become consistent after a certain period of time. - -Strong consistency has high requirements on distributed data management and may be used in distributed server deployment. The DDS supports only the eventual consistency because mobile devices are not always online and the network has no center. - -### Distributed Database Synchronization - -After discovering and authenticating a device, the underlying communication component notifies the upper-layer application (including the DDS) that the device goes online. The DDS then establishes an encrypted transmission channel to synchronize data between the two devices. - -The DDS provides the following synchronization modes: - -- **Manual synchronization**: Applications call **sync()** to trigger a synchronization. The list of devices to be synchronized and the synchronization mode must be specified. The synchronization mode can be **PULL_ONLY** (pulling remote data to the local end), **PUSH_ONLY** (pushing local data to the remote end), or **PUSH_PULL** (pushing local data to the remote end and pulling remote data to the local end). The internal interface supports condition-based synchronization. The data that meets the conditions can be synchronized to the remote end. -- **Automatic synchronization**: includes full synchronization and condition-based subscription synchronization. In full synchronization, the distributed database automatically pushes local data to the remote end and pulls remote data to the local end when a device goes online or application data is updated. Applications do not need to call **sync()**. The internal interface supports condition-based subscription synchronization. The data that meets the subscription conditions on the remote end is automatically synchronized to the local end. - -### Single KV Store - -Data is saved locally in the unit of a single KV entry. Only one entry is saved for each key. Data can be modified only locally and synchronized to remote devices in sequence based on the update time. - -### Device KV Store - -The device KV store is based on the single KV store. The local device ID is added to the key when KV data is stored in the device KV store. Data can be isolated, managed, and queried by device. However, the data synchronized from remote devices cannot be modified locally. - -### Conflict Resolution - -A data conflict occurs when multiple devices modify the same data and commit the modification to the database. The last write wins (LWW) is the default conflict resolution policy used for data conflicts. Based on the commit timestamps, the data with a later timestamp is used. Currently, customized conflict resolution policies are not supported. - -### Schema-based Database Management and Predicate-based Data Query - -A schema is specified when you create or open a single KV store. Based on the schema, the database detects the value format of KV pairs and checks the value structure. Based on the fields in the values, the database implements index creation and predicate-based query. - -### Distributed Database Backup - -The DDS provides the database backup capability. You can set **backup** to **true** to enable daily backup. If a distributed database is damaged, the DDS deletes the database and restores the most recent data from the backup database. If no backup database is available, the DDS creates one. The DDS can also back up encrypted databases. - - -## Working Principles - -The DDS supports distributed management of application database data in the OpenHarmony system. Data can be synchronized between multiple devices with the same account, delivering a consistent user experience across devices. - -The DDS consists of the following: - -- **APIs**
The DDS provides APIs to create databases, access data, and subscribe to data. The APIs support the KV data model and common data types. They are highly compatible and easy to use, and can be released. - -- **Service component**
The service component implements management of metadata, permissions, encryption, backup and restore, and multiple users, and completes initialization of the storage component, synchronization component, and communication adaptation layer of the distributed database. - -- **Storage component**
The storage component implements data access, data reduction, transactions, snapshots, database encryption, data combination, and conflict resolution. - -- **Synchronization component**
The synchronization component interacts with the storage component and the communication adaptation layer to maintain data consistency between online devices. It synchronizes data generated on the local device to other devices and merges data from other devices into the local device. - -- **Communication adaptation layer**
The communication adaptation layer calls APIs of the underlying public communication layer to create and connect to communication channels, receive device online and offline messages, update metadata of the connected and disconnected devices, send device online and offline messages to the synchronization component. The synchronization component updates the list of connected devices, and calls the APIs of the communication adaption layer to encapsulate data and send the data to the connected devices. - -Applications call the DDS APIs to create, access, and subscribe to distributed databases. The APIs store data to the storage component based on the capabilities provided by the service component. The storage component interacts with the synchronization component to synchronize data. The synchronization component uses the communication adaptation layer to synchronize data to remote devices, which update the data in the storage component and provide the data for applications through service APIs. - - -**Figure 1** How DDS works - - -![](figures/en-us_image_0000001183386164.png) - - -## Constraints - -- The DDS supports the KV data model only. It does not support foreign keys or triggers of the relational database. - -- The KV data model specifications supported by the DDS are as follows: - - - For each record in a device KV store, the key must be less than or equal to 896 bytes and the value be less than 4 MB. - - For each record in a single KV store, the key must be less than or equal to 1 KB and the value be less than 4 MB. - - An application can open a maximum of 16 KV stores simultaneously. - -- The data that needs to be synchronized between devices should be stored in distributed databases rather than local databases. - -- The DDS does not support customized conflict resolution policies. - -- The maximum number of access requests to the KvStore API is 1000 per second and 10000 per minute. The maximum number of access requests to the KvManager API is 50 per second and 500 per minute. - -- Blocking operations, such as modifying UI components, are not allowed in the distributed database event callback. diff --git a/en/application-dev/database/database-preference-guidelines.md b/en/application-dev/database/database-preference-guidelines.md deleted file mode 100644 index 724e273675061c4b6969fb3fcd6f6cbdd984a15f..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-preference-guidelines.md +++ /dev/null @@ -1,203 +0,0 @@ -# Preferences Development - -> **NOTE** -> -> This feature is supported since API version 9. For the versions earlier than API version 9, use [Lightweight Storage](../reference/apis/js-apis-data-storage.md) APIs. - -## When to Use - -Preferences are used for storing the data that is frequently used by applications, but not for storing a large amount of data or data frequently changed. The application data is persistently stored on a device in the form of files. - -Note that the instance accessed by an application contains all data of the file. The data is always loaded to the memory of the device until the application removes it from the memory. The application can call the **Preferences** APIs to manage data. - -## Available APIs - -The **Preferences** module provides APIs for processing data in the form of key-value (KV) pairs and supports persistence of the KV pairs when required. - -The key is of the string type, and the value can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values. - -For details about **Preferences** APIs, see [Preferences](../reference/apis/js-apis-data-preferences.md). - -### Obtaining a **Preferences** Instance - -Obtain a **Preferences** instance for data operations. A **Preferences** instance is obtained after data is read from a specified file and loaded to the instance. - -**Table 1** API for obtaining a **Preferences** instance - -| Bundle Name | API | Description | -| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| ohos.data.preferences | getPreferences(context: Context, name: string): Promise\ | Obtains a **Preferences** instance.| - -### Processing Data - -Call **put()** to add or modify data in a **Preferences** instance. - -Call **get()** to read data from a **Preferences** instance. - -Call **getAll()** to obtain an **Object** instance that contains all KV pairs in a **Preferences** instance. - -Call **delete()** to delete the KV pair of the specified key from the **Preferences** instance. - -**Table 2** APIs for processing **Preferences** data - -| Class | API | Description | -| ----------- | ---------------------------------------------------------- | ------------------------------------------------------------ | -| Preferences | put(key: string, value: ValueType): Promise\ | Writes data to the **Preferences** instance. The value to write can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values.| -| Preferences | get(key: string, defValue: ValueType): Promise\ | Obtains data from the **Preferences** instance. The value to read can be a number, a string, a Boolean value, or an array of numbers, strings, or Boolean values.| -| Preferences | getAll(): Promise\ | Obtains an **Object** instance that contains all KV pairs in the **Preferences** instance. | -| Preferences | delete(key: string): Promise\ | Deletes the KV pair of the specified key from the **Preferences** instance. | - - -### Storing Data Persistently - -Call **flush()** to write the cached data back to its text file for persistent storage. - -**Table 4** API for data persistence - -| Class | API | Description | -| ----------- | ----------------------- | ------------------------------------------- | -| Preferences | flush(): Promise\ | Flushes data from the **Preferences** instance to its file through an asynchronous thread.| - -### Observing Data Changes - -You can subscribe to data changes. When the value of the subscribed key is changed and saved by **flush()**, a callback will be invoked to return the new data. - -**Table 5** APIs for observing **Preferences** changes - -| Class | API | Description | -| ----------- | ------------------------------------------------------------ | -------------- | -| Preferences | on(type: 'change', callback: Callback<{ key : string }>): void | Subscribes to data changes.| -| Preferences | off(type: 'change', callback: Callback<{ key : string }>): void | Unsubscribes from data changes. | - -### Deleting Data - -You can use the following APIs to delete a **Preferences** instance or data file. - -**Table 6** APIs for deleting **Preferences** - -| Bundle Name | API | Description | -| --------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| ohos.data.preferences | deletePreferences(context: Context, name: string): Promise\ | Deletes a **Preferences** instance from the memory and its files from the device.| -| ohos.data.preferences | removePreferencesFromCache(context: Context, name: string): Promise\ | Removes a **Preferences** instance from the memory to release memory. | - -## How to Develop - -1. Import @ohos.data.preferences and related modules to the development environment. - - ```js - import data_preferences from '@ohos.data.preferences'; - ``` - -2. Obtain a **Preferences** instance. - - Read the specified file and load its data to the **Preferences** instance for data operations. - - FA model: - - ```js - // Obtain the context. - import featureAbility from '@ohos.ability.featureAbility' - let context = featureAbility.getContext(); - - let preferences = null; - let promise = data_preferences.getPreferences(context, 'mystore'); - - promise.then((pref) => { - preferences = pref; - }).catch((err) => { - console.info("Failed to get the preferences."); - }) - ``` - - Stage model: - - ```ts - // Obtain the context. - import UIAbility from '@ohos.app.ability.UIAbility'; - let preferences = null; - export default class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage) { - let promise = data_preferences.getPreferences(this.context, 'mystore'); - promise.then((pref) => { - preferences = pref; - }).catch((err) => { - console.info("Failed to get the preferences."); - }) - } - } - - - ``` - -3. Write data. - - Use **preferences.put()** to write data to the **Preferences** instance. - - ```js - let putPromise = preferences.put('startup', 'auto'); - putPromise.then(() => { - console.info("Put the value of 'startup' successfully."); - }).catch((err) => { - console.info("Failed to put the value of 'startup'. Cause: " + err); - }) - ``` - -4. Read data. - - Use **preferences.get()** to read data. - - ```js - let getPromise = preferences.get('startup', 'default'); - getPromise.then((value) => { - console.info("The value of 'startup' is " + value); - }).catch((err) => { - console.info("Failed to get the value of 'startup'. Cause: " + err); - }) - ``` - -5. Store data persistently. - - Use **preferences.flush()** to flush data from the **Preferences** instance to its file. - - ```js - preferences.flush(); - ``` - -6. Observe data changes. - - Specify an observer as the callback to subscribe to data changes for an application. When the value of the subscribed key is changed and saved by **flush()**, the observer callback will be invoked to return the new data. - - ```js - let observer = function (key) { - console.info("The key" + key + " changed."); - } - preferences.on('change', observer); - // The data is changed from 'auto' to 'manual'. - preferences.put('startup', 'manual', function (err) { - if (err) { - console.info("Failed to put the value of 'startup'. Cause: " + err); - return; - } - console.info("Put the value of 'startup' successfully."); - preferences.flush(function (err) { - if (err) { - console.info("Failed to flush data. Cause: " + err); - return; - } - console.info("Flushed data successfully."); // The observer will be called. - }) - }) - ``` - -7. Delete the specified file. - - Use **deletePreferences()** to delete the **Preferences** instance and its persistent file and backup and corrupted files. After the specified files are deleted, the application cannot use that instance to perform any data operation. Otherwise, data inconsistency will be caused. The deleted data and files cannot be restored. - - ```js - let proDelete = data_preferences.deletePreferences(context, 'mystore'); - proDelete.then(() => { - console.info("Deleted data successfully."); - }).catch((err) => { - console.info("Failed to delete data. Cause: " + err); - }) - ``` diff --git a/en/application-dev/database/database-preference-overview.md b/en/application-dev/database/database-preference-overview.md deleted file mode 100644 index 4987b6e8494c738ef29e2f362b2acda43528bbb6..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-preference-overview.md +++ /dev/null @@ -1,34 +0,0 @@ -# Preferences Overview - -Preferences are used to implement quick access and persistence of the data in the `key-value` structure. - -After an application obtains a **Preferences** instance, the data in the instance will be cached in the memory for faster access. - -The cached data can also be written to a text file for persistent storage. Since file read and write consume system resources, you are advised to minimize the frequency of reading and writing files. - -You do not need to care about the implementation of the database locking mechanism. - -## Basic Concepts - -- **Key-value structure** - - A type of data structure. The `Key` is the unique identifier for a piece of data, and the `Value` is the specific data being identified. - -- **Non-relational database** - - A database not in compliance with the atomicity, consistency, isolation, and durability (ACID) properties of relational data transactions. The data in a non-relational database is independent. The database that organizes data in the `key-value` structure is a non-relational database. - -## Working Principles - -1. An application can load data from a **Preferences** persistent file to a **Preferences** instance. The system stores the **Preferences** instance in the memory through a static container. Each file of an application or process has only one **Preferences** instance in the memory, till the application removes the instance from the memory or deletes the **Preferences** persistent file. -2. When obtaining a **Preferences** instance, the application can read data from or write data to the instance. The data in the `Preferences` instance can be flushed to its **Preferences** persistent file by calling the **flush()** method. - -**Figure 1** Working mechanism - -![](figures/preferences.png) - -## Constraints - -- **Preferences** instances are loaded to the memory. To minimize non-memory overhead, the number of data records stored in a **Preferences** instance cannot exceed 10,000. Delete the instances that are no longer used in a timely manner. -- The `Key` in key-value pairs is of the string type. It cannot be empty or exceed 80 bytes. -- The `Value` of the string type in key-value pairs can be empty, but cannot exceed 8192 bytes if not empty. diff --git a/en/application-dev/database/database-relational-guidelines.md b/en/application-dev/database/database-relational-guidelines.md deleted file mode 100644 index da675bed88bcb465310432e9844ab90b6313dc6f..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-relational-guidelines.md +++ /dev/null @@ -1,464 +0,0 @@ -# RDB Development - -## When to Use - -A relational database (RDB) store allows you to manage local data with or without native SQL statements based on SQLite. - - -## Available APIs - -Most of the RDB store APIs are asynchronous interfaces, which can use a callback or promise to return the result. This document uses the promise-based APIs as an example. For more information about the APIs, see [RDB Store](../reference/apis/js-apis-data-relationalStore.md). - -### Creating or Deleting an RDB Store - -The following table describes the APIs for creating and deleting an RDB store. - -**Table 1** APIs for creating and deleting an RDB store - -| API | Description | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| getRdbStore(context: Context, config: StoreConfig): Promise<RdbStore> | Obtains an RDB store. This API uses a promise to return the result. You can set parameters for the RDB store based on service requirements and call APIs to perform data operations.
- **context**: application context.
- **config**: configuration of the RDB store.| -| deleteRdbStore(context: Context, name: string): Promise<void> | Deletes an RDB store. This API uses a promise to return the result.
- **context**: application context.
- **name**: name of the RDB store to delete.| - -### Managing Data in an RDB Store - -The RDB provides APIs for inserting, deleting, updating, and querying data in a local RDB store. - -- **Inserting Data** - - The RDB provides APIs for inserting data through a **ValuesBucket** in a data table. If the data is inserted, the row ID of the data inserted will be returned; otherwise, **-1** will be returned. - - **Table 2** API for inserting data - - - | Class | API | Description | - | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | - | RdbStore | insert(table: string, values: ValuesBucket): Promise<number> | Inserts a row of data into a table. This API uses a promise to return the result.
If the operation is successful, the row ID will be returned; otherwise, **-1** will be returned.
- **table**: name of the target table.
- **values**: data to be inserted into the table.| - -- **Updating Data** - - Call **update()** to pass in new data and specify the update conditions by using **RdbPredicates**. If the data is updated, the number of rows of the updated data will be returned; otherwise, **0** will be returned. - - **Table 3** API for updating data - - - | Class | API | Description | - | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | - | RdbStore | update(values: ValuesBucket, predicates: RdbPredicates): Promise<number> | Updates data based on the specified **RdbPredicates** object. This API uses a promise to return the number of rows updated.
- **values**: data to update, which is stored in **ValuesBucket**.
- **predicates**: conditions for updating data.| - -- **Deleting Data** - - Call **delete()** to delete the data that meets the conditions specified by **RdbPredicates**. If the data is deleted, the number of rows of the deleted data will be returned; otherwise, **0** will be returned. - - **Table 4** API for deleting data - - - | Class | API | Description | - | ---------- | ---------------------------------------------------------- | ------------------------------------------------------------ | - | RdbStore | delete(predicates: RdbPredicates): Promise<number> | Deletes data from the RDB store based on the specified **RdbPredicates** object. This API uses a promise to return the number of rows deleted.
- **predicates**: conditions for deleting data.| - -- **Querying Data** - - You can query data in an RDB store in either of the following ways: - - - Call the **query()** method to query data based on the predicates, without passing any SQL statement. - - Run the native SQL statement. - - **Table 5** APIs for querying data - - | Class | API | Description | - | ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | - | RdbStore | query(predicates: RdbPredicates, columns?: Array<string>): Promise<ResultSet> | Queries data from the RDB store based on specified conditions. This API uses a promise to return the result.
- **predicates**: conditions for querying data.
- **columns**: columns to query. If this parameter is not specified, the query applies to all columns.| - | RdbStore | querySql(sql: string, bindArgs?: Array<ValueType>): Promise<ResultSet> | Queries data using the specified SQL statement. This API uses a promise to return the result.
- **sql**: SQL statement.
- **bindArgs**: arguments in the SQL statement.| - | RdbStore | remoteQuery(device: string, table: string, predicates: RdbPredicates, columns: Array<string>): Promise<ResultSet> | Queries data from the database of a remote device based on specified conditions. This API uses a promise to return the result.
- **device**: network ID of the remote device.
- **table**: name of the table to be queried.
- **predicates**: **RdbPredicates** that specifies the query condition.
- **columns**: columns to query. If this parameter is not specified, the query applies to all columns.| - -### Using Predicates - -The **RDB** module provides **RdbPredicates** for you to set database operation conditions. - -The following table lists common predicates. For more information about predicates, see [**RdbPredicates**](../reference/apis/js-apis-data-relationalStore.md#rdbpredicates). - -**Table 6** APIs for using RDB store predicates - -| Class | API | Description | -| --------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbPredicates | equalTo(field: string, value: ValueType): RdbPredicates | Sets an **RdbPredicates** to search for the data that is equal to the specified value.
- **field**: column name in the database table.
- **value**: value to match the **RdbPredicates**.
- **RdbPredicates**: **RdbPredicates** object created.| -| RdbPredicates | notEqualTo(field: string, value: ValueType): RdbPredicates | Sets an **RdbPredicates** to search for the data that is not equal to the specified value.
- **field**: column name in the database table.
- **value**: value to match the **RdbPredicates**.
- **RdbPredicates**: **RdbPredicates** object created.| -| RdbPredicates | or(): RdbPredicates | Adds the OR condition to the **RdbPredicates**.
- **RdbPredicates**: **RdbPredicates** with the OR condition.| -| RdbPredicates | and(): RdbPredicates | Adds the AND condition to the **RdbPredicates**.
- **RdbPredicates**: **RdbPredicates** with the AND condition.| -| RdbPredicates | contains(field: string, value: string): RdbPredicates | Sets an **RdbPredicates** to search for the data that contains the specified value.
- **field**: column name in the database table.
- **value**: value to match the **RdbPredicates**.
- **RdbPredicates**: **RdbPredicates** object created.| - - -### Using the Result Set - -You can use the APIs provided by **ResultSet** to traverse and access the data you have queried. A result set can be regarded as a row of data in the queried result. - -For details about how to use **ResultSet** APIs, see [ResultSet](../reference/apis/js-apis-data-relationalStore.md#resultset). - -> **NOTICE**
-> After a result set is used, you must call the **close()** method to close it explicitly. - -**Table 7** APIs for using the result set - -| Class | API | Description | -| ----------- | ---------------------------------------- | ------------------------------------------ | -| ResultSet | goToFirstRow(): boolean | Moves to the first row of the result set. | -| ResultSet | getString(columnIndex: number): string | Obtains the value in the form of a string based on the specified column and current row. | -| ResultSet | getBlob(columnIndex: number): Uint8Array | Obtains the value in the form of a byte array based on the specified column and the current row.| -| ResultSet | getDouble(columnIndex: number): number | Obtains the value in the form of double based on the specified column and current row. | -| ResultSet | getLong(columnIndex: number): number | Obtains the value in the form of a long integer based on the specified column and current row. | -| ResultSet | close(): void | Closes the result set. | - - - -### Setting Distributed Tables - -> **NOTE** -> -> - The **ohos.permission.DISTRIBUTED_DATASYNC** permission is required for calling the **setDistributedTables**, **obtainDistributedTableName**, **sync**, **on** and **off** APIs of **RdbStore**. -> - The devices must be connected over network before the distributed tables are used. For details about the APIs and usage, see [Device Management](../reference/apis/js-apis-device-manager.md). - -**Setting Distributed Tables** - -**Table 8** API for setting distributed tables - -| Class | API | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbStore | setDistributedTables(tables: Array\): Promise\ | Sets distributed tables. This API uses a promise to return the result.
- **tables**: names of the distributed tables to set.| - -**Obtaining the Distributed Table Name for a Remote Device** - -You can obtain the distributed table name for a remote device based on the local table name. The distributed table name can be used to query the RDB store of the remote device. - -**Table 9** API for obtaining the distributed table name of a remote device - -| Class | API | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbStore | obtainDistributedTableName(device: string, table: string): Promise\ | Obtains the distributed table name for a remote device based on the local table name. The distributed table name is required when the RDB store of a remote device is queried. This API uses a promise to return the result.
- **device**: remote device.
- **table**: local table name.| - -**Synchronizing Data Between Devices** - -**Table 10** API for synchronizing data between devices - -| Class | API | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbStore | sync(mode: SyncMode, predicates: RdbPredicates): Promise\> | Synchronizes data between devices. This API uses a promise to return the result.
- **mode**: synchronization mode. **SYNC_MODE_PUSH** means to push data from the local device to a remote device. **SYNC_MODE_PULL** means to pull data from a remote device to the local device.
- **predicates**: specifies the data and devices to synchronize.
- **string**: device ID.
- **number**: synchronization status of each device. The value **0** indicates a successful synchronization. Other values indicate a synchronization failure.| - -**Registering an RDB Store Observer** - -**Table 11** API for registering an observer - -| Class | API | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbStore | on(event: 'dataChange', type: SubscribeType, observer: Callback\>): void | Registers an observer for this RDB store to subscribe to distributed data changes. When data in the RDB store changes, a callback will be invoked to return the data changes.
- **type**: subscription type. **SUBSCRIBE_TYPE_REMOTE**: subscribes to remote data changes.
- **observer**: observer that listens for data changes in the RDB store.| - -**Unregistering an RDB Store Observer** - -**Table 12** API for unregistering an observer - -| Class | API | Description | -| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | -| RdbStore | off(event:'dataChange', type: SubscribeType, observer: Callback\>): void; | Unregisters the observer of the specified type from the RDB store. This API uses an asynchronous callback to return the result.
- **type**: subscription type. **SUBSCRIBE_TYPE_REMOTE**: subscribes to remote data changes.
- **observer**: observer to unregister.| - -### Backing Up and Restoring an RDB Store - -**Backing Up an RDB Store** - -**Table 13** API for backing up an RDB store - -| Class | API | Description | -| ---------- | --------------------------------------------- | ------------------------------------------------------------ | -| RdbStore | backup(destName: string): Promise<void> | Backs up an RDB store. This API uses a promise to return the result.
- **destName**: name of the RDB backup file.| - -**Restoring an RDB Store** - -**Table 14** API for restoring an RDB store - -| Class | API | Description | -| ---------- | --------------------------------------------- | ------------------------------------------------------------ | -| RdbStore | restore(srcName: string): Promise<void> | Restores an RDB store from a backup file. This API uses a promise to return the result.
- **srcName**: name of the backup file used to restore the RDB store.| - -### Transaction - -**Table 15** Transaction APIs - -| Class | API | Description | -| -------- | ----------------------- | --------------------------------- | -| RdbStore | beginTransaction(): void | Starts the transaction before executing SQL statements.| -| RdbStore | commit(): void | Commits the executed SQL statements. | -| RdbStore | rollBack(): void | Rolls back the SQL statements that have been executed. | - -## How to Develop - -1. Create an RDB store. - - (1) Configure the RDB store attributes, including the RDB store name, storage mode, and whether read-only mode is used. - - (2) Initialize the table structure and related data in the RDB store. - - (3) Create an RDB store. - - FA model: - - ```js - import relationalStore from '@ohos.data.relationalStore' - import featureAbility from '@ohos.ability.featureAbility' - - var store; - - // Obtain the context. - let context = featureAbility.getContext(); - - const STORE_CONFIG = { - name: "RdbTest.db", - securityLevel: relationalStore.SecurityLevel.S1 - }; - - relationalStore.getRdbStore(context, STORE_CONFIG, function (err, rdbStore) { - store = rdbStore; - if (err) { - console.error(`Get RdbStore failed, err: ${err}`); - return; - } - console.info(`Get RdbStore successfully.`); - }) - ``` - Stage model: - ```ts - import relationalStore from '@ohos.data.relationalStore' - import UIAbility from '@ohos.app.ability.UIAbility' - - class EntryAbility extends UIAbility { - onWindowStageCreate(windowStage) { - var store; - const STORE_CONFIG = { - name: "RdbTest.db", - securityLevel: relationalStore.SecurityLevel.S1 - }; - - relationalStore.getRdbStore(this.context, STORE_CONFIG, function (err, rdbStore) { - store = rdbStore; - if (err) { - console.error(`Get RdbStore failed, err: ${err}`); - return; - } - console.info(`Get RdbStore successfully.`); - }) - } - } - ``` - -2. Insert data. - - (1) Create a **ValuesBucket** to store the data you need to insert. - - (2) Call the **insert()** method to insert data into the RDB store. - - The sample code is as follows: - - ```js - let u8 = new Uint8Array([1, 2, 3]); - const valueBucket = { "name": "Tom", "age": 18, "salary": 100.5, "blobType": u8 }; - let insertPromise = store.insert("test", valueBucket); - ``` - - ```js - // Use a transaction to insert data. - try { - store.beginTransaction(); - let u8 = new Uint8Array([1, 2, 3]); - const valueBucket = { "name": "Tom", "age": 18, "salary": 100.5, "blobType": u8 }; - let promise = store.insert("test", valueBucket); - promise.then(() => { - store.commit(); - }) - } catch (err) { - console.error(`Transaction failed, err: ${err}`); - store.rollBack(); - } - ``` - -3. Query data. - - (1) Create an **RdbPredicates** object to specify query conditions. - - (2) Call the **query()** API to query data. - - (3) Call the **resultSet()** API to obtain the result. - - The sample code is as follows: - - ```js - let predicates = new relationalStore.RdbPredicates("test"); - predicates.equalTo("name", "Tom"); - let promisequery = store.query(predicates); - promisequery.then((resultSet) => { - resultSet.goToFirstRow(); - const id = resultSet.getLong(resultSet.getColumnIndex("id")); - const name = resultSet.getString(resultSet.getColumnIndex("name")); - const age = resultSet.getLong(resultSet.getColumnIndex("age")); - const salary = resultSet.getDouble(resultSet.getColumnIndex("salary")); - const blobType = resultSet.getBlob(resultSet.getColumnIndex("blobType")); - resultSet.close(); - }) - ``` - -4. Set the distributed tables to be synchronized. - - (1) Add the following permission to the permission configuration file: - - ```json - "requestPermissions": - { - "name": "ohos.permission.DISTRIBUTED_DATASYNC" - } - ``` - - (2) Obtain the required permissions. - - (3) Set the distributed tables. - - (4) Check whether the setting is successful. - - The sample code is as follows: - - ```js - let context = featureAbility.getContext(); - context.requestPermissionsFromUser(['ohos.permission.DISTRIBUTED_DATASYNC'], 666, function (result) { - console.info(`result.requestCode=${result.requestCode}`); - }) - let promise = store.setDistributedTables(["test"]); - promise.then(() => { - console.info(`setDistributedTables success.`); - }).catch((err) => { - console.error(`setDistributedTables failed, ${err}`); - }) - ``` - -5. Synchronize data across devices. - - (1) Construct an **RdbPredicates** object to specify remote devices within the network to be synchronized. - - (2) Call **rdbStore.sync()** to synchronize data. - - (3) Check whether the data synchronization is successful. - - The sample code is as follows: - - ```js - let predicate = new relationalStore.RdbPredicates('test'); - predicate.inDevices(['12345678abcde']); - let promise = store.sync(relationalStore.SyncMode.SYNC_MODE_PUSH, predicate); - promise.then((result) => { - console.info(`sync done.`); - for (let i = 0; i < result.length; i++) { - console.info(`device=${result[i][0]}, status=${result[i][1]}`); - } - }).catch((err) => { - console.error(`sync failed, err: ${err}`); - }) - ``` - -6. Subscribe to distributed data. - - (1) Register an observer to listen for distributed data changes. - - (2) When data in the RDB store changes, a callback will be invoked to return the data changes. - - The sample code is as follows: - - ```js - function storeObserver(devices) { - for (let i = 0; i < devices.length; i++) { - console.info(`device= ${devices[i]} data changed`); - } - } - - try { - store.on('dataChange', relationalStore.SubscribeType.SUBSCRIBE_TYPE_REMOTE, storeObserver); - } catch (err) { - console.error(`register observer failed, err: ${err}`); - } - ``` - -7. Query data across devices. - - (1) Obtain the distributed table name for a remote device based on the local table name. - - (2) Call the resultSet() API to obtain the result. - - The sample code is as follows: - - ```js - import deviceManager from '@ohos.distributedHardware.deviceManager' - - let deviceIds = []; - deviceManager.createDeviceManager('bundleName', (err, value) => { - if (!err) { - let devManager = value; - if (devManager != null) { - // Obtain device IDs. - let devices = devManager.getTrustedDeviceListSync(); - for (let i = 0; i < devices.length; i++) { - deviceIds[i] = devices[i].deviceId; - } - } - } - }) - - let tableName = store.obtainDistributedTableName(deviceIds[0], "test"); - let resultSet = store.querySql("SELECT * FROM " + tableName); - ``` - -8. Query data of a remote device. - - (1) Construct a predicate object for querying distributed tables, and specify the remote distributed table name and the remote device. - - (2) Call the resultSet() API to obtain the result. - - The sample code is as follows: - - ```js - let rdbPredicate = new relationalStore.RdbPredicates('employee'); - predicates.greaterThan("id", 0) ; - let promiseQuery = store.remoteQuery('12345678abcde', 'employee', rdbPredicate); - promiseQuery.then((resultSet) => { - while (resultSet.goToNextRow()) { - let idx = resultSet.getLong(0); - let name = resultSet.getString(1); - let age = resultSet.getLong(2); - console.info(`indx: ${idx}, name: ${name}, age: ${age}`); - } - resultSet.close(); - }).catch((err) => { - console.error(`failed to remoteQuery, err: ${err}`); - }) - ``` - -9. Back up and restore an RDB store. - - (1) Back up the current RDB store. - - The sample code is as follows: - - ```js - let promiseBackup = store.backup("dbBackup.db"); - promiseBackup.then(() => { - console.info(`Backup success.`); - }).catch((err) => { - console.error(`Backup failed, err: ${err}`); - }) - ``` - - (2) Restore the RDB store using the backup file. - - The sample code is as follows: - - ```js - let promiseRestore = store.restore("dbBackup.db"); - promiseRestore.then(() => { - console.info(`Restore success.`); - }).catch((err) => { - console.error(`Restore failed, err: ${err}`); - }) - ``` diff --git a/en/application-dev/database/database-relational-overview.md b/en/application-dev/database/database-relational-overview.md deleted file mode 100644 index b02f2d646b9dc8e89b1b8a32f2743c8656c6d1f9..0000000000000000000000000000000000000000 --- a/en/application-dev/database/database-relational-overview.md +++ /dev/null @@ -1,43 +0,0 @@ -# RDB Overview - -A relational database (RDB) store manages data based on relational models. With the underlying SQLite database, the RDB store provides a complete mechanism for managing data as in a local database. To satisfy different needs in complicated scenarios, the RDB store offers APIs for performing operations, such as adding, deleting, modifying, and querying data, and supports direct execution of SQL statements. After an application is uninstalled, the related RDB store will be automatically deleted. - -You do not need to care about the implementation of the database locking mechanism. - -## Basic Concepts - -- **RDB store** - - A type of database created on the basis of relational models. A RDB store holds data in rows and columns. - -- **Predicate** - - A representation of the property or feature of a data entity, or the relationship between data entities. Predicates are used to define operation conditions. - -- **Result set** - - A set of query results used to access data. You can access the required data in a result set in flexible modes. - -- **SQLite database** - - A lightweight open-source relational database management system that complies with Atomicity, Consistency, Isolation, and Durability (ACID). - -## Working Principles - -The RDB store provides common operation APIs for external systems. It uses the SQLite as the underlying persistent storage engine, which supports all SQLite database features. - -**Figure 1** Working mechanism - -![how-rdb-works](figures/how-rdb-works.png) - -## Default Settings - -- The default RDB logging mode is Write Ahead Log (WAL). -- The default data flushing mode is **FULL** mode. -- The default size of the shared memory used by an OpenHarmony database is 2 MB. - -## Constraints - -- An RDB store can be connected to a maximum of four connection pools to manage read and write operations. - -- To ensure data accuracy, the RDB store supports only one write operation at a time. diff --git a/en/application-dev/database/figures/dataManagement.jpg b/en/application-dev/database/figures/dataManagement.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a43ca576222ad1da550242ed34c5f82700d52392 Binary files /dev/null and b/en/application-dev/database/figures/dataManagement.jpg differ diff --git a/en/application-dev/database/figures/dataShare.jpg b/en/application-dev/database/figures/dataShare.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bd6b1d08eeedd0cc50c6c9813b064ca4504ffa98 Binary files /dev/null and b/en/application-dev/database/figures/dataShare.jpg differ diff --git a/en/application-dev/database/figures/deviceKVStore.jpg b/en/application-dev/database/figures/deviceKVStore.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ed4b8aadd266d8531a24cb9ef6b72bfd33258759 Binary files /dev/null and b/en/application-dev/database/figures/deviceKVStore.jpg differ diff --git a/en/application-dev/database/figures/distributedObject.jpg b/en/application-dev/database/figures/distributedObject.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a877e89bc5714fe730e05130d1dd0a89b135151d Binary files /dev/null and b/en/application-dev/database/figures/distributedObject.jpg differ diff --git a/en/application-dev/database/figures/distributedObject_sync.jpg b/en/application-dev/database/figures/distributedObject_sync.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f85325f29cf85692711a89367ddb3dd70d3b9b5a Binary files /dev/null and b/en/application-dev/database/figures/distributedObject_sync.jpg differ diff --git a/en/application-dev/database/figures/distributedObject_syncView.jpg b/en/application-dev/database/figures/distributedObject_syncView.jpg new file mode 100644 index 0000000000000000000000000000000000000000..751aaab344c417316b5054ef9ff1d4c14d1b1288 Binary files /dev/null and b/en/application-dev/database/figures/distributedObject_syncView.jpg differ diff --git a/en/application-dev/database/figures/en-us_image_0000001183386164.png b/en/application-dev/database/figures/en-us_image_0000001183386164.png deleted file mode 100644 index e1fb8747d48c315d0e88504135c0bd388cc81077..0000000000000000000000000000000000000000 Binary files a/en/application-dev/database/figures/en-us_image_0000001183386164.png and /dev/null differ diff --git a/en/application-dev/database/figures/en-us_image_0000001542496993.png b/en/application-dev/database/figures/en-us_image_0000001542496993.png new file mode 100644 index 0000000000000000000000000000000000000000..de0fa2afbd5c9301700618436126ca60e2666a06 Binary files /dev/null and b/en/application-dev/database/figures/en-us_image_0000001542496993.png differ diff --git a/en/application-dev/database/figures/en_DataShare.png b/en/application-dev/database/figures/en_DataShare.png deleted file mode 100644 index b56e96d3bb3ff338f082efd75959d9b15cea6a86..0000000000000000000000000000000000000000 Binary files a/en/application-dev/database/figures/en_DataShare.png and /dev/null differ diff --git a/en/application-dev/database/figures/how-distributedobject-works.png b/en/application-dev/database/figures/how-distributedobject-works.png deleted file mode 100644 index 33785a3fd4c66624b298b1aa36959dbf635d2343..0000000000000000000000000000000000000000 Binary files a/en/application-dev/database/figures/how-distributedobject-works.png and /dev/null differ diff --git a/en/application-dev/database/figures/how-rdb-works.png b/en/application-dev/database/figures/how-rdb-works.png deleted file mode 100644 index 35e10dc3a07aef0e85a8f02332c3c6cd9d605448..0000000000000000000000000000000000000000 Binary files a/en/application-dev/database/figures/how-rdb-works.png and /dev/null differ diff --git a/en/application-dev/database/figures/kvStore.jpg b/en/application-dev/database/figures/kvStore.jpg new file mode 100644 index 0000000000000000000000000000000000000000..98f3274ee425215f7027a835fbb1f98afdbd4f16 Binary files /dev/null and b/en/application-dev/database/figures/kvStore.jpg differ diff --git a/en/application-dev/database/figures/kvStore_development_process.png b/en/application-dev/database/figures/kvStore_development_process.png new file mode 100644 index 0000000000000000000000000000000000000000..e5babf2b70994940afbb6e205c05ae96128a82d7 Binary files /dev/null and b/en/application-dev/database/figures/kvStore_development_process.png differ diff --git a/en/application-dev/database/figures/preferences.jpg b/en/application-dev/database/figures/preferences.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fea03ea640a6a88184efbc2a94de6f1d0a8da918 Binary files /dev/null and b/en/application-dev/database/figures/preferences.jpg differ diff --git a/en/application-dev/database/figures/preferences.png b/en/application-dev/database/figures/preferences.png deleted file mode 100644 index be3c6feef3c1f8d9da83e5d3c0065655bd07cb57..0000000000000000000000000000000000000000 Binary files a/en/application-dev/database/figures/preferences.png and /dev/null differ diff --git a/en/application-dev/database/figures/relationStore_local.jpg b/en/application-dev/database/figures/relationStore_local.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d09a410c6e4036b637ec97861106e8c178969ad6 Binary files /dev/null and b/en/application-dev/database/figures/relationStore_local.jpg differ diff --git a/en/application-dev/database/figures/relationalStore_sync.jpg b/en/application-dev/database/figures/relationalStore_sync.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cba72cb076d58b2422c2939d883ca3b611e570bf Binary files /dev/null and b/en/application-dev/database/figures/relationalStore_sync.jpg differ diff --git a/en/application-dev/database/figures/silent_dataShare.jpg b/en/application-dev/database/figures/silent_dataShare.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6b1fce306ed8824acc343adf4359f29f4fa31b4 Binary files /dev/null and b/en/application-dev/database/figures/silent_dataShare.jpg differ diff --git a/en/application-dev/database/figures/singleKVStore.jpg b/en/application-dev/database/figures/singleKVStore.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0f95d076872c9607238503407cb77c535392747 Binary files /dev/null and b/en/application-dev/database/figures/singleKVStore.jpg differ diff --git a/en/application-dev/database/share-data-by-datashareextensionability.md b/en/application-dev/database/share-data-by-datashareextensionability.md new file mode 100644 index 0000000000000000000000000000000000000000..ea6bdbcd210f4ddc1ffa67f4d50dcca8b96cf8dd --- /dev/null +++ b/en/application-dev/database/share-data-by-datashareextensionability.md @@ -0,0 +1,213 @@ +# Sharing Data Using DataShareExtensionAbility + + +## When to Use + +If complex services are involved in cross-application data access, you can use **DataShareExtensionAbility** to start the application of the data provider to implement data access. + +You need to implement flexible service logics via callbacks of the service provider. + + +## Working Principles + +There are two roles in **DataShare**: + +- Data provider: implements operations, such as adding, deleting, modifying, and querying data, and opening a file, using [DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md). + +- Data consumer: accesses the data provided by the provider using [createDataShareHelper()](../reference/apis/js-apis-data-dataShare.md#datasharecreatedatasharehelper). + +**Figure 1** Data sharing mechanism + +![dataShare](figures/dataShare.jpg) + +- The **DataShareExtensionAbility** module, as the data provider, implements services related to data sharing between applications. + +- The **DataShareHelper** module, as the data consumer, provides APIs for accessing data, including adding, deleting, modifying, and querying data. + +- The data consumer communicates with the data provider via inter-process communication (IPC). The data provider can be implemented through a database or other data storage. + +- The **ResultSet** module is implemented through shared memory. Shared memory stores the result sets, and interfaces are provided to traverse result sets. + + +## How to Develop + + +### Data Provider Application Development (Only for System Applications) + +[DataShareExtensionAbility](../reference/apis/js-apis-application-dataShareExtensionAbility.md) provides the following APIs. You can override these APIs as required. + +- **onCreate**: called by the server to initialize service logic when the DataShare client connects to the DataShareExtensionAbility server. + +- **insert**: called to insert data upon the request of the client. Data insertion must be implemented in this callback on the server. + +- **update**: called to update data upon the request of the client. Data update must be implemented in this callback on the server. + +- **delete**: called to delete data upon the request of the client. Data deletion must be implemented in this callback on the server. + +- **query**: called to query data upon the request of the client. Data query must be implemented in this callback on the server. + +- **batchInsert**: called to batch insert data upon the request of the client. Batch data insertion must be implemented in this callback on the server. + +- **normalizeUri**: converts the URI provided by the client to the URI used by the server. + +- **denormalizeUri**: converts the URI used by the server to the initial URI passed by the client. + +Before implementing a **DataShare** service, you need to create a **DataShareExtensionAbility** object in the DevEco Studio project as follows: + +1. In the **ets** directory of the **Module** project, right-click and choose **New > Directory** to create a directory named **DataShareExtAbility**. + +2. Right-click the **DataShareAbility** directory, and choose **New > TypeScript File** to create a file named **DataShareExtAbility.ts**. + +3. Import **@ohos.application.DataShareExtensionAbility** and other dependencies to the **DataShareExtAbility.ts** file, and +override the service implementation as required. For example, if the data provider provides only the data insertion, deletion, and query services, you can override only these APIs. + + ```js + import Extension from '@ohos.application.DataShareExtensionAbility'; + import rdb from '@ohos.data.relationalStore'; + import dataSharePredicates from '@ohos.data.dataSharePredicates'; + ``` + +4. Implement the data provider services. For example, implement data storage of the data provider by using a database, reading and writing files, or accessing the network. + + ```js + const DB_NAME = 'DB00.db'; + const TBL_NAME = 'TBL00'; + const DDL_TBL_CREATE = "CREATE TABLE IF NOT EXISTS " + + TBL_NAME + + ' (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER, isStudent BOOLEAN, Binary BINARY)'; + + let rdbStore; + let result; + + export default class DataShareExtAbility extends Extension { + private rdbStore_; + + // Override onCreate(). + onCreate(want, callback) { + result = this.context.cacheDir + '/datashare.txt'; + // Create an RDB store. + rdb.getRdbStore(this.context, { + name: DB_NAME, + securityLevel: rdb.SecurityLevel.S1 + }, function (err, data) { + rdbStore = data; + rdbStore.executeSql(DDL_TBL_CREATE, [], (err) => { + console.info(`DataShareExtAbility onCreate, executeSql done err:${err}`); + }); + if (callback) { + callback(); + } + }); + } + + // Override query(). + query(uri, predicates, columns, callback) { + if (predicates === null || predicates === undefined) { + console.info('invalid predicates'); + } + try { + rdbStore.query(TBL_NAME, predicates, columns, (err, resultSet) => { + if (resultSet !== undefined) { + console.info(`resultSet.rowCount:${resultSet.rowCount}`); + } + if (callback !== undefined) { + callback(err, resultSet); + } + }); + } catch (err) { + console.error(`Failed to query. Code:${err.code},message:${err.message}`); + } + } + // Override other APIs as required. + }; + ``` + +5. Define **DataShareExtensionAbility** in **module.json5**. + + **Table 1** Fields in module.json5 + + | Field| Description| Mandatory| + | -------- | -------- | -------- | + | name | Ability name, corresponding to the **ExtensionAbility** class name derived from **Ability**.| Yes| + | type | Ability type. The value is **dataShare**, indicating the development is based on the **datashare** template.| Yes| + | uri | URI used for communication. It is the unique identifier for the data consumer to connect to the provider.| Yes| + | exported | Whether it is visible to other applications. Data sharing is allowed only when the value is **true**.| Yes| + | readPermission | Permission required for accessing data. If this parameter is not set, the read permission is not verified by default.| No| + | writePermission | Permission required for modifying data. If this parameter is not set, write permission verification is not performed by default.| No| + + **module.json5 example** + + + ```json + "extensionAbilities": [ + { + "srcEntrance": "./ets/DataShareExtAbility/DataShareExtAbility.ts", + "name": "DataShareExtAbility", + "icon": "$media:icon", + "description": "$string:description_datashareextability", + "type": "dataShare", + "uri": "datashare://com.samples.datasharetest.DataShare", + "exported": true + } + ] + ``` + + +### Data Consumer Application Development + +1. Import the dependencies. + + ```js + import UIAbility from '@ohos.app.ability.UIAbility'; + import dataShare from '@ohos.data.dataShare'; + import dataSharePredicates from '@ohos.data.dataSharePredicates'; + ``` + +2. Define the URI string for communicating with the data provider. + + ```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'); + ``` + +3. Create a **DataShareHelper** instance. + + ```js + let dsHelper; + let abilityContext; + + export default class EntryAbility extends UIAbility { + onWindowStageCreate(windowStage) { + abilityContext = this.context; + dataShare.createDataShareHelper(abilityContext, dseUri, (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}`); + }); + ``` diff --git a/en/application-dev/database/share-data-by-silent-access.md b/en/application-dev/database/share-data-by-silent-access.md new file mode 100644 index 0000000000000000000000000000000000000000..50ff03f084c889a807c6caf4d7c369bfbe0d2a51 --- /dev/null +++ b/en/application-dev/database/share-data-by-silent-access.md @@ -0,0 +1,46 @@ +# Data Sharing Through Silent Access + + +## When to Use + +In a typical cross-application data access scenario, an application 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. + +Silent access supports only basic database access. If service processing is required, implement service processing in the data consumer. + +If the service processing is complex, use **DataShareExtensionAbility** to start the data provider. + + +## Working Principles + +**Figure 1** Silent access + +![silent_dataShare](figures/silent_dataShare.jpg) + +- 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 + + "Proxy=true" means to access data without starting the data provider. If **Proxy** is not set to **true**, the data provider is started. + + The **DatamgrService** obtains the data provider application based on **bundleName**, reads the configuration, verifies the permission, and accesses data. + + +## Constraints + +- Currently, only RDB stores support silent access. + +- The system supports a maximum of 16 concurrent query operations. Excess query requests need to be queued for processing. + +- A proxy cannot be used to create a database. If a database needs to be created, the data provider must be started. + + +## How to Develop + +The URI must be in the following format: + +datashare:///{bundleName}/{moduleName}/{storeName}/{tableName}?Proxy=true + +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/share-device-data-across-apps-overview.md b/en/application-dev/database/share-device-data-across-apps-overview.md new file mode 100644 index 0000000000000000000000000000000000000000..0d8fb24ae88a3a5ea0d9f9f8b386afbce5d8f1a1 --- /dev/null +++ b/en/application-dev/database/share-device-data-across-apps-overview.md @@ -0,0 +1,46 @@ +# Cross-Application Data Sharing Overview + + +## Function + +The application data on a device, such as the Contacts, short message service (SMS), and Gallery data, always needs to be shared with other applications. However, certain data, such as the accounts and passwords, cannot be shared. Certain data, such as SMS messages, can be accessed but not modified by other applications. The **DataShare** module provides a secure and easy-to-use mechanism for sharing data of an application with other applications on the same device. + +## Basic Concepts + +Before developing cross-application data sharing on a device, understand the following concepts: + +- Data provider: an application that provides data and implements related services. It is also called the data producer or server. + +- Data consumer: an application that accesses the data or services provided by the data provider. It is also called the client. + +- **ValuesBucket**: a set of data to be inserted. It can be one or more data records in KV pairs. In each KV pair, the key must be of the string type, and the value can be a number, a string, a Boolean value, or an unsigned integer array. + +- **ResultSet**: a set of query results. It provides flexible modes for obtaining various data. + +- **Predicates**: an object that specifies the conditions for updating, deleting, or querying data in a database. + +## Implementation + +The data provider can directly use **DataShare** to share data with other applications without complex encapsulation. The data consumer only needs to use a set of APIs to access the data, because the **DataShare** access mode does not vary with the data provisioning mode. This greatly reduces the learning time and development difficulty. + +The cross-application data sharing can be implemented in either of the following ways: + +- **DataShareExtensionAbility** + + You can implement an ExtensionAbility with a callback in the HAP. When the data consumer calls an API, the ExtensionAbility of the data provider will be automatically started to invoke the registered callback. + + This method is recommended when the cross-application data access involves service operations other than mere addition, deletion, modification, and query of data in databases. + +- Silent access + + You can configure database access rules in the HAP. When the data consumer calls an API, the system ability automatically obtains the access rules in the HAP and returns data without starting the data provider. + + This method is recommended when the cross-application data access involves only the operations for adding, deleting, modifying, and querying data in databases. + +## Constraints + +- **DataShare** is subject to the limitations on the database used by the data provider. The supported data models, length of the keys and values, and maximum number of databases that can be accessed at a time by each application vary with the database in use. + +- The payloads of **ValuesBucket**, **Predicates**, and **ResultSet** are restricted by inter-process communication (IPC). + +- Currently, **dataShare** supports development based on the stage model only. 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 new file mode 100644 index 0000000000000000000000000000000000000000..c2f6361786325ccd753aa8fa4afa3446d37b6e89 --- /dev/null +++ b/en/application-dev/database/sync-app-data-across-devices-overview.md @@ -0,0 +1,39 @@ +# Overview of Distributed Application Data Synchronization + + +## When to Use + +The distributed application data synchronization allows the data of an application to be synchronized with other devices that are connected to form a Virtual Device. This feature enables seamless synchronization, modification, and query of use application data across trusted devices. + +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). + +The data storage modes vary depending on the lifecycle of data to be synchronized: + +- Temporary data has a short lifecycle and is usually stored in memory. For example, distributed data objects are recommended for process data generated by game applications. + +- Persistent data has a long lifecycle and needs to be stored in databases. You can use RDB stores or KV stores based on data characteristics and relationships. For example, RDB stores are recommended for storing Gallery attribute information, such as albums, covers, and images, and KV stores are recommended for storing Gallery image thumbnails. + + +## Basic Concepts + +In a distributed scenario, cross-device collaboration demands consistent data between the devices in the same network. + + +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. + +- 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. + +- Eventual consistency: When data is added, deleted, or modified on a device, other devices in the same network may not obtain the updates immediately. However, data on these devices will become consistent after a certain period of time. + + +Strong consistency has high requirements on distributed data management and may be used in distributed server deployment. Because mobile devices are not always online and there is no central node, the cross-device application data synchronization supports eventual consistency only. + + +## Access Control Mechanism in Cross-Device Synchronization + +When data is synchronized across devices, access control is performed based on the device level and data security label. For details, see [Access Control Mechanism in Cross-Device Synchronization](access-control-by-device-and-data-level.md#access-control-mechanism-in-cross-device-synchronization). diff --git a/en/application-dev/device/inputdevice-guidelines.md b/en/application-dev/device/inputdevice-guidelines.md index da6eef71d750b74e01d1ea8a9eaaf49b1bf598cb..c15955d9b01239605d0ce1afa9bfe5f693b22940 100644 --- a/en/application-dev/device/inputdevice-guidelines.md +++ b/en/application-dev/device/inputdevice-guidelines.md @@ -29,7 +29,6 @@ When a user enters text, the input method determines whether to launch the virtu 1. Call the **getDeviceList** API to obtain the list of connected input devices. Call the **getKeyboardType** API to traverse all connected devices to check whether a physical keyboard exists. If a physical keyboard exists, mark the physical keyboard as connected. This step ensures that your application detects all inserted input devices before listening for device hot swap events. 2. Call the **on** API to listen for device hot swap events. If a physical keyboard is inserted, mark the physical keyboard as connected. If a physical keyboard is removed, mark the physical keyboard as disconnected. -3. When a user enters text, check whether a physical keyboard is connected. If a physical keyboard is not connected, launch the virtual keyboard. ```js @@ -65,6 +64,4 @@ try { } catch (error) { console.log(`Execute failed, error: ${JSON.stringify(error, [`code`, `message`])}`); } - // 3. Determine whether to launch the virtual keyboard based on the value of isPhysicalKeyboardExist. - // TODO ``` diff --git a/en/application-dev/device/usb-guidelines.md b/en/application-dev/device/usb-guidelines.md index cdb6c325271099a38739eeaa87f2e36920b5d8e6..70258c31f55f6ea630ed7edc7a85e4b2e6e98d17 100644 --- a/en/application-dev/device/usb-guidelines.md +++ b/en/application-dev/device/usb-guidelines.md @@ -129,8 +129,6 @@ You can set a USB device as the USB host to connect to other USB devices for dat usb.bulkTransfer(pipe, inEndpoint, dataUint8Array, 15000).then(dataLength => { if (dataLength >= 0) { console.info("usb readData result Length : " + dataLength); - let resultStr = this.ab2str(dataUint8Array); // Convert uint8 data into a string. - console.info("usb readData buffer : " + resultStr); } else { console.info("usb readData failed : " + dataLength); } diff --git a/en/application-dev/dfx/Readme-EN.md b/en/application-dev/dfx/Readme-EN.md index b8a4496e09420b3a7557e5c8b8996deaf14ce1c9..b5990650c61eae7ed57a0b1dbc35489947de8bc8 100644 --- a/en/application-dev/dfx/Readme-EN.md +++ b/en/application-dev/dfx/Readme-EN.md @@ -3,6 +3,10 @@ - [Development of Application Event Logging](hiappevent-guidelines.md) - [Development of Performance Tracing](hitracemeter-guidelines.md) - [Development of Distributed Call Chain Tracing](hitracechain-guidelines.md) +- [HiLog Development (Native)](hilog-guidelines.md) - Error Management - [Development of Error Manager](errormanager-guidelines.md) - [Development of Application Recovery](apprecovery-guidelines.md) +- Log Management + - [Application Freeze (appfreeze) Log Analysis](appfreeze-guidelines.md) + - [Process Crash (cppcrash) Log Analysis](cppcrash-guidelines.md) diff --git a/en/application-dev/dfx/appfreeze-guidelines.md b/en/application-dev/dfx/appfreeze-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..fb12c9384ed00af5bb99b870f2cbef280bf25290 --- /dev/null +++ b/en/application-dev/dfx/appfreeze-guidelines.md @@ -0,0 +1,200 @@ +# Application Freeze (appfreeze) Log Analysis + +## Introduction + +Application freeze (appfreeze) means that an application does not respond to user operations (for example, clicking) within a given period of time. OpenHarmony provides a mechanism for detecting appfreeze faults and generates appfreeze logs for fault analysis. + +> **NOTE** +> +> This guide applies only to applications in the stage model. +> Before using this guide, you must have basic knowledge about the JS applications, C++ program stacks, and application-related subsystems. + +## How to Obtain + +appfreeze log is a type of fault logs managed together with the native process crash, JS application crash, and system process crash logs . You can obtain the log in any of the following ways. + +### Collecting Logs by Using Shell + +appfreeze logs start with **appfreeze-** in **/data/log/faultlog/faultlogger/**. + +The log files are named in the format of **appfreeze-application package name-application UID-time (seconds level)**. + +![appfreeze_20230308145160](figures/appfreeze_20230308145160.png) + +### Collecting Logs by Using DevEco Studio + +DevEco Studio collects device fault logs and saves them to FaultLog. + +The logs are displayed by the bundle name, fault, and time. + +![appfreeze_20230308145161](figures/appfreeze_20230308145161.png) + + +### Collecting Logs by Using faultLogger APIs + +The FaultLogger module provides APIs to query various fault information. For details, see [@ohos.faultLogger](../reference/apis/js-apis-faultLogger.md). + + +## appfreeze Detection + +Currently, appfreeze detection supports the fault types listed in the following table. + +| Fault Type| Description| +| -------- | -------- | +| THREAD_BLOCK_6S | The application main thread times out due to a suspension.| +| APPLICATION_BLOCK_INPUT | The user input response times out.| +| LIFECYCLE_TIMEOUT | Ability lifecycle switching times out.| +| APP_LIFECYCLE_TIMEOUT | Application lifecycle switching times out.| + +### Application Main Thread Timeout + +If this fault occurs, the main thread of the current application is suspended or too many tasks are executed, affecting task execution smoothness and experience. + +Such a fault can be detected as follows: The watchdog thread of the application periodically inserts an activation detection subthread to the main thread and inserts a timeout reporting subthread to its own thread. If activation detection is not executed within 3 seconds, the THREAD_BLOCK_3S event is reported; if activation detection is not executed within 6 seconds, the THREAD_BLOCK_6S event is reported. The two events together form an appfreeze log. The following figure shows the working principle. + +![appfreeze_20230308145163](figures/appfreeze_20230308145163.png) + +### User Input Response Timeout + +This fault affects user experience. If this fault occurs, the system does not respond to a click event within 10 seconds. + +Such a fault can be detected as follows: When a user clicks a certain button of the application, the input system sends a click event to the application. If the input system does not receive a response from the application within 10 seconds, a fault event is reported. The following figure shows the working principle. + +![appfreeze_20230308145162](figures/appfreeze_20230308145162.png) + +### Lifecycle Switching Timeout + +This fault refers to an ability lifecycle switching timeout (LIFECYCLE\_TIMEOUT) or an application lifecycle switching timeout (APP\_LIFECYCLE\_TIMEOUT). + +The fault occurs during lifecycle switching and affects the switchover between abilities in the current application or the switchover between applications. + +Such a fault can be detected as follows: Upon the start of a lifecycle switchover process, the main thread inserts a timeout task to the watchdog thread, and then removes the timeout task when the lifecycle switchover is complete. If the timeout duration expires, a fault event is reported. + +![appfreeze_20230308145164](figures/appfreeze_20230308145164.png) + +The timeout duration varies according to the lifecycle. + +| Lifecycle| Timeout Duration| +| -------- | -------- | +| Load | 10s | +| Terminate | 10s | +| Connect | 3s | +| Disconnect | 0.5s | +| Foreground | 5s | +| Background | 3s | + +## appfreeze Log Analysis + +To identify the cause of appfreeze, analyze the appfreeze logs together with HiLog logs. + +The following example uses main tread suspension as an example to illustrate how to conduct log analysis. You can treat other types of faults in a similar way. + +appfreeze logs are divided into several parts, including header information, and general and specific information in the body. + +### Log Header Information + +| Field| Description| +| -------- | -------- | +| Reason | Reason why the application does not respond. For details, see [appfreeze Detection](#appfreeze-detection).| +| PID | PID of the failed process, which can be used to search for related process information in the log.| +| PACKAGE_NAME | Application package name.| + +![appfreeze_20230310105865](figures/appfreeze_20230310105865.png) + +### General Information in the Log Body + +General information is present in all logs. It contains the fields listed in the following table. You can search for these fields to locate the related information in the log. + +| Field| Description| +| -------- | -------- | +| EVENTNAME | One or more fault events that constitute the cause of main thread suspension.| +| TIMESTAMP | Time when the fault event reported. You can narrow down the time range for viewing HiLog logs based on the timeout duration described in [appfreeze Detection](#appfreeze-detection).| +| PID | PID of the failed process, which can be used with the timestamp and timeout duration to search for related process information in the log.| +| PACKAGE_NAME | Application package name.| +| MSG | Dump information or description of the fault.| +| OpenStacktraceCatcher | Stack trace information of the process.| +| BinderCatcher | Information about IPC calls between a process and other system processes, such as the call waiting time.| +| PeerBinder Stacktrace | Information about stack trace of the peer process.| +| cpuusage | CPU usage of the device.| +| memory | Memory usage of the process.| + +The following is an example process stack of OpenStacktraceCatcher. + +In this example, when the stack surface window sends events through IPC, the process is stuck in the IPC communication phase. + +![appfreeze_20230310105869](figures/appfreeze_20230310105869.png) + +Example BinderCatcher information: + +In the following example, process 1561 sends an IPC request to process 685 but does not receive any response within 10 seconds. + +![appfreeze_20230310105868](figures/appfreeze_20230310105868.png) + +Example PeerBinder Stacktrace information: + +The following example shows the stack information of process 685, which is suspended at the peer end. + +![appfreeze_20230310105870](figures/appfreeze_20230310105870.png) + +Example CPU usage information: + +The following example shows the CPU usage information of the device. + +![appfreeze_20230310105871](figures/appfreeze_20230310105871.png) + +Example memory usage information: + +The following example shows the memory usage information of the process. + +![appfreeze_20230310105872](figures/appfreeze_20230310105872.png) + +### Specific Information in the Log Body (Application Main Thread Timeout) + +According to [Application Main Thread Timeout] (#application-main-thread-timeout), the log in which **Reason** is **THREAD\_BLOCK\_6S** consists of two parts: THREAD\_BLOCK\_3S and THREAD\_BLOCK\_6S. By comparing the two parts, you can determine whether the appfreeze is due to a suspension or an excess number of tasks. + +THREAD\_BLOCK\_3S is followed by THREAD\_BLOCK\_6S in the log. You can use the **EVENTNAME** field to search for the locations of the two events in the log. + +Both events contain the **MSG** field, which stores information about the processing queue of the main thread when the suspension occurs. Hence, you can view the status of the event processing queue of the main thread at the two time points. + +The example log shows that the event carrying **05:06:18.145** in the low-priority queue is being processed, and it is present in both the THREAD_BLOCK_3S and THREAD_BLOCK_6S. This indicates that the main thread suspension is not caused by an excess number of tasks. + +Because THREAD_BLOCK_6S indicates a main thread suspension, you only need to analyze the stack information of the main thread (the ID of the main thread is the same as the process ID). In the example log, the main thread stack is run in the JS through ArkUI and therefore it can be concluded that the suspension occurs in the JS. Because stack is present in both THREAD_BLOCK_3S and THREAD_BLOCK_6S in the same position, the JS suspension is not caused by an excess number of tasks. +THREAD_BLOCK_3S: + +![appfreeze_20230310105866](figures/appfreeze_20230310105866.png) + +THREAD_BLOCK_6S: + +![appfreeze_20230310105867](figures/appfreeze_20230310105867.png) + +Then, you can check for the code segment being executed on the application side based on the HiLog log. + +Generally, you can view the [general information in the log body](#general-information-in-the-log-body) to check for the cause of the suspension, for example, IPC suspension, high CPU usage, memory leakage, or high memory usage. + +### Specific Information in the Log Body (User Input Response Timeout) + +If **Reason** is **APPLICATION\_BLOCK\_INPUT**, no response is received within 10 seconds after a user click. + +You can find the event description in **MSG**. + +For details, see [General Information in the Log Body](#general-information-in-the-log-body). Note that there is a high probability that the main thread is suspended in the case of no response to the user input. You can compare the stack and BinderCatcher information in two log records for further analysis. If there is no log record indicating a main thread suspension, a large number of other events may exist before the input event. This may not cause a main thread suspension but can probably result in no response to user input. + +### Specific Information in the Log Body (Lifecycle Switching Timeout) + +For a lifecycle switching timeout, **Reason** can be **LIFECYCLE\_TIMEOUT** or **APP\_LIFECYCLE\_TIMEOUT**. + +**LIFECYCLE\_TIMEOUT** indicates a lifecycle switching timeout at the ability level, and **APP\_LIFECYCLE\_TIMEOUT** indicates a lifecycle switching timeout at the application level. + +MSG indicates the lifecycle that encounters a timeout. + +In this example, **LIFECYCLE\_TIMEOUT** indicates that the timeout occurs during switching of the ability to the background, and **APP\_LIFECYCLE\_TIMEOUT** indicates that the timeout occurs in the application termination phase. You can locate related HiLog logs according to the timeout duration described in [Lifecycle Switching Timeout] (#lifecycle-switching-timeout). + +LIFECYCLE_TIMEOUT: + +![appfreeze_20230310105873](figures/appfreeze_20230310105873.png) + +APP_LIFECYCLE_TIMEOUT: + +![appfreeze_20230310105874](figures/appfreeze_20230310105874.png) + +For details about other log information, see [General Information in the Log Body](#general-information-in-the-log-body). Note that there is a high probability that the main thread is suspended during lifecycle switching. You can compare the stack and BinderCatcher information in two log records for further analysis. diff --git a/en/application-dev/dfx/apprecovery-guidelines.md b/en/application-dev/dfx/apprecovery-guidelines.md index cb009645b020c8dea95d41d71b75d4ff0f10d8fb..284de5ca6d5f6027f2cce975a29b3259b2778021 100644 --- a/en/application-dev/dfx/apprecovery-guidelines.md +++ b/en/application-dev/dfx/apprecovery-guidelines.md @@ -2,16 +2,16 @@ ## When to Use -During application running, some unexpected behaviors are inevitable. For example, unprocessed exceptions and errors are thrown, and the call or running constraints of the framework are violated. +During application running, some unexpected behaviors are inevitable. For example, unprocessed exceptions and errors are thrown, and the call or running constraints of the recovery framework are violated. -By default, the processes will exit as exception handling. However, if user data is generated during application use, process exits may interrupt user operations and cause data loss. +Process exit is treated as the default exception handling method. However, if user data is generated during application use, process exit may interrupt user operations and cause data loss. In this way, application recovery APIs may help you save temporary data, restart an application after it exits, and restore its status and data, which deliver a better user experience. Currently, the APIs support only the development of an application that adopts the stage model, single process, and single ability. ## Available APIs -The application recovery APIs are provided by the **appRecovery** module, which can be imported via **import**. For details, please refer to [Development Example](#development-example). This document describes behaviors of APIs in API version 9, and the content will update with changes. +The application recovery APIs are provided by the **appRecovery** module, which can be imported via **import**. For details, see [Development Example](#development-example). This document describes behaviors of APIs in API version 9, and the content will update with changes. ### Available APIs @@ -23,39 +23,41 @@ The application recovery APIs are provided by the **appRecovery** module, which The APIs are used for troubleshooting and do not return any exception. Therefore, you need to be familiar with when they are used. -**enableAppRecovery**: This API should be called during application initialization. For example, you can call this API in **onCreate** of **AbilityStage**. For details, please refer to the [parameter description](../reference/apis/js-apis-app-ability-appRecovery.md). +**enableAppRecovery**: This API should be called during application initialization. For example, you can call this API in **onCreate** of **AbilityStage**. For details, see [Parameter Description](../reference/apis/js-apis-app-ability-appRecovery.md). -**saveAppState**: After this API is called, the framework calls back **onSaveState** of the ability. If data saving is agreed to in this method, relevant data and the page stack of the ability are persisted to the local cache of the application. +**saveAppState**: After this API is called, the framework calls back **onSaveState** of the ability. If data saving is accepted in this API, relevant data and the page stack of the ability are persisted to the local cache of the application. **restartApp**: After this API is called, the framework kills the current application process and restarts the ability in the foreground, with **APP_RECOVERY** specified as the startup cause. -### Framework Fault Management Process +### Framework Fault Management Fault management is an important way for applications to deliver a better user experience. The application framework offers three methods for application fault management: fault listening, fault rectification, and fault query. -- Fault listening refers to the process of registering [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) via [errorManager](../reference/apis/js-apis-app-ability-errorManager.md), listening for fault occurrence, and notifying the fault listener. +- Fault listening refers to the process of registering an [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) via [errorManager](../reference/apis/js-apis-app-ability-errorManager.md), listening for faults, and notifying the listener of the faults. -- Fault rectification refers to [appRecovery](../reference/apis/js-apis-app-ability-appRecovery.md) and restarts an application to restore its status previous to a fault. +- Fault rectification refers to the process of restoring the application state and data through [appRecovery](../reference/apis/js-apis-app-ability-appRecovery.md). -- Fault query indicates that [faultLogger](../reference/apis/js-apis-faultLogger.md) obtains the fault information using its query API. +- Fault query is the process of calling APIs of [faultLogger](../reference/apis/js-apis-faultLogger.md) to obtain the fault information. + +The figure below does not illustrate the time when [faultLogger](../reference/apis/js-apis-faultLogger.md) is called. You can refer to the [LastExitReason](../reference/apis/js-apis-app-ability-abilityConstant.md#abilityconstantlastexitreason) passed during application initialization to determine whether to call [faultLogger](../reference/apis/js-apis-faultLogger.md) to query information about the previous fault. -The figure below does not illustrate the time when [faultLogger](../reference/apis/js-apis-faultLogger.md) is called. You can refer to [LastExitReason](../reference/apis/js-apis-app-ability-abilityConstant.md#abilityconstantlastexitreason) passed during application initialization to determine whether to call [faultLogger](../reference/apis/js-apis-faultLogger.md) to query the information about the last fault. ![Fault rectification process](./figures/fault_rectification.png) -It is recommended that you call [errorManager](../reference/apis/js-apis-app-ability-errorManager.md) to process the exception. After the processing is complete, you can call the status saving API and restart the application. + +It is recommended that you call [errorManager](../reference/apis/js-apis-app-ability-errorManager.md) to handle the exception. After the processing is complete, you can call the **saveAppState** API and restart the application. If you do not register [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) or enable application recovery, the application process will exit according to the default processing logic of the system. Users can restart the application from the home screen. -If you have enabled application recovery, the framework first checks whether a fault allows for ability status saving and whether you have configured ability status saving. If so, [onSaveState](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonsavestate) of [Ability](../reference/apis/js-apis-app-ability-uiAbility.md) is called back. Finally, the application is restarted. +If you have enabled application recovery, the recovery framework first checks whether application state saving is supported and whether the application state saving is enabled. If so, the recovery framework invokes [onSaveState](../reference/apis/js-apis-app-ability-uiAbility.md#uiabilityonsavestate) of the [Ability](../reference/apis/js-apis-app-ability-uiAbility.md). Finally, the application is restarted. -### Scenarios Supported by Application Fault Management APIs +### Supported Application Recovery Scenarios Common fault types include JavaScript application crash, application freezing, and C++ application crash. Generally, an application is closed when a crash occurs. Application freezing occurs when the application does not respond. The fault type can be ignored for the upper layer of an application. The recovery framework implements fault management in different scenarios based on the fault type. -| Fault | Fault Listening | Status Saving| Automatic Restart| Log Query| +| Fault | Fault Listening | State Saving| Automatic Restart| Log Query| | ----------|--------- |--------- |--------- |--------- | | [JS_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | Supported|Supported|Supported|Supported| | [APP_FREEZE](../reference/apis/js-apis-faultLogger.md#faulttype) | Not supported|Not supported|Supported|Supported| | [CPP_CRASH](../reference/apis/js-apis-faultLogger.md#faulttype) | Not supported|Not supported|Not supported|Supported| -**Status Saving** in the table header means status saving when a fault occurs. To protect user data as much as possible in the application freezing fault, you can adopt either the periodic or automatic way, and the latter will save user data when an ability is switched to the background. +**State Saving** in the table header means saving of the application state when a fault occurs. To protect user data as much as possible when an AppFreeze occurs, you can adopt either the periodic or automatic way, and the latter will save user data when an ability is switched to the background. @@ -81,7 +83,7 @@ export default class MyAbilityStage extends AbilityStage { ### Saving and Restoring Data -After enabling **appRecovery**, you can use this function by either actively or passively saving the status and restoring data in the ability. +After enabling **appRecovery**, you can use this function by either actively or passively saving the application state and restoring data in the ability. The following is an example of **MainAbility**: #### Importing the Service Package @@ -92,14 +94,14 @@ import appRecovery from '@ohos.app.ability.appRecovery' import AbilityConstant from '@ohos.app.ability.AbilityConstant' ``` -#### Actively Saving Status and Restoring Data +#### Actively Saving the Application State and Restoring Data -- Define and register the [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback. +- Define and register the [ErrorObserver](../reference/apis/js-apis-inner-application-errorObserver.md) callback. For details about its usage, see [errorManager](../reference/apis/js-apis-app-ability-errorManager.md). ```ts var registerId = -1; var callback = { - onUnhandledException: function (errMsg) { + onUnhandledException(errMsg) { console.log(errMsg); appRecovery.saveAppState(); appRecovery.restartApp(); @@ -111,7 +113,7 @@ import AbilityConstant from '@ohos.app.ability.AbilityConstant' console.log("[Demo] MainAbility onWindowStageCreate") globalThis.registerObserver = (() => { - registerId = errorManager.registerErrorObserver(callback); + registerId = errorManager.on('error', callback); }) windowStage.loadContent("pages/index", null); @@ -127,7 +129,7 @@ After the callback triggers **appRecovery.saveAppState()**, **onSaveState(state, // Ability has called to save app data console.log("[Demo] MainAbility onSaveState") wantParams["myData"] = "my1234567"; - return AbilityConstant.onSaveResult.ALL_AGREE; + return AbilityConstant.OnSaveResult.ALL_AGREE; } ``` @@ -157,16 +159,16 @@ onWindowStageDestroy() { console.log("[Demo] MainAbility onWindowStageDestroy") globalThis.unRegisterObserver = (() => { - errorManager.unregisterErrorObserver(registerId, (result) => { - console.log("[Demo] result " + result.code + ";" + result.message) + errorManager.off('error', registerId, (err) => { + console.error("[Demo] err:", err); }); }) } ``` -#### Passively Saving Status and Restoring Data +#### Passively Saving the Application State and Restoring Data -This is triggered by the recovery framework. You do not need to register an **ErrorObserver** callback. You only need to implement **onSaveState** of the ability for status saving and **onCreate** of the ability for data restoration. +This is triggered by the recovery framework. You do not need to register an **ErrorObserver** callback. You only need to implement **onSaveState** for application state saving and **onCreate** for data restore. ```ts export default class MainAbility extends Ability { @@ -186,7 +188,7 @@ export default class MainAbility extends Ability { // Ability has called to save app data console.log("[Demo] MainAbility onSaveState") wantParams["myData"] = "my1234567"; - return AbilityConstant.onSaveResult.ALL_AGREE; + return AbilityConstant.OnSaveResult.ALL_AGREE; } } ``` diff --git a/en/application-dev/dfx/cppcrash-guidelines.md b/en/application-dev/dfx/cppcrash-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..4454422fe4f3aec6c781090a2e833ee103488dab --- /dev/null +++ b/en/application-dev/dfx/cppcrash-guidelines.md @@ -0,0 +1,110 @@ +# cppcrash Log Analysis + +## Introduction + +A process crash refers to a C/C++ runtime crash. The FaultLogger module of OpenHarmony provides capabilities such as process crash detection, log collection, log storage, and log reporting, helping you to locate faults more effectively. + +In this document, you'll be guided through how to implement process crash detection, crash log collection, and crash log analysis. Before getting started, make sure you have basic knowledge about C/C++ program stacks. + +## Process Crash Detection + +Process crash detection is implemented based on the Linux signal mechanism. Currently, C/C++ crash exception signals listed in the following table are supported. + +| Signal Value| Signal| Description| Trigger Cause| +| ------ | --------- | --------------- | ------------------------------------------- | +| 4 | SIGILL | Invalid instruction | An invalid, incorrectly formatted, unknown, or privileged instruction is executed.| +| 5 | SIGTRAP | Breakpoint or trap | An exception occurs or a trap instruction is executed.| +| 6 | SIGABRT | Process abort | The process is aborted abnormally. Generally, this problem occurs if the process calls the `abort()` function of the standard function library.| +| 7 | SIGBUS | Illegal memory access | The process accesses an aligned or nonexistent physical address.| +| 8 | SIGFPE | Floating-point exception | The process performs an incorrect arithmetic operation, for example, a 0 divisor, floating point overflow, or integer overflow.| +| 11 | SIGSEGV | Invalid memory access | The process accesses an invalid memory reference.| +| 16 | SIGSTKFLT | Stack error | The processor performs an incorrect stack operation, such as a pop when the stack is empty or a push when the stack is full.| +| 31 | SIGSYS | Incorrect system call | An incorrect or invalid parameter is used in a system call.| + +## Crash Log Collection + +Process crash log is the fault log managed together with the app freeze and JS application crash logs by the FaultLogger module. You can collect process crash logs in any of the following ways: + +### Collecting Logs by Using Shell + +- Fault logs in the `/data/log/faultlog/faultlogger/` directory of the device. The log files are named in the format of `cppcrash-process name-process UID-time (seconds level)`. They contain only information such as the device name, system version, and process crash call stack. + + ![cppcrash-faultlogger-log](figures/20230407112159.png) + +- Fault logs in the `/data/log/faultlog/temp/` directory of the device. The log files are named in the format of `cppcrash-process name-process PID-system timestamp (seconds level)`. In addition to basic information, they also contain information such as the stack memory and process maps at the time of process crash. + + ![cppcrash-temp-log](figures/20230407111853.png) + +### Collecting Logs by Using DevEco Studio + +DevEco Studio collects process crash logs from `/data/log/faultlog/faultlogger/` to FaultLog, where logs are displayed by process name, fault, and time. + +![DevEco Studio cppcrash](figures/20230407112620.png) + +### Collecting Logs by Using faultLogger APIs + +The FaultLogger module provides APIs to query various fault information. For details, see [@ohos.faultLogger](../reference/apis/js-apis-faultLogger.md). + +## Process Crash Log Analysis + +### Log Format + +The following is an example process crash log archived by DevEco Studio in FaultLog: + +``` +Generated by HiviewDFX@OpenHarmony +================================================================== +Device info:OpenHarmony 3.2 <- Device information +Build info:OpenHarmony 4.0.5.5 <- Version information +Module name:crasher_c <- Module name +Pid:1205 <- Process ID +Uid:0 <- User ID +Reason:Signal:SIGSEGV(SEGV_ACCERR)@0x0042d33d <- Exception information +Thread name:crasher <- Abnormal thread +#00 pc 0000332c /data/crasher(TriggerSegmentFaultException+15)(8bc37ceb8d6169e919d178fdc7f5449e) <- Call stack +#01 pc 000035c7 /data/crasher(ParseAndDoCrash+277)(8bc37ceb8d6169e919d178fdc7f5449e) +#02 pc 00003689 /data/crasher(main+39)(8bc37ceb8d6169e919d178fdc7f5449e) +#03 pc 000c3b08 /system/lib/ld-musl-arm.so.1(__libc_start_main+116) +#04 pc 000032f8 /data/crasher(_start_c+112)(8bc37ceb8d6169e919d178fdc7f5449e) +#05 pc 00003284 /data/crasher(_start+32)(8bc37ceb8d6169e919d178fdc7f5449e) +... +``` + +### Locating Faults Through Logs + +1. Determine the faulty module and fault type based on fault logs. + + Generally, you can identify the faulty module based on the crash process name and identify the crash cause based on the signal. Besides, you can restore the function call chain of the crash stack based on the method name in the stack. + + In the example, **SIGSEGV** is thrown by the Linux kernel because of access to an invalid memory address. The problem occurs in the **TriggerSegmentFaultException** function. + + In most scenarios, a crash is caused by the top layer of the crash stack, such as null pointer access and proactive program abort. + + If the cause cannot be located through the call stack, you need to check for other faults, for example, memory corruption or stack overflow. + +2. Use the addr2line tool of Linux to parse the code line number to restore the call stack at the time of process crash. + + When using the addr2line tool to parse the code line number of the crash stack, make sure that binary files with debugging information is used. Generally, such files are generated during version build or application build. + + Application binary files are located in DevEco Studio's temporary directory for application build, for example, `build/default/intermediates/libs`. + + System binary files are stored in the directories listed below. For versions that are directly obtained, the binary files are archived in the image package. + ``` + \code root directory\out\product\lib.unstripped + \code root directory\out\product\exe.unstripped + ``` + + You can run `apt-get install addr2line` to install the addr2line tool on Linux. + + On On DevEco Studio, you can also use the llvm-addr2line tool archived in the SDK to parse code line numbers. The usage method is the same. + + The following example shows how to use the addr2line tool to parse the code line number based on the offset address: + + ``` + root:~/OpenHarmony/out/rk3568/exe.unstripped/hiviewdfx/faultloggerd$ addr2line -e crasher 0000332c + base/hiviewdfx/faultloggerd/tools/crasher/dfx_crasher.c:57 + ``` + + In this example, the crash is caused by assignment of a value to an unwritable area. It is in code line 57 in the **dfx_crasher.c** file. You can modify it to avoid the crash. + + If the obtained code line number is seemingly incorrect, you can fine-tune the address (for example, subtract the address by 1) or disable some compilation optimization items. It is known that the obtained code line number may be incorrect when Link Time Optimization (LTO) is enabled. diff --git a/en/application-dev/dfx/figures/20230407111853.png b/en/application-dev/dfx/figures/20230407111853.png new file mode 100644 index 0000000000000000000000000000000000000000..cedfb46aecd8a3a1a9482619e4b0ea5f18eccc4a Binary files /dev/null and b/en/application-dev/dfx/figures/20230407111853.png differ diff --git a/en/application-dev/dfx/figures/20230407112159.png b/en/application-dev/dfx/figures/20230407112159.png new file mode 100644 index 0000000000000000000000000000000000000000..c2bce4198850fc25bdb2a4328c1b600d99d39e88 Binary files /dev/null and b/en/application-dev/dfx/figures/20230407112159.png differ diff --git a/en/application-dev/dfx/figures/20230407112620.png b/en/application-dev/dfx/figures/20230407112620.png new file mode 100644 index 0000000000000000000000000000000000000000..59a25256717ee791e0a3c40ec1d46c78d889a60f Binary files /dev/null and b/en/application-dev/dfx/figures/20230407112620.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230308145160.png b/en/application-dev/dfx/figures/appfreeze_20230308145160.png new file mode 100644 index 0000000000000000000000000000000000000000..2be8a97cf7b2518a0361cb0f8965642731282350 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230308145160.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230308145161.png b/en/application-dev/dfx/figures/appfreeze_20230308145161.png new file mode 100644 index 0000000000000000000000000000000000000000..92c236037b7c75f6480b3781ee6b1aa15e44cc51 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230308145161.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230308145162.png b/en/application-dev/dfx/figures/appfreeze_20230308145162.png new file mode 100644 index 0000000000000000000000000000000000000000..9169db911499f350a302eb060300d2c83c9fae19 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230308145162.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230308145163.png b/en/application-dev/dfx/figures/appfreeze_20230308145163.png new file mode 100644 index 0000000000000000000000000000000000000000..14c42dc9159174e7ddb283b1454963d40c6871f1 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230308145163.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230308145164.png b/en/application-dev/dfx/figures/appfreeze_20230308145164.png new file mode 100644 index 0000000000000000000000000000000000000000..7311f26bd906fa24331eaa10b66643eb09cf266f Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230308145164.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105865.png b/en/application-dev/dfx/figures/appfreeze_20230310105865.png new file mode 100644 index 0000000000000000000000000000000000000000..0082b4a012d79e2a407934abc6b3e45e580e20dd Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105865.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105866.png b/en/application-dev/dfx/figures/appfreeze_20230310105866.png new file mode 100644 index 0000000000000000000000000000000000000000..87e25e01d01d5e039c1089ee7604d2e14403efee Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105866.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105867.png b/en/application-dev/dfx/figures/appfreeze_20230310105867.png new file mode 100644 index 0000000000000000000000000000000000000000..c77c4686a66c56dc051aaa3a1d7996a3cb93985b Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105867.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105868.png b/en/application-dev/dfx/figures/appfreeze_20230310105868.png new file mode 100644 index 0000000000000000000000000000000000000000..7c1f36e709e4b07908567a7124df5e45fbd9bad1 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105868.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105869.png b/en/application-dev/dfx/figures/appfreeze_20230310105869.png new file mode 100644 index 0000000000000000000000000000000000000000..a55b3373405f2f4db972507e2f35102021c7f92a Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105869.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105870.png b/en/application-dev/dfx/figures/appfreeze_20230310105870.png new file mode 100644 index 0000000000000000000000000000000000000000..73c0549142e49e233b3ea38f8e1b6e44c642a30e Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105870.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105871.png b/en/application-dev/dfx/figures/appfreeze_20230310105871.png new file mode 100644 index 0000000000000000000000000000000000000000..ea8fe6bb1156255571e45e4e22d69cea70ae474f Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105871.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105872.png b/en/application-dev/dfx/figures/appfreeze_20230310105872.png new file mode 100644 index 0000000000000000000000000000000000000000..43406b804f9c981e636f4ad6099ce1abd69fb3d1 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105872.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105873.png b/en/application-dev/dfx/figures/appfreeze_20230310105873.png new file mode 100644 index 0000000000000000000000000000000000000000..71457fa6a49bf657a3b4527c7a000818c824ba94 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105873.png differ diff --git a/en/application-dev/dfx/figures/appfreeze_20230310105874.png b/en/application-dev/dfx/figures/appfreeze_20230310105874.png new file mode 100644 index 0000000000000000000000000000000000000000..cb4fc75b47b439fe131121586f13bdbf41408082 Binary files /dev/null and b/en/application-dev/dfx/figures/appfreeze_20230310105874.png differ diff --git a/en/application-dev/dfx/hilog-guidelines.md b/en/application-dev/dfx/hilog-guidelines.md new file mode 100644 index 0000000000000000000000000000000000000000..a399a520ba96aa3a64eea8968ed359c05da5440b --- /dev/null +++ b/en/application-dev/dfx/hilog-guidelines.md @@ -0,0 +1,70 @@ +# HiLog Development (Native) + +## Introduction + +HiLog is the log system of OpenHarmony that provides logging for the system framework, services, and applications to record information on user operations and system running status. + +> **NOTE** +> +> This development guide is applicable only when you use Native APIs for application development. For details about the APIs, see [HiLog Native API Reference](../reference/native-apis/_hi_log.md). + +## Available APIs + +| API/Macro| Description| +| -------- | -------- | +| int OH_LOG_Print(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...) | Outputs logs based on the specified log type, log level, service domain, log tag, and variable parameters determined by the format specifier and privacy identifier in the printf format.
Input arguments: See [Parameter Description](#parameter-description).
Output arguments: None
Return value: total number of bytes if log printing is successful; **-1** otherwise.| +| #define OH_LOG_DEBUG(type, ...) ((void)OH_LOG_Print((type), LOG_DEBUG, LOG_DOMAIN, LOG_TAG, \_*VA*ARGS__))| Outputs DEBUG logs. This is a function-like macro.| +| #define OH_LOG_INFO(type, ...) ((void)OH_LOG_Print((type), LOG_INFO, LOG_DOMAIN, LOG_TAG, \_*VA*ARGS__)) | Outputs INFO logs. This is a function-like macro.| +| #define OH_LOG_WARN(type, ...) ((void)OH_LOG_Print((type), LOG_WARN, LOG_DOMAIN, LOG_TAG, \_*VA*ARGS__)) | Outputs WARN logs. This is a function-like macro.| +| #define OH_LOG_ERROR(type, ...) ((void)OH_LOG_Print((type), LOG_ERROR, LOG_DOMAIN, LOG_TAG, \_*VA*ARGS__)) | Outputs ERROR logs. This is a function-like macro.| +| #define OH_LOG_FATAL(type, ...) ((void)OH_LOG_Print((type), LOG_FATAL, LOG_DOMAIN, LOG_TAG, \_*VA*ARGS__)) | Outputs FATAL logs. This is a function-like macro.| +| bool OH_LOG_IsLoggable(unsigned int domain, const char *tag, LogLevel level) | Checks whether logs of the specified service domain, tag, and level can be printed.
Input arguments: See [Parameter Description](#parameter-description).
Output arguments: none
Return value: **true** if the specified logs can be printed; **false** otherwise.| + +## Parameter Description + +| Name| Type | Mandatory| Description | +| ------ | ------ | ---- | ------------------------------------------------------------ | +| type | enum | Yes | Log printing type. The default value is **LOG_APP** for application logs.| +| level | enum | Yes | Log printing level. For details, see [Log Level](#loglevel).| +| domain | number | Yes | Service domain of logs. The value ranges from **0x0** to **0xFFFF**.
You can define the value as required. | +| tag | string | Yes | Log tag in the string format. You are advised to use this parameter to identify a particular service behavior or the class holding the ongoing method.| +| fmt | string | Yes | Format string used to output logs in a specified format. It can contain several parameters, where the parameter type and privacy identifier are mandatory.
Parameters labeled **{public}** are public data and are displayed in plaintext; parameters labeled **{private}** (default value) are private data and are filtered by **\**.| +| args | any[] | Yes | Variable-length parameter list corresponding to the format string. The number and type of parameters must map to the identifier in the format string.| + +## LogLevel + +Log level. + +| Name | Value | Description | +| ----- | ------ | ------------------------------------------------------------ | +| DEBUG | 3 | Log level used to record more detailed process information than INFO logs to help developers analyze service processes and locate faults.| +| INFO | 4 | Log level used to record key service process nodes and exceptions that occur during service running,
Log level used to record information about unexpected exceptions, such as network signal loss and login failure.
These logs should be recorded by the dominant module in the service to avoid repeated logging conducted by multiple invoked modules or low-level functions.| +| WARN | 5 | Log level used to record severe, unexpected faults that have little impact on users and can be rectified by the programs themselves or through simple operations.| +| ERROR | 6 | Log level used to record program or functional errors that affect the normal running or use of the functionality and can be fixed at a high cost, for example, by resetting data.| +| FATAL | 7 | Log level used to record program or functionality crashes that cannot be rectified. + +## Development Example + +1. Add the link of **libhilog_ndk.z.so** to **CMakeLists.txt**: +``` +target_link_libraries(entry PUBLIC libhilog_ndk.z.so) +``` +2. Include the **hilog** header file in the source file, and define the **domain** and **tag** macros. +```c++ +#include "hilog/log.h" +``` + +```c++ +#undef LOG_DOMAIN +#undef LOG_TAG +#define LOG_DOMAIN 0x3200 // Global domain, which identifies the service domain. +#define LOG_TAG "MY_TAG" // Global tag, which identifies the module log tag. +``` +3. Print logs. For example, to print ERROR logs, use the following code: +```c++ +OH_LOG_ERROR(LOG_APP, "Failed to visit %{private}s, reason:%{public}d.", url, errno); +``` +4. View the output log information. +``` +12-11 12:21:47.579 2695 2695 E A03200/MY_TAG: Failed to visit , reason:11. +``` diff --git a/en/application-dev/dfx/hitracemeter-guidelines.md b/en/application-dev/dfx/hitracemeter-guidelines.md index 3adf91286aaf410d7862c60320878e57acb359e8..3244aa9356bbcd3748594061a3752fad8aa3d3f3 100644 --- a/en/application-dev/dfx/hitracemeter-guidelines.md +++ b/en/application-dev/dfx/hitracemeter-guidelines.md @@ -21,7 +21,7 @@ Due to the asynchronous I/O feature of JS, the hiTraceMeter module provides only ## Available APIs -The performance tracing APIs are provided by the **hiTraceMeter** module. For details, see [API Reference]( ../reference/apis/js-apis-hitracemeter.md). +The performance tracing APIs are provided by the **hiTraceMeter** module. For details, see [API Reference](../reference/apis/js-apis-hitracemeter.md). **APIs for performance tracing** diff --git a/en/application-dev/faqs/Readme-EN.md b/en/application-dev/faqs/Readme-EN.md index 63535a32ae16eca13b03d20b4bce93569e2fe1d0..b7ff0dc957de51621a24016d127d8aca585676d5 100644 --- a/en/application-dev/faqs/Readme-EN.md +++ b/en/application-dev/faqs/Readme-EN.md @@ -1,21 +1,22 @@ # FAQs -- [Programming Languages](faqs-language.md) -- [Ability Framework Development](faqs-ability.md) -- [Bundle Management Development](faqs-bundle.md) -- [ArkUI (ArkTS) Development](faqs-ui-ets.md) -- [ArkUI Web Component (ArkTS) Development](faqs-web-arkts.md) -- [ArkUI (JavaScript) Development](faqs-ui-js.md) +- [Ability Development](faqs-ability.md) +- [ArkUI Development](faqs-arkui.md) +- [Web Development](faqs-arkui-web.md) +- [Bundle Management Development](faqs-bundle-management.md) +- [Resource Manager Development](faqs-globalization.md) - [Common Event and Notification Development](faqs-event-notification.md) -- [Graphics and Image Development](faqs-graphics.md) +- [Graphics Development](faqs-graphics.md) +- [Window Management Development](faqs-window-manager.md) +- [Multimedia Development](faqs-multimedia.md) +- [Basic Security Capability Development](faqs-security.md) +- [Ability Access Control Development](faqs-ability-access-control.md) +- [Data Management Development](faqs-distributed-data-management.md) - [File Management Development](faqs-file-management.md) -- [Media Development](faqs-media.md) -- [Network and Connection Development](faqs-connectivity.md) -- [Data Management Development](faqs-data-management.md) -- [Device Management Development](faqs-device-management.md) +- [Network Management Development](faqs-network-management.md) - [DFX Development](faqs-dfx.md) -- [Intl Development](faqs-international.md) -- [Native API Usage](faqs-native.md) -- [Usage of Third- and Fourth-Party Libraries](faqs-third-party-library.md) -- [IDE Usage](faqs-ide.md) -- [Development Board](faqs-development-board.md) \ No newline at end of file +- [Pan-Sensor Development](faqs-sensor.md) +- [Startup Development](faqs-startup.md) +- [Distributed Device Development](faqs-distributed-device-profile.md) +- [SDK Usage](faqs-sdk.md) +- [Usage of Third- and Fourth-Party Libraries](faqs-third-fourth-party-library.md) diff --git a/en/application-dev/faqs/faqs-ability-access-control.md b/en/application-dev/faqs/faqs-ability-access-control.md new file mode 100644 index 0000000000000000000000000000000000000000..f336120a2b131eb75604fa3e51b1b8ff0130072e --- /dev/null +++ b/en/application-dev/faqs/faqs-ability-access-control.md @@ -0,0 +1,7 @@ +# Application Access Control Development + +## Can the app listen for the permission change after its permission is modified in Settings? + +Applicable to: OpenHarmony 3.1 Beta 5 (API version 9) + +Third-party apps cannot listen for the permission change. diff --git a/en/application-dev/faqs/faqs-ability.md b/en/application-dev/faqs/faqs-ability.md index 7b3136f70aa6f01dcc376524252e8002adaef7a3..b6901e40e6d84c622c41e487e30c62ba87a28ff4 100644 --- a/en/application-dev/faqs/faqs-ability.md +++ b/en/application-dev/faqs/faqs-ability.md @@ -1,68 +1,124 @@ -# Ability Framework Development +# Ability Development -## Is a guide similar to the Data ability development in the FA model available for the stage model? +## How do I obtain a notification when the device orientation changes? -Applicable to: OpenHarmony SDK 3.2.3.5, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -A guide is available for the **DataShareExtensionAbility** class, which provides APIs for sharing data with other applications and managing the data, in the stage model. +**Solution** -Reference: [DataShare Development](../database/database-datashare-guidelines.md) +Use the **UIAbility.onConfigurationUpdate\(\)** callback to subscribe to system environment variable changes (including the language, color mode, and screen orientation). -## What should I do if the UI does not respond when an ability is started? +**Reference** -Applicable to: OpenHarmony SDK 3.2.5.3, stage model of API version 9 +[Subscribing to System Environment Variable Changes](../application-models/subscribe-system-environment-variable-changes.md#using-uiability-for-subscription) -1. If the ability is started using **startAbility**, check whether the **abilityName** field in **want** uses the bundle name as the prefix. If yes, delete the bundle name. +## How do I redirect a user to a specified page after they touch a service widget? -2. Make sure the ability's home page file configured by **onWindowStageCreate** in the **MainAbility.ts** file is defined in the **main_pages.json** file. +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -3. You are advised to use the SDK and OpenHarmony SDK versions released on the same day. +**Solution** -Reference: [Release Testing Version](https://gitee.com/openharmony-sig/oh-inner-release-management/blob/master/Release-Testing-Version.md) +Configure a widget event with the redirected-to UIAbility specified, and call **loadContent** in the **onWindowStageCreate\(\)** callback of the target UIAbility to redirect to the specified page. -## How do I set the UI of an ability to transparent? +**Reference** -Applicable to: OpenHarmony SDK 3.2.3.5, stage model of API version 9 +[Developing Widget Events](../application-models/arkts-ui-widget-configuration.md) -Set the background color of the top container component to transparent, and then set the **opacity** attribute of the XComponent to **0.01**. +## How do I create a background service in the stage model? -Example: +Applicable to: OpenHarmony 3.2 Beta5 -``` -build() { - Stack() { - XComponent({ - id: 'componentId', - type: 'surface', - }) - .width('100%') - .height('100%') - .opacity(0.01) - // Page content - } - .width('100%') - .height('100%') - .backgroundColor('rgba(255,255,255, 0)') -} -``` +**Symptom** + +**ServiceExtensionAbility** in the stage model is a system API. Therefore, third-party applications cannot use it to create a background service. + +**Solution** + +Create a background task to provide the background service. + +**Reference** + +[Background Task](../task-management/background-task-overview.md) + +## Can I create a UIAbility and specify the process to run the UIAbility in the FA and Stage models? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +Yes. + +- FA model + + The FA model supports multiple processes. By default, all components of an application run in the same process. This default scenario is suitable for most applications. To run a specific component in an independent process, configure the **process** tag under **ability** in the configuration file. Note that this tag is available only for system applications. + +- Stage model + + The stage model supports multiple processes. The process model is defined by the system, and third-party applications cannot be configured with multiple processes. To customize an independent process, you must request special permissions, and then specify the **process** tag under **module** in the configuration file. This tag specifies the process in which all the abilities in an HAP run. If this tag is not set, the bundle name is used as the process name by default. + + +## What are the differences between the stage model and the FA model in intra-process object sharing? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +- In the stage model, multiple application components share the same ArkTS engine instance. Therefore, they can easily share objects and state with each other. This also reduces the memory usage of complex applications. +- In the FA model, each application component exclusively uses an ArkTS engine instance. Therefore, you are advised to use the stage model when developing complex applications in distributed scenarios. + +**Reference** + +[Data Synchronization Between UIAbility and UI](../application-models/uiability-data-sync-with-ui.md) + +## How do I use the lifecycle functions of AbilityStage? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +Add the field **"srcEntry": "./ets/myabilitystage/MyAbilityStage.ts"** under **module** in the **module.json5** file. + +**Reference** + +[AbilityStage Component Container](../application-models/abilitystage.md) + + +## How do I delete the mission snapshot in Recents after terminateself is called in the multiton scenario? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +You can set **removeMissionAfterTerminate** to **true** in the **module.json5** file. + +## Why can't I start a UIAbility instance by using startAbility\(\)? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +- If the UIAbility is started using **startAbility**, check whether the **abilityName** field in **want** uses the bundle name as the prefix. If yes, delete the bundle name. +- Make sure the UIAbility's home page file configured by **onWindowStageCreate** in the **MainAbility.ts** file is defined in the **main\_pages.json** file. You are advised to use the SDK and OpenHarmony SDK versions released on the same day. ## How do I prevent "this" in a method from changing to "undefined" when the method is called? -Applicable to: OpenHarmony SDK 3.2.5.3, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** -Method 1: Add **.bind(this)** when calling the method. +Method 1: Add **.bind\(this\)** when calling the method. Method 2: Use the arrow function. -## What should I do when the message "must have required property 'startWindowIcon'" is displayed? +## What should I do when the error message "must have required property 'startWindowIcon'" is displayed during the UIAbility startup? -Applicable to: OpenHarmony SDK 3.2.3.5, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -Configure the **startWindowIcon** attribute under **abilities** in the **module.json5** file. +**Solution** -Reference: [Application Package Structure (Stage Model)](../quick-start/module-configuration-file.md) +Configure the **startWindowIcon** attribute under **abilities** in the **module.json5** file. -Example: +**Example** ``` { @@ -77,140 +133,226 @@ Example: } ``` -## How do I obtain a notification when the device orientation changes? - -Applicable to: OpenHarmony SDK 3.2.3.5, stage model of API version 9 +**Reference** -Implement the **onConfigurationUpdated** callback in the **Ability** class. The callback is triggered when the system language, color mode, or display parameters (such as the orientation and density) change. +[module.json5 Configuration File](../quick-start/module-configuration-file.md) ## Can I obtain the context through globalThis in the stage model? -Applicable to: OpenHarmony SDK 3.2.5.5, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +Do not use **globalThis** to obtain the context in the stage model. + +This is because all the processes of an application share a JS VM instance in the stage model. Multiple abilities can run on these processes and share the same global object. If **globalThis** is used, the context of different abilities of the same JS VM instance may be returned. + +**Reference** + +[Data Synchronization Between UIAbility and UI](../application-models/uiability-data-sync-with-ui.md) -Do not use **globalThis** to obtain the context in the stage model. This is because all the processes of an application share a JS VM instance in the stage model. Multiple abilities can run on these processes and share the same global object. If **globalThis** is used, the context of different abilities of the same JS VM instance may be returned. +## What should I do when an error indicating too large size is reported during HAP deployment? -Reference of the recommended operation: [Context (Stage Model)](../application-models/application-context-stage.md) +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -## How do I obtain the HAP installation path of application B from application A? +**Symptom** -Applicable to: OpenHarmony SDK 3.0 or later, stage model of API version 9 +During HAP deployment, the following error message is displayed: -First, request the system permission. For details, see [Having Your App Automatically Signed](https://developer.harmonyos.com/en/docs/documentation/doc-guides/ohos-auto-configuring-signature-information-0000001271659465). Then, import the **bundle** module, and call **bundle.getApplicationInfo()** to obtain application information based on the bundle name. Finally, use **application.moduleSourceDirs** to obtain the application storage path. +Failure\[INSTALL\_FAILED\_SIZE\_TOO\_LARGE\] error while deploying hap? + +**Solution** + +You can split the HAP into multiple HAPs. ## How is data returned when startAbilityForResult is called? -Applicable to: OpenHarmony SDK3.0, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -The callee uses **AbilityContext.terminateSelfWithResult** to destroy its ability and pass parameters to **startAbilityForResult**. For details, see [AbilityContext](../reference/apis/js-apis-ability-context.md#abilitycontextterminateselfwithresult). +**Solution** -## Can the lifecycle callback of a released FA widget be triggered when the widget is displayed in the service center so that the user login information can be obtained without opening the FA application? +The target UIAbilities uses **AbilityContext.terminateSelfWithResult** to terminate itselef and pass the result to **startAbilityForResult**. -Applicable to: OpenHarmony SDK 3.2.5.5, FA model of API version 8 +**Reference** -After a widget is added, the **oncreate()** lifecycle is triggered so that related user information (silent login) can be displayed even when the application is not started. However, users must manually add the widget after the application is installed. +[Starting UIAbility in the Same Application and Obtaining the Return Result](../application-models/uiability-intra-device-interaction.md) -## How do I obtain the context? -Applicable to: OpenHarmony SDK 3.2.7.5, stage model of API version 9 +## How do I obtain the system timestamp? -You can use **this.context** to obtain the context in the **MainAbility.ts** file or call **getContext(this)** to obtain the context on the component page. +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -## What should I do when undefined is displayed for the calling of **abilityAccessCtrl.grantUserGrantedPermission** during API version 8 syntax verification? +**Solution** -Applicable to: OpenHarmony SDK 3.0, FA model of API version 8 +Use **getCurrentTime** of **@ohos.systemDateTime** to obtain the system time and time zone. -**abilityAccessCtrl.grantUserGrantedPermission** is a system API. It is not available in the public SDK, which is provided as default on DevEco Studio. To use system APIs, switch to the full SDK. For details, see [Guide to Switching to Full SDK](../quick-start/full-sdk-switch-guide.md). +**Example** -## Which of the following Extension abilities are available in the public SDK: ServiceExtensionAbility, FormExtensionAbility, and DataShareExtensionAbility? +Use the **@ohos.systemDateTime** API as follows: -Applicable to: OpenHarmony SDK 3.2.5.6, stage model of API version 9 + ``` + try { + systemDateTime.getCurrentTime(true, (error, time) => { + if (error) { + console.info(`Failed to get currentTime. message: ${error.message}, code: ${error.code}`); + return; + } + console.info(`Succeeded in getting currentTime : ${time}`); + }); + } catch(e) { + console.info(`Failed to get currentTime. message: ${e.message}, code: ${e.code}`); + } + ``` -Among the aforementioned Extension abilities, only **FormExtensionAbility** is available in the public SDK. **ServiceExtensionAbility** and **DataShareExtensionAbility** are system APIs and available only in the full SDK. -Public SDK: intended for application developers and does not contain system APIs that require system permissions. +**Reference** -Full SDK: intended for original equipment manufacturers (OEMs) and contains system APIs that require system permissions. +[System time and time zone] (../reference/apis/js-apis-system-date-time.md#systemdatetimegetcurrenttime) -## Why can't I play GIF images cyclically on the widget? +## How do I obtain the cache directory of the current application? -Applicable to: OpenHarmony SDK 3.2.5.6, stage model of API version 9 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -The system does not support the display of GIF images on the widget. +**Solution** -## How do I implement service login by touching a widget? +Use **Context.cacheDir** to obtain the cache directory of the application. -Applicable to: OpenHarmony SDK 3.2.5.5, stage model of API version 9 +**Reference** -You can start an ability upon the touch and implement service login in the ability. +[Obtaining the Application Development Path](../application-models/application-context-stage.md#obtaining-the-application-development-path) -## How do I redirect to the application details page in Settings? +## In which JS file is the service widget lifecycle callback invoked? -Applicable to: OpenHarmony SDK 3.2.6.5 +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -Refer to the following code: +**Solution** -``` -this.context.startAbility( -{ - action: "action.settings.app.info", - parameters: { "settingsParamBundleName": "your app bundlename" } -}) -``` +When a widget is created, a **FormAblity.ts** file is generated, which contains the widget lifecycle. -## How do I listen for screen rotation events? +**Reference** -Applicable to: OpenHarmony SDK 3.2.5.5, stage model of API version 9 +[FormExtensionAbility](../reference/apis/js-apis-app-form-formExtensionAbility.md) -Refer to the following code: +## What should I do when the compilation on DevEco Studio fails while ServiceExtensionAbility and DataShareExtensionAbility APIs are used? -``` -let listener = mediaquery.matchMediaSync('(orientation: landscape)') -onPortrait(mediaQueryResult) { -if (mediaQueryResult.matches) { -// Do something here. - } else { -// Do something here. - } -} -listener.on('change', onPortrait) -``` +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) -## How do I control the shadow background size during checkbox selection? +**Symptom** -Applicable to: OpenHarmony SDK 3.2.5.5, stage model of API version 9 +After the **ServiceExtensionAbility** and **DataShareExtensionAbility** APIs are used, DevEco Studio reports an error indicating that the compilation fails. -Set the **padding** attribute of the **\** component to control the shadow size. +**Cause** -## How do I set the widget background to transparent? +The following types of SDKs are provided: -Applicable to: OpenHarmony SDK 3.2.5.5 +- Public SDK: intended for application developers and does not contain system APIs that require system permissions. +- Full SDK: intended for original equipment manufacturers (OEMs) and contains system APIs that require system permissions. -1. Create the **widget/resources/styles/default.json** file in the root directory of the widget. +The SDK downloaded using DevEco Studio is the public SDK. -2. Add the following code to the **default.json** file: +**Solution** -``` -{ - "style": { - "app_background": "#00000000" - } -} -``` +Third-party application cannot use **ServiceExtensionAbility** and **DataShareExtensionAbility**. To develop a system application, first [download the full SDK](../quick-start/full-sdk-switch-guide.md). + +## How do I obtain the temp and files paths at the application level? + +Applicable to: OpenHarmony 3.2 Beta5 + +**Solution** + +Obtain them from the application context. Specifically, use **this.context.getApplicationContext.tempDir** i to obtain the **temp** path, and use **this.context.getApplicationContext.filesDir** to obtain the **files** path. + +**Reference** + +[Obtaining the Application Development Path](../application-models/application-context-stage.md#obtaining-the-application-development-path) + + +## Can the lifecycle callback of a released FA widget be triggered when the widget is displayed in the service center so that the user login information can be obtained without opening the FA application? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +After a widget is added, the **onCreate()** lifecycle is triggered so that related user information (silent login) can be displayed even when the application is not started. However, users must manually add the widget after the application is installed. + +## How do I implement service login by touching a widget? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** -## How do I pass parameters for FA widgets? +To create a service widget in the FA model, perform the following steps: -Applicable to: OpenHarmony SDK 3.2.5.5 +1. Implement lifecycle callbacks for the widget. -Use **featureAbility.getWant()** and **featureAbility.getContext()** to send data through **router** in the JSON file and use **featureAbility** to receive data in the JS file. +2. Configure the widget configuration file. -## How do I trigger router.disableAlertBeforeBackPage and router.enableAlertBeforeBackPage? +3. Persistently store widget data. -Applicable to: OpenHarmony SDK 3.2.5.5 +4. Update widget data. -The following conditions must be met: +5. Develop the widget UI page. -1. Before the redirection to the previous page, a confirm dialog box will be displayed. Note that **router.disableAlertBeforeBackPage** is used to disable the display of a confirm dialog box before returning to the previous page (default), and **router.enableAlertBeforeBackPage** is used to enable the display. +6. Develop a widget event. You can start a UIAbility upon the touch and implement service login in the UIAbility. -2. The system return key is used. +**Reference** - \ No newline at end of file +[Widget Development in the FA Model](../application-models/widget-development-fa.md) + +## How do I redirect to the application details page in Settings? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +Refer to the following code: + +``` +this.context.startAbility( +{ + action: "action.settings.app.info", + parameters: { "settingsParamBundleName": "your app bundlename" } +}) +``` + +## How do I get UIAbilityContext within the @Component component in the stage model? + +Applicable to: OpenHarmony 3.2 Beta5 (API version 9) + +**Solution** + +You can use **UIAbility.Context** to obtain the context. + +**Example** + +``` +import UIAbility from '@ohos.app.ability.UIAbility'; + +let UIAbilityContext = UIAbility.context; +let ApplicationContext = UIAbility.context.getApplicationContext(); +@Entry +@Component +struct AbilityContextTest { + // abilityContext + @State UIabilityInfo: string = 'Obtaining abilityInfo' + UIabilityContext: UIAbilityContext + + aboutToAppear() { + // Use getContext to obtain the context and convert it to abilityContext. + this.UIabilityContext = getContext(this) as UIAbilityContext + } + + build() { + Row() { + Column({ space: 20 }) { + Text(this.abilityInfo) + .fontSize(20) + .onClick(()=>{ + this.UIabilityInfo = JSON.stringify(this.UIabilityContext.UIabilityInfo) + console.log(`ContextDemo abilityInfo= ${this.UIabilityInfo}`) + }) + } + .width('100%') + } + .height('100%') + } +} +``` diff --git a/en/application-dev/faqs/faqs-arkui-web.md b/en/application-dev/faqs/faqs-arkui-web.md new file mode 100644 index 0000000000000000000000000000000000000000..074a13ac37392b4404876149e582948e84f2464e --- /dev/null +++ b/en/application-dev/faqs/faqs-arkui-web.md @@ -0,0 +1,175 @@ +# Web Development + +## How does the return result of onUrlLoadIntercept affect onInterceptRequest in the \ component? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +The operation that follows **onUrlLoadIntercept** is subject to its return result. + +- If **true** is returned, the URL request is intercepted. +- If **false** is returned , the **onInterceptRequest** callback is performed. + +**Reference** + +[onUrlloadIntercept](../reference/arkui-ts/ts-basic-components-web.md#onurlloadinterceptdeprecated) + +## What should I do if the onKeyEvent event of the \ component is not triggered as expected? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Problem** + +The **onKeyEvent** event is set for the **\** component to listen for keyboard events. However, it is not triggered when a key is pressed or lifted. + +**Solution** + +Currently, the **\** component does not support the **onKeyEvent** event. To listen for keyboard events for the **\** component, you can use the **onInterceptKeyEvent** callback function. + +**Reference** + +[onInterceptKeyEvent](../reference/arkui-ts/ts-basic-components-web.md#oninterceptkeyevent9) + +## What should I do if page loading fails when onInterceptRequest is used to intercept URLs and return the custom HTML file? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Problem** + +The **onInterceptRequest** API intercepts URLs specified by **src** and returns the custom HTML file. However, the content in the **script** tag in the HTML file is not loaded. + +**Solution** + +If only **setResponseData** is set for the interceptor, the kernel cannot identify the HTML file. You must also set parameters such as **setResponseEncoding**, **setResponseMimeType**, and **setResponseHeader** for the kernel to identify the HTML file. + +**Example** + +``` +Web({ src: 'www.example.com', controller: this.controller }) + .onInterceptRequest((event) => { + console.log('url:' + event.request.getRequestUrl()) + this.responseweb = new WebResourceResponse(); + var head1:Header = { + headerKey:"Connection", + headerValue:"keep-alive" + } + var length = this.heads.push(head1) + this.responseweb.setResponseHeader(this.heads) + this.responseweb.setResponseData(this.webdata) + this.responseweb.setResponseEncoding('utf-8') + this.responseweb.setResponseMimeType('text/html') + this.responseweb.setResponseCode(200) + this.responseweb.setReasonMessage('OK') + return this.responseweb +}) +``` + +**Reference** + +[WebResourceResponse](../reference/arkui-ts/ts-basic-components-web.md#webresourceresponse) + +## How do I execute JS functions in HTML in ArkTS code? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +Use the **runJavaScript** API in **WebviewController** to asynchronously execute JavaScript scripts and obtain the execution result in a callback. + +>**NOTE** +> +>**runJavaScript** can be invoked only after l**oadUrl** is executed. For example, it can be invoked in **onPageEnd**. + +**Reference** + +[runJavaScript](../reference/apis/js-apis-webview.md#runjavascript) + +## How do I invoke an ArkTS method on a local web page loaded in the \ component? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +1. Prepare an HTML file. Below is the sample code: + + ``` + + + + + + + Document + + +

Title

+
+
+ + + + + ``` + +2. Use the **JavaScriptProxy** API in ArkTs to register the object in ArkTS with the window object of H5, and then use the window object to call the method in H5. In the following example, the **testObj** object in ArkTS is registered with the window object of H5 with the alias **objName**. In H5, **window.objName** can then be used to access the object. + + ``` + // xxx.ets + import web_webview from '@ohos.web.webview' + @Entry + @Component + struct Index { + @State message: string = 'Hello World' + controller: web_webview.WebviewController = new web_webview.WebviewController() + testObj = { + test: (data1, data2, data3) => { + console.log("data1:" + data1); + console.log("data2:" + data2); + console.log("data3:" + data3); + return "AceString"; + }, + toString: () => { + console.log('toString' + "interface instead."); + } + } + build() { + Row() { + Column() { + Web({ src:$rawfile('index.html'), controller:this.controller }) + .javaScriptAccess(true) + .javaScriptProxy({ + object: this.testObj, + name: "objName", + methodList: ["test", "toString"], + controller: this.controller, + }) + } + .width('100%') + } + .height('100%') + } + } + ``` + + +**Reference** + +[javaScriptProxy](../reference/arkui-ts/ts-basic-components-web.md#javascriptproxy) + +## How do I set the domStorageAccess attribute of the \ component? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Solution** + +The **domStorageAccess** attribute sets whether to enable the DOM Storage API. By default, this feature is disabled. + +**Reference** + +[domStorageAccess](../reference/arkui-ts/ts-basic-components-web.md#domstorageaccess) diff --git a/en/application-dev/faqs/faqs-arkui.md b/en/application-dev/faqs/faqs-arkui.md new file mode 100644 index 0000000000000000000000000000000000000000..0cce1a7add01d15e8bb6dcb1aa11e6c0ce5c2ab8 --- /dev/null +++ b/en/application-dev/faqs/faqs-arkui.md @@ -0,0 +1,1416 @@ +# ArkUI Development + +## How do I dynamically replace the %s placeholder in a resource file? + +Applicable to: OpenHarmony 3.2 Beta 5 (API version 9) + +**Symptom** + +How do I dynamically replace the %s placeholder in a resource file? + +**Solution** + +In an application, you can replace the **%s** placeholder by using the second parameter in **$r('app.string.xx')**, which is used to reference application resources. + +**Example** + +``` +build() { + //do something + // The second parameter indicates the referenced string resource, which can be used to replace the %s placeholder. + Text($r('app.string.entry_desc','aaa')) + .fontSize(100) + .fontColor(Color.Black) + //do something +} +``` + +## Can custom dialog boxes be defined or used in .ts files? + +Applicable to: OpenHarmony 3.2 Beta5 (API version 9, stage model) + +Unfortunately, no. ArkTS syntax is required for defining and initializing custom dialog boxes. Therefore, they can be defined and used only in .ets files. + +**Reference** + +[Custom Dialog Box](../reference/arkui-ts/ts-methods-custom-dialog-box.md) + +## How do I transfer variables in a custom dialog box to a page? + +Applicable to: OpenHarmony 3.2 Beta5 (API version 9, stage model) + +**Symptom** + +The variable defined in a custom dialog box needs to be transferred to the page when the dialog box is closed or the variable changes. + +**Solution** + +- Method 1: Define the variable as a state variable of the custom dialog box. +- Method 2: During initialization of the custom dialog box, pass to it a method, which is triggered in the custom dialog box and accepts the variable in the custom dialog box as a parameter. +- Method 3: Use AppStorage or LocalStorage to manage page state and implement state sharing between the custom dialog box and page. + +**Example** + +- Method 1: + + ``` + @CustomDialog + struct CustomDialog01 { + @Link inputValue: string + controller: CustomDialogController + build() { + Column() { + Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 }) + TextInput({ placeholder: '', text: this.inputValue }).height(60).width('90%') + .onChange((value: string) => { + this.inputValue = value + }) + } + } + } + + @Entry + @Component + struct DialogDemo01 { + @State inputValue: string = 'click me' + dialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialog01({ + inputValue: $inputValue + }) + }) + + build() { + Column() { + Button(this.inputValue) + .onClick(() => { + this.dialogController.open() + }).backgroundColor(0x317aff) + }.width('100%').margin({ top: 5 }) + } + } + + ``` + +- Method 2: + + ``` + @CustomDialog + struct CustomDialog02 { + private inputValue: string + changeInputValue: (val: string) => void + controller: CustomDialogController + build() { + Column() { + Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 }) + TextInput({ placeholder: '', text: this.inputValue }).height(60).width('90%') + .onChange((value: string) => { + this.changeInputValue(value) + }) + } + } + } + + @Entry + @Component + struct DialogDemo02 { + @State inputValue: string = 'click me' + dialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialog02({ + inputValue: this.inputValue, + changeInputValue: (val: string) => { + this.inputValue = val + } + }) + }) + + build() { + Column() { + Button(this.inputValue) + .onClick(() => { + this.dialogController.open() + }).backgroundColor(0x317aff) + }.width('100%').margin({ top: 5 }) + } + } + + ``` + +- Method 3: + + ``` + let storage = LocalStorage.GetShared() + @CustomDialog + struct CustomDialog03 { + @LocalStorageLink('inputVal') inputValue: string = '' + controller: CustomDialogController + build() { + Column() { + Text('Change text').fontSize(20).margin({ top: 10, bottom: 10 }) + TextInput({ placeholder: '', text: this.inputValue }).height(60).width('90%') + .onChange((value: string) => { + this.inputValue = value; + }) + } + } + } + + @Entry(storage) + @Component + struct DialogDemo03 { + @LocalStorageLink('inputVal') inputValue: string = '' + dialogController: CustomDialogController = new CustomDialogController({ + builder: CustomDialog03() + }) + + build() { + Column() { + Button(this.inputValue) + .onClick(() => { + this.dialogController.open() + }).backgroundColor(0x317aff) + }.width('100%').margin({ top: 5 }) + } + } + + ``` + + +## How do I obtain the width and height of a component? + +Applicable to: OpenHarmony 3.2 Beta5 (API version 9, stage model) + +**Symptom** + +The width and height of a component need to be obtained to calculate the size and offset of the layout area. + +**Solution** + +- Method 1: Use the **onAreaChange** event of the component, which is triggered when the component is initialized or the component size changes. +- Manner 2: Use the callback in the click or touch event, which provides the area information of the target element. + +**Reference** + +[Component Area Change Event](../reference/arkui-ts/ts-universal-component-area-change-event.md), [Click Event](../reference/arkui-ts/ts-universal-events-click.md), [Touch Event](../reference/arkui-ts/ts-universal-events-touch.md) + +## How do I clear the content of the \ and \