尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Flutter/OpenHarmony 三方库适配实战:just_audio 音频播放
Flutter/OpenHarmony 三方库适配实战just_audio 音频播放一、项目信息项目内容仓库名称just_audio仓库地址https://atomgit.com/oh-flutter/just_audio基于版本just_audio 0.10.5适配 TAG0.10.5-ohos-1.0.4OHOS 插件包just_audio内置ohos实现OHOS 原生入口JustAudioOhosPlugin原生播放器MediaAvPlayer主方法通道com.ryanheise.just_audio.methodsjust_audio提供播放器、播放列表、进度流、缓冲状态和音频源抽象。OHOS 适配的重点是让这些概念对应到AVPlayer生命周期同时保留原库的 Dart API。二、通道设计原库使用“主通道创建播放器每个播放器独立方法和事件通道”的结构JustAudioOhosPlugin | -- com.ryanheise.just_audio.methods | init / disposePlayer / disposeAllPlayers | -- AudioPlayer(id) | -- com.ryanheise.just_audio.methods.id -- com.ryanheise.just_audio.events.id -- com.ryanheise.just_audio.data.id | -- MediaAvPlayer | -- ohos.multimedia.media.AVPlayer主通道只管理播放器实例的创建和销毁具体load、play、pause、seek、setVolume等命令由每个播放器自己的通道处理。这样多个播放器之间不会串号。三、插件注册与生命周期插件声明在主just_audio/pubspec.yaml中flutter:plugin:platforms:ohos:pluginClass:JustAudioOhosPlugin原生插件创建主方法通道并监听引擎重启exportdefaultclassJustAudioOhosPluginimplementsFlutterPlugin{onAttachedToEngine(binding:FlutterPluginBinding):void{constcontextbinding.getApplicationContext();constmessengerbinding.getBinaryMessenger();this.methodCallHandlernewMainMethodCallHandler(context,messenger);this.channelnewMethodChannel(messenger,com.ryanheise.just_audio.methods,);this.channel.setMethodCallHandler(this.methodCallHandler);this.lifecycleListener{onPreEngineRestart:(){this.methodCallHandler?.dispose();},}asEngineLifecycleListener;binding.getFlutterEngine().addEngineLifecycleListener(this.lifecycleListener,);}}热重启时必须先释放所有底层播放器否则旧AVPlayer会继续占用音频焦点和媒体资源。引擎销毁时还需要移除监听器、解绑 MethodChannel 并清理全局 Context。四、创建播放器时等待原生初始化主处理器使用播放器 id 管理实例caseinit:{constidcall.argument(id)asstring;if(this.players.has(id)){result.error(Platform player id already exists,null,null);break;}constplayernewAudioPlayer(this.messenger!,id);awaitplayer.waitForInitialization();this.players.set(id,player);result.success(null);break;}MediaAvPlayer的构造函数不能等待异步初始化因此它保存初始化 Promiseconstructor(){this.initPromisethis.initAudioPlayer();}asyncwaitForInitialization():Promisevoid{awaitthis.initPromise;if(this.initError!null){thrownewError(this.initError.message);}}主通道在init响应前等待这个 Promise避免 Dart 端刚收到创建结果就调用load时底层avPlayer仍为null。五、每个播放器的通道与事件播放器构造时创建三条通道this.methodChannelnewMethodChannel(messenger,com.ryanheise.just_audio.methods.id,);this.eventChannelnewBetterEventChannel(messenger,com.ryanheise.just_audio.events.id,);this.dataEventChannelnewBetterEventChannel(messenger,com.ryanheise.just_audio.data.id,);BetterEventChannel保存当前EventSink。没有 Dart 监听器时不发送事件取消订阅后清空 sinkonListen(args:Object,eventSink:EventSink):void{this.eventSinkeventSink;}onCancel(args:Object):void{this.eventSinknull;}播放器方法通道支持加载、播放、暂停、音量、速度、跳转和播放列表调整switch(call.method){caseload:awaitthis.load(call,result);break;caseplay:awaitthis.play(result);break;casepause:this.pause();result.success(newMapString,Object());break;casesetVolume:this.setVolume(call.argument(volume)asnumber);break;casesetSpeed:this.setSpeed(call.argument(speed)asnumber);break;caseseek:awaitthis.seek(call.argument(positionMs)asnumber,call.argument(index)asnumber|null,result,);break;}跳转操作需要等待异步完成并回执否则 Dart 的seek()Future 可能永久等待。六、AVPlayer 状态与错误MediaAvPlayer将AVPlayer状态机转换为播放器状态初始化阶段等待avPlayer创建完成。load设置播放源并等待initialized/prepared。缓冲事件更新 buffered position。完成事件将处理状态改为 completed。错误事件通过 EventChannel 抛回 Dart。初始化失败必须明确上报onErrorCallback(err:BusinessErrorvoid):void{this.sendError(err.code?.toString()??err.name,err.message,);}如果加载失败但不发送错误Dart 端的load()可能一直处于 loading。适配中特别处理了未 prepare 完成时的播放器操作错误使其转换为可读错误已 prepare 后的时序错误则记录日志避免正常切歌流程触发误报。七、音频源处理适配层支持网络 URL、本地文件、Asset 和播放列表来源。本地文件使用文件描述符避免将路径字符串交给不支持该路径的播放器接口。MediaSource负责将 Dart 参数转换为具体播放源。加载时根据progressive、hls、dash、concatenating、clipping、looping等类型创建不同数据结构。播放列表操作通过concatenatingInsertAll、concatenatingRemoveRange和concatenatingMove更新底层列表。索引变化后必须同步当前播放项、初始位置和shuffle 顺序。八、Dart 使用示例网络音频播放finalplayerAudioPlayer();awaitplayer.setUrl(https://example.com/audio.mp3);awaitplayer.setVolume(0.8);awaitplayer.setSpeed(1.25);player.playbackEventStream.listen((event){debugPrint(position${event.position}, state${event.processingState});});awaitplayer.seek(constDuration(seconds:30));awaitplayer.pause();awaitplayer.dispose();加载本地文件awaitplayer.setFilePath(/data/storage/el2/base/files/audio.wav);网络播放需要宿主 HAP 声明{ module: { requestPermissions: [ { name: ohos.permission.INTERNET }, ], }, }页面销毁时必须调用dispose()不能只暂停播放器。暂停不会释放AVPlayer、音频焦点和所有事件监听。九、构建、测试与结果执行flutter analyze --no-pub lib fluttertest结果静态分析无问题。70 个 Dart 测试通过。示例 HAP 构建、安装和启动成功。十、遗留问题截图不能替代不同采样率、编码格式和网络抖动的真机测试。音频焦点、蓝牙耳机切换和来电中断需要系统级场景验证。后台播放需要宿主应用配置长时任务和相关权限不能只依赖插件。播放列表、Clipping、DASH/HLS 组合场景应继续补充集成测试。十一、代码提交与协作欢迎加入 CPF-Flutter 鸿蒙社区参与 FlutterOHOS 经验交流。
RELATED

相关推荐

Harbor 从 HTTP 部署到 HTTPS 改造全流程

Harbor 从 HTTP 部署到 HTTPS 改造全流程

目标:先在 Ubuntu 26.04 上部署好容器环境,再以 HTTP 方式快速跑通 Harbor,最后平滑切换到 HTTPS 并让所有客户端适配信任。 目录[TOC](目录)一、文档说明与前置条件1.1 适用版本与环境1.2 拓扑与关键参数(按需替换)1.…

📅 2026/10/22 7:37:14
3D 模型压完还要转 GLB 再压贴图?不到 10 MB 的工具里切页签

3D 模型压完还要转 GLB 再压贴图?不到 10 MB 的工具里切页签

干 3D 模型这活,两种干法摆得很清楚:一种是压完导出、开下一个软件导入、再导出、再导入,几个软件之间来回倒文件;另一种是找一个把压缩、转格式、压贴图都收进去的工具,在同一套界面里切页签。我后来选了后者&#xf…

📅 2026/10/22 7:37:14
安徽网站定制开发公司选择指南:同样说能做开发费用该怎么比较

安徽网站定制开发公司选择指南:同样说能做开发费用该怎么比较

只是把首页颜色和图片换了,算不算定制?现成后台本来就能发产品,为什么报价又列一项产品管理开发费?找安徽定制网站开发公司,这两件事值得先弄明白。定制费应该付给确实需要另外设计、修改和开发的工作,不能…

📅 2026/10/22 7:32:13
MORE NEWS

更多资讯

📰

EFI程序替代版本更换指南:从原理到实操避坑手册

看到“EFI程序”这四个字,很多人第一反应是刷BIOS时看到的那个黑色命令行界面,也有人会以为是固态硬盘里那个不带盘符的隐藏分区。其实它泛指一切在UEFI引导环境下运行的可执行文件,包括启动管理器、固件更新工具、驱动程序,甚至像…

📰

Linux kill命令本质:信号机制与进程优雅终止原理

1. 为什么你总在 kill 命令上栽跟头?——一个运维老手的十年血泪总结 “kill -9 进程号”这行命令,我第一次敲出来是在大学实验室的 Linux 终端里,当时以为自己掌握了系统控制权。结果三分钟后,整个测试环境的服务全挂了——不是因…

📰

SkillsBench实战:从16%技能调用成功率提升看Agent评测与优化

有段时间我被一个现象搞得很头疼:智能体(Agent)在演示环境里表现无懈可击,一丢进生产环境就间歇性抽风。用户问一句“帮我订个明天下午两点的会议室”,它能正确理解意图,却在调用会议室预订技能时把日期参数…

📰

Spring Boot整合Spark on Yarn的工程化实践与避坑指南

1. 为什么非得把 Spring Boot 和 Spark on Yarn 拼在一起? 我第一次接到这个需求时,心里是打问号的:一个轻量级 Web 框架,一个重型分布式计算引擎,中间还隔着 Yarn 这个资源调度层——这组合听起来像让咖啡机去调度高铁…

📰

C#接入大模型生产化:网关、重试与成本控制实战

上周帮一个朋友看他的C# AI项目,他在周末用一天时间把一个大模型Demo跑通了:控制台里敲一句话,回车,模型乖乖吐出结构化JSON,他兴奋地给我看效果。但我翻了翻代码,问他三个问题——并发50时会不会把限流打穿…

📰

LangSmith实战:用可观测性拆解Agent黑箱

先说个我真实踩过的坑。某开发者把一个带工具调用的Agent丢到测试环境,第一次问“某个客户的账为什么对不上”,Agent调用财务接口查到一笔异常,紧接着又自作主张调了订单接口、还调了库存接口,最后基于三个接口的数据“推断”出一…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬