audioplayers是一个能够支持同时播放多个音频文件的Flutter
的库。用法也是至关的简单:ios
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();
复制代码
AudioPlayer audioPlayer = new AudioPlayer();
await audioPlayer.play(localPath, isLocal: true);
//播放后,立刻调用暂停,底层抛出`PlatformException`异常,程序崩溃
await audioPlayer.pause();
复制代码
嗯,就是这么简单,还没写满10行代码就GG了。git
遇到了这个崩溃后,本人也是怀揣着好奇心第一时间翻看了audioplayers
的代码,以及又看了一遍文档。原来,文档中介绍,为了实现先加载后播放的功能,咱们能够调用audioPlayer.setUrl
函数,以后须要播放时候调用resume
进行播放便可。github
嗯,当时我没看到这段,因祸得福焉知非福,这样的实现也是一样存在问题的。 先来一张Android MediaPlayer
的State Diagram
:异步
不管是audioPlayer.play
仍是andioPlayer.setUrl
,他们最重要的工做就是调用Android原生的prepareAsync
方法,让MediaPlayer
对象处于Prepared
状态。引用一下Android
官方文档:async
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 ......lua
归纳起来就两点:url
Prepared
状态prepareAsync
是一个异步操做,会使MediaPlayer
进入preparing
状态, 当底层的player engine
完成preparation
的工做时,将会调用用户提供的回调函数。因此,上面崩溃的问题也比较明了了,audioPlayer.play
调用以后底层的MediaPlayer
实际上是一个preparing
状态,此时调用pause
致使了PlatformException
异常的抛出,程序直接崩溃。这实际上是一个audioplayers
没有处理好的状态,play
和setUrl
这类的Future
不该该只在perparing
状态就resolve
,而应该等到prepared
。spa
所以...我反手去audioplayers github上提了个issue,深藏功与名。3d
audioplayers
在Android
还支持SoundPool
(new AudioPlayer(mode: PlayerMode.LOW_LATENCY)
),也存在着一样的问题。
推测ios
应该也会出现一样的问题。
写的比较杂乱简单,但愿可以帮助到碰到一样问题的人。