简介
是一个可以支持同时播放多个音频文件的Flutter
的库。用法也是相当的简单:
AudioPlayer audioPlayer = new AudioPlayer(); await audioPlayer.play(url); //or //await audioPlayer.play(localPath, isLocal: true); // 一些控制API await audioPlayer.pause(); await audioPlayer.stop(); await audioPlayer.resume();复制代码
如何使程序崩溃(Android)
AudioPlayer audioPlayer = new AudioPlayer();await audioPlayer.play(localPath, isLocal: true);//播放后,马上调用暂停,底层抛出`PlatformException`异常,程序崩溃await audioPlayer.pause();复制代码
嗯,就是这么简单,还没写满10行代码就GG了。
分析原因
遇到了这个崩溃后,本人也是怀揣着好奇心第一时间翻看了audioplayers
的代码,以及又看了一遍文档。原来,文档中介绍,为了实现先加载后播放的功能,我们可以调用audioPlayer.setUrl
函数,之后需要播放时候调用resume
进行播放即可。
嗯,当时我没看到这段,塞翁失马焉知非福,这样的实现也是同样存在问题的。 先来一张Android MediaPlayer
的State Diagram
:
无论是audioPlayer.play
还是andioPlayer.setUrl
,他们最重要的工作就是调用Android原生的prepareAsync
方法,让MediaPlayer
对象处于Prepared
状态。引用一下Android
官方文档:
A MediaPlayer object must first enter the Prepared state before playback can be started.
...... or a call to prepareAsync() (asynchronous) which first transfers the object to the Preparing state after the call returns (which occurs almost right away) while the internal player engine continues working on the rest of preparation work until the preparation work completes. When the preparation completes or when prepare() call returns, the internal player engine then calls a user supplied callback method ......
概括起来就两点:
- 播放/暂停等操作之前,必须进入
Prepared
状态 prepareAsync
是一个异步操作,会使MediaPlayer
进入preparing
状态, 当底层的player engine
完成preparation
的工作时,将会调用用户提供的回调函数。
所以,上面崩溃的问题也比较明了了,audioPlayer.play
调用之后底层的MediaPlayer
其实是一个preparing
状态,此时调用pause
导致了PlatformException
异常的抛出,程序直接崩溃。这其实是一个audioplayers
没有处理好的状态,play
和setUrl
这类的Future
不应该只在perparing
状态就resolve
,而应该等到prepared
。
因此...我反手去上提了个issue,深藏功与名。
尾声
audioplayers
在Android
还支持SoundPool
(new AudioPlayer(mode: PlayerMode.LOW_LATENCY)
),也存在着同样的问题。
推测ios
应该也会出现同样的问题。
写的比较杂乱简单,希望能够帮助到碰到同样问题的人。