From a0c860cd20480d401ad07778522e6da75fa71245 Mon Sep 17 00:00:00 2001 From: Gloria Date: Thu, 15 Dec 2022 17:02:02 +0800 Subject: [PATCH] Update docs against 10031+12082 Signed-off-by: wusongqing --- .../ability/fa-serviceability.md | 473 ++++++++---------- 1 file changed, 203 insertions(+), 270 deletions(-) diff --git a/en/application-dev/ability/fa-serviceability.md b/en/application-dev/ability/fa-serviceability.md index 3bafb0bc09..3cd2620cfd 100644 --- a/en/application-dev/ability/fa-serviceability.md +++ b/en/application-dev/ability/fa-serviceability.md @@ -1,64 +1,69 @@ # Service Ability Development ## When to Use -A Service ability is used to run tasks in the background, such as playing music or downloading files. It does not provide a UI for user interaction. Service abilities can be started by other applications or abilities and can remain running in the background even after the user switches to another application. +A Service ability is used to run tasks in the background, such as playing music or downloading files. It does not provide a UI for user interaction. Service abilities can be started by other applications or abilities and can keep running in the background even after the user switches to another application. -## Available APIs +## Lifecycle APIs **Table 1** Service ability lifecycle APIs |API|Description| |:------|:------| -|onStart?(): void|Called to initialize a Service ability being created. This callback is invoked only once in the entire lifecycle of a Service ability. The **Want** object passed to this callback must be null.| -|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on a client. You can collect calling statistics and perform initialization operations in this callback.| +|onStart?(): void|Called to initialize a Service ability when the Service ability is being created. This callback is invoked only once in the entire lifecycle of a Service ability.| +|onCommand?(want: Want, startId: number): void|Called every time a Service ability is created on the client. You can collect calling statistics and perform initialization operations in this callback.| |onConnect?(want: Want): rpc.RemoteObject|Called when another ability is connected to the Service ability.| |onDisconnect?(want: Want): void|Called when another ability is disconnected from the Service ability.| |onStop?(): void|Called when the Service ability is being destroyed. You should override this callback for your Service ability to clear its resources, such as threads and registered listeners.| +The differences between **onCommand()** and **onConnect()** are as follows: + - The **onCommand()** callback is triggered each time the client starts the Service ability by calling **startAbility** or **startAbilityForResult**. + - The **onConnect()** callback is triggered each time the client establishes a new connection with the Service ability by calling **connectAbility**. + ## How to Develop ### Creating and Registering a Service Ability -1. Override the Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests. +1. Override the Service ability-related lifecycle callbacks to implement your own logic for processing interaction requests. - ```javascript - export default { - onStart() { - console.log('ServiceAbility onStart'); - }, - onCommand(want, startId) { - console.log('ServiceAbility onCommand'); - }, - onConnect(want) { - console.log('ServiceAbility OnConnect'); - return new FirstServiceAbilityStub('test'); - }, - onDisconnect(want) { - console.log('ServiceAbility OnDisConnect'); - }, - onStop() { - console.log('ServiceAbility onStop'); - }, - } + ```ts + export default { + onStart() { + console.log('ServiceAbility onStart'); + }, + onCommand(want, startId) { + console.log('ServiceAbility onCommand'); + }, + onConnect(want) { + console.log('ServiceAbility OnConnect'); + // Below lists the implementation of ServiceAbilityStub. + return new ServiceAbilityStub('test'); + }, + onDisconnect(want) { + console.log('ServiceAbility OnDisConnect'); + }, + onStop() { + console.log('ServiceAbility onStop'); + } + } ``` 2. Register a Service ability. Declare the Service ability in the **config.json** file by setting its **type** attribute to **service**. - ```javascript + ```json { - "module": { - "abilities": [ - { - "name": ".ServiceAbility", - "type": "service", - "visible": true - ... - } - ] + "module": { + "abilities": [ + { + "name": ".ServiceAbility", + "type": "service", + "visible": true ... - } + } + ] ... + } + ... } ``` @@ -68,333 +73,261 @@ A Service ability is used to run tasks in the background, such as playing music The **Ability** class provides the **startAbility()** API for you to start another Service ability by passing a **Want** object. -To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified. The meanings of the parameters are as follows: +To set information about the target Service ability, you can first construct a **Want** object with the **bundleName** and **abilityName** parameters specified. -- **bundleName** indicates the name of the bundle to which the target ability belongs. -- **abilityName** indicates the target ability name. +- **bundleName** specifies the bundle name of the target application. +- **abilityName** specifies the target ability name. The following code snippet shows how to start a Service ability running on the local device: -```javascript -import featureAbility from '@ohos.ability.featureAbility'; -let promise = featureAbility.startAbility( +```ts +import featureAbility from '@ohos.ability.featureAbility' + +featureAbility.startAbility( { want: { bundleName: "com.jstest.service", - abilityName: "com.jstest.service.ServiceAbility", - }, + abilityName: "com.jstest.service.ServiceAbility" + } } -); +).then((err) => { + console.log("startService success"); +}).catch (err => { + console.log("startService FAILED"); +}); ``` -After the preceding code is executed, the **startAbility()** API is called to start the Service ability. -- If the Service ability is not running, the system calls **onStart()** to initialize the Service ability, and then calls **onCommand()** on the Service ability. +In the preceding code, the **startAbility()** API is used to start the Service ability. +- If the Service ability is not running, the system initializes the Service ability, and calls **onStart()** and **onCommand()** on the Service ability in sequence. - If the Service ability is running, the system directly calls **onCommand()** on the Service ability. -The following code snippet shows how to start a Service ability running on the remote device. For details about **getRemoteDeviceId()**, see [Connecting to a Remote Service Ability](#connecting-to-a-remote-service-ability). +The following code snippet shows how to start a Service ability running on the remote device. For details, see [Connecting to a Remote Service Ability](#connecting-to-a-remote-service-ability). + +```ts +import featureAbility from '@ohos.ability.featureAbility' -```javascript -import featureAbility from '@ohos.ability.featureAbility'; -let promise = featureAbility.startAbility( +featureAbility.startAbility( { want: { - deviceId: getRemoteDeviceId(), // Remote device ID + deviceId: remoteDeviceId, // Remote device ID. bundleName: "com.jstest.service", - abilityName: "com.jstest.service.ServiceAbility", - }, + abilityName: "com.jstest.service.ServiceAbility" + } } -); +).then((err) => { + console.log("startService success"); +}).catch (err => { + console.log("startService FAILED"); +}); ``` ### Stopping a Service Ability -Once created, the Service ability keeps running in the background. The system does not stop or destroy it unless memory resources must be reclaimed. + In normal cases, a Service ability can be stopped by itself or by the system. + - The Service ability can call **particleAbility.terminateSelf()** to stop itself. + - If the application process where the Service ability is located exits, the Service ability is reclaimed along with the process. + - If the Service ability is only accessed through **connectAbility()** (the **onCommand()** callback has never been triggered), the system stops the Service ability when the last connection to the Service ability is disconnected. ### Connecting to a Local Service Ability -If you need to connect a Service ability to a Page ability or to a Service ability in another application, you must first implement the **IAbilityConnection** API for the connection. A Service ability allows other abilities to connect to it through **connectAbility()**. +If a Service ability wants to interact with a Page ability or a Service ability in another application, you must first create a connection. A Service ability allows other abilities to connect to it through **connectAbility()**. You can use either of the following methods to connect to a Service ability: 1. Using the IDL to automatically generate code - Use OpenHarmony Interface Definition Language (IDL) to automatically generate the corresponding client, server, and **IRemoteObject** code. For details, see “Development Using TS" in [OpenHarmony IDL Specifications and User Guide](../IDL/idl-guidelines.md). + Use OpenHarmony Interface Definition Language (IDL) to automatically generate the corresponding client, server, and **IRemoteObject** code. For details, see [Development Using TS](../IDL/idl-guidelines.md#development-using-ts). 2. Writing code in the corresponding file - When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails. + When using **connectAbility()**, pass the **Want** and **ConnectOptions** objects of the target Service ability, where **ConnectOptions** encapsulates the following three callbacks that need to be implemented. + - **onConnect()**: callback used for processing when the Service ability is connected. + - **onDisconnect()**: callback used for processing when the Service ability is disconnected. + - **onFailed()**: callback used for processing when the connection to the Service ability fails. The following code snippet shows how to implement the callbacks: - ```javascript + ```ts import prompt from '@system.prompt' var option = { onConnect: function onConnectCallback(element, proxy) { - console.log(`onConnectLocalService onConnectDone`) + console.log(`onConnectLocalService onConnectDone`); if (proxy === null) { prompt.showToast({ message: "Connect service failed" - }) - return + }); + return; } - let data = rpc.MessageParcel.create() - let reply = rpc.MessageParcel.create() - let option = new rpc.MessageOption() - data.writeInterfaceToken("connect.test.token") - proxy.sendRequest(0, data, reply, option) + // After obtaining the proxy of the Service ability, the calling ability can communicate with the Service ability. + let data = rpc.MessageParcel.create(); + let reply = rpc.MessageParcel.create(); + let option = new rpc.MessageOption(); + data.writeString("InuptString"); + proxy.sendRequest(0, data, reply, option); prompt.showToast({ message: "Connect service success" - }) + }); }, onDisconnect: function onDisconnectCallback(element) { - console.log(`onConnectLocalService onDisconnectDone element:${element}`) + console.log(`onConnectLocalService onDisconnectDone element:${element}`); prompt.showToast({ message: "Disconnect service success" - }) + }); }, onFailed: function onFailedCallback(code) { - console.log(`onConnectLocalService onFailed errCode:${code}`) + console.log(`onConnectLocalService onFailed errCode:${code}`); prompt.showToast({ message: "Connect local service onFailed" - }) + }); } - } + }; ``` The following code snippet shows how to connect to a local Service ability: - ```javascript - import featureAbility from '@ohos.ability.featureAbility'; - let connId = featureAbility.connectAbility( - { - bundleName: "com.jstest.service", - abilityName: "com.jstest.service.ServiceAbility", - }, - { - onConnect: onConnectCallback, - onDisconnect: onDisconnectCallback, - onFailed: onFailedCallback, - }, - ); + ```ts + import featureAbility from '@ohos.ability.featureAbility' + + let want = { + bundleName: "com.jstest.service", + abilityName: "com.jstest.service.ServiceAbility" + }; + let connectId = featureAbility.connectAbility(want, option); ``` - When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of **IRemoteObject**. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**. + When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides the default implementation of **IRemoteObject**. You can inherit **rpc.RemoteObject** to create a custom implementation class for interaction with the Service ability. For details, see the [RPC API Reference](..\reference\apis\js-apis-rpc.md). - The following code snippet shows how the Service ability instance returns itself to the calling ability: + The following code snippet shows how the Service ability returns itself to the calling ability: - ```javascript - import rpc from "@ohos.rpc"; + ```ts + import rpc from "@ohos.rpc" - class FirstServiceAbilityStub extends rpc.RemoteObject { - constructor(des: any) { - if (typeof des === 'string') { - super(des) - } else { - return + class ServiceAbilityStub extends rpc.RemoteObject { + constructor(des: any) { + if (typeof des === 'string') { + super(des); + } else { + console.log("Error, the input param is not string"); + return; + } + } + + onRemoteRequest(code: number, data: any, reply: any, option: any) { + console.log("onRemoteRequest called"); + // Execute the service logic. + if (code === 1) { + // Sort the input strings. + let string = data.readString(); + console.log(`Input string = ${string}`); + let result = Array.from(string).sort().join(''); + console.log(`Output result = ${result}`); + reply.writeString(result); + } else { + console.log(`Unknown request code`); + } + return true; } } - onRemoteRequest(code: number, data: any, reply: any, option: any) { - console.log(printLog + ` onRemoteRequest called`) - if (code === 1) { - let string = data.readString() - console.log(printLog + ` string=${string}`) - let result = Array.from(string).sort().join('') - console.log(printLog + ` result=${result}`) - reply.writeString(result) - } else { - console.log(printLog + ` unknown request code`) + export default { + onStart() { + console.log('ServiceAbility onStart'); + }, + onCommand(want, startId) { + console.log('ServiceAbility onCommand'); + }, + onConnect(want) { + console.log('ServiceAbility OnConnect'); + return new ServiceAbilityStub('ServiceAbilityRemoteObject'); + }, + onDisconnect(want) { + console.log('ServiceAbility OnDisConnect'); + }, + onStop() { + console.log('ServiceAbility onStop'); } - return true; } ``` ### Connecting to a Remote Service Ability ->**NOTE** -> ->This feature applies only to system applications, since the **getTrustedDeviceListSync** API of the **DeviceManager** class is open only to system applications. - -If you need to connect a Service ability to a Page ability or another Service ability on a remote device, you must first implement the **IAbilityConnection** interface for the connection. A Service ability allows abilities on another device to connect to it through **connectAbility()**. - -When calling **connectAbility()**, you should pass a **Want** object containing information about the target Service ability and an **IAbilityConnection** object to the API. **IAbilityConnection** provides the following callbacks that you should implement: **onConnect()**, **onDisconnect()**, and **onFailed()**. The **onConnect()** callback is invoked when a Service ability is connected, **onDisconnect()** is invoked when a Service ability is unexpectedly disconnected, and **onFailed()** is invoked when a connection to a Service ability fails. +This feature applies only to system applications. The method of creating a **ConnectOptions** object for connecting to a remote Service ability is similar to that for connecting to a local Service ability. The differences are as follows: + - The application must apply for the data synchronization permission from the user. + - **Want** of the target Service ability must contain the remote device ID. -The following code snippet shows how to implement the callbacks: - -```ts -import prompt from '@system.prompt' - -var option = { - onConnect: function onConnectCallback(element, proxy) { - console.log(`onConnectRemoteService onConnectDone`) - if (proxy === null) { - prompt.showToast({ - message: "Connect service failed" - }) - return - } - let data = rpc.MessageParcel.create() - let reply = rpc.MessageParcel.create() - let option = new rpc.MessageOption() - data.writeInterfaceToken("connect.test.token") - proxy.sendRequest(0, data, reply, option) - prompt.showToast({ - message: "Connect service success" - }) - }, - onDisconnect: function onDisconnectCallback(element) { - console.log(`onConnectRemoteService onDisconnectDone element:${element}`) - prompt.showToast({ - message: "Disconnect service success" - }) - }, - onFailed: function onFailedCallback(code) { - console.log(`onConnectRemoteService onFailed errCode:${code}`) - prompt.showToast({ - message: "Connect local service onFailed" - }) - } +> **NOTE** +> +> The **getTrustedDeviceList** API of **DeviceManager** is open only to system applications. Currently, only system applications can connect to a remote Service ability. +> +> For details about the API definition, see [Device Management](..\reference\apis\js-apis-device-manager.md). + +The data synchronization permission is required in the cross-device scenario. Configure the permission in the **config.json** file. + +```json +{ + ... + "module": { + ... + "reqPermissions": [{ + "name": "ohos.permission.DISTRIBUTED_DATASYNC" + }] + } } ``` -The **Want** of the target Service ability must contain the remote **deviceId**, which can be obtained from **DeviceManager**. The sample code is as follows: +The **DISTRIBUTED_DATASYNC** permission is user granted. Therefore, your application, when being started, must display a dialog box to request the permission. The sample code is as follows: ```ts -import deviceManager from '@ohos.distributedHardware.deviceManager'; +import abilityAccessCtrl from "@ohos.abilityAccessCtrl" +import bundle from '@ohos.bundle' -// For details about the implementation of dmClass, see the implementation in Distributed Demo in Samples. -let dmClass; - -function getRemoteDeviceId() { - if (typeof dmClass === 'object' && dmClass != null) { - let list = dmClass.getTrustedDeviceListSync(); - if (typeof (list) == 'undefined' || typeof (list.length) == 'undefined') { - console.log("MainAbility onButtonClick getRemoteDeviceId err: list is null"); - return; +async function RequestPermission() { + console.info('RequestPermission begin'); + let array: Array = ["ohos.permission.DISTRIBUTED_DATASYNC"]; + let bundleFlag = 0; + let tokenID = undefined; + let userID = 100; + let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID); + tokenID = appInfo.accessTokenId; + let atManager = abilityAccessCtrl.createAtManager(); + let requestPermissions: Array = []; + for (let i = 0;i < array.length; i++) { + let result = await atManager.verifyAccessToken(tokenID, array[i]); + console.info("verifyAccessToken result:" + JSON.stringify(result)); + if (result != abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { + requestPermissions.push(array[i]); } - console.log("MainAbility onButtonClick getRemoteDeviceId success:" + list[0].deviceId); - return list[0].deviceId; - } else { - console.log("MainAbility onButtonClick getRemoteDeviceId err: dmClass is null"); } -} -``` - -The following code snippet shows how to connect to a remote Service ability: - -```ts -import featureAbility from '@ohos.ability.featureAbility'; -let connId = featureAbility.connectAbility( - { - deviceId: getRemoteDeviceId(), - bundleName: "ohos.samples.etsDemo", - abilityName: "ohos.samples.etsDemo.ServiceAbility", - }, - { - onConnect: onConnectCallback, - onDisconnect: onDisconnectCallback, - onFailed: onFailedCallback, - }, -); -``` -In the cross-device scenario, the application must also apply for the data synchronization permission from end users. The sample code is as follows: - -```ts -import abilityAccessCtrl from "@ohos.abilityAccessCtrl"; -import bundle from '@ohos.bundle'; -async function RequestPermission() { - console.info('RequestPermission begin'); - let array: Array = ["ohos.permission.DISTRIBUTED_DATASYNC"]; - let bundleFlag = 0; - let tokenID = undefined; - let userID = 100; - let appInfo = await bundle.getApplicationInfo('ohos.samples.etsDemo', bundleFlag, userID); - tokenID = appInfo.accessTokenId; - let atManager = abilityAccessCtrl.createAtManager(); - let requestPermissions: Array = []; - for (let i = 0;i < array.length; i++) { - let result = await atManager.verifyAccessToken(tokenID, array[i]); - console.info("verifyAccessToken result:" + JSON.stringify(result)); - if (result == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { - } else { - requestPermissions.push(array[i]); + console.info("requestPermissions:" + JSON.stringify(requestPermissions)); + if (requestPermissions.length == 0 || requestPermissions == []) { + return; } - } - console.info("requestPermissions:" + JSON.stringify(requestPermissions)); - if (requestPermissions.length == 0 || requestPermissions == []) { - return; - } - let context = featureAbility.getContext(); - context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{ - console.info("data:" + JSON.stringify(data)); - }); - console.info('RequestPermission end'); + let context = featureAbility.getContext(); + context.requestPermissionsFromUser(requestPermissions, 1, (data)=>{ + console.info("data:" + JSON.stringify(data)); + }); + console.info('RequestPermission end'); } ``` -When a Service ability is connected, the **onConnect()** callback is invoked and returns an **IRemoteObject** defining the proxy used for communicating with the Service ability. OpenHarmony provides a default implementation of **IRemoteObject**. You can extend **rpc.RemoteObject** to implement your own class of **IRemoteObject**. +To obtain the device ID, import the **@ohos.distributedHardware.deviceManager** module, which provides **getTrustedDeviceList** to obtain the remote device ID. For details about how to use the API, see [Device Management](..\reference\apis\js-apis-device-manager.md). -The following code snippet shows how the Service ability instance returns itself to the calling ability: +To connect to a remote Service ability, you only need to define **deviceId** in **Want**. The sample code is as follows: ```ts -import rpc from "@ohos.rpc"; - -class FirstServiceAbilityStub extends rpc.RemoteObject { - constructor(des: any) { - if (typeof des === 'string') { - super(des) - } else { - return - } - } - - onRemoteRequest(code: number, data: any, reply: any, option: any) { - console.log(printLog + ` onRemoteRequest called`) - if (code === 1) { - let string = data.readString() - console.log(printLog + ` string=${string}`) - let result = Array.from(string).sort().join('') - console.log(printLog + ` result=${result}`) - reply.writeString(result) - } else { - console.log(printLog + ` unknown request code`) - } - return true; - } -} +import featureAbility from '@ohos.ability.featureAbility' -export default { - onStart() { - console.info('ServiceAbility onStart'); - }, - onStop() { - console.info('ServiceAbility onStop'); - }, - onConnect(want) { - console.log("ServiceAbility onConnect"); - try { - let value = JSON.stringify(want); - console.log("ServiceAbility want:" + value); - } catch(error) { - console.log("ServiceAbility error:" + error); - } - return new FirstServiceAbilityStub("first ts service stub"); - }, - onDisconnect(want) { - console.log("ServiceAbility onDisconnect"); - let value = JSON.stringify(want); - console.log("ServiceAbility want:" + value); - }, - onCommand(want, startId) { - console.info('ServiceAbility onCommand'); - let value = JSON.stringify(want); - console.log("ServiceAbility want:" + value); - console.log("ServiceAbility startId:" + startId); - } +let want = { + deviceId: remoteDeviceId, + bundleName: "com.jstest.service", + abilityName: "com.jstest.service.ServiceAbility" }; +let connectId = featureAbility.connectAbility(want, option); ``` + +The other implementations are the same as those for the connection to a local Service ability. For details, see the sample code provided under [Connecting to a Local Service Ability](#connecting-to-a-local-service-ability). -- GitLab