☰
Flutter/OpenHarmony 三方库适配实战:audioplayers 多播放器
2026/10/22 17:52:29 网站建设 项目流程

Flutter/OpenHarmony 三方库适配实战:audioplayers 多播放器

一、项目信息

项目内容
仓库名称audioplayers
仓库地址https://atomgit.com/oh-flutter/audioplayers
基于版本audioplayers 6.5.1
适配 TAG6.5.1-ohos-1.0.1
OHOS 插件包audioplayers_ohos 5.2.1
OHOS 原生入口AudioplayersPlugin
原生播放器WrappedPlayer + MediaPlayerPlayer
实例方法通道xyz.luan/audioplayers
全局方法通道xyz.luan/audioplayers.global

audioplayers支持多个播放器实例同时存在,公开 API 包括音源设置、播放、暂停、
跳转、音量、倍速、事件流和音频上下文。OHOS 适配需要在保留这些语义的同时处理
多个AVPlayer、音频焦点、后台任务和资源释放。

二、插件架构

本次适配采用实例通道与全局通道分离的结构:

audioplayers Dart | +-- xyz.luan/audioplayers | create / setSource / resume / pause / seek / dispose | +-- xyz.luan/audioplayers.global | init / setAudioContext / global log / global error | +-- xyz.luan/audioplayers.global/events | v AudioplayersPlugin | +-- HashMap<playerId, WrappedPlayer> | +-- 每个播放器独立 EventChannel | +-- MediaPlayerPlayer / SoundPoolPlayer | +-- @ohos.multimedia.media.AVPlayer

实例命令通过playerId找到对应播放器,全局命令管理默认音频上下文、日志和全局
错误。每个播放器拥有独立事件通道,多个播放器同时播放时不会互相串流。

三、插件声明与初始化

audioplayers_ohos/pubspec.yaml:

name:audioplayers_ohosversion:5.2.1flutter:plugin:implements:audioplayersplatforms:ohos:package:xyz.luan.audioplayerspluginClass:AudioplayersPlugin

原生插件在引擎附加时创建通道和 SoundPool 管理器:

onAttachedToEngine(binding:FlutterPluginBinding):void{this.context=binding.getApplicationContext();this.binaryMessenger=binding.getBinaryMessenger();this.soundPoolManager=newSoundPoolManager(this);this.methods=newMethodChannel(this.binaryMessenger,'xyz.luan/audioplayers',);this.globalMethods=newMethodChannel(this.binaryMessenger,'xyz.luan/audioplayers.global',);this.globalEvents=newEventHandler(newEventChannel(this.binaryMessenger,'xyz.luan/audioplayers.global/events',),);}

Ability 分离时暂停所有正在播放的播放器并停止后台长时任务;引擎销毁时释放所有
实例、SoundPool 管理器和事件通道。

四、创建播放器与事件通道

实例通道收到create后创建独立播放器和事件通道:

if(call.method==='create'){constplayerId=call.argument('playerId')asstring;consteventHandler=newEventHandler(newEventChannel(this.binaryMessenger!,`xyz.luan/audioplayers/events/${playerId}`,),);this.players.set(playerId,newWrappedPlayer(this,eventHandler,this.defaultAudioContext.copy(),this.soundPoolManager!,),);response.success(1);return;}

事件通道会发送 prepared、duration、complete、seek complete、current position、
log 和 error。位置更新使用定时器,只在存在实际播放中的播放器时启动:

startUpdates():void{this.stopUpdates();this.updateInterval=setInterval(()=>{this.updateCallback();},200);}stopUpdates():void{if(this.updateInterval!=null){clearInterval(this.updateInterval);this.updateInterval=null;}}

没有播放任务后停止定时器,可以避免空闲页面持续消耗计时器资源。

五、方法映射

实例命令按 Dart API 映射到底层播放器:

switch(call.method){case'setSourceUrl':awaitplayer.setSource(newUrlSource(call.argument('url')asstring,call.argument('isLocal')asboolean,),);break;case'setSourceBytes':player.setSource(newBytesSource(call.argument('bytes')asArrayBuffer),);break;case'resume':awaitthis.startContinuousTask();player.play();break;case'pause':player.pause();awaitthis.stopContinuousTask();break;case'stop':awaitplayer.stop();break;case'seek':player.seek(call.argument('position')asnumber);break;case'setVolume':player.setVolume(call.argument('volume')asnumber);break;case'setPlaybackRate':player.setRate(call.argument('playbackRate')asnumber);break;case'dispose':player.dispose();this.players.remove(playerId);break;}

未知或文本 MIME 不能作为音频播放。URL 音源会先按扩展名判断 MIME,文本文件会
通过错误事件返回,而不是让AVPlayer抛出难以理解的底层错误。

六、URL 和字节流音源

URL 音源区分远程和本地:

setForMediaPlayer(mediaPlayer:media.AVPlayer):void{if(this.isLocal){constfile=fs.openSync(this.url);mediaPlayer.url=`fd://${file.fd}`;}else{mediaPlayer.url=this.url;}}

本地文件通过 fd 加载,远程地址交给AVPlayer。字节流音源使用
AVDataSrcDescriptor提供读取回调:

constsource:media.AVDataSrcDescriptor={fileSize:this.bytes.byteLength,callback:(buffer,length,position)=>{if(position==null||position>=source.fileSize){return-1;}constcopyLength=Math.min(length,this.bytes.byteLength-position,);newUint8Array(buffer).set(newUint8Array(this.bytes.slice(position,position+copyLength)),);returncopyLength;},};mediaPlayer.dataSrc=source;

低延迟 SoundPool 当前不支持任意字节流,这种组合会明确抛出不支持错误。

七、音频焦点与后台任务

播放器通过FocusManager管理音频会话。获得焦点后播放;临时失去焦点时暂停并
标记恢复状态;永久失去焦点时停止等待自动恢复:

maybeRequestAudioFocus(player:WrappedPlayer):void{conststrategy:audio.AudioSessionStrategy={concurrencyMode:this.getFocusStrategy(),};this.audioSessionManager.activateAudioSession(strategy).then(()=>player.actuallyPlay()).catch((err:BusinessError)=>{Log.e(TAG,`activate failed:${err.code},${err.message}`);});}

后台播放通过 AVSession 和长时任务实现:

this.session=awaitAVSessionManager.createAVSession(context,'audioplayers','audio',);awaitthis.session.activate();awaitbackgroundTaskManager.startBackgroundRunning(context,backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK,wantAgentObj,);

宿主 HAP 必须配置:

{ module: { abilities: [ { backgroundModes: ['audioPlayback'], }, ], requestPermissions: [ { name: 'ohos.permission.KEEP_BACKGROUND_RUNNING', reason: '$string:background_running_reason', usedScene: { abilities: ['EntryAbility'], when: 'inuse', }, }, ], }, }

同时需要在资源文件中提供权限用途文案。未配置长时任务时,切后台后播放可能被系统
回收。

八、Dart 使用示例

多个实例播放:

finalbackground=AudioPlayer(playerId:'background');finaleffect=AudioPlayer(playerId:'effect');awaitbackground.setSourceUrl('https://example.com/background.mp3',isLocal:false,);awaitbackground.setReleaseMode(ReleaseMode.loop);awaitbackground.play();awaiteffect.setSourceAsset('sounds/click.wav');awaiteffect.setVolume(0.6);awaiteffect.play();effect.onPlayerComplete.listen((_){effect.dispose();});

内存字节流:

finalplayer=AudioPlayer();finalbytes=awaitrootBundle.load('assets/audio.wav');awaitplayer.setSourceBytes(bytes.buffer.asUint8List());

页面销毁时依次停止并释放播放器:

awaitplayer.stop();awaitplayer.dispose();

九、构建、测试结果

验证环境:

  • Flutter:3.44.9+ohos-0.0.1-canary1
  • OHOS SDK:26
  • DevEco Studio:26.0.0
  • 模拟器:emulator 7.0.0.107(SP8DEVC00E999R4P11)

验证内容:

  • 适配包flutter analyze --no-pub通过。
  • 主包flutter analyze --no-pub通过。
  • 22 个 Dart 测试通过。
  • 示例 HAP 构建、安装和启动成功。

十、遗留问题

  • setBalance、音效、空间音频和不同音频焦点策略仍需按系统版本验证。
  • HLS、低延迟直播和复杂网络切换需要真机测试。
  • 后台播放依赖宿主 HAP 的长时任务和权限配置。
  • audioplayers_android_exo中部分.gitignore是循环符号链接,构建工具链扫描
    全仓库时应排除该目录。
  • 示例中的icudtl.dat较大,本次远端快照不提交该构建资源。

十一、代码提交与协作

欢迎加入 CPF-Flutter 鸿蒙社区,参与 Flutter
OHOS 经验交流。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询