OpenHarmony项目如何进行ServiceExtensionAbility开发
ServiceExtensionAbility是Service类型的ExtensionAbility组件,可以提供接口给其他应用或系统服务使用,通过rpc进行通信。本文参考官方文档,介绍了如何进行服务端开发以及客户端如何进行连接通信。 一、服务端开发 1.对外暴露接口定义 (1)新建项目,在ets下新建目录IdlS
ServiceExtensionAbility是Service类型的ExtensionAbility组件,可以提供接口给其他应用或系统服务使用,通过rpc进行通信。本文参考官方文档,介绍了如何进行服务端开发以及客户端如何进行连接通信。
一、服务端开发
1.对外暴露接口定义
(1)新建项目,在ets下新建目录IdlServiceExt
(2)在IdlServiceExt下新建IIdlTestService.idl文件,需要向外部提供可调用的接口,定义在该文件中,用IDL工具生成三个ts文件,放入到该目录下。IDL工具使用教程:https://gitee.com/openharmony/docs/blob/master/zh-cn/application-dev/IDL/idl-guidelines.md
-- 进入IDL可执行工具目录下,打开命令窗口,执行命令:
.\idl -gen-ts -d IIdlTestServiceTs -c IIdlTestServiceTs/IIdlTestService.idl
├── ets
│ ├── IdlServiceExt
│ │ ├── i_idl_test_service.ts # 生成文件
│ │ ├── idl_test_service_proxy.ts # 生成文件
│ │ ├── idl_test_service_stub.ts # 生成文件
│ │ ├── idl_test_service_impl.ts # 开发者自定义文件,对idl接口的具体实现
│ └
└
2.接口实现
在IdlServiceExt文件夹下创建一个名为idl_test_service_impl.ts的文件,作为idl接口的实现,在对应的方法中可对客户端访问身份信息进行校验,编写业务逻辑
import IdlServiceExtStub from './idl_test_service_stub';
import hilog from '@ohos.hilog';
import type { getDataCallback } from './i_idl_test_service';
import rpc from '@ohos.rpc';
import bundleManager from '@ohos.bundle.bundleManager';
import Base from '@ohos.base';
const ERR_OK = 0;
const TAG: string = "[IdlTestServiceImpl]";
const DOMAIN_NUMBER: number = 0xFF00;
// 开发者需要在这个类型里对接口进行实现
export default class ServiceExtImpl extends IdlServiceExtStub {
constructor(des) {
super(des)
}
getData(data: number, callback: getDataCallback): void {
//身份校验
let callerUid = rpc.IPCSkeleton.getCallingUid();
bundleManager.getBundleNameByUid(callerUid).then((callerBundleName) => {
hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid: ' + callerBundleName);
// 对客户端包名进行识别
if (callerBundleName !== 'com.oh.clientest') { // 识别不通过
hilog.info(DOMAIN_NUMBER, TAG, 'The caller bundle is not in trustlist, reject');
return;
}
// 识别通过,执行正常业务逻辑,开发者自行实现业务逻辑,这里将客户端传过来的数据加1再返回
hilog.info(DOMAIN_NUMBER, TAG, `processData: ${data}`);
callback(ERR_OK, data + 1);
}).catch((err: Base.BusinessError) => {
hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
});
}
}
3.创建ServiceExtensionAbility
在DevEco Studio工程中手动新建一个ServiceExtensionAbility,具体步骤如下:
(1)在工程Module对应的ets目录下,右键选择“New > Directory”,新建一个目录并命名为ServiceExtAbility。
(2)在ServiceExtAbility目录,右键选择“New > ArkTS File”,新建一个文件并命名为ServiceExtAbility.ets。
(3)在ServiceExtAbility.ets文件中,增加导入ServiceExtensionAbility的依赖包,自定义类继承ServiceExtensionAbility并实现生命周期回调,在onConnect生命周期回调里,需要将之前定义的ServiceExtImpl对象返回。
import ServiceExtensionAbility from '@ohos.app.ability.ServiceExtensionAbility';
import Want from '@ohos.application.Want';
import rpc from '@ohos.rpc';
import hilog from '@ohos.hilog';
import ServiceExtImpl from '../IdlServiceExt/idl_test_service_impl';
const TAG: string = '[ServiceExtAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class ServiceExtAbility extends ServiceExtensionAbility {
serviceExtImpl: ServiceExtImpl = new ServiceExtImpl('ExtImpl');
onCreate(want: Want): void {
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"
}
]
}
}
4.配置应用特权(allowAppUsePrivilegeExtension)
(1)获取签名指纹
应用签名和证书指纹获取参考文档:https://laval.csdn.net/6544b3a934bf9e25c799c326.html
(2)获取设备的特权管控白名单文件install_list_capability.json
-- 连接设备
hdc shell
-- 执行如下命令查看设备的特权管控白名单文件install_list_capability.json的位置
find /system -name install_list_capability.json
-- 执行如下命令拉取install_list_capability.json,其中install_list_capability.json文件位置需根据实际情况进行调整
hdc shell mount -o rw,remount /
hdc file recv /system/etc/app/install_list_capability.json D:\install_list_capability.json
-- 修改install_list_capability.json文件,配置签名指纹app_signature(步骤一中的签名指纹)和应用包名
{
"bundleName": "com.oh.myapplication",
"app_signature" : ["CA4B96B85448F3C4D204DD7EC8B00FB565C2514E8CE13E339FC7524AF5158F48"],
"allowAppDesktopIconHide": false,
"keepAlive": true,
"allowAppUsePrivilegeExtension": true
}
-- 将修改后的install_list_capability.json文件重新推到设备上,并重启设备
hdc shell mount -o rw,remount /
hdc file send D:\install_list_capability.json /system/etc/app/install_list_capability.json
hdc shell chmod 777 /system/etc/app/install_list_capability.json
hdc shell reboot
二、客户端开发
1.导入服务端暴露的文件
新建项目,创建文件夹IdlServiceExt,复制服务端对外暴露的文件到文件夹中
├── ets
│ ├── IdlServiceExt
│ │ ├── i_idl_test_service.ts # 工具生成的文件
│ │ ├── idl_test_service_proxy.ts # 工具生成的文件
│ └
└
2.配置权限
在模块下的module.json5配置文件中添加权限,并将应用签名为系统应用
"requestPermissions": [
{"name": "ohos.permission.DISTRIBUTED_DATASYNC"},
{"name": "ohos.permission.START_INVISIBLE_ABILITY"},
]
3.业务开发
启动或连接服务器,获取到remote: rpc.IRemoteObject对象,创建IdlServiceExtProxy对象,与服务端通信,调用服务端暴露的接口,获取到的IdlServiceExtProxy对象可作为全局变量,在其他的地方使用。
import promptAction from '@ohos.promptAction';
import Want from '@ohos.application.Want';
import hilog from '@ohos.hilog';
import Base from '@ohos.base';
import common from '@ohos.app.ability.common';
import rpc from '@ohos.rpc';
// 客户端需要将服务端对外提供的idl_service_ext_proxy.ts导入到本地工程中
import IdlServiceExtProxy from '../IdlServiceExt/idl_test_service_proxy';
const TAG: string = '[Page_ServiceExtensionAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let connectionId: number;
//后台服务应用信息
let want: Want = {
deviceId: '',
bundleName: 'com.oh.myapplication',
abilityName: 'ServiceExtAbility'
};
let serviceExtProxy: IdlServiceExtProxy
// 通信方式一:通过接口调用的方式进行通信,屏蔽了RPC通信的细节,简洁明了
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;
}
serviceExtProxy = new IdlServiceExtProxy(remote);
//远程调用
serviceExtProxy.getData(3, (errorCode: number, retVal: number) => {
//retVal - 远程调用的返回值
console.log(`return data: ${retVal}`);
hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
});
},
onDisconnect(elementName): void {
hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
},
onFailed(code: number): void {
hilog.info(DOMAIN_NUMBER, TAG, 'onFailed callback', JSON.stringify(code));
}
};
const REQUEST_CODE = 1;
@Entry
@Component
struct Index {
@State message: string = 'Hello World';
@State returnData:number = 0;
private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
build() {
Column() {
//...
List({ initialIndex: 0,space:20}) {
//启动一个后台服务(仅对系统应用开放)
ListItem() {
Row() {
Button("启动后台服务")
}
.onClick(() => {
this.context.startServiceExtensionAbility(want).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in starting ServiceExtensionAbility.');
// 成功启动后台服务
promptAction.showToast({
message: $r('app.string.SuccessfullyStartBackendService')
});
}).catch((err:Base.BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to start ServiceExtensionAbility. code is ${err.code}, message is ${err.message}`);
});
})
}
//停止一个已启动的ServiceExtensionAbility
ListItem() {
Row() {
Button("停止后台服务")
}
.onClick(() => {
this.context.stopServiceExtensionAbility(want).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in stopping ServiceExtensionAbility.');
promptAction.showToast({
message: $r('app.string.SuccessfullyStoppedAStartedBackendService')
});
}).catch((err: Base.BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, `Failed to stop ServiceExtensionAbility. Code is ${err.code}, message is ${err.message}`);
});
})
}
ListItem() {
Row() {
Button("连接后台服务")
}
.onClick(() => {
// 建立连接后返回的Id需要保存下来,在解绑服务时需要作为参数传入
connectionId = this.context.connectServiceExtensionAbility(want, options);
// 成功连接后台服务
promptAction.showToast({
message: $r('app.string.SuccessfullyConnectBackendService')
});
// connectionId = context.connectAbility(want, options);
hilog.info(DOMAIN_NUMBER, TAG, `connectionId is : ${connectionId}`);
})
}
ListItem() {
Row() {
Button("连接成功点击调用函数")
if (this.returnData!==0) {
Text(`函数返回值:${this.returnData}`)
}
}
.onClick(() => {
serviceExtProxy.getData(5, (errorCode: number, retVal: number) => {
//retVal - 远程调用的返回值
console.log(`return data: ${retVal}`);
this.returnData=retVal
hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
});
})
}
ListItem() {
Row() {
Button("断连后台服务")
}
.onClick(() => {
// connectionId为调用connectServiceExtensionAbility接口时的返回值,需开发者自行维护
this.context.disconnectServiceExtensionAbility(connectionId).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');
// 成功断连后台服务
promptAction.showToast({
message: $r('app.string.SuccessfullyDisconnectBackendService')
});
}).catch((error: Base.BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');
});
})
}
}
}.width('100%')
}
}
注意点:
1.本文示例所用sdk版本为11,各版本sdk中略有差异,请以官方文档为准
2.sdk需要替换为full sdk,签名为系统应用
3.权限配置完整
4.确保签名指纹正确
5.本文示例代码详见附件
更多推荐
所有评论(0)