概述
ServiceAbility是OpenHarmony中用于实现后台服务的组件,它可以在后台长时间运行,没有用户界面。ServiceAbility主要用于执行需要长时间运行的操作,如音乐播放、文件下载、数据同步等任务。
ServiceAbility具有以下特点:
- 无用户界面,在后台运行
- 可被其他组件(如Ability)启动或绑定
- 可以在设备重启后自动启动(需要配置)
- 支持多进程通信
ServiceAbility生命周期
ServiceAbility的生命周期包含以下关键回调方法:
| 生命周期方法 | 说明 |
|---|---|
| onStart() | ServiceAbility创建时调用,用于初始化操作 |
| onCommand() | 启动ServiceAbility时调用,可多次调用 |
| onConnect() | 绑定ServiceAbility时调用,返回IRemoteObject对象 |
| onDisconnect() | 解绑ServiceAbility时调用 |
| onStop() | ServiceAbility销毁时调用,用于资源清理 |
ServiceAbility配置
在module.json5文件中配置ServiceAbility:
json
{
"module": {
"abilities": [
{
"name": "ServiceAbility",
"srcEntry": "./ets/serviceability/ServiceAbility.ts",
"description": "$string:serviceability_desc",
"icon": "$media:icon",
"type": "service",
"backgroundModes": [
"dataTransfer",
"audioPlayback"
]
}
]
}
}
配置项说明:
- type:设置为"service"表示这是一个ServiceAbility
- backgroundModes:指定后台运行模式,如数据传输、音频播放等
ServiceExtensionAbility概述
ServiceExtensionAbility是Stage模型中替代ServiceAbility的后台服务组件,主要面向系统应用。与ServiceAbility相比,ServiceExtensionAbility提供了更严格的生命周期管理和更安全的运行环境。
ServiceExtensionAbility有两种运行形式:
- 启动型:通过startServiceExtensionAbility启动
- 连接型:通过connectServiceExtensionAbility连接
ServiceExtensionAbility生命周期
ServiceExtensionAbility的生命周期包含以下回调方法:
| 生命周期方法 | 说明 |
|---|---|
| onCreate() | ServiceExtensionAbility创建时调用 |
| onRequest() | 启动ServiceExtensionAbility时调用 |
| onConnect() | 绑定ServiceExtensionAbility时调用 |
| onDisconnect() | 解绑ServiceExtensionAbility时调用 |
| onDestroy() | ServiceExtensionAbility销毁时调用 |
ServiceExtensionAbility开发步骤
1. 创建ServiceExtensionAbility
typescript
import { ServiceExtensionAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = '[ServiceExtensionAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
export default class ServiceExtAbility extends ServiceExtensionAbility {
onCreate(want: Want, launchMode: number) {
hilog.info(DOMAIN_NUMBER, TAG, 'onCreate, want: %{public}s, launchMode: %{public}d',
JSON.stringify(want), launchMode);
}
onRequest(want: Want, startId: number) {
hilog.info(DOMAIN_NUMBER, TAG, 'onRequest, want: %{public}s, startId: %{public}d',
JSON.stringify(want), startId);
}
onConnect(want: Want) {
hilog.info(DOMAIN_NUMBER, TAG, 'onConnect, want: %{public}s', JSON.stringify(want));
return null;
}
onDisconnect(want: Want) {
hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect, want: %{public}s', JSON.stringify(want));
}
onDestroy() {
hilog.info(DOMAIN_NUMBER, TAG, 'onDestroy');
}
}
2. 配置ServiceExtensionAbility
在module.json5文件中配置:
json
{
"module": {
"extensionAbilities": [
{
"name": "ServiceExtAbility",
"srcEntry": "./ets/serviceextensionability/ServiceExtAbility.ts",
"description": "$string:serviceextability_desc",
"icon": "$media:icon",
"type": "service",
"visible": true
}
]
}
}
3. 启动ServiceExtensionAbility
typescript
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
const TAG: string = '[Page_ServiceExtensionAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
@Entry
@Component
struct Page_ServiceExtensionAbility {
build() {
Column() {
Button('启动后台服务')
.onClick(() => {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let want: Want = {
bundleName: 'com.example.myapplication',
abilityName: 'ServiceExtAbility'
};
context.startServiceExtensionAbility(want).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'startServiceExtensionAbility success');
}).catch((error: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, 'startServiceExtensionAbility failed');
});
})
}
}
}
4. 停止ServiceExtensionAbility
typescript
// 在ServiceExtensionAbility内部
this.terminateSelf();
// 在外部组件中
context.stopServiceExtensionAbility(want).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'stopServiceExtensionAbility success');
}).catch((error: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, 'stopServiceExtensionAbility failed');
});
连接ServiceExtensionAbility
1. 建立连接
typescript
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
const TAG: string = '[Page_ServiceExtensionAbility]';
const DOMAIN_NUMBER: number = 0xFF00;
let connectionId: number;
@Entry
@Component
struct Page_ServiceExtensionAbility {
build() {
Column() {
Button('连接后台服务')
.onClick(() => {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
let want: Want = {
bundleName: 'com.example.myapplication',
abilityName: 'ServiceExtAbility'
};
let options: common.ConnectOptions = {
onConnect(elementName, remote) {
hilog.info(DOMAIN_NUMBER, TAG, 'onConnect callback');
// 保存remote对象用于通信
},
onDisconnect(elementName) {
hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
},
onFailed(code) {
hilog.error(DOMAIN_NUMBER, TAG, 'onFailed callback');
}
};
connectionId = context.connectServiceExtensionAbility(want, options);
})
}
}
}
2. 断开连接
typescript
Button('断开连接')
.onClick(() => {
let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
context.disconnectServiceExtensionAbility(connectionId).then(() => {
hilog.info(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility success');
}).catch((error: BusinessError) => {
hilog.error(DOMAIN_NUMBER, TAG, 'disconnectServiceExtensionAbility failed');
});
})
客户端与服务端通信
使用IDL接口通信(推荐)
typescript
// 客户端
import { common } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
import IdlServiceExtProxy from '../IdlServiceExt/idl_service_ext_proxy';
let options: common.ConnectOptions = {
onConnect(elementName, remote: rpc.IRemoteObject) {
let serviceExtProxy: IdlServiceExtProxy = new IdlServiceExtProxy(remote);
// 通过接口调用的方式进行通信
serviceExtProxy.processData(1, (errorCode: number, retVal: number) => {
hilog.info(DOMAIN_NUMBER, TAG, `processData, errorCode: ${errorCode}, retVal: ${retVal}`);
});
},
onDisconnect(elementName) {
hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
},
onFailed(code: number) {
hilog.error(DOMAIN_NUMBER, TAG, 'onFailed callback');
}
};
使用MessageSequence通信
typescript
import { rpc } from '@kit.IPCKit';
let options: common.ConnectOptions = {
onConnect(elementName, remote: rpc.IRemoteObject) {
let option = new rpc.MessageOption();
let data = new rpc.MessageSequence();
let reply = new rpc.MessageSequence();
data.writeInt(99);
remote.sendMessageRequest(REQUEST_CODE, data, reply, option).then((ret: rpc.RequestResult) => {
let errCode = reply.readInt();
let msg: number = 0;
if (errCode === 0) {
msg = reply.readInt();
}
hilog.info(DOMAIN_NUMBER, TAG, `sendRequest msg:${msg}`);
});
},
onDisconnect(elementName) {
hilog.info(DOMAIN_NUMBER, TAG, 'onDisconnect callback');
},
onFailed(code) {
hilog.error(DOMAIN_NUMBER, TAG, 'onFailed callback');
}
};
服务端对客户端身份校验
通过callerUid识别客户端应用
typescript
import { bundleManager } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
export default class ServiceExtImpl extends IdlServiceExtStub {
processData(data: number, callback: ProcessDataCallback) {
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;
}
// 识别通过,执行正常业务逻辑
callback(ERR_OK, data + 1);
}).catch((err: BusinessError) => {
hilog.info(DOMAIN_NUMBER, TAG, 'getBundleNameByUid failed: ' + err.message);
});
}
}
通过callerTokenId对客户端进行鉴权
typescript
import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit';
import { rpc } from '@kit.IPCKit';
export default class ServiceExtImpl extends IdlServiceExtStub {
processData(data: number, callback: ProcessDataCallback) {
let callerTokenId = rpc.IPCSkeleton.getCallingTokenId();
let accessManger = abilityAccessCtrl.createAtManager();
// 所校验的具体权限由开发者自行选择
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); // 鉴权通过,执行正常业务逻辑
}
}
ServiceAbility向ServiceExtensionAbility迁移
迁移策略
- 系统应用:建议迁移至ServiceExtensionAbility
- 三方应用:根据业务类型选择场景化ExtensionAbility或公共模块/后台任务方案
生命周期对比
| ServiceAbility | ServiceExtensionAbility | 说明 |
|---|---|---|
| onStart() | onCreate() | 创建时的初始化 |
| onCommand() | onRequest() | 启动服务时的回调 |
| onConnect() | onConnect() | 绑定服务时的回调 |
| onDisconnect() | onDisconnect() | 解绑服务时的回调 |
| onStop() | onDestroy() | 销毁服务时的回调 |
总结
ServiceAbility和ServiceExtensionAbility是OpenHarmony中用于实现后台服务的重要组件。ServiceExtensionAbility作为Stage模型中的后台服务组件,提供了更严格的生命周期管理和更安全的运行环境,是系统应用开发的首选方案。通过合理使用这些组件,开发者可以实现各种后台服务功能,满足应用的不同需求。