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模型中的后台服务组件,提供了更严格的生命周期管理和更安全的运行环境,是系统应用开发的首选方案。通过合理使用这些组件,开发者可以实现各种后台服务功能,满足应用的不同需求。

相关推荐
richard_yuu1 小时前
鸿蒙心理测评模块实战|PHQ-9/GAD7双量表答题、实时计分与结果本地化存储
华为·harmonyos
不爱吃糖的程序媛4 小时前
2026年Electron 鸿蒙PC环境搭建指南
人工智能·华为·harmonyos
nashane4 小时前
HarmonyOS 6学习:长截图功能开发中的滚动拼接与权限处理实战
人工智能·华为·harmonyos
大师兄66685 小时前
从零开发一个 HarmonyOS 输入法——KikaInputMethod 完整拆解
harmonyos·服务卡片·harmonyos6·formkit
Python私教11 小时前
鸿蒙 NEXT 也能接 MCP?用 ArkTS 跑通 AI Agent 工具链
人工智能·华为·harmonyos
Swift社区13 小时前
分布式能力在鸿蒙 PC 上到底怎么用?
分布式·华为·harmonyos
nashane1 天前
HarmonyOS 6学习:外接键盘CapsLock与长截图功能的实战调试与完整解决方案
学习·华为·计算机外设·harmonyos
aqi001 天前
一文理清 HarmonyOS 6.0.2 涵盖的十个升级点
android·华为·harmonyos·鸿蒙·harmony
环信即时通讯云1 天前
环信Flutter UIKit适配鸿蒙实战指南
flutter·华为·harmonyos