一、ServiceAbility基础概念

HarmonyOS 5中的ServiceAbility是后台服务能力的抽象,用于执行长时间运行的操作或为其他组件提供功能服务。与UIAbility不同,ServiceAbility没有用户界面,专注于后台任务处理和数据服务。

ServiceAbility的主要特点包括:

  • 独立生命周期:可独立于UI运行
  • 跨进程通信:支持同一设备内不同应用间的服务调用
  • 持续运行:适合执行下载、播放等后台任务

二、ServiceAbility连接机制

HarmonyOS 5提供了两种连接ServiceAbility的方式:

  1. **启动模式(startAbility)**:一次性操作,不保持连接
  2. **连接模式(connectAbility)**:建立持续连接,可进行多次交互

本文将重点讲解连接模式,这是实现应用与服务间持续通信的关键机制。

三、完整示例:音乐播放服务

下面我们实现一个音乐播放服务的完整案例,包含服务端和客户端代码。

1. 服务端实现

首先创建ServiceAbility,在entry/src/main/ets/serviceability/目录下创建MusicService.ts

import Ability from '@ohos.app.ability.ServiceAbility'
import rpc from '@ohos.rpc'

// 定义音乐播放服务的Action
const ACTION_PLAY = 'play'
const ACTION_PAUSE = 'pause'
const ACTION_GET_PROGRESS = 'getProgress'

class MusicRemoteObject extends rpc.RemoteObject {
  private currentProgress: number = 0
  private isPlaying: boolean = false

  constructor(descriptor: string) {
    super(descriptor)
  }

  // 处理远程调用
  onRemoteRequest(code: number, data: rpc.MessageSequence, reply: rpc.MessageSequence, options: rpc.MessageOptions) {
    const action = data.readString()
    switch (action) {
      case ACTION_PLAY:
        this.isPlaying = true
        reply.writeInt(1) // 返回成功
        break
      case ACTION_PAUSE:
        this.isPlaying = false
        reply.writeInt(1)
        break
      case ACTION_GET_PROGRESS:
        reply.writeInt(this.currentProgress)
        break
      default:
        reply.writeInt(-1) // 返回失败
    }
    return true
  }

  // 模拟更新播放进度
  updateProgress() {
    setInterval(() => {
      if (this.isPlaying && this.currentProgress < 100) {
        this.currentProgress += 1
      }
    }, 1000)
  }
}

export default class MusicService extends Ability {
  private remoteObject: MusicRemoteObject | null = null

  onStart() {
    console.log('MusicService onStart')
    this.remoteObject = new MusicRemoteObject('MusicService')
    this.remoteObject.updateProgress()
  }

  onConnect(want) {
    console.log('MusicService onConnect')
    if (this.remoteObject) {
      return this.remoteObject
    }
    return null
  }

  onDisconnect(want) {
    console.log('MusicService onDisconnect')
  }

  onStop() {
    console.log('MusicService onStop')
  }
}

module.json5中注册ServiceAbility:

{
  "module": {
    "abilities": [
      {
        "name": "MusicService",
        "type": "service",
        "backgroundModes": ["audioPlayback"]
      }
    ]
  }
}

2. 客户端实现

创建客户端页面MusicPlayerPage.ets

@Entry
@Component
struct MusicPlayerPage {
  @State playStatus: string = 'Stopped'
  @State progress: number = 0
  private connectionId: number = -1
  private remoteProxy: rpc.IRemoteObject | null = null

  // 连接音乐服务
  connectService() {
    let want = {
      bundleName: 'com.example.musicdemo',
      abilityName: 'MusicService'
    }
    this.connectionId = globalThis.abilityContext.connectAbility(
      want,
      {
        onConnect: (elementName, proxy) => {
          this.remoteProxy = proxy
          this.playStatus = 'Connected'
          console.log('Service connected')
        },
        onDisconnect: (elementName) => {
          this.remoteProxy = null
          this.playStatus = 'Disconnected'
          console.log('Service disconnected')
        }
      }
    )
  }

  // 断开连接
  disconnectService() {
    if (this.connectionId !== -1 && this.remoteProxy) {
      globalThis.abilityContext.disconnectAbility(this.connectionId)
      this.connectionId = -1
    }
  }

  // 播放音乐
  playMusic() {
    if (this.remoteProxy) {
      let data = rpc.MessageSequence.create()
      let reply = rpc.MessageSequence.create()
      data.writeString(ACTION_PLAY)
      this.remoteProxy.sendRequest(1, data, reply, {})
      let result = reply.readInt()
      if (result === 1) {
        this.playStatus = 'Playing'
        this.updateProgress()
      }
    }
  }

  // 暂停音乐
  pauseMusic() {
    if (this.remoteProxy) {
      let data = rpc.MessageSequence.create()
      let reply = rpc.MessageSequence.create()
      data.writeString(ACTION_PAUSE)
      this.remoteProxy.sendRequest(1, data, reply, {})
      let result = reply.readInt()
      if (result === 1) {
        this.playStatus = 'Paused'
      }
    }
  }

  // 获取播放进度
  updateProgress() {
    setInterval(() => {
      if (this.remoteProxy && this.playStatus === 'Playing') {
        let data = rpc.MessageSequence.create()
        let reply = rpc.MessageSequence.create()
        data.writeString(ACTION_GET_PROGRESS)
        this.remoteProxy.sendRequest(1, data, reply, {})
        this.progress = reply.readInt()
      }
    }, 1000)
  }

  build() {
    Column() {
      Text('Music Player').fontSize(24).margin(20)
      Text(`Status: ${this.playStatus}`).fontSize(18).margin(10)
      Text(`Progress: ${this.progress}%`).fontSize(18).margin(10)
      
      Row() {
        Button('Connect').onClick(() => this.connectService())
          .margin(10)
        Button('Disconnect').onClick(() => this.disconnectService())
          .margin(10)
      }
      
      Row() {
        Button('Play').onClick(() => this.playMusic())
          .margin(10)
        Button('Pause').onClick(() => this.pauseMusic())
          .margin(10)
      }
      
      Progress({ value: this.progress, total: 100 })
        .style({ strokeWidth: 20 })
        .margin(20)
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

四、关键点解析

  1. 远程对象通信:通过继承rpc.RemoteObject实现跨进程方法调用
  2. 连接管理:使用connectAbility建立持久连接,通过回调处理连接状态
  3. 数据序列化:使用MessageSequence进行参数传递和结果返回
  4. 生命周期协调:正确处理服务的启动、连接和断开事件

五、最佳实践建议

  1. 资源释放:确保在页面销毁时断开服务连接
  2. 错误处理:添加适当的错误处理逻辑
  3. 性能优化:避免频繁的跨进程调用
  4. 安全考虑:对敏感操作添加权限检查

通过这个完整示例,我们展示了HarmonyOS 5中ServiceAbility的连接机制和实际应用。开发者可以基于此模式构建各种后台服务,如下载服务、位置服务等,实现应用功能的模块化和服务化。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐