OpenHarmony后台服务开发指南:ServiceAbility与ServiceExtensionAbility全解析

概述

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有两种运行形式:

  1. 启动型:通过startServiceExtensionAbility启动
  2. 连接型:通过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迁移

迁移策略

  1. 系统应用:建议迁移至ServiceExtensionAbility
  2. 三方应用:根据业务类型选择场景化ExtensionAbility或公共模块/后台任务方案

生命周期对比

ServiceAbility ServiceExtensionAbility 说明
onStart() onCreate() 创建时的初始化
onCommand() onRequest() 启动服务时的回调
onConnect() onConnect() 绑定服务时的回调
onDisconnect() onDisconnect() 解绑服务时的回调
onStop() onDestroy() 销毁服务时的回调

总结

ServiceAbility和ServiceExtensionAbility是OpenHarmony中用于实现后台服务的重要组件。ServiceExtensionAbility作为Stage模型中的后台服务组件,提供了更严格的生命周期管理和更安全的运行环境,是系统应用开发的首选方案。通过合理使用这些组件,开发者可以实现各种后台服务功能,满足应用的不同需求。

相关推荐
Georgewu13 小时前
ArkGenie 的技术原理:从一段 SSE 到一个可交互 Surface
harmonyos
Georgewu14 小时前
在 HarmonyOS 上做一套真正能接入应用的 AI 对话组件:ArkGenie 项目介绍
harmonyos
2501_9445255415 小时前
开源鸿蒙平台 KMP_CMP 三方库「EitherNet」适配全流程
华为·开源·harmonyos
程序猿追16 小时前
返回 null 不一定入队成功:鸿蒙 KMP上的并发优先队列观察笔记
笔记·华为·harmonyos·kmp
2501_9445255416 小时前
开源鸿蒙平台 KMP_CMP 三方库「kotlinx.immutable」适配全流程
华为·开源·harmonyos
2501_9445257618 小时前
开源鸿蒙平台 KMP_CMP 三方库「Kaml」适配全流程
开源·kotlin·harmonyos
2501_9445255419 小时前
开源鸿蒙平台 KMP_CMP 三方库「Clikt」适配全流程
华为·开源·harmonyos
特立独行的猫a20 小时前
用仓颉写桌面应用的“薄壳框架”cj-tauri ,以及它的 AI 对话客户端 ai-chat
人工智能·harmonyos·tauri·仓颉·鸿蒙pc·cj-tarui·ai客户端
万物智能信息科技1 天前
Zigbee透传驱动开发—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
驱动开发·华为·开源·harmonyos·鸿蒙
牛哥带你学代码1 天前
鸿蒙远程控制电脑最新体验:华为荣耀鸿蒙用户2026远控软件选择指南
华为·电脑·harmonyos