随着鸿蒙生态的不断壮大,越来越多的开发者开始关注如何在这个全新的分布式操作系统上开发应用。本文将以一个经典的应用场景——音乐播放——为例,带你初探鸿蒙应用开发的核心流程,并详解其中的关键代码。

我们将在鸿蒙的Ability框架基础上,利用其提供的音频播放API,实现一个具备基本播放/暂停、上一曲/下一曲功能的音乐播放器。

一、环境准备与项目结构

首先,你需要确保已经配置好鸿蒙应用开发环境(DevEco Studio)。创建一个新的“Empty Ability”项目。

一个典型的鸿蒙音乐应用项目结构可能包含:

  • entry/src/main/ets/

    • MainAbility/: 应用入口Ability。

    • pages/: 页面目录。

      • index.ets: 主页面,包含UI布局和交互。

    • model/: (可选) 模型层,如管理音乐数据。

    • utils/: 工具类,如音乐播放控制类。

二、核心代码实现与解释

我们将聚焦于三个核心部分:UI布局、播放控制、以及生命周期管理。

1. 界面布局 (UI) - index.ets

UI部分使用鸿蒙的ArkUI声明式开发范式,通过装饰器(如@State)来驱动视图更新。

// index.ets
import { MusicController } from '../utils/MusicController'; // 导入我们即将编写的播放控制类

@Entry
@Component
struct Index {
  // @State装饰器:当songName变化时,会自动更新绑定的UI文本
  @State songName: string = "经典音乐";
  // @State装饰器:控制播放/暂停按钮的图标
  @State isPlaying: boolean = false;

  // 创建MusicController实例,用于调用播放控制方法
  private musicController: MusicController = new MusicController();

  build() {
    Column() {
      Text(this.songName)
        .fontSize(24)
        .margin(20)

      Row() {
        // 上一曲按钮
        Button('←')
          .onClick(() => {
            this.musicController.playPrevious(); // 调用上一曲方法
            this.updateUI(); // 更新UI状态
          })

        // 播放/暂停按钮
        Button(this.isPlaying ? '❚❚' : '▶')
          .onClick(() => {
            if (this.isPlaying) {
              this.musicController.pause(); // 调用暂停方法
            } else {
              this.musicController.play(); // 调用播放方法
            }
            this.isPlaying = !this.isPlaying; // 切换播放状态
          })

        // 下一曲按钮
        Button('→')
          .onClick(() => {
            this.musicController.playNext(); // 调用下一曲方法
            this.updateUI();
          })
      }
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }

  // 一个用于更新UI的方法(例如切歌后更新歌曲名)
  private updateUI() {
    // 这里可以是从播放器获取当前歌曲信息
    this.songName = this.musicController.getCurrentSongName();
  }
}

代码解释:

  • @State:这是ArkUI最核心的装饰器之一。它标记的数据是状态数据,当这些数据发生变化时,会触发UI的重新渲染。例如,当用户点击播放按钮,isPlaying的值改变,按钮的文本会自动从变为❚❚

  • onClick():是组件的点击事件回调。在这里我们调用了MusicController类的方法来控制播放逻辑,并更新本地状态。

  • MusicController:是我们自己封装的类,它封装了鸿蒙系统提供的音频API,实现了具体的播放逻辑,使UI层和业务逻辑分离,代码更清晰。

2. 播放控制层 - MusicController.ts

这是整个应用的大脑,负责与鸿蒙的音频API交互。

// utils/MusicController.ts
import media from '@ohos.multimedia.media';

export class MusicController {
  private avPlayer: media.AVPlayer | null = null; // 鸿蒙媒体播放器实例
  private currentIndex: number = 0;
  private playList: string[] = [ // 简单的模拟播放列表,存放资源路径
    '/entry/resources/rawfile/song1.mp3',
    '/entry/resources/rawfile/song2.mp3'
  ];

  constructor() {
    this.initAVPlayer();
  }

  // 初始化AVPlayer
  private async initAVPlayer() {
    // 1. 创建AVPlayer实例
    this.avPlayer = await media.createAVPlayer();
    
    // 2. 监听播放完成事件,实现自动播放下一首
    this.avPlayer.on('stateChange', async (state: media.AVPlayerState) => {
      if (state === 'completed') {
        this.playNext();
      }
    });
  }

  // 播放音乐
  async play() {
    if (!this.avPlayer) return;

    // 设置播放源(URL或资源路径)
    this.avPlayer.url = this.playList[this.currentIndex];
    // 准备播放
    await this.avPlayer.prepare();
    // 开始播放
    await this.avPlayer.play();
  }

  // 暂停播放
  async pause() {
    if (this.avPlayer?.state === 'started') {
      await this.avPlayer.pause();
    }
  }

  // 播放下一首
  async playNext() {
    this.currentIndex = (this.currentIndex + 1) % this.playList.length;
    await this.resetAndPlay(); // 重置播放器并播放新资源
  }

  // 播放上一首
  async playPrevious() {
    this.currentIndex = (this.currentIndex - 1 + this.playList.length) % this.playList.length;
    await this.resetAndPlay();
  }

  // 重置播放器并播放当前选中的歌曲
  private async resetAndPlay() {
    if (!this.avPlayer) return;
    await this.avPlayer.reset(); // 重置是关键,用于切换资源
    this.avPlayer.url = this.playList[this.currentIndex];
    await this.avPlayer.prepare();
    await this.avPlayer.play();
  }

  // 获取当前歌曲名(简单示例)
  getCurrentSongName(): string {
    const fullPath = this.playList[this.currentIndex];
    return fullPath.split('/').pop() || 'Unknown Song'; // 从路径中提取文件名
  }

  // 释放资源,防止内存泄漏
  release() {
    if (this.avPlayer) {
      this.avPlayer.release();
      this.avPlayer = null;
    }
  }
}

代码解释:

  • @ohos.multimedia.media:这是鸿蒙系统提供的多媒体开发套件,AVPlayer是其中用于音视频播放的核心类。

  • 状态管理AVPlayer有自己的生命周期和状态(如idlepreparedstartedpausedcompleted)。我们通过监听stateChange事件,在播放完成时自动触发下一首。

  • 播放流程:标准的播放流程是:createAVPlayer -> 设置url -> prepare() -> play()

  • 切换歌曲:切换歌曲时,必须调用reset()方法将播放器状态重置为idle,然后才能设置新的url并重新prepare()。这是鸿蒙API与Android MediaPlayer类似的设计。

  • 资源释放:在播放器不再使用时(如页面销毁),必须调用release()方法来释放底层硬件资源,这是一个良好的编程习惯。

3. 权限与资源配置

module.json5配置文件中,你需要声明网络和音频播放权限,并将音乐文件放入指定目录。

{
  "module": {
    ...
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET" // 如果需要播放网络音乐
      }
    ],
    "abilities": [
      {
        "name": ".MainAbility",
        "srcEntrance": "./ets/MainAbility/MainAbility.ts",
        "description": "$string:MainAbility_desc",
        "icon": "$media:app_icon",
        "label": "$string:MainAbility_label",
        "startWindowIcon": "$media:app_icon",
        "startWindowBackground": "$color:start_window_background",
        "audioBackgroundMode": "audioPlayback" // 关键:声明后台音频播放能力
      }
    ]
  }
}

将音乐文件(如song1.mp3)放入entry/src/main/resources/rawfile/目录下。

三、总结与展望

通过以上代码,我们成功实现了一个鸿蒙系统上的简易音乐播放器。它演示了:

  1. ArkUI声明式开发:如何使用@State驱动UI更新。

  2. 多媒体API调用:如何使用@ohos.multimedia.media中的AVPlayer进行音频播放控制。

  3. 项目分层架构:将UI与业务逻辑分离,提高代码可维护性。

当然,一个完整的音乐应用还包括很多功能,如:

  • 后台播放:通过Service Ability实现应用退到后台后继续播放。

  • 通知栏控制:更新系统媒体通知,实现后台控制。

  • 分布式音乐接力:利用鸿蒙的分布式特性,将音乐从手机切换到音箱上继续播放。

  • 歌词显示、音效处理等更高级的功能。

希望本文能为你打开鸿蒙应用开发的大门,期待你创造出更多出色的鸿蒙原生应用!

Logo

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

更多推荐