鸿蒙next 中后台播放音乐完整步骤

418 阅读2分钟

看官方文档的话很坑, 模棱两可的. 目前最完整的官方例子只有这个: gitee.com/harmonyos_c…

1. 创建AVSession

export class ArticlePlayerManager {
  player: MyAVPlayer = new MyAVPlayer("ArticlePlayerManager");
  currentFmData?: FmItem = undefined;
  private static instance: ArticlePlayerManager = new ArticlePlayerManager();
  state: number = MyPlayState.Idle;
  duration: number = 0;
  private session: avSession.AVSession | null = null;

  static getInstance(): ArticlePlayerManager {
    return ArticlePlayerManager.instance;
  }

  private constructor() {
    this.createSession()
  }

完整的实现方法

async createSession(): Promise<void> {
  if (this.session != null) {
    return
  }

  let ret: boolean = true;
  this.session = await avSession.createAVSession(getContext(this), "article_player", 'audio').catch((err: Error) => {
    logE("createSession error: " + err)
    ret = false;
    return null
  })

  //监听事件
  this.setListenerForMesFromController()
  // 激活接口要在元数据、控制命令注册完成之后再执行
  await this.session?.activate().catch((err: Error) => {
    logE("activate error: " + err)
    ret = false;
  })

  this.startContinuousTask()
}

监听事件, 你希望在状态栏里面显示哪些按钮就监听哪几个

image.png

async setListenerForMesFromController() {
  if (!this.session) {
    return
  }

  this.session.on('play', () => {
    if (this.state === MyPlayState.Paused) {
      ArticlePlayerManager.getInstance().resume()
    } else {
      ArticlePlayerManager.getInstance().play()
    }
  });
  this.session.on('pause', () => {
    ArticlePlayerManager.getInstance().pause()
  });
  this.session.on('stop', () => {
    ArticlePlayerManager.getInstance().stop()
  });
  this.session.on('playNext', () => {
    ArticlePlayerManager.getInstance().next()
  });
  this.session.on('playPrevious', () => {
    ArticlePlayerManager.getInstance().pre()
  });
  /*this.session.on('fastForward', () => {
    console.info(`on fastForward , do fastForward task`);
    // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('fastForward')取消监听
    // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position
  });
  this.session.on('rewind', () => {
    console.info(`on rewind , do rewind task`);
    // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('rewind')取消监听
    // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position
  });

  this.session.on('seek', (time) => {
    console.info(`on seek , the seek time is ${time}`);
    // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('seek')取消监听
    // 处理完毕后,请使用SetAVPlayState上报播放状态和播放position
  });
  this.session.on('setSpeed', (speed) => {
    console.info(`on setSpeed , the speed is ${speed}`);
    // do some tasks ···
  });
  this.session.on('setLoopMode', (mode) => {
    console.info(`on setLoopMode , the loop mode is ${mode}`);
    // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('setLoopMode')取消监听
    // 应用自定下一个模式,处理完毕后,请使用SetAVPlayState上报切换后的LoopMode
  });
  this.session.on('toggleFavorite', (assetId) => {
    console.info(`on toggleFavorite , the target asset Id is ${assetId}`);
    // 如暂不支持该指令,请勿注册;或在注册后但暂不使用时,通过session.off('toggleFavorite')取消监听
    // 处理完毕后,请使用SetAVPlayState上报收藏结果isFavorite
  });*/

2. 申请后台运行

async startContinuousTask(): Promise<void> {
  let wantAgentInfo: WantAgent.WantAgentInfo = {
    wants: [
      {
        bundleName: AppUtil.getContext().abilityInfo.bundleName,
        abilityName: AppUtil.getContext().abilityInfo.name
      }
    ],
    operationType: WantAgent.OperationType.START_ABILITY,
    requestCode: 0,
    wantAgentFlags: [WantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
  };
  let agent = await WantAgent.getWantAgent(wantAgentInfo);
  backgroundTaskManager.startBackgroundRunning(getContext(this),
    backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK, agent).then((result) => {
    logE("申请后台任务成功: " + result)
  }).catch((error: Error) => {
    logE("申请后台任务失败: " + error)
  })

  this.session?.setLaunchAbility(agent)
}

3.更新状态

需要在 AVPlayer 里面监听 player 的各种播放状态, 然后同步给通知栏 在 player 的prepared里面同步 Meta

async updateArticleToMediaSession() {
  let article = this.currentFmData?.getCurrentArticle()
  let metadata: avSession.AVMetadata = {
    assetId: article?.objectId!,
    title: article?.name,
    author: article?.owner?.nickname,
    mediaImage: article?.imageUrl,
    description: article?.content,
    duration: this.duration,
  };

  this.session?.setAVMetadata(metadata).then(() => {
    logE(`SetAVMetadata successfully`);
  }).catch((err: BusinessError) => {
    logE(`Failed to set AVMetadata. Code: ${err.code}, message: ${err.message}`);
  });
}

大概是这样

url: this.currentFmData?.getCurrentArticle().audioUrl,
onPrepared: () => {
  EventBus.postArticlePlayState(MyPlayState.Prepared)
  this.state = MyPlayState.Prepared
  this.updateArticleToMediaSession()
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_BUFFERING,
  })
},
onPlaying: () => {
  if (this.currentFmData) {
    this.currentFmData.playing = true;
  }
  EventBus.postArticlePlayState(MyPlayState.Playing)
  this.state = MyPlayState.Playing
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_PLAY,
  })
},
onPaused: () => {
  if (this.currentFmData) {
    this.currentFmData.playing = false;
  }
  EventBus.postArticlePlayState(MyPlayState.Paused)
  this.state = MyPlayState.Paused
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_PAUSE,
  })
},
onCompleted: () => {
  if (this.currentFmData) {
    this.currentFmData.playing = false;
  }
  EventBus.postArticlePlayState(MyPlayState.Completed)
  this.state = MyPlayState.Completed
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_COMPLETED,
  })
},
onStopped: () => {
  if (this.currentFmData) {
    this.currentFmData.playing = false;
  }

  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_STOP,
  })
},
onReleased: () => {
  //AppStorage.set("state", MyPlayState.Completed)
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_RELEASED,
  })
},
onProgress: (progress: number) => {
  EventBus.postArticlePlayProgress(progress)
},
onTimeUpdate: (time: number) => {
  EventBus.postArticlePlayTimeUpdate(time)
  this.session?.setAVPlaybackState({
    //state: avSession.PlaybackState.PLAYBACK_STATE_PLAY,
    position: { updateTime: new Date().getTime(), elapsedTime: time }
  })
},
onDuration: (duration: number) => {
  EventBus.postArticlePlayDuration(duration)
  this.duration = duration
  this.updateArticleToMediaSession()
},
onError: (error: Error) => {
  if (this.currentFmData) {
    this.currentFmData.playing = false;
  }
  ToastUtil.showToast("播放错误: " + error.message)
  EventBus.postArticlePlayError(error)
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_ERROR,
  })
},
onIdle: () => {
  EventBus.postArticlePlayState(MyPlayState.Idle)
  this.state = MyPlayState.Idle
  this.session?.setAVPlaybackState({
    state: avSession.PlaybackState.PLAYBACK_STATE_IDLE,
  })
},