ServiceExtensionAbility


ServiceExtensionAbility

ServiceExtensionAbility是SERVICE类型的ExtensionAbility组件,提供后台服务相关扩展能力。

ServiceExtensionAbility可以被其他组件启动或连接,并根据调用者的请求信息在后台处理相关事务。ServiceExtensionAbility支持以启动和连接两种形式运行,系统应用可以调用startServiceExtensionAbility()方法启动后台服务,也可以调用connectServiceExtensionAbility()方法连接后台服务,而三方应用只能调用connectServiceExtensionAbility()方法连接后台服务。启动和连接后台服务的差别:

  • 启动:AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。

  • 连接:AbilityA绑定ServiceB,绑定后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。

每个类型的ExtensionAbility都有自己的Context,ServiceExtensionAbility通过ServiceExtensionContext提供相关能力。本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。

本章节将从如下场景来介绍ServiceExtensionAbility的基本使用。

说明:

  1. OpenHarmony当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见后台任务

  2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。

  3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。

实现一个后台服务(仅对系统应用开放)

ServiceExtensionAbility提供了onCreate()、onRequest()、onConnect()、onDisconnect()和onDestory()生命周期回调,根据需要重写对应的回调方法。下图展示了ServiceExtensionAbility的生命周期。

图1 ServiceExtensionAbility生命周期 ServiceExtensionAbility-lifecycle

  • onCreate 服务被首次创建时触发该回调,开发者可以在此进行一些初始化的操作,例如注册公共事件监听等。

    说明: 如果服务已创建,再次启动该ServiceExtensionAbility不会触发onCreate()回调。

  • onRequest 当另一个组件调用startServiceExtensionAbility()方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。

  • onConnect 当另一个组件调用connectServiceExtensionAbility()方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象(IRemoteObject),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信。

  • onDisconnect 其他组件调用disconnectServiceExtensionAbility()方法时,如果没有任何其他组件连接该服务,触发该回调。

  • onDestroy 当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。

开发步骤

开发者在实现一个后台服务时,需要在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下。

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为serviceextability。

  2. 在serviceextability目录,右键选择“New > ts File”,新建一个TS文件并命名为ServiceExtAbility.ts。

  3. 打开ServiceExtAbility.ts文件,导入RPC通信模块,重载onRemoteMessageRequest()方法,接收客户端传递过来的消息,并将处理的结果返回给客户端。REQUEST_VALUE用于校验客户端发送的服务请求码。

    import rpc from '@ohos.rpc';
    
    const REQUEST_CODE = 99;
    
    class StubTest extends rpc.RemoteObject {
      constructor(des) {
        super(des);
      }
      
      // 接收客户端传递过来的消息处理,以及将处理的结果返回给客户端
      onRemoteMessageRequest(code, data, reply, option) {
        if (code === REQUEST_CODE) {
          // 接收客户端传递过来的数据
          // 客户端使用多次调用data.writeInt()写入多个数据时,服务端可以通过多次调用data.readInt()方法接收对应的数据
          let optFir = data.readInt();
          let optSec = data.readInt();
          // 服务端将数据的处理结果返回给客户端
          // 示例中为接收了两个数据,并将两个数据的求和返回给客户端
          reply.writeInt(optFir + optSec);
        }
        return true;
      }
    
      // 以同步或异步方式向客户端发送消息
      sendRequest(code, data, reply, options) {
        return null;
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  4. 在ServiceExtAbility.ts文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并加上需要的生命周期回调。

    import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
    import rpc from '@ohos.rpc';
    
    const TAG: string = "[Example].[Entry].[ServiceExtAbility]";
    const REQUEST_CODE = 99;
    
    class StubTest extends rpc.RemoteObject {
      // ...
    }
    
    export default class ServiceExtAbility extends ServiceExtensionAbility {
      onCreate(want) {
        console.info(TAG, `onCreate, want: ${want.abilityName}`);
      }
    
      onRequest(want, startId) {
        console.info(TAG, `onRequest, want: ${want.abilityName}`);
      }
    
      onConnect(want) {
        console.info(TAG, `onConnect, want: ${want.abilityName}`);
        return new StubTest("test");
      }
    
      onDisconnect(want) {
        console.info(TAG, `onDisconnect, want: ${want.abilityName}`);
      }
    
      onDestroy() {
        console.info(TAG, `onDestroy`);
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
  5. 在工程Module对应的module.json5配置文件中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntrance标签表示当前ExtensionAbility组件所对应的代码路径。

    {
      "module": {
        // ...
        "extensionAbilities": [
          {
            "name": "ServiceExtAbility",
            "icon": "$media:icon",
            "description": "service",
            "type": "service",
            "visible": true,
            "srcEntrance": "./ets/serviceextability/ServiceExtAbility.ts"
          }
        ]
      }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

启动一个后台服务(仅对系统应用开放)

系统应用通过startServiceExtensionAbility()方法启动一个后台服务,服务的onRequest()回调就会被调用,并在该回调方法中接收到调用者传递过来的want对象。后台服务启动后,其生命周期独立于客户端,即使客户端已经销毁,该后台服务仍可继续运行。因此,后台服务需要在其工作完成时通过调用ServiceExtensionContext的terminateSelf()来自行停止,或者由另一个组件调用stopServiceExtensionAbility()来将其停止。

说明: ServiceExtensionContext的startServiceExtensionAbility()stopServiceExtensionAbility()terminateSelf()为系统接口,三方应用不支持调用。

  1. 在系统应用中启动一个新的ServiceExtensionAbility。示例中的context的获取方式参见获取UIAbility的Context属性

    let want = {
        "deviceId": "",
        "bundleName": "com.example.myapplication",
        "abilityName": "ServiceExtAbility"
    };
    this.context.startServiceExtensionAbility(want).then(() => {
        console.info('startServiceExtensionAbility success');
    }).catch((error) => {
        console.info('startServiceExtensionAbility failed');
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  2. 在系统应用中停止一个已启动的ServiceExtensionAbility。

    let want = {
        "deviceId": "",
        "bundleName": "com.example.myapplication",
        "abilityName": "ServiceExtAbility"
    };
    this.context.stopServiceExtensionAbility(want).then(() => {
        console.info('stopServiceExtensionAbility success');
    }).catch((error) => {
        console.info('stopServiceExtensionAbility failed');
    })
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
  3. 已启动的ServiceExtensionAbility停止自身。

    // this是当前ServiceExtensionAbility
    this.context.terminateSelf().then(() => {
        console.info('terminateSelf success');
    }).catch((error) => {
        console.info('terminateSelf failed');
    })
    
    1
    2
    3
    4
    5
    6

说明: 后台服务可以在后台长期运行,为了避免资源浪费,需要对后台服务的生命周期进行管理。即一个后台服务完成了请求方的任务,需要及时销毁。销毁已启动的后台服务有两种方式:

调用terminateSelf()stopServiceExtensionAbility()方法之后,系统将销毁后台服务。

连接一个后台服务

系统应用或者三方应用可以通过connectServiceExtensionAbility()连接一个服务(在Want对象中指定启动的目标服务),服务的onConnect()就会被调用,并在该回调方法中接收到调用者传递过来的Want对象,从而建立长连接。

ServiceExtensionAbility服务组件在onConnect()中返回IRemoteObject对象,开发者通过该IRemoteObject定义通信接口,用于客户端与服务端进行RPC交互。多个客户端可以同时连接到同一个后台服务,客户端完成与服务的交互后,客户端需要通过调用disconnectServiceExtensionAbility()来断开连接。如果所有连接到某个后台服务的客户端均已断开连接,则系统会销毁该服务。

  • 使用connectServiceExtensionAbility()建立与后台服务的连接。示例中的context的获取方式参见获取UIAbility的Context属性

    import rpc from '@ohos.rpc';
    
    const REQUEST_CODE = 99;
    let want = {
        "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 表示客户端发送的服务请求代码。
            // @param data 表示客户端发送的{@link MessageParcel}对象。
            // @param reply 表示远程服务发送的响应消息对象。
            // @param options 指示操作是同步的还是异步的。
            // 
            // @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
            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')
        }
    }
    // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
    let connectionId = this.context.connectServiceExtensionAbility(want, options);
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
  • 使用disconnectServiceExtensionAbility()断开与后台服务的连接。

    let connectionId = 1 // 在通过connectServiceExtensionAbility绑定服务时返回的Id
    this.context.disconnectServiceExtensionAbility(connectionId).then((data) => {
        console.info('disconnectServiceExtensionAbility success');
    }).catch((error) => {
        console.error('disconnectServiceExtensionAbility failed');
    })
    
    1
    2
    3
    4
    5
    6

相关示例

针对ServiceExtensionAbility开发,有以下相关示例可供参考:

ServiceExtAbility:StageExtAbility的创建与使用(ArkTS)(API9)(Full SDK)