概述
对于音频播放类应用,除了歌曲播控的基础能力外,各种交互场景的设计也对用户体验有着重要的影响。本文以音乐播放器应用为例,从应用与用户、播放设备以及其他应用的交互三方面入手,分别对典型使用场景给出示例方案,为应用带来灵活多样、符合用户直觉的交互体验。
场景分析
根据当前HarmonyOS APP开发过程中遇到的实际音频类应用业务场景,总结提炼出如下典型场景,设计其交互功能与方案以供参考:
场景分类 | 典型场景 | 场景描述 |
---|---|---|
与用户交互 | 播控中心操控 | 无需进入应用直接通过播控中心操控歌曲状态 |
后台播放音乐 | 应用位于后台长时间持续播放音乐 | |
与音频播放设备交互 | 播放设备的状态发生改变 | 新设备可用、旧设备不可用、用户切换设备场景的适配方案 |
响应播放设备的指令 | 蓝牙耳机控制应用播放状态 | |
用户主动切换播放设备 | 通过投播组件选择播放设备 | |
与其他应用交互 | 与其他应用音频冲突 | 被其他音频打断、与其他音频并发 |
与用户交互
播控中心控制音乐状态
在应用接入播控中心后,用户可以直接通过播控中心修改音频播放状态,如暂停/播放、下一首、切换播放模式、拖动进度条等,应用内的音频信息和状态也会同步显示在播控中心界面上,使得用户与应用的交互更加灵活易用。同时与播控中心交互使用的AVSession还管控着应用的后台播放能力,因此对于音乐类应用一般推荐接入播控中心
应用与播控中心交互的过程如下图所示:
由图可见,应用的接入流程可以主要分为三个部分:创建AVSession、监听播控中心通知以及向播控中心上报应用状态,以下分别给出具体实现:
-
应用创建媒体会话
在应用的音频渲染器初始化的时候创建并激活AVSession,将此会话用作应用与播控中心交互的媒介,音乐类应用选择创建audio类型的AVSession,不同类型的AVSession对应不同的播控中心样式
import { avSession } from '@kit.AVSessionKit';
// ...
export class AVSessionController {
private context: common.UIAbilityContext | undefined = undefined;
private AVSession: avSession.AVSession | undefined = undefined;
private songList: SongItem[] = [];
private musicIndex: number | undefined = undefined;
private audioRendererController: AudioRendererController | undefined = undefined;
// ...
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();
// ...
}
// ...
}
-
注册播控中心通知回调
当用户在锁屏或者其他界面时希望通过播控中心控制应用的播放状态,比如播放、暂停、下一首等,应用需要设置监听回调来对播控中心的通知做出对应的调整,只有设置了回调,播控中心侧的按钮才会亮起来,否则按钮将会置灰。
// 监听播控中心的各种通知如播放、暂停、下一首、调整进度,调用应用对应的方法响应该通知
async setListenerForMesFromController() {
if (!this.AVSession) {
return;
}
this.AVSession.on('play', this.playCall);
this.AVSession.on('pause', this.pauseCall);
this.AVSession.on('playNext', this.playNextCall);
this.AVSession.on('playPrevious', this.playPreviousCall);
this.AVSession.on('seek', this.seekCall);
this.AVSession.on('setLoopMode', this.setLoopModeCall);
this.AVSession.on('toggleFavorite', this.toggleFavoriteCall);
}
适配时需要注意播控中心点击循环模式图标后,应用在回调中只会收到当前播控中心的图标状态,因此开发者需要在业务代码中自己管理状态切换顺序(如:单曲循环->顺序播放->随机播放->单曲循环),根据收到的当前状态指定要切换到的下一个状态。
// 响应播控中心点击循环模式图标通知
private setLoopModeCall: (loopMode: avSession.LoopMode) => void = (loopMode: avSession.LoopMode) => {
Logger.info(TAG, `on loopMode , do loopMode task`);
if (!this.audioRendererController) {
Logger.error(TAG, 'audioRendererController is undefined in setLoopModeCall');
return;
}
switch (loopMode) {
case avSession.LoopMode.LOOP_MODE_SINGLE:
this.audioRendererController.setPlayModel(MusicPlayMode.ORDER);
break;
case avSession.LoopMode.LOOP_MODE_SEQUENCE:
this.audioRendererController.setPlayModel(MusicPlayMode.RANDOM);
break;
case avSession.LoopMode.LOOP_MODE_SHUFFLE:
this.audioRendererController.setPlayModel(MusicPlayMode.SINGLE_CYCLE);
break;
default:
break;
}
this.setPlayModeToAVSession();
this.setPlayModeToControlArea();
}
应用如果已切换到后台,用户点击播控中心,将由播控中心负责拉起应用。应用需要配置参数来选择拉起的应用和页面,并通过setLaunchAbility()方法配置给媒体会话。
private setLaunchAbility() {
if (!this.context) {
return;