HarmonyOS开发:系统服务开发——SA服务扩展

举报
Jack20 发表于 2026/06/28 21:09:38 2026/06/28
【摘要】 HarmonyOS开发:系统服务开发——SA服务扩展📌 核心要点:System Ability(SA)是鸿蒙系统服务的基石,掌握SA的开发、注册、发布和跨进程调用,才能把你的服务真正融入系统。 背景与动机你有没有想过,鸿蒙系统里那些"天生就在"的服务是怎么来的?WiFi管理、蓝牙控制、电源管理、包管理——它们不是凭空出现的,都是以System Ability(简称SA)的形式运行在系统进...

HarmonyOS开发:系统服务开发——SA服务扩展

📌 核心要点:System Ability(SA)是鸿蒙系统服务的基石,掌握SA的开发、注册、发布和跨进程调用,才能把你的服务真正融入系统。

背景与动机

你有没有想过,鸿蒙系统里那些"天生就在"的服务是怎么来的?WiFi管理、蓝牙控制、电源管理、包管理——它们不是凭空出现的,都是以System Ability(简称SA)的形式运行在系统进程里。

那你自己开发的服务能不能也像它们一样?开机自动启动、其他应用随时调用、系统帮你管理生命周期?当然可以,但前提是你得学会写SA。

普通应用的服务是ServiceExtension,跑在你自己的进程里,系统不关心你什么时候启动什么时候销毁。SA不一样,它跑在系统进程里,由samgr(System Ability Manager)统一管理,有严格的生命周期和注册机制。

做设备定制、做系统中间件、做平台级SDK,你绕不开SA开发。这篇就把SA从开发到注册到调用的全流程讲清楚。

核心原理

SA是什么

System Ability本质上就是一个跑在系统进程里的服务组件。它有几个关键特征:

  • 系统级生命周期:由samgr管理,开机按依赖关系启动
  • 统一注册发现:所有SA都注册在samgr里,通过SA ID查找
  • 跨进程调用:基于IPC/Binder机制,任何进程都能调用
  • 按需启动:可以配置为懒加载,有调用时才启动

SA的架构

flowchart TD
    A[应用进程] -->|IPC调用| B[samgr 进程]
    B -->|查找SA| C[SA 注册表]
    C -->|返回SA代理| A
    
    B -->|启动/管理| D[SystemServer 进程]
    D --> D1[SA: WiFi服务]
    D --> D2[SA: 蓝牙服务]
    D --> D3[SA: 自定义SA]
    
    B -->|启动/管理| E[独立SA进程]
    E --> E1[SA: 包管理服务]
    E --> E2[SA: 电源管理服务]
    
    classDef app fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
    classDef samgr fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
    classDef registry fill:#fff3e0,stroke:#ff9800,color:#e65100
    classDef sysproc fill:#fce4ec,stroke:#f44336,color:#b71c1c
    classDef sa fill:#f3e5f5,stroke:#9c27b0,color:#4a148c
    
    class A app
    class B,C registry
    class D,E sysproc
    class D1,D2,D3,E1,E2 sa

SA开发的核心步骤

开发一个SA需要做四件事:

  1. 定义SA ID:每个SA有唯一标识,在系统头文件中定义
  2. 实现SA类:继承SystemAbility,实现生命周期回调
  3. 注册SA:在sa_profile中声明SA的ID、名称、进程等信息
  4. 发布SA:在OnStart中调用Publish将SA注册到samgr

SA的生命周期

flowchart LR
    A[注册到samgr] --> B[OnStart]
    B --> C[Publish]
    C --> D[运行中]
    D -->|系统关机/按需回收| E[OnStop]
    E --> F[已销毁]
    
    D -->|OnDump| G[调试信息输出]
    
    classDef lifecycle fill:#e3f2fd,stroke:#2196f3,color:#0d47a1
    classDef running fill:#e8f5e9,stroke:#4caf50,color:#1b5e20
    classDef debug fill:#fff3e0,stroke:#ff9800,color:#e65100
    
    class A,B,E,F lifecycle
    class C,D running
    class G debug

代码实战

基础用法:定义和实现一个SA

先看最基础的SA实现。假设你要开发一个"设备信息查询服务"。

第一步:定义SA ID

// system_ability_id.h - SA ID定义
// 系统SA ID范围:0x0001 - 0x8FFF
// 厂商自定义SA ID范围:0x9000 - 0xFFFF
#ifndef SYSTEM_ABILITY_ID_H
#define SYSTEM_ABILITY_ID_H

// 自定义SA ID
static const int32_t DEVICE_INFO_SA_ID = 0x9001;

#endif // SYSTEM_ABILITY_ID_H

第二步:实现SA类

// DeviceInfoAbility.ets - 设备信息SA实现
import { SystemAbility, SystemAbilityStatus } from '@ohos.app.ability.SystemAbility';
import { rpc } from '@ohos.rpc';
import deviceInfo from '@ohos.deviceInfo';

const SA_ID = 0x9001;
const SA_NAME = 'DeviceInfoService';

class DeviceInfoAbility extends SystemAbility {
  private tag: string = 'DeviceInfoAbility';

  constructor() {
    // 传入SA ID和是否独立运行
    // 第二个参数true表示在独立进程中运行
    super(SA_ID, true);
  }

  // SA启动回调
  OnStart(): void {
    console.info(this.tag, 'DeviceInfoSA OnStart');
    
    // 发布SA到samgr
    this.Publish(this);
    
    console.info(this.tag, 'DeviceInfoSA Publish成功');
  }

  // SA停止回调
  OnStop(): void {
    console.info(this.tag, 'DeviceInfoSA OnStop');
  }

  // 调试信息回调
  OnDump(fd: number, args: Array<string>): void {
    console.info(this.tag, 'DeviceInfoSA OnDump');
  }

  // 处理IPC请求
  OnRemoteRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, 
                  option: rpc.MessageOption): boolean {
    console.info(this.tag, `收到请求, code: ${code}`);
    
    switch (code) {
      case 1: // 获取设备型号
        reply.writeString(deviceInfo.productModel);
        return true;
      case 2: // 获取设备类型
        reply.writeString(deviceInfo.deviceType);
        return true;
      case 3: // 获取SDK版本
        reply.writeString(deviceInfo.sdkApiVersion.toString());
        return true;
      default:
        console.error(this.tag, `未知请求码: ${code}`);
        return false;
    }
  }
}

export default DeviceInfoAbility;

进阶用法:SA注册与跨进程调用

SA实现好了,还得注册到samgr里,其他进程才能找到它。

SA注册配置文件

// sa_profile/device_info_sa.json - SA注册配置
{
  "process": "device_info_service",
  "systemability": [
    {
      "name": 0x9001,
      "libpath": "libdevice_info_sa.z.so",
      "run-on-create": false,
      "distributed": false,
      "dump-level": 1,
      "start-on-demand": {
        "condition": [
          {
            "name": "device_info_query",
            "value": "1"
          }
        ]
      },
      "stop-on-demand": {
        "condition": [
          {
            "name": "device_info_query",
            "value": "0"
          }
        ]
      }
    }
  ]
}

配置说明:

  • process:SA运行的进程名
  • run-on-create:是否开机自动启动,false表示按需启动
  • start-on-demand:按需启动的条件
  • distributed:是否支持分布式部署

跨进程调用SA

// common/SAClient.ets - SA客户端调用工具
import { rpc } from '@ohos.rpc';
import { systemAbilityManager } from '@ohos.systemAbilityManager';

const DEVICE_INFO_SA_ID = 0x9001;

class SAClient {
  private tag: string = 'SAClient';
  private proxy: rpc.IRemoteObject | null = null;

  // 获取SA代理对象
  async getSAProxy(): Promise<rpc.IRemoteObject | null> {
    if (this.proxy) {
      return this.proxy;
    }

    try {
      // 通过SA ID从samgr获取代理
      this.proxy = await systemAbilityManager.getSystemAbility(DEVICE_INFO_SA_ID);
      console.info(this.tag, '获取SA代理成功');
      return this.proxy;
    } catch (err) {
      console.error(this.tag, `获取SA代理失败: ${JSON.stringify(err)}`);
      return null;
    }
  }

  // 查询设备型号
  async getDeviceModel(): Promise<string> {
    const proxy = await this.getSAProxy();
    if (!proxy) {
      return '获取失败';
    }

    try {
      const option = new rpc.MessageOption();
      const data = new rpc.MessageSequence();
      const reply = new rpc.MessageSequence();

      // 发送IPC请求,code=1表示获取设备型号
      await proxy.sendMessageRequest(1, data, reply, option);
      const model = reply.readString();
      return model;
    } catch (err) {
      console.error(this.tag, `查询设备型号失败: ${JSON.stringify(err)}`);
      return '查询失败';
    }
  }

  // 查询设备类型
  async getDeviceType(): Promise<string> {
    const proxy = await this.getSAProxy();
    if (!proxy) {
      return '获取失败';
    }

    try {
      const option = new rpc.MessageOption();
      const data = new rpc.MessageSequence();
      const reply = new rpc.MessageSequence();

      await proxy.sendMessageRequest(2, data, reply, option);
      const type = reply.readString();
      return type;
    } catch (err) {
      console.error(this.tag, `查询设备类型失败: ${JSON.stringify(err)}`);
      return '查询失败';
    }
  }

  // 清理代理缓存
  clearProxy(): void {
    this.proxy = null;
  }
}

export default new SAClient();

完整示例:SA服务管理页面

把SA的调用封装成页面,方便调试和验证:

// pages/SAManagerPage.ets - SA服务管理页面
import SAClient from '../common/SAClient';
import { systemAbilityManager } from '@ohos.systemAbilityManager';
import promptAction from '@ohos.promptAction';

@Entry
@Component
struct SAManagerPage {
  @State deviceModel: string = '未查询';
  @State deviceType: string = '未查询';
  @State sdkVersion: string = '未查询';
  @State saStatus: string = '未知';
  @State isQuerying: boolean = false;

  // 检查SA状态
  async checkSAStatus() {
    try {
      const proxy = await systemAbilityManager.getSystemAbility(0x9001);
      this.saStatus = proxy ? '运行中' : '未启动';
    } catch (err) {
      this.saStatus = '未启动';
    }
  }

  aboutToAppear() {
    this.checkSAStatus();
  }

  // 查询所有设备信息
  async queryAllInfo() {
    this.isQuerying = true;
    try {
      const model = await SAClient.getDeviceModel();
      const type = await SAClient.getDeviceType();
      
      this.deviceModel = model;
      this.deviceType = type;
      this.saStatus = '运行中';
      
      promptAction.showToast({ message: '查询成功' });
    } catch (err) {
      promptAction.showToast({ message: '查询失败' });
    } finally {
      this.isQuerying = false;
    }
  }

  build() {
    Scroll() {
      Column() {
        // SA状态指示
        Row() {
          Text('SA服务状态')
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
          Badge({
            count: 0,
            position: BadgePosition.Right,
            style: { badgeSize: 8, badgeColor: this.saStatus === '运行中' ? '#4caf50' : '#f44336' }
          }) {
            Text(this.saStatus)
              .fontSize(14)
              .fontColor(this.saStatus === '运行中' ? '#4caf50' : '#f44336')
          }
        }
        .width('100%')
        .justifyContent(FlexAlign.SpaceBetween)
        .margin({ bottom: 24 })

        // 设备信息卡片
        Column() {
          Text('设备信息')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 16 })

          this.InfoItem('设备型号', this.deviceModel)
          this.InfoItem('设备类型', this.deviceType)
          this.InfoItem('SDK版本', this.sdkVersion)
        }
        .width('100%')
        .padding(20)
        .backgroundColor('#ffffff')
        .borderRadius(12)
        .margin({ bottom: 16 })

        // 操作按钮
        Button('查询设备信息')
          .width('100%')
          .height(48)
          .backgroundColor('#1976d2')
          .borderRadius(8)
          .enabled(!this.isQuerying)
          .onClick(() => this.queryAllInfo())

        Button('刷新SA状态')
          .width('100%')
          .height(48)
          .backgroundColor('#616161')
          .borderRadius(8)
          .margin({ top: 12 })
          .onClick(() => this.checkSAStatus())

        Button('清除代理缓存')
          .width('100%')
          .height(48)
          .backgroundColor('#795548')
          .borderRadius(8)
          .margin({ top: 12 })
          .onClick(() => {
            SAClient.clearProxy();
            promptAction.showToast({ message: '缓存已清除' });
          })
      }
      .padding(24)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#f5f5f5')
  }

  @Builder
  InfoItem(label: string, value: string) {
    Row() {
      Text(label)
        .fontSize(14)
        .fontColor('#999999')
        .width(80)
      Text(value)
        .fontSize(14)
        .fontColor('#333333')
        .layoutWeight(1)
    }
    .width('100%')
    .margin({ bottom: 12 })
  }
}

踩坑与注意事项

坑一:SA ID冲突

SA ID是全局唯一的,如果你自定义的ID跟系统已有的ID冲突了,注册的时候不会报错,但调用的时候会拿到错误的SA代理。

系统SA ID范围是0x0001 - 0x8FFF,厂商自定义范围是0x9000 - 0xFFFF。务必使用厂商范围的ID,不要碰系统范围。

坑二:OnStart里忘记Publish

这是新手最常犯的错误。SA启动了,但没调用Publish,其他进程通过getSystemAbility拿到的就是null。

Publish必须在OnStart里调用,而且要在SA初始化完成之后。如果你的SA需要异步加载配置,等加载完了再Publish:

// 错误:还没初始化完就Publish了
OnStart(): void {
  this.Publish(this); // 其他进程拿到代理后调用可能失败
  this.loadConfig();  // 配置还没加载完
}

// 正确:初始化完再Publish
async OnStart(): void {
  await this.loadConfig();
  this.Publish(this); // 确保SA完全就绪
}

坑三:IPC调用超时

跨进程调用不是同步的,有超时机制。默认超时时间是5秒,如果你的SA处理耗时操作,客户端会收到超时错误。

解决办法:

  1. MessageOption.TF_ASYNC发送异步请求
  2. 把耗时操作放到工作线程,IPC方法只做分发
  3. 增加超时时间(不推荐,治标不治本)

坑四:按需启动的SA首次调用慢

配置了run-on-create: false的SA是按需启动的。第一次调用时,samgr要先启动SA进程、初始化SA、Publish,然后才能返回代理。这个过程可能需要几秒。

如果你的场景对响应时间敏感,要么改成开机自启动,要么在应用启动时提前触发一次SA调用,把SA"预热"起来。

坑五:SA进程崩溃后代理失效

SA进程崩溃后,你缓存的代理对象就失效了。再通过这个代理发IPC请求会直接报错。

解决办法:调用失败时清除代理缓存,下次调用重新获取:

async callSA(code: number): Promise<string> {
  try {
    const proxy = await this.getSAProxy();
    if (!proxy) return '获取代理失败';
    
    const option = new rpc.MessageOption();
    const data = new rpc.MessageSequence();
    const reply = new rpc.MessageSequence();
    
    await proxy.sendMessageRequest(code, data, reply, option);
    return reply.readString();
  } catch (err) {
    // 代理可能失效,清除缓存重试
    this.clearProxy();
    console.error('SA调用失败,已清除代理缓存');
    return '调用失败';
  }
}

HarmonyOS 6适配说明

HarmonyOS 6在SA开发方面有几个重要更新:

  1. SA动态注册:新增systemAbilityManager.registerSystemAbility接口,支持运行时动态注册SA,不再需要修改配置文件重新编译。这对插件化架构非常友好。

  2. SA依赖声明:SA注册配置新增depend-on字段,可以声明SA之间的启动依赖关系。samgr会按依赖顺序启动SA,不再需要手动控制启动顺序。

// HarmonyOS 6 SA依赖声明
{
  "systemability": [{
    "name": 0x9001,
    "depend-on": [0x9000],  // 依赖0x9000先启动
    "run-on-create": true
  }]
}
  1. SA健康检查:新增OnHealthCheck回调,samgr会定期检查SA的健康状态。如果SA不健康,samgr会自动重启SA进程。

  2. IPC性能优化:Binder驱动优化了跨进程调用的序列化/反序列化性能,大数据量传输场景下性能提升约30%。

适配建议:如果你的SA依赖其他SA,务必使用新的depend-on字段声明依赖,避免启动顺序问题。

总结

SA开发是鸿蒙系统级开发的核心能力。理解SA的生命周期、注册机制、IPC调用模型,你才能把服务真正融入系统。

核心要点回顾:

  • SA ID要在厂商范围内定义,避免跟系统冲突
  • OnStart里必须调用Publish,且在初始化完成之后
  • 跨进程调用有超时,耗时操作不要放在IPC方法里
  • 按需启动的SA首次调用慢,对响应敏感的场景用开机自启
  • SA进程崩溃后代理失效,调用失败要清除缓存重试
维度 评价
学习难度 ⭐⭐⭐⭐⭐ 涉及IPC、进程管理、生命周期,概念多且抽象
使用频率 ⭐⭐⭐ 做系统级服务必用,普通应用开发用不到
重要程度 ⭐⭐⭐⭐⭐ 系统服务开发的基石,不会SA就做不了系统中间件
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。