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

发布时间:2026/10/22 17:54:09

Flutter/OpenHarmony 三方库适配实战:audioplayers 多播放器 Flutter/OpenHarmony 三方库适配实战audioplayers 多播放器一、项目信息项目内容仓库名称audioplayers仓库地址https://atomgit.com/oh-flutter/audioplayers基于版本audioplayers 6.5.1适配 TAG6.5.1-ohos-1.0.1OHOS 插件包audioplayers_ohos 5.2.1OHOS 原生入口AudioplayersPlugin原生播放器WrappedPlayer MediaPlayerPlayer实例方法通道xyz.luan/audioplayers全局方法通道xyz.luan/audioplayers.globalaudioplayers支持多个播放器实例同时存在公开 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 | -- HashMapplayerId, WrappedPlayer | -- 每个播放器独立 EventChannel | -- MediaPlayerPlayer / SoundPoolPlayer | -- ohos.multimedia.media.AVPlayer实例命令通过playerId找到对应播放器全局命令管理默认音频上下文、日志和全局错误。每个播放器拥有独立事件通道多个播放器同时播放时不会互相串流。三、插件声明与初始化audioplayers_ohos/pubspec.yamlname:audioplayers_ohosversion:5.2.1flutter:plugin:implements:audioplayersplatforms:ohos:package:xyz.luan.audioplayerspluginClass:AudioplayersPlugin原生插件在引擎附加时创建通道和 SoundPool 管理器onAttachedToEngine(binding:FlutterPluginBinding):void{this.contextbinding.getApplicationContext();this.binaryMessengerbinding.getBinaryMessenger();this.soundPoolManagernewSoundPoolManager(this);this.methodsnewMethodChannel(this.binaryMessenger,xyz.luan/audioplayers,);this.globalMethodsnewMethodChannel(this.binaryMessenger,xyz.luan/audioplayers.global,);this.globalEventsnewEventHandler(newEventChannel(this.binaryMessenger,xyz.luan/audioplayers.global/events,),);}Ability 分离时暂停所有正在播放的播放器并停止后台长时任务引擎销毁时释放所有实例、SoundPool 管理器和事件通道。四、创建播放器与事件通道实例通道收到create后创建独立播放器和事件通道if(call.methodcreate){constplayerIdcall.argument(playerId)asstring;consteventHandlernewEventHandler(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.updateIntervalsetInterval((){this.updateCallback();},200);}stopUpdates():void{if(this.updateInterval!null){clearInterval(this.updateInterval);this.updateIntervalnull;}}没有播放任务后停止定时器可以避免空闲页面持续消耗计时器资源。五、方法映射实例命令按 Dart API 映射到底层播放器switch(call.method){casesetSourceUrl:awaitplayer.setSource(newUrlSource(call.argument(url)asstring,call.argument(isLocal)asboolean,),);break;casesetSourceBytes:player.setSource(newBytesSource(call.argument(bytes)asArrayBuffer),);break;caseresume:awaitthis.startContinuousTask();player.play();break;casepause:player.pause();awaitthis.stopContinuousTask();break;casestop:awaitplayer.stop();break;caseseek:player.seek(call.argument(position)asnumber);break;casesetVolume:player.setVolume(call.argument(volume)asnumber);break;casesetPlaybackRate:player.setRate(call.argument(playbackRate)asnumber);break;casedispose:player.dispose();this.players.remove(playerId);break;}未知或文本 MIME 不能作为音频播放。URL 音源会先按扩展名判断 MIME文本文件会通过错误事件返回而不是让AVPlayer抛出难以理解的底层错误。六、URL 和字节流音源URL 音源区分远程和本地setForMediaPlayer(mediaPlayer:media.AVPlayer):void{if(this.isLocal){constfilefs.openSync(this.url);mediaPlayer.urlfd://${file.fd};}else{mediaPlayer.urlthis.url;}}本地文件通过 fd 加载远程地址交给AVPlayer。字节流音源使用AVDataSrcDescriptor提供读取回调constsource:media.AVDataSrcDescriptor{fileSize:this.bytes.byteLength,callback:(buffer,length,position){if(positionnull||positionsource.fileSize){return-1;}constcopyLengthMath.min(length,this.bytes.byteLength-position,);newUint8Array(buffer).set(newUint8Array(this.bytes.slice(position,positioncopyLength)),);returncopyLength;},};mediaPlayer.dataSrcsource;低延迟 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.sessionawaitAVSessionManager.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 使用示例多个实例播放finalbackgroundAudioPlayer(playerId:background);finaleffectAudioPlayer(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();});内存字节流finalplayerAudioPlayer();finalbytesawaitrootBundle.load(assets/audio.wav);awaitplayer.setSourceBytes(bytes.buffer.asUint8List());页面销毁时依次停止并释放播放器awaitplayer.stop();awaitplayer.dispose();九、构建、测试结果验证环境Flutter:3.44.9ohos-0.0.1-canary1OHOS SDK:26DevEco 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 鸿蒙社区参与 FlutterOHOS 经验交流。
延伸阅读

更多相关文章

2026/10/22 17:54:09

备份只留一份等于没备份?聊聊 NAS 上的 3-2-1 怎么摆

文章目录 📖 介绍 📖 🏡 演示环境 🏡 📒 三份、两种、一份异地:每个数字在家里意味着什么 📒 🧭 先把 3-2-1 拆开 🧱 第一份:本机高频副本,主打「频繁」 🔌 第二份:换一种介质,主打「不同死法」 ☁️ 第三份:真异地,主打「场所不在了」 🧪 两个「看…

2026/10/22 17:54:09

企业 AI 交付的五个协作接口:字段级清单与验收硬线

企业 AI 交付的五个协作接口:字段级清单与验收硬线 企业 AI 项目最常见的失败形态不是跑不起来,是跑起来了没人用。系统在线,接口正常,监控没报警,业务侧的实际操作仍然停在旧流程上。这种状态在合同里不算失败&#x…

2026/10/22 17:49:08

远程升级zynq/zu47的qspiflash裸机

1)上电完成读取qspi flash的型号 根据地址0x9F,调用qspi flash底层函数 xqspips_polledtransfer,实现读取4字节,1、2、3字节有效,0字节可舍去,读到的数据如0x21,0xBA,0x20。 2&#…

2026/10/22 22:32:03

【Pytorch】Anaconda: 新建环境出现No module named ‘pip._internal.main‘

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 22:32:03

PJ85718DM与PIC18F85J10协同的HVAC远程温度采集方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 22:32:03

【Pytorch】Pytorch: Automatic Differentitation (二)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/22 22:27:03

用 Python 批量导出数据库数据到 Excel 的完整指南

你是不是也遇到过这种情况:运营同事突然丢过来一句“帮我导一份上个月所有订单的Excel”,你打开数据库客户端,点导出,等上十分钟,好不容易导出成功,结果文件大到打开就卡死。更别提还要手动挑字段、调格式、…

2026/10/22 11:29:28

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/22 0:01:49

电商智能运营体系从0到1:架构设计、落地步骤与避坑指南

前两年我们团队接了一个电商平台的运营中台项目,目标很直白:把过去靠运营拍脑袋做的选品、定价、发券、推活动这些事,逐步用数据算法和自动化流程代替。项目名字内部就叫“电商智能运营体系”。最近有不少朋友问我当时是怎么设计的、踩过哪些…

2026/10/22 0:01:49

若依框架AI代码生成不稳定?用Agent Skill三层结构+五步法搞定

最近一段时间,我一直想把后台管理系统里那些机械重复的增删改查代码交给AI去写。框架用的是若依,按理说这类业务代码模式非常固定,应该是AI最擅长的事情。可真试下来却不是那么回事——你让AI生成一个用户管理模块,它给出的Contro…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑