0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

鸿蒙Ability Kit(程序框架服务)【ServiceExtensionAbility】

jf_46214456 来源:jf_46214456 作者:jf_46214456 2024-06-04 14:50 次阅读

ServiceExtensionAbility

概述

[ServiceExtensionAbility]是SERVICE类型的ExtensionAbility组件,提供后台服务能力,其内部持有了一个[ServiceExtensionContext],通过[ServiceExtensionContext]提供了丰富的接口供外部使用。

本文描述中称被启动的ServiceExtensionAbility为服务端,称启动ServiceExtensionAbility的组件为客户端。

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

  • 启动 :AbilityA启动ServiceB,启动后AbilityA和ServiceB为弱关联,AbilityA退出后,ServiceB可以继续存在。
  • 连接 :AbilityA连接ServiceB,连接后AbilityA和ServiceB为强关联,AbilityA退出后,ServiceB也一起退出。

此处有如下细节需要注意:

  • 若Service只通过connect的方式被拉起,那么该Service的生命周期将受客户端控制,当客户端调用一次[connectServiceExtensionAbility()]方法,将建立一个连接,当客户端退出或者调用[disconnectServiceExtensionAbility()]方法,该连接将断开。当所有连接都断开后,Service将自动退出。
  • Service一旦通过start的方式被拉起,将不会自动退出,系统应用可以调用[stopServiceExtensionAbility()]方法将Service退出。
  • 只能在主线程线程中执行connect/disconnect操作,不要在Worker、TaskPool等子线程中执行connect/disconnect操作。

说明:
开发前请熟悉鸿蒙开发指导文档gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。

  1. 当前不支持三方应用实现ServiceExtensionAbility。如果三方开发者想要实现后台处理相关事务的功能,可以使用后台任务,具体请参见[后台任务]。
  2. 三方应用的UIAbility组件可以通过Context连接系统提供的ServiceExtensionAbility。
  3. 三方应用需要在前台获焦的情况下才能连接系统提供的ServiceExtensionAbility。
    搜狗高速浏览器截图20240326151450.png

生命周期

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

图1 ServiceExtensionAbility生命周期
ServiceExtensionAbility-lifecycle

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

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

  • onRequest 当另一个组件调用[startServiceExtensionAbility()]方法启动该服务组件时,触发该回调。执行此方法后,服务会启动并在后台运行。每调用一次[startServiceExtensionAbility()]方法均会触发该回调。
  • onConnect 当另一个组件调用[connectServiceExtensionAbility()]方法与该服务连接时,触发该回调。开发者在此方法中,返回一个远端代理对象(IRemoteObject),客户端拿到这个对象后可以通过这个对象与服务端进行RPC通信,同时系统侧也会将该远端代理对象(IRemoteObject)储存。后续若有组件再调用[connectServiceExtensionAbility()]方法,系统侧会直接将所保存的远端代理对象(IRemoteObject)返回,而不再触发该回调。
  • onDisconnect 当最后一个连接断开时,将触发该回调。客户端死亡或者调用[disconnectServiceExtensionAbility()]方法可以使连接断开。
  • onDestroy 当不再使用服务且准备将其销毁该实例时,触发该回调。开发者可以在该回调中清理资源,如注销监听等。

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

开发准备

只有系统应用才允许实现ServiceExtensionAbility,因此开发者在开发之前需做如下准备:

  • 替换Full SDK :ServiceExtensionAbility相关接口都被标记为System-API,默认对开发者隐藏,因此需要手动从镜像站点获取Full SDK,并在DevEco Studio中替换,具体操作可参考[替换指南]。
  • 申请AllowAppUsePrivilegeExtension特权 :只有具有AllowAppUsePrivilegeExtension特权的应用才允许开发ServiceExtensionAbility,具体申请方式可参考[应用特权配置指南])。

定义IDL接口

ServiceExtensionAbility作为后台服务,需要向外部提供可调用的接口,开发者可将接口定义在idl文件中,并使用[IDL工具]生成对应的proxy、stub文件。此处定义一个名为IIdlServiceExt.idl的文件作为示例:

interface OHOS.IIdlServiceExt {
  int ProcessData([in] int data);
  void InsertDataToMap([in] String key, [in] int val);
}

在DevEco Studio工程Module对应的ets目录下手动新建名为IdlServiceExt的目录,将[IDL工具]生成的文件复制到该目录下,并创建一个名为idl_service_ext_impl.ts的文件,作为idl接口的实现:

├── ets
│ ├── IdlServiceExt
│ │   ├── i_idl_service_ext.ts      # 生成文件
│ │   ├── idl_service_ext_proxy.ts  # 生成文件
│ │   ├── idl_service_ext_stub.ts   # 生成文件
│ │   ├── idl_service_ext_impl.ts   # 开发者自定义文件,对idl接口的具体实现
│ └
└

idl_service_ext_impl.ts实现如下:

import IdlServiceExtStub from './idl_service_ext_stub';
import hilog from '@ohos.hilog';
import type { insertDataToMapCallback } from './i_idl_service_ext';
import type { processDataCallback } from './i_idl_service_ext';

const ERR_OK = 0;
const TAG: string = "[IdlServiceExtImpl]";
const DOMAIN_NUMBER: number = 0xFF00;

// 开发者需要在这个类型里对接口进行实现
export default class ServiceExtImpl extends IdlServiceExtStub {
  processData(data: number, callback: processDataCallback): void {
    // 开发者自行实现业务逻辑
    hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
    callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑
  }

  insertDataToMap(key: string, val: number, callback: insertDataToMapCallback): void {
    // 开发者自行实现业务逻辑
    hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
    callback(ERR_OK);
  }
}

创建ServiceExtensionAbility

在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下:

  1. 在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为ServiceExtAbility。
  2. 在ServiceExtAbility目录,右键选择“New > ArkTS File”,新建一个文件并命名为ServiceExtAbility.ets。
    ├── ets
    │ ├── IdlServiceExt
    │ │   ├── i_idl_service_ext.ets      # 生成文件
    │ │   ├── idl_service_ext_proxy.ets  # 生成文件
    │ │   ├── idl_service_ext_stub.ets   # 生成文件
    │ │   ├── idl_service_ext_impl.ets   # 开发者自定义文件,对idl接口的具体实现
    │ ├── ServiceExtAbility
    │ │   ├── ServiceExtAbility.ets
    └
    
  3. 在ServiceExtAbility.ets文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并实现生命周期回调,在onConnect生命周期回调里,需要将之前定义的ServiceExtImpl对象返回。
    import hilog from '@ohos.hilog';
    import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
    import ServiceExtImpl from '../IdlServiceExt/idl_service_ext_impl';
    import type Want from '@ohos.app.ability.Want';
    import type rpc from '@ohos.rpc';
    
    const TAG: string = '[ServiceExtAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    export default class ServiceExtAbility extends ServiceExtensionAbility {
      serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl');
    
      onCreate(want: Want): void {
        let serviceExtensionContext = this.context;
        hilog.info(DOMAIN_NUMBER, TAG, `onCreate, want: ${want.abilityName}`);
      };
    
      onRequest(want: Want, startId: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, `onRequest, want: ${want.abilityName}`);
      };
    
      onConnect(want: Want): rpc.RemoteObject {
        hilog.info(DOMAIN_NUMBER, TAG, `onConnect, want: ${want.abilityName}`);
        // 返回ServiceExtImpl对象,客户端获取后便可以与ServiceExtensionAbility进行通信
        return this.serviceExtImpl as rpc.RemoteObject;
      };
    
      onDisconnect(want: Want): void {
        hilog.info(DOMAIN_NUMBER, TAG, `onDisconnect, want: ${want.abilityName}`);
      };
    
      onDestroy(): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');
      };
    };
    
  4. 在工程Module对应的[module.json5配置文件]中注册ServiceExtensionAbility,type标签需要设置为“service”,srcEntry标签表示当前ExtensionAbility组件所对应的代码路径。
    {
      "module": {
        ...
        "extensionAbilities": [
          {
            "name": "ServiceExtAbility",
            "icon": "$media:icon",
            "description": "service",
            "type": "service",
            "exported": true,
            "srcEntry": "./ets/ServiceExtAbility/ServiceExtAbility.ets"
          }
        ]
      }
    }
    

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

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

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

  1. 在系统应用中启动一个新的ServiceExtensionAbility。示例中的context的获取方式请参见[获取UIAbility的上下文信息]。
    import common from '@ohos.app.ability.common';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                let want: Want = {
                  deviceId: '',
                  bundleName: 'com.samples.stagemodelabilitydevelop',
                  abilityName: 'ServiceExtAbility'
                };
                context.startServiceExtensionAbility(want).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.');
                  // 成功启动后台服务
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStartBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  2. 在系统应用中停止一个已启动的ServiceExtensionAbility。
    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import Want from '@ohos.app.ability.Want';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                let want: Want = {
                  deviceId: '',
                  bundleName: 'com.samples.stagemodelabilitydevelop',
                  abilityName: 'ServiceExtAbility'
                };
                context.stopServiceExtensionAbility(want).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.');
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStoppedAStartedBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  3. 已启动的ServiceExtensionAbility停止自身。
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    import promptAction from '@ohos.promptAction';
    import hilog from '@ohos.hilog';
    import Want from '@ohos.app.ability.Want';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                context.terminateSelf().then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in terminating self.');
                  // 成功停止当前后台服务
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyStopStartedBackendService')
                  });
                }).catch((err: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, `Failed to terminate self. Code is ${err.code}, message is ${err.message}`);
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    

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

  • 后台服务自身调用[terminateSelf()]方法来自行停止。
  • 由其他组件调用[stopServiceExtensionAbility()]方法来停止。 调用[terminateSelf()]或[stopServiceExtensionAbility()]方法之后,系统将销毁后台服务。

连接一个后台服务

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

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

  • 使用connectServiceExtensionAbility()建立与后台服务的连接。示例中的context的获取方式请参见[获取UIAbility的上下文信息]。
    import common from '@ohos.app.ability.common';
    import deviceManager from '@ohos.distributedDeviceManager';
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import Want from '@ohos.app.ability.Want';
    // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let connectionId: number;
    let want: Want = {
      deviceId: '',
      bundleName: 'com.samples.stagemodelabilitydevelop',
      abilityName: 'ServiceExtAbility'
    };
    
    let options: common.ConnectOptions = {
      onConnect(elementName, remote: rpc.IRemoteObject): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
        // 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
        serviceExtProxy.processData(1, (errorCode: number, retVal: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
        });
        serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
        })
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
      }
    };
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                // 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
                connectionId = context.connectServiceExtensionAbility(want, options);
                // 成功连接后台服务
                promptAction.showToast({
                  message: $r('app.string.SuccessfullyConnectBackendService')
                });
                // connectionId = context.connectAbility(want, options);
                hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    
  • 使用disconnectServiceExtensionAbility()断开与后台服务的连接。
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let connectionId: number;
    @Entry
    @Component
    struct Page_ServiceExtensionAbility {
      build() {
        Column() {
          //...
          List({ initialIndex: 0 }) {
            ListItem() {
              Row() {
                //...
              }
              .onClick(() = > {
                let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // UIAbilityContext
                // connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护
                context.disconnectServiceExtensionAbility(connectionId).then(() = > {
                  hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');
                  // 成功断连后台服务
                  promptAction.showToast({
                    message: $r('app.string.SuccessfullyDisconnectBackendService')
                  });
                }).catch((error: BusinessError) = > {
                  hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');
                });
              })
            }
            //...
          }
          //...
        }
        //...
      }
    }
    

客户端与服务端通信

客户端在onConnect()中获取到[rpc.RemoteObject]对象后便可与Service进行通信,有如下两种方式:

  • 使用服务端提供的IDL接口进行通信(推荐
    // 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
    import common from '@ohos.app.ability.common';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
    
    const TAG: string = '[Page_ServiceExtensionAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    let options: common.ConnectOptions = {
      onConnect(elementName, remote: rpc.IRemoteObject): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
        // 通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
        serviceExtProxy.processData(1, (errorCode: number, retVal: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
        });
        serviceExtProxy.insertDataToMap('theKey', 1, (errorCode: number) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, errorCode: ${errorCode}`);
        })
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code: number): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
      }
    };
    
  • 直接使用[sendMessageRequest]接口向服务端发送消息(不推荐)
    import hilog from '@ohos.hilog';
    import promptAction from '@ohos.promptAction';
    import rpc from '@ohos.rpc';
    import common from '@ohos.app.ability.common';
    import { BusinessError } from '@ohos.base';
    
    const TAG: string = '[Page_CollaborateAbility]';
    const DOMAIN_NUMBER: number = 0xFF00;
    const REQUEST_CODE = 1;
    let options: common.ConnectOptions = {
      onConnect(elementName, remote): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
        if (remote === null) {
          hilog.info(DOMAIN_NUMBER, TAG, `onConnect remote is null`);
          return;
        }
        let option = new rpc.MessageOption();
        let data = new rpc.MessageSequence();
        let reply = new rpc.MessageSequence();
    
        data.writeInt(99);
        // 开发者可发送data到目标端应用进行相应操作
        // @param code 表示客户端发送的服务请求代码。
        // @param data 表示客户端发送的{@link MessageSequence}对象。
        // @param reply 表示远程服务发送的响应消息对象。
        // @param options 指示操作是同步的还是异步的。
        // @return 如果操作成功返回{@code true}; 否则返回 {@code false}。
    
        remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) = > {
          let errCode = reply.readInt(); // 在成功连接的情况下,会收到来自目标端返回的信息(100)
          let msg: number = 0;
          if (errCode === 0) {
            msg = reply.readInt();
          }
          hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);
          // 成功连接后台服务
          promptAction.showToast({
            message: `sendRequest msg:${msg}`
          });
        }).catch((error: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, `sendRequest failed, ${JSON.stringify(error)}`);
        });
      },
      onDisconnect(elementName): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
      },
      onFailed(code): void {
        hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback');
      }
    };
    //...
    

服务端对客户端身份校验

部分开发者需要使用ServiceExtension提供一些较为敏感的服务,因此需要对客户端身份进行校验,开发者可在IDL接口的stub端进行校验,IDL接口实现详见上文[定义IDL接口],此处推荐两种校验方式:

  • 通过callerUid识别客户端应用
    通过调用[getCallingUid()]接口获取客户端的uid,再调用[getBundleNameByUid()]接口获取uid对应的bundleName,从而识别客户端身份。此处需要注意的是[getBundleNameByUid()]是一个异步接口,因此服务端无法将校验结果返回给客户端,这种校验方式适合客户端向服务端发起执行异步任务请求的场景,示例代码如下:
    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';
    
    const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = "[IdlServiceExtImpl]";
    const DOMAIN_NUMBER: number = 0xFF00;
    
    // 开发者需要在这个类型里对接口进行实现
    export default class ServiceExtImpl extends IdlServiceExtStub {
      processData(data: number, callback: ProcessDataCallback): void {
        // 开发者自行实现业务逻辑
        hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
        let callerUid = rpc.IPCSkeleton.getCallingUid();
        bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
          // 对客户端包名进行识别
          if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过
            hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
            return;
          }
          // 识别通过,执行正常业务逻辑
        }).catch((err: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
        });
        //...
      };
    
      insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
        // 开发者自行实现业务逻辑
        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
        callback(ERR_OK);
      };
    };
    
  • 通过callerTokenId对客户端进行鉴权
    通过调用[getCallingTokenId()]接口获取客户端的tokenID,再调用[verifyAccessTokenSync()]接口判断客户端是否有某个具体权限,由于当前不支持自定义权限,因此只能校验当前[系统所定义的权限]。示例代码如下:
    import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
    import bundleManager from '@ohos.bundle.bundleManager';
    import IdlServiceExtStub from './idl_service_ext_stub';
    import hilog from '@ohos.hilog';
    import rpc from '@ohos.rpc';
    import type { BusinessError } from '@ohos.base';
    import type { InsertDataToMapCallback } from './i_idl_service_ext';
    import type { ProcessDataCallback } from './i_idl_service_ext';
    
    const ERR_OK = 0;
    const ERR_DENY = -1;
    const TAG: string = '[IdlServiceExtImpl]';
    const DOMAIN_NUMBER: number = 0xFF00;
    
    // 开发者需要在这个类型里对接口进行实现
    export default class ServiceExtImpl extends IdlServiceExtStub {
      processData(data: number, callback: ProcessDataCallback): void {
        // 开发者自行实现业务逻辑
        hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
    
        let callerUid = rpc.IPCSkeleton.getCallingUid();
        bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
          // 对客户端包名进行识别
          if (callerBundleName !== 'com.samples.stagemodelabilitydevelop') { // 识别不通过
            hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
            return;
          }
          // 识别通过,执行正常业务逻辑
        }).catch((err: BusinessError) = > {
          hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
        });
    
        let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
        let accessManger = abilityAccessCtrl.createAtManager();
        // 所校验的具体权限由开发者自行选择,此处ohos.permission.GET_BUNDLE_INFO_PRIVILEGED只作为示例
        let grantStatus = accessManger.verifyAccessTokenSync(callerTokenId, 'ohos.permission.GET_BUNDLE_INFO_PRIVILEGED');
        if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
          hilog.info(DOMAIN_NUMBER, TAG, 'PERMISSION_DENIED');
          callback(ERR_DENY, data); // 鉴权失败,返回错误
          return;
        }
        hilog.info(DOMAIN_NUMBER, TAG, 'verify access token success.');
        callback(ERR_OK, data + 1); // 鉴权通过,执行正常业务逻辑
      };
    
      insertDataToMap(key: string, val: number, callback: InsertDataToMapCallback): void {
        // 开发者自行实现业务逻辑
        hilog.info(DOMAIN_NUMBER, TAG, `insertDataToMap, key: ${key}  val: ${val}`);
        callback(ERR_OK);
      };
    };
    

审核编辑 黄宇

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 框架
    +关注

    关注

    0

    文章

    399

    浏览量

    17434
  • 程序
    +关注

    关注

    116

    文章

    3776

    浏览量

    80848
  • 鸿蒙
    +关注

    关注

    57

    文章

    2309

    浏览量

    42740
收藏 人收藏

    评论

    相关推荐

    鸿蒙开发接口Ability框架:【@ohos.application.Ability (Ability)】

    Ability模块提供对Ability生命周期、上下文环境等调用管理的能力,包括Ability创建、销毁、转储客户端信息等。
    的头像 发表于 04-30 17:42 2212次阅读
    <b class='flag-5'>鸿蒙</b>开发接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【@ohos.application.<b class='flag-5'>Ability</b> (<b class='flag-5'>Ability</b>)】

    鸿蒙开发接口Ability框架:【@ohos.ability.featureAbility (FeatureAbility模块)】

    FeatureAbility模块提供带有UI设计与用户交互的能力,包括启动新的ability、获取dataAbilityHelper、设置此Page Ability、获取当前Ability对应的窗口,连接
    的头像 发表于 05-06 16:31 931次阅读
    <b class='flag-5'>鸿蒙</b>开发接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【@ohos.<b class='flag-5'>ability</b>.featureAbility (FeatureAbility模块)】

    鸿蒙开发接口Ability框架:【 (ServiceExtensionAbility)】

    ServiceExtensionAbility模块提供ServiceExtension服务扩展相关接口的能力。
    的头像 发表于 05-09 09:59 768次阅读
    <b class='flag-5'>鸿蒙</b>开发接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【 (<b class='flag-5'>ServiceExtensionAbility</b>)】

    鸿蒙开发接口Ability框架:【 (Context模块)】

    Context模块提供了ability或application的上下文的能力,包括允许访问特定于应用程序的资源、请求和验证权限等。
    的头像 发表于 05-13 16:04 670次阅读
    <b class='flag-5'>鸿蒙</b>开发接口<b class='flag-5'>Ability</b><b class='flag-5'>框架</b>:【 (Context模块)】

    鸿蒙Ability Kit程序框架服务)【UIExtensionAbility】

    [UIExtensionAbility]是UI类型的ExtensionAbility组件,需要与[UIExtensionComponent]一起配合使用,开发者可以在UIAbility的页面中通过UIExtensionComponent嵌入提供方应用的UIExtensionAbility提供的UI。UIExtensionAbility会在独立于UIAbility的进程中运行,完成其页面的布局和渲染。常用于有进程隔离诉求的系统弹窗、状态栏、胶囊等模块化开发的场景。
    的头像 发表于 06-05 09:19 1320次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>)【UIExtensionAbility】

    HarmonyOS NEXT Developer Beta1中的Kit

    、AI六大领域,例如: 应用框架相关Kit开放能力:Ability Kit程序框架
    发表于 06-26 10:47

    鸿蒙应用模型:【Ability Kit】简介

    Ability Kit程序框架服务)提供了应用程序开发和运行的应用模型,是系统为开发者提供的应
    的头像 发表于 05-29 14:41 589次阅读
    <b class='flag-5'>鸿蒙</b>应用模型:【<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>】简介

    鸿蒙Ability Kit程序框架服务)【Ability内页面间的跳转】

    基于Stage模型下的Ability开发,实现Ability内页面间的跳转和数据传递。
    的头像 发表于 06-03 20:43 275次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>)【<b class='flag-5'>Ability</b>内页面间的跳转】

    鸿蒙Ability Kit程序框架服务)【ExtensionAbility组件】

    ExtensionAbility组件是基于特定场景(例如服务卡片、输入法等)提供的应用组件,以便满足更多的使用场景。
    的头像 发表于 06-04 15:54 485次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>)【ExtensionAbility组件】

    鸿蒙Ability Kit程序框架服务)【AbilityServiceExtensionAbility通信】

    本示例展示通过[IDL的方式]和 [@ohos.rpc] 等接口实现了AbilityServiceExtensionAbility之间的通信。
    的头像 发表于 06-05 09:28 454次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>)【<b class='flag-5'>Ability</b>与<b class='flag-5'>ServiceExtensionAbility</b>通信】

    鸿蒙Ability Kit程序框架服务)【组件启动规则(Stage模型)】

    组件启动规则(Stage模型) 启动组件是指一切启动或连接应用组件的行为: 启动UIAbility、ServiceExtensionAbility、DataShareExtensionAbility
    的头像 发表于 06-10 18:47 825次阅读
    <b class='flag-5'>鸿蒙</b><b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b>(<b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>)【组件启动规则(Stage模型)】

    鸿蒙Ability Kit程序框架服务)【应用启动框架AppStartup】

    `AppStartup`提供了一种更加简单高效的初始化组件的方式,支持异步初始化组件加速应用的启动时间。使用启动框架应用开发者只需要分别为待初始化的组件实现`AppStartup`提供
    的头像 发表于 06-10 18:38 667次阅读

    鸿蒙开发Ability Kit程序框架服务:任务管理

    AbilityRecord:系统服务侧管理一个UIAbility实例的最小单元,对应一个应用侧的UIAbility组件实例。系统服务侧管理UIAbility实例数量上限为512个。
    的头像 发表于 06-24 14:46 401次阅读
    <b class='flag-5'>鸿蒙</b>开发<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>:任务管理

    鸿蒙开发Ability Kit程序框架服务:FA模型绑定Stage模型ServiceExtensionAbility

    本文介绍FA模型的三种应用组件如何绑定Stage模型的ServiceExtensionAbility组件。
    的头像 发表于 06-25 10:43 257次阅读
    <b class='flag-5'>鸿蒙</b>开发<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>:FA模型绑定Stage模型<b class='flag-5'>ServiceExtensionAbility</b>

    鸿蒙开发Ability Kit程序框架服务:ServiceAbility切换 组件切换

    FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API,只有系统
    的头像 发表于 06-28 10:24 276次阅读
    <b class='flag-5'>鸿蒙</b>开发<b class='flag-5'>Ability</b> <b class='flag-5'>Kit</b><b class='flag-5'>程序</b><b class='flag-5'>框架</b><b class='flag-5'>服务</b>:ServiceAbility切换 组件切换