關於MediaPlayer播放音頻的使用及狀態管理

最近在app播放音頻的時候,時常出現 IllegalStateException,在網上看了別人寫的博客,想了解一下MediaPlayer的生命週期及使用時要注意的點,沒想到查的資料說法都不一樣(後面會指出),索性去看了官方文檔,特此記錄~~~

使用MediaPlayer

MediaPlayer類是媒體框架最重要的組成部分之一。此類的對象可以以最少的設置來獲取,解碼和播放音頻和視頻。它支持幾種不同的媒體來源,例如:
本地資源
內部URI,例如從內容解析器獲取的URI
外部URL(流媒體)

有關Android支持的媒體格式列表,請參閱Supported Media Formats

這裏舉個例子,其他的媒體來源怎麼寫請自行查詢資料

通過HTTP流式傳輸從遠程URL播放,如下所示:

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

注意:使用setDataSource()時,必須捕獲或傳遞IllegalArgumentException和IOException,因爲您引用的文件可能不存在。

 Prepare準備階段

上面的代碼很簡單,只要直接用,就可以播放。但是有一點要注意的,準備階段,即prepare()方法會執行很長時間,因爲它可能包括獲取和解碼媒體數據。所以你不能在UI線程調用這個方法,否則會產生ANR錯誤,就算你覺得你的資源加載很快也不行,因爲在UI線程中響應超過十分之一秒的任何內容都會產生明顯的暫停,給用戶不好的印象。

爲了解決這個問題,我們調用另外一個方法prepareAsync() ,這個方法會在後臺開始prepare並且立即返回,當媒體資源prepare完成後,就會回調MediaPlayer.OnPreparedListener的onPrepared()方法,我們可以通過setOnPreparedListener設置監聽,並在onPrepared方法中做相應的處理。

管理狀態

你需要注意的另一方面是MediaPlayer是基於狀態的。什麼是基於狀態呢?

MediaPlayer有一個內部狀態,你在編寫代碼時必須時刻注意,因爲某些操作只在MediaPlayer處於某個特定狀態時纔有效。如果你在錯誤的狀態執行一個操作,系統可能拋出異常或導致無法預料的行爲!

舉個例子

當你new一個MediaPlayer對象時,它是處於Idle(空閒)狀態的。在這時候,你需要調用setDataSource()方法進行初始化,此時它處於Initialized狀態。接下來,你需要準備資源文件,即調用prepare()或prepareAsync()方法。當MediaPlayer準備完成後,它進入Prepared狀態,這以爲着你可以調用start()方法來播放媒體了。在播放階段,你可以調用start(),pause()和seekTo()等方法在Started,Paused和PlaybackCompleted狀態之間移動。但是,當您調用stop()時,請注意,在重新調用MediaPlayer的prepare()方法之前,不能再次調用start()。

釋放MediaPlayer 

MediaPlayer會消耗昂貴的系統資源。因此,你應該在不使用MediaPlayer的時候調用release()來釋放資源。例如:在Activity的onStop()方法中調用release方法(除非你需要在後臺播放媒體資源),當你的Activity resumed或restarted的時候,你需要重新創建一個MediaPlayer並且再次調用prepare()來恢復播放。

這裏注意一點:有一些博客說在調用了release方法後,可以用reset方法來重置狀態,這是錯誤的。調用了release方法後,MediaPlayer無法回到其他任何狀態!!

下面是如何釋放MediaPlayer的代碼

mediaPlayer.release();
mediaPlayer = null;

Wake locks

在設計在後臺播放媒體的應用程序時,設備可能會在您的服務運行時進入休眠狀態。由於Android系統在設備休眠時嘗試節省電池,因此係統會嘗試關閉不必要的任何功能,包括CPU和WiFi硬件。但是,如果您正在播放服務或播放音樂,則需要防止系統干擾播放。 爲了確保您的服務在這些條件下繼續運行,您必須使用“wake locks”。喚醒鎖定是一種向系統發出信號的方式,即您的應用程序正在使用某些功能,即使手機處於空閒狀態也應保持可用狀態。

要確保MediaPlayer在播放時CPU繼續運行,需要在初始化MediaPlayer時調用setWakeMode()方法。完成後,MediaPlayer在播放時持有指定的鎖,並在暫停或停止時釋放鎖定:

mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

但是,在此示例中獲取的wake locks僅保證CPU保持喚醒狀態。如果您通過網絡流式傳輸媒體並且正在使用Wi-Fi,則可能還需要持有WifiLock,您必須手動獲取並釋放它。因此,當您開始使用遠程URL準備MediaPlayer時,您應該創建並獲取Wi-Fi鎖。例如:

WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
    .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

暫停或停止媒體或不再需要網絡時,應釋放鎖:

wifiLock.release();

執行清理

如前所述,MediaPlayer對象可能會佔用大量系統資源,因此您應該只在需要時保留它,並在完成後調用release()。顯式調用此清理方法,而不是依賴系統的垃圾收集,因爲垃圾收集器可能需要一些時間來回收MediaPlayer,因爲它只對內存需求敏感,而不是缺少其他與媒體相關的資源。因此,在您使用服務時,應始終覆蓋onDestroy()方法以確保釋放MediaPlayer:

public class MyService extends Service {
   MediaPlayer mediaPlayer;
   // ...

   @Override
   public void onDestroy() {
       super.onDestroy()
       if (mediaPlayer != null) mediaPlayer.release();
   }
}

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章