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

相关推荐
浅蓝色6 小时前
flutter平台判断,这次应该没问题了。支持鸿蒙,插件已发布
flutter·harmonyos
小雨青年14 小时前
鸿蒙 HarmonyOS 6|ArkUI(03):状态管理
华为·harmonyos·1024程序员节
夏洛特飞21 小时前
X86 OpenHarmony5.1.0编译及安装
鸿蒙系统
猫林老师1 天前
HarmonyOS分布式数据库深度应用
harmonyos
LucianaiB1 天前
【成长纪实】从“Hello World”到分布式实战的进阶之路
harmonyos·鸿蒙·成长纪实
万添裁1 天前
基于ArkAnalyzer的HarmonyOS通用API多端安全性分析工具
harmonyos·ark
无风听海1 天前
HarmonyOS之启动应用内的UIAbility组件
前端·华为·harmonyos
Bert丶seven1 天前
鸿蒙Harmony实战开发教学(No.8)-Hyperlink超链接组件基础到进阶篇
华为·harmonyos·arkts·arkui·1024程序员节·开发教程
JohnnyDeng941 天前
ArkTs-Android 与 ArkTS (HarmonyOS) 存储目录全面对比
android·harmonyos·arkts·1024程序员节