HarmonyOS鸿蒙Next中怎么实现锁屏音频播放

HarmonyOS鸿蒙Next中怎么实现锁屏音频播放 怎么实现锁屏音频播放,同时系统的下拉框里边也要有播放,监听被别的音乐播放器挤掉的通知。

8 回复

【解决方案】

应用如果要实现锁屏播放功能,可以参考以下内容:

  1. AVSession:使用媒体会话(AVSession)功能注册到系统内统一管理。具体参考应用接入AVSession场景介绍。开始创建并激活媒体会话代码如下:

    private async initAVSession() {
      this.context = AppStorage.get('context');
      if (!this.context) {
        Logger.info(TAG, `session create failed : conext is undefined`);
        return;
      }
      this.audioRendererController = AppStorage.get('audioRendererController');
      if (!this.audioRendererController) {
        Logger.info(TAG, `session create failed : audioRendererController is undefined`);
        return;
      }
      this.AVSession = await avSession.createAVSession(this.context, "PLAY_AUDIO", 'audio');
      await this.AVSession.activate();
      // [StartExclude avsession_controller]
      Logger.info(TAG, `session create successed : sessionId : ${this.AVSession.sessionId}`);
      await this.setAVMetadata();
      this.setLaunchAbility();
      this.setListenerForMesFromController();
      if (this.musicIndex !== undefined) {
        this.getAndUpdateFavoriteState(this.musicIndex.toString());
      }
    }
    
  2. 长时任务:申请长时任务避免进入挂起(Suspend)状态。具体参考申请长时任务。后台任务权限:ohos.permission.KEEP_BACKGROUND_RUNNING。应用在后台播放音频可申请类型为AUDIO_PLAYBACK的长时任务。

  3. 音频焦点处理策略:具体可参考音频焦点介绍。在发生音频打断事件时,audioRenderer收到interruptEvent回调:

    private setInterruptCallback() {
      if (!this.audioRenderer) {
        return;
      }
      this.audioRenderer.on('audioInterrupt', this.interruptCallback);
    }
    
    private interruptCallback: (interruptEvent: audio.InterruptEvent) => void =
      (interruptEvent: audio.InterruptEvent) => {
        if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
          switch (interruptEvent.hintType) {
            case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
              this.updateIsPlay(false);
              break;
            case audio.InterruptHint.INTERRUPT_HINT_STOP:
              this.updateIsPlay(false);
              this.pause();
              break;
            case audio.InterruptHint.INTERRUPT_HINT_DUCK:
              break;
            case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
              break;
            default:
              break;
          }
        } else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) {
          switch (interruptEvent.hintType) {
            case audio.InterruptHint.INTERRUPT_HINT_RESUME:
              this.start();
              break;
            default:
              break;
          }
        }
      }
    

    完整示例可参考使用AudioRenderer后台播放

更多关于HarmonyOS鸿蒙Next中怎么实现锁屏音频播放的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


工具类

import { bundleManager, wantAgent } from '@kit.AbilityKit'
import { process } from '@kit.ArkTS'
import { avSession } from '@kit.AVSessionKit'
import { backgroundTaskManager } from '@kit.BackgroundTasksKit'

class BackgroundRunningManager {
  // 申请长时任务
  async startBackgroundRunning() {
    const context = getContext()
    // 重点1: 提供音频后台约束能力,音频接入AVSession后,可以进行后台音频播放
    const session = await avSession.createAVSession(context, 'guardianSession', 'audio')
    await session.activate()
    // 获取 bundle 应用信息
    const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
    // 通过wantAgent模块下getWantAgent方法获取WantAgent对象
    const wantAgentObj = await wantAgent.getWantAgent({
      // 添加需要被拉起应用的bundleName和abilityName
      wants: [{ bundleName: bundleInfo.name, abilityName: "EntryAbility" }],
      // 使用者自定义的一个私有值
      requestCode: 0,
     })
    // 重点2: 创建后台任务
    await backgroundTaskManager.startBackgroundRunning(context,
       backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgentObj)
  }

  // 停止后台任务
  async stopBackgroundRunning() {
    backgroundTaskManager.stopBackgroundRunning(getContext())
  }
}

export const backgroundRunningManager = new BackgroundRunningManager()

配置后台播放

"abilities": [
  {
  
    "backgroundModes": [
      // 音频播放
      "audioPlayback"
     ],
  
  }
],
"requestPermissions": [
  {
    "name":  "ohos.permission.KEEP_BACKGROUND_RUNNING",
  }
],

Index.ets

import { audio } from '@kit.AudioKit';
import { media } from '@kit.MediaKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { backgroundRunningManager } from '../utils/A';

async function play(fileNames: string[]) {
  backgroundRunningManager.startBackgroundRunning()


  // 执行应用本身业务
  let index = 0 //指向当前播放的数据
  let soundIds: number[] = []
  let streamIds: number[] = []

  let playParameters: media.PlayParameters = {
    loop: 0, // 不循环
    rate: audio.AudioRendererRate.RENDER_RATE_NORMAL, // 正常倍速(1倍速) 只能 1倍速 2倍速 0.5倍速
    leftVolume: 0.5, // range = 0.0-1.0
    rightVolume: 0.5, // range = 0.0-1.0
    priority: 0, // 最低优先级
  };

  // audioRenderInfo中的参数usage取值为STREAM_USAGE_UNKNOWN,STREAM_USAGE_MUSIC,STREAM_USAGE_MOVIE,
  // STREAM_USAGE_AUDIOBOOK时,SoundPool播放短音时为混音模式,不会打断其他音频播放。
  let audioRendererInfo: audio.AudioRendererInfo = {
    usage: audio.StreamUsage.STREAM_USAGE_MUSIC,
    rendererFlags: 1
  };
  //创建soundPool实例
  let soundPool = await media.createSoundPool(5, audioRendererInfo);
  //注册监听
  // 加载完成回调
  soundPool.on('loadComplete', (soundId_: number) => {
    //全部加载完成
    if(soundId_ === fileNames.length){
      // 开始播放,这边play也可带播放播放的参数PlayParameters,请在音频资源加载完毕,即收到loadComplete回调之后再执行play操作
      soundPool.play(soundIds[index], playParameters, (error, streamID: number) => {
        if (error) {
          console.info(`play sound Error: errCode is ${error.code}, errMessage is ${error.message}`)
        } else {
          streamIds.push(streamID)
        }
      });
    }
    console.info('loadComplete, soundId: ' + soundId_);
  })
  // 播放完成回调
  soundPool.on('playFinished', async () => {
    console.info("receive play finished message");
    index++
    if(index < fileNames.length){
      // 播放下一个
      soundPool.play(soundIds[index], playParameters, (error, streamID: number) => {
        if (error) {
          console.info(`play sound Error: errCode is ${error.code}, errMessage is ${error.message}`)
        } else {
          streamIds.push(streamID)
        }
      });
    }else{
      //释放资源
      // 终止指定流的播放
      for (let streamId of streamIds) {
        await soundPool.stop(streamId);
      }
      // 卸载音频资源
      for (const soundId of soundIds) {
        await soundPool.unload(soundId);
      }
      //关闭监听
      soundPool.off('loadComplete');
      soundPool.off('playFinished');
      soundPool.off('error');
      // 释放SoundPool
      await soundPool.release();
      //关闭长时任务
      backgroundRunningManager.startBackgroundRunning()
    }

  })
  //错误监听
  soundPool.on('error', (error: BusinessError) => {
    console.info('error happened,message is :' + error.message);
  })

  for (let i = 0; i < fileNames.length; i++) {
    // 加载音频资源
    const file =
      AppStorage.get<common.UIAbilityContext>('UIContext')!.resourceManager.getRawFdSync(`audio/${fileNames[i]}`)
    let soundId = await soundPool.load(file.fd, file.offset, file.length);
    soundIds.push(soundId);
  }

}


@Entry
@Component
struct Page {
  @State message: string = 'Hello World';
  aboutToAppear(): void {
    //todo 放uiAbility中
    AppStorage.setOrCreate<common.UIAbilityContext>('UIContext', getContext(this) as common.UIAbilityContext)
  }
  build() {
    Column() {
      Row()
        .width(200)
        .backgroundColor(Color.Red)
        .aspectRatio(1)
        .blur(20)
      Button('121.45')
        .onClick(() => {
          //123.45
          //1B2S3D45Y
          let fileNames: string[] =
            ['start.mp3', '1.mp3','B.mp3','2.mp3','S.mp3','1.mp3','D.mp3','4.mp3','5.mp3','Y.mp3'];
          play(fileNames);
        });
    }
    .height('100%')
    .width('100%')
  }
}

权限配置(config.json)

"reqPermissions": [
  { "name": "ohos.permission.KEEP_BACKGROUND_RUNNING" }, // 后台持续运行
  { "name": "ohos.permission.MANAGE_MEDIA_RESOURCES" }   // 媒体资源管理
]

1. 锁屏播放保持

  • onWindowStageCreate生命周期中启动后台任务
  • 使用BackgroundTaskManager维持播放进程
  • 播放器状态变化时更新AVSession

2. 控制中心交互

  • 通过AVSession自动同步播放状态到控制中心
  • 响应系统播放控制事件

3. 中断监听处理

  • 通过音频管理器的onInterrupt回调监听焦点变化
  • 被其他应用中断时的处理逻辑

1. 创建并配置AVSession(媒体会话)

  • 目的:接入系统播控中心,实现锁屏界面和播控中心的播放控制与信息展示。
  • 步骤
    • 调用 AVSessionManager.createAVSession() 创建会话(类型为 'audio')。
    • 通过 setAVMetadata() 设置媒体元数据(如标题、封面、音源标识等)。
    • 通过 setAVPlaybackState() 实时更新播放状态(如播放/暂停、进度、倍速等)。

2. 申请长时任务(Background Task)

  • 目的:允许应用在后台(包括锁屏状态下)持续播放音频,避免被系统挂起或终止。
  • 步骤
    • 使用 backgroundTaskManager 申请类型为 AUDIO_PLAYBACK 的长时任务。
    • 播放时申请任务,暂停/停止时取消任务。

3. 监听音频中断事件(被其他应用打断)

  • 目的:监听系统音频焦点变化,当其他应用抢占焦点时(如来电、其他音乐播放),及时处理中断事件。
  • 步骤
    • 使用 AVPlayerAudioRendereraudioInterrupt 事件监听。
    • 在回调中根据 InterruptEvent.hintType 判断中断类型(如 PAUSESTOPDUCK),并更新应用状态(如暂停播放)。

4. 核心代码

import { avSession, AVSessionManager } from '@kit.AVSessionKit';
import { backgroundTaskManager } from '@kit.ResourceScheduleBackgroundTaskManager';
import { audio } from '@kit.AudioKit';

// 1. 创建AVSession
let session = await AVSessionManager.createAVSession(context, 'audio_session', 'audio');

// 2. 设置元数据(播控中心显示)
let metadata = {
  title: '歌曲名',
  mediaImage: '封面URL',
  displayTags: AVSessionManager.DisplayTag.TAG_AUDIO_VIVID // 音源标识
};
session.setAVMetadata(metadata);

// 3. 更新播放状态
let playbackState = {
  state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PLAY,
  position: 0, // 播放进度
  speed: 1.0   // 倍速
};
session.setAVPlaybackState(playbackState);

// 4. 申请长时任务
let wantAgent = await backgroundTaskManager.getWantAgent();
await backgroundTaskManager.startBackgroundRunning(context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, wantAgent);

// 5. 监听音频中断事件(以AVPlayer为例)
avPlayer.on('audioInterrupt', (interruptEvent: audio.InterruptEvent) => {
  if (interruptEvent.hintType === audio.InterruptHint.INTERRUPT_HINT_PAUSE) {
    // 被其他应用暂停:更新本地状态及播控中心
    session.setAVPlaybackState({ state: AVSessionManager.PlaybackState.PLAYBACK_STATE_PAUSE });
  }
});

// 6. 应用退后台时保持播放(需已申请长时任务并接入AVSession)

注意事项:

  1. 权限与配置

    • 声明 ohos.permission.INTERNET(网络播放时需添加)。
    • module.json5 中申请长时任务权限:"ohos.permission.KEEP_BACKGROUND_RUNNING"
  2. 中断处理
    被其他应用打断时,需通过 setAVPlaybackState() 同步更新播控中心的播放状态(如改为暂停)。

  3. 资源释放
    退出播放时需调用 session.destroy()backgroundTaskManager.stopBackgroundRunning() 释放资源。

系统播控中心通过媒体会话AVSession进行信息交互。创建并初始化媒体会话实例后,应用需要通过setAVMetaData()接口设置会话元数据,同时使用setAVPlaybackState()接口主动向播控中心同步当前播放状态,并通过on(‘controlCommand’)注册事件监听实时响应播控中心的音频操作事件,最终实现本应用与播控中心的双向状态同步,确保两端数据的一致性。

具体实现参考文档: 音频投播-典型全场景协同开发案例-全场景协同 - 华为HarmonyOS开发者

实现锁屏音频播放及系统播控交互,需要结合后台任务管理、AVSession(音视频会话)以及音频焦点管理来实现。

锁屏播放与后台任务

1/在 module.json5 中添加权限声明:

"abilities": [

  {

    "name": "EntryAbility",

    "backgroundModes": ["audioPlayback"]

  }

],

"requestPermissions": [

  {

    "name": "ohos.permission.KEEP_BACKGROUND_RUNNING"

  }

]

2/在播放开始时申请后台任务:

import backgroundTaskManager from '@kit.ResourceSchedule.BackgroundTaskManager';

startPlay() {

  // 启动长时任务

  let taskId = backgroundTaskManager.requestSuspendDelay("AudioPlayback");

  // 播放音频逻辑

  AVPlayerManager.play();

}

系统下拉控件的播控集成

1/初始化播放时注册AVSession:

import avSession from '@kit.Multimedia.AVSession';

let session: avSession.AVSession;

onCreateSession() {

  session = avSession.createAVSession(context, "MusicSession", "music");

  session.activate();

}

2/定义可接收的系统控制指令:

session.setController({

  onPlay: () => AVPlayerManager.play(),

  onPause: () => AVPlayerManager.pause(),

  onSkipToNext: () => this.playNext(),

  onSetSpeed: (speed: number) => AVPlayerManager.setSpeed(speed)

});

3/更新播放状态到会话:

updatePlaybackState() {

  let state: avSession.AVPlaybackState = {

    state: AVPlayerManager.isPlaying ? avSession.PlaybackState.PLAYING : avSession.PlaybackState.PAUSED,

    speed: AVPlayerManager.currentSpeed,

    position: AVPlayerManager.currentPosition

  };

  session.setAVPlaybackState(state);

}

监听音频焦点冲突

当其他应用抢占音频焦点时处理:

import audio from '@kit.Multimedia.Audio';

let focusManager = audio.getAudioManager().getAudioFocusManager();

let focusRequest: audio.AudioFocusRequest = {

  focusType: audio.AudioFocusType.GAIN,

  options: {

    willPauseWhenDucked: true

  }

};

focusManager.requestAudioFocus(focusRequest).then(() => {

  focusManager.on('audioFocusChange', (focusEvent: audio.AudioFocusChangeEvent) => {

    if (focusEvent.focusChange === audio.AudioFocusInterruptType.INTERRUPTED_BY_OTHERS) {

      AVPlayerManager.pause(); // 被其他应用打断时暂停

    }

  });

});

在HarmonyOS Next中,锁屏音频播放通过AVSession框架实现。首先创建AVSession实例并设置音频播放控制器,通过dispatchMediaKeyEvent处理媒体按键事件。使用AVSessionController管理播放状态,确保锁屏界面可控制音频。系统UI自动显示播放控件,无需额外配置。

在HarmonyOS Next中实现锁屏音频播放,同时集成系统下拉控件并监听播放中断通知,可通过以下步骤完成:

  1. 音频服务与播放控制
    使用AVSessionAVPlayer进行音频播放管理。创建AVSession实例并设置播放状态、元数据(如歌曲名、歌手),系统会自动在锁屏界面和通知栏生成播放控件。

    import avSession from '[@ohos](/user/ohos).multimedia.avsession';
    import media from '[@ohos](/user/ohos).multimedia.media';
    
    // 创建AVSession
    let session: avSession.AVSession;
    avSession.createAVSession(context, 'audioPlayer', 'audio').then((s) => {
      session = s;
    });
    
    // 设置播放元数据
    let metadata: avSession.AVMetadata = {
      assetId: '123',
      title: '歌曲名称',
      artist: '歌手',
      // 其他元数据字段
    };
    session.setAVMetadata(metadata);
    
    // 创建AVPlayer并控制播放
    let avPlayer = media.createAVPlayer();
    // 设置播放源并准备播放
    
  2. 系统下拉控件集成
    通过AVSessionactivate()方法激活会话,系统会自动将播放控件添加到通知栏和锁屏界面。支持播放/暂停、上一首/下一首等标准操作。

    session.activate(); // 激活会话以显示控件
    
  3. 监听播放中断事件
    注册audioInterrupt事件,监听其他应用抢占音频焦点的情况:

    import audio from '[@ohos](/user/ohos).multimedia.audio';
    
    let audioManager = audio.getAudioManager();
    audioManager.on('audioInterrupt', (interruptEvent) => {
      if (interruptEvent.forcePaused) {
        // 被其他播放器中断,暂停当前播放
        avPlayer.pause();
        session.setAVPlaybackState({ state: avSession.PlaybackState.PAUSED });
      }
    });
    
  4. 处理AVSession命令
    通过监听AVSession的on('controlCommand')事件响应系统控件的操作(如用户点击通知栏的暂停按钮):

    session.on('controlCommand', (command) => {
      if (command.command === 'play') {
        avPlayer.play();
      } else if (command.command === 'pause') {
        avPlayer.pause();
      }
      // 其他命令处理
    });
    

注意事项:

  • 需申请ohos.permission.MANAGE_MEDIA_RESOURCES权限以管理音频资源。
  • 测试时需确保应用在后台或锁屏状态,以验证控件和中断监听是否生效。
回到顶部