资讯动态

Flutter/OpenHarmony 三方库适配实战:just_audio 音频播放

发布时间:2026/10/22 7:36:48 来源:尧图企业网站定制
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 经验交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑