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
應該也會出現一樣的問題。
寫的比較雜亂簡單,但願可以幫助到碰到一樣問題的人。