【Harmony OS 5】UniApp开发鸿蒙音乐类应用全攻略:ArkTS代码实践

247 阅读5分钟

##UniApp##

UniApp开发鸿蒙音乐类应用全攻略:ArkTS代码实践

一、UniApp在鸿蒙音乐应用开发中的优势

UniApp X作为新一代跨平台开发框架,在鸿蒙音乐应用开发中展现出独特价值:

  1. 原生性能体验:编译为ArkTS原生代码,直接调用鸿蒙多媒体API
  2. 开发效率倍增:Vue语法+TypeScript开发,学习曲线平缓
  3. 多端统一:一套代码可同时发布到鸿蒙、iOS和Android平台
  4. 完整生态支持:丰富的插件市场支持各种音乐场景需求
// 示例:检测鸿蒙设备音频输出能力
import audio from '@ohos.multimedia.audio';

async function checkAudioCapability() {
  const info = await audio.getAudioManager().getDevices(audio.DeviceFlag.OUTPUT_DEVICES_FLAG);
  console.log("可用音频输出设备:", JSON.stringify(info));
}

二、音乐播放器核心架构设计

基于UniApp的鸿蒙音乐应用推荐架构:

image.png

三、音乐播放器核心功能实现

1. 音频播放服务封装

// lib/music-player.uts
import audio from '@ohos.multimedia.audio';

export class MusicPlayer {
  private player: audio.AudioPlayer | null = null;
  private currentTrack: MusicTrack | null = null;
  private playList: MusicTrack[] = [];
  private playMode: 'sequence' | 'random' | 'single' = 'sequence';
  
  async init(): Promise<void> {
    this.player = await audio.createAudioPlayer();
    this.player.on('stateChange', (state) => {
      console.log('播放状态变更:', state);
      if (state === 'completed') {
        this.playNext();
      }
    });
  }
  
  async load(track: MusicTrack): Promise<void> {
    if (!this.player) return;
    
    this.currentTrack = track;
    await this.player.reset();
    await this.player.setSource(track.url);
    await this.player.prepare();
  }
  
  async play(): Promise<void> {
    if (this.player && this.currentTrack) {
      await this.player.start();
    }
  }
  
  async pause(): Promise<void> {
    if (this.player) {
      await this.player.pause();
    }
  }
  
  async seekTo(position: number): Promise<void> {
    if (this.player) {
      await this.player.seek(position);
    }
  }
  
  async playNext(): Promise<void> {
    if (!this.playList.length) return;
    
    let nextIndex = 0;
    if (this.playMode === 'random') {
      nextIndex = Math.floor(Math.random() * this.playList.length);
    } else if (this.currentTrack) {
      const currentIndex = this.playList.findIndex(t => t.id === this.currentTrack?.id);
      nextIndex = (currentIndex + 1) % this.playList.length;
    }
    
    await this.load(this.playList[nextIndex]);
    await this.play();
  }
}

2. 播放器UI界面实现

<!-- pages/player/player.uvue -->
<template>
  <view class="player-container">
    <!-- 专辑封面 -->
    <image 
      :src="currentTrack?.cover || 'default-cover.png'" 
      class="cover-image"
      @click="togglePlay"
    ></image>
    
    <!-- 歌曲信息 -->
    <view class="track-info">
      <text class="track-title">{{ currentTrack?.title || '未选择歌曲' }}</text>
      <text class="track-artist">{{ currentTrack?.artist || '未知艺术家' }}</text>
    </view>
    
    <!-- 进度条 -->
    <slider
      :value="currentPosition"
      :max="duration"
      @change="onSeek"
      class="progress-bar"
    ></slider>
    
    <!-- 控制按钮 -->
    <view class="control-buttons">
      <button @click="playPrev" class="control-btn">上一首</button>
      <button @click="togglePlay" class="play-btn">
        {{ isPlaying ? '暂停' : '播放' }}
      </button>
      <button @click="playNext" class="control-btn">下一首</button>
    </view>
  </view>
</template>

<script>
import { MusicPlayer } from '@/lib/music-player.uts';

export default {
  data() {
    return {
      player: new MusicPlayer(),
      currentTrack: null,
      playList: [],
      isPlaying: false,
      currentPosition: 0,
      duration: 0,
      positionUpdateInterval: null
    }
  },
  async onLoad() {
    await this.player.init();
    await this.loadPlayList();
    
    // 启动进度更新定时器
    this.positionUpdateInterval = setInterval(() => {
      this.updatePosition();
    }, 1000);
  },
  onUnload() {
    clearInterval(this.positionUpdateInterval);
  },
  methods: {
    async loadPlayList() {
      // 实际项目中从网络或本地加载播放列表
      this.playList = [
        {
          id: '1',
          title: '示例歌曲1',
          artist: '歌手A',
          url: 'resources/media/sample1.mp3',
          cover: 'resources/images/cover1.jpg'
        },
        // 更多歌曲...
      ];
      
      if (this.playList.length > 0) {
        this.currentTrack = this.playList[0];
        await this.player.load(this.currentTrack);
      }
    },
    async togglePlay() {
      if (this.isPlaying) {
        await this.player.pause();
      } else {
        await this.player.play();
      }
      this.isPlaying = !this.isPlaying;
    },
    async playNext() {
      await this.player.playNext();
      this.currentTrack = this.player.currentTrack;
      this.isPlaying = true;
    },
    async onSeek(e) {
      await this.player.seekTo(e.detail.value);
    },
    async updatePosition() {
      if (this.player && this.isPlaying) {
        const info = await this.player.getCurrentPosition();
        this.currentPosition = info.position;
        this.duration = info.duration;
      }
    }
  }
}
</script>

<style>
.player-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
}

.cover-image {
  width: 300px;
  height: 300px;
  border-radius: 10px;
  margin-bottom: 20px;
}

.track-info {
  text-align: center;
  margin-bottom: 20px;
}

.track-title {
  font-size: 24px;
  font-weight: bold;
}

.track-artist {
  font-size: 18px;
  color: #666;
}

.progress-bar {
  width: 100%;
  margin-bottom: 20px;
}

.control-buttons {
  display: flex;
  justify-content: center;
  gap: 20px;
}

.play-btn {
  width: 120px;
  height: 120px;
  border-radius: 60px;
}
</style>

四、高级功能实现

1. 本地音乐扫描

// lib/media-scanner.uts
import mediaLibrary from '@ohos.multimedia.mediaLibrary';

export class MediaScanner {
  private mediaLib: mediaLibrary.MediaLibrary;
  
  constructor() {
    this.mediaLib = mediaLibrary.getMediaLibrary();
  }
  
  async scanAudioFiles(): Promise<MusicTrack[]> {
    const fileKeyObj = mediaLibrary.FileKey;
    const fetchOp = {
      selections: `${fileKeyObj.MEDIA_TYPE}=?`,
      selectionArgs: [mediaLibrary.MediaType.AUDIO.toString()],
    };
    
    const files = await this.mediaLib.getFileAssets(fetchOp);
    const tracks: MusicTrack[] = [];
    
    for (let i = 0; i < files.length; i++) {
      const file = files[i];
      tracks.push({
        id: file.id.toString(),
        title: file.displayName || '未知歌曲',
        artist: '未知艺术家',
        url: file.uri,
        duration: file.duration
      });
    }
    
    return tracks;
  }
}

2. 歌词同步显示

// lib/lyric-parser.uts
export class LyricParser {
  static parse(lrcText: string): LyricLine[] {
    const lines = lrcText.split('\n');
    const result: LyricLine[] = [];
    const timeRegex = /[(\d{2}):(\d{2}).(\d{2,3})]/;
    
    lines.forEach(line => {
      const matches = timeRegex.exec(line);
      if (matches) {
        const minutes = parseInt(matches[1]);
        const seconds = parseInt(matches[2]);
        const milliseconds = parseInt(matches[3]);
        const time = minutes * 60 + seconds + milliseconds / 1000;
        const text = line.replace(timeRegex, '').trim();
        
        if (text) {
          result.push({ time, text });
        }
      }
    });
    
    return result.sort((a, b) => a.time - b.time);
  }
  
  static getCurrentLine(lyrics: LyricLine[], currentTime: number): number {
    for (let i = 0; i < lyrics.length; i++) {
      if (currentTime < lyrics[i].time) {
        return i - 1;
      }
    }
    return lyrics.length - 1;
  }
}

3. 后台播放与通知控制

// lib/background-player.uts
import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager';
import notification from '@ohos.notification';

export class BackgroundPlayer {
  private player: MusicPlayer;
  private notificationId = 1001;
  
  constructor(player: MusicPlayer) {
    this.player = player;
  }
  
  async enableBackgroundPlay(): Promise<void> {
    // 申请后台播放权限
    await backgroundTaskManager.requestSuspendDelay(
      'MusicBackgroundPlay',
      () => {
        this.updateNotification();
      }
    );
    
    // 创建媒体通知
    await notification.requestEnableNotification();
    this.updateNotification();
  }
  
  async updateNotification(): Promise<void> {
    const currentTrack = this.player.currentTrack;
    
    await notification.publish({
      id: this.notificationId,
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: currentTrack?.title || '音乐播放中',
          text: currentTrack?.artist || '未知艺术家',
          additionalText: this.player.isPlaying ? '正在播放' : '已暂停'
        }
      },
      actionButtons: [
        {
          title: '上一首',
          wantAgent: {
            pkgName: 'com.example.music',
            abilityName: 'PrevAction'
          }
        },
        {
          title: this.player.isPlaying ? '暂停' : '播放',
          wantAgent: {
            pkgName: 'com.example.music',
            abilityName: 'TogglePlayAction'
          }
        },
        {
          title: '下一首',
          wantAgent: {
            pkgName: 'com.example.music',
            abilityName: 'NextAction'
          }
        }
      ]
    });
  }
}

五、性能优化技巧

1. 音频缓冲优化

// 预加载下一首歌曲
async preloadNextTrack() {
  if (!this.playList.length) return;
  
  const nextIndex = (this.playList.findIndex(t => t.id === this.currentTrack?.id) + 1;
  if (nextIndex < this.playList.length) {
    const tempPlayer = await audio.createAudioPlayer();
    await tempPlayer.setSource(this.playList[nextIndex].url);
    await tempPlayer.prepare();
    await tempPlayer.release();
  }
}

2. 省电模式适配

// 省电模式适配
import batteryInfo from '@ohos.batteryInfo';

async checkBatteryMode() {
  const info = await batteryInfo.getBatteryInfo();
  if (info.batterySaverMode === 'power_save_mode') {
    // 降低音频质量节省电量
    await audio.setAudioEffectMode('low_power');
  }
}

六、鸿蒙特有功能集成

1. 分布式设备协同

// 跨设备播放控制
import distributedAudio from '@ohos.distributedAudio';

class DistributedMusicPlayer {
  private controller: distributedAudio.AudioController;
  
  async init() {
    this.controller = await distributedAudio.createAudioController();
    this.controller.on('playbackStateChange', (state) => {
      console.log('远端设备播放状态变更:', state);
    });
  }
  
  async controlRemoteDevice(deviceId: string, command: 'play' | 'pause' | 'next') {
    await this.controller.sendControlCommand(deviceId, command);
  }
}

2. 音频焦点管理

// 音频焦点管理
import audioFocus from '@ohos.multimedia.audioFocus';

async manageAudioFocus() {
  const focusManager = audioFocus.createAudioFocusManager();
  
  const options = {
    focusType: audioFocus.AudioFocusType.GAIN,
    focusMode: audioFocus.AudioFocusMode.INDEPENDENT
  };
  
  const result = await focusManager.requestAudioFocus(options);
  if (result === audioFocus.AudioFocusRequestResult.GRANTED) {
    console.log('获得音频焦点');
  }
  
  focusManager.on('audioFocusInterrupt', (interrupt) => {
    if (interrupt === audioFocus.AudioInterruptType.BEGIN) {
      this.player.pause();
    } else {
      this.player.play();
    }
  });
}

七、完整项目结构建议

harmony-music-app/
├── app.uvue                # 应用入口
├── manifest.json           # 应用配置
├── pages/
│   ├── player/             # 播放器页面
│   │   ├── player.uvue
│   │   └── player.css
│   ├── playlist/           # 播放列表页面
│   └── settings/           # 设置页面
├── lib/
│   ├── music-player.uts    # 播放器核心逻辑
│   ├── media-scanner.uts   # 媒体扫描
│   └── lyric-parser.uts    # 歌词解析
└── resources/
    ├── media/              # 音频资源
    └── images/             # 图片资源

八、发布与注意事项

  1. 权限配置module.json5):
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.READ_MEDIA",
        "reason": "读取本地音乐文件"
      },
      {
        "name": "ohos.permission.MICROPHONE",
        "reason": "语音控制功能"
      },
      {
        "name": "ohos.permission.DISTRIBUTED_DATASYNC",
        "reason": "跨设备音乐同步"
      }
    ]
  }
}

2. 打包发布

# 开发环境运行
npm run dev:harmony

# 生产环境打包
npm run build:harmony

九、未来扩展方向

  1. AI音乐推荐:集成鸿蒙AI引擎实现个性化推荐
  2. 车载模式:适配鸿蒙车机系统的特殊交互
  3. 空间音频:利用鸿蒙3D音频技术提升体验
  4. 智能家居联动:与鸿蒙IoT设备协同播放
// 未来可能的空间音频实现示例
import spatialAudio from '@ohos.multimedia.spatialAudio';

async enableSpatialAudio() {
  const audioRenderer = await spatialAudio.createSpatialAudioRenderer();
  await audioRenderer.setRoomType('large_concert_hall');
  await audioRenderer.enableHeadTracking(true);
}

通过UniApp X开发鸿蒙音乐应用,开发者可以充分利用鸿蒙系统的多媒体能力和分布式特性,同时保持跨平台开发的效率优势。本文提供的ArkTS代码示例涵盖了音乐应用的核心功能模块,可作为实际项目开发的参考基础。