編輯:Android開發實例
前言
上一篇文章裡已經將了MediaPlayer的簡單應用,如何使用MediaPlayer在Android應用中播放音頻。這篇在MediaPlayer使用的基礎上,講解一下MediaPlayer的一些高級功能的使用,以及它的狀態轉換。
本文主要內容如下:
MediaPlayer的狀態變換
之前講到,使用MediaPlayer播放音頻,主要使用的是start()、pause()、stop()等方法操作MediaPlayer。但是除了開始、暫停、停止等,MediaPlayer還涉及到一些其他的狀態切換,有些狀態是可以雙向轉換的,有些只能單向環形轉換。如果在某狀態下,強行轉換狀態,會應發程序錯誤,例如在Preparing狀態下切換到Started狀態,是准備中強行開始播放,會出錯。下圖是官方文檔上的圖例,可以很清晰的表名MediaPlayer各個狀態的轉換情況。
上圖已經對MediaPlayer的各種狀態轉換有的清晰的介紹,這裡不再詳細講解了,只是提一下需要注意的地方:
MediaPlayer的喚醒鎖
一般使用MediaPlayer播放音頻流,推薦使用一個Service來承載MediaPlayer,而不是直接在Activity裡使用。但是Android系統的功耗設計裡,為了節約電池消耗,如果設備處於睡眠狀態,系統將試圖降低或者關閉一些沒設備必須的特性,包括CUP和Wifi硬件,然後,如果是一個後台播放音樂的應用,降低CUP可能導致在後台運行的時候干擾音頻的正常播放,關閉Wifi將可能導致網絡音頻流的獲取出現錯誤。
為了確保MediaPlayer的承載的服務在系統睡眠的時候繼續正常運行下去,Android為我們提供了一種喚醒鎖(wake locks)的機制。它可以在系統睡眠的,依然保持鎖定硬件的正常工作。
確保在MediaPlayer運行的時候,哪怕系統睡眠了CUP也能正常運行,需要使用MediaPlayer.setWakeMode()為MediaPlayer設定喚醒鎖。下面是setWakMode()的簽名:
setWakeMode(Context context, int mode)
第一個參數是當前上下文,第二個參數為需要加鎖的狀態,被設定為int類型的常量,定義在PowerManager這個final類中。PowerManager是專門用來管理Android功率消耗的鎖定狀態,與鎖定CUP相關的,有四種,分別設定CUP、屏幕、鍵盤等的各種保持喚醒的狀態,在這裡只需要設定為PARTIAL_WAKE_LOCK即可。
- mediaPlayer = new MediaPlayer();
- // 設定CUP鎖定
- mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
一般對於鎖而言,鎖定了通常需要解鎖,但是這裡的喚醒說與MediaPlayer關聯,所以只需要在使用完之後release()釋放MediaPlayer即可,無需顯式的為其解鎖。在使用setWakeMode設定喚醒鎖的時候,還必須為應用賦予相應的權限:
- <uses-permission android:name="android.permission.WAKE_LOCK"/>
再來說說如何鎖定wifi硬件在系統睡眠的時候保持正常運行。wifi鎖通過WifiLock進行操作,而WifiLock通過WifiManager進行管理,通過WifiManager.createWifiLock()進行Wifi鎖定。
WifiManager.WifiLock createWifiLock(int lockType, String tag)
這個方法有多個重載,這裡介紹的這個,第一個參數設定鎖的狀態,為一個int類型的常量,定義在Context類中,這裡的應用場景一般設定為WIFI_MODE_FULL即可。第二個參數為WifiLock的標志,用於確定wifiLock的。
- wifiLock= ((WifiManager) getSystemService(this.WIFI_SERVICE))
- .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");
- wifiLock.acquire();
當然,在應用中把Wifi鎖定之後,還需要在MediaPlayer.release()的時候為wifi硬件解鎖,為避免意外關閉的情況,最好在Android組件的onDestory()裡對其進行釋放,釋放Wifi鎖使用WifiLock.release()。
- /**
- * 停止播放
- */
- protected void stop() {
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.stop();
- mediaPlayer.release();
- mediaPlayer = null;
- // 釋放wifi鎖
- wifiLock.acquire();
- btn_play.setEnabled(true);
- Toast.makeText(this, "停止播放", 0).show();
- }
- }
- @Override
- protected void onDestroy() {
- // 在activity結束的時候回收資源
- if (mediaPlayer != null && mediaPlayer.isPlaying()) {
- mediaPlayer.stop();
- mediaPlayer.release();
- mediaPlayer = null;
- // 釋放wifi鎖
- wifiLock.release();
- }
- super.onDestroy();
- }
MediaPlayer的音頻焦點
眾所周知,Android是一個多任務的操作系統,所以對於音頻的播放,也許有幾個不同的媒體服務會同時播放,這樣可能導致一個比較雜亂的聲音環境,而錯過一些重要的聲音提醒。在Android2.2之後,Android提供了一種應用協商使用設備音頻輸出的機制,這種機制稱為音頻焦點。
當應用程序需要輸出音頻或通知的時候,需要請求音頻焦點,當請求得到音頻焦點之後,監聽音頻焦點的變換,當音頻焦點變換了,根據返回回來的音頻焦點碼進行相應的處理。音頻焦點的注冊使用音頻管理器的AudioManager.requestAudioFocus()方法設定。它的簽名如下:
int requestAudioFocus(AudioManager.OnAudioFocusChangeListener l, int streamType, int durationHint)
這個方法的返回值是int類型,其含義被定義在AudioManager中以常量表示AUDIOFOCUS_REQUEST_FAILED(獲取音頻焦點成功)
、AUDIOFOCUS_REQUEST_GRANTED(獲取音頻焦點失敗)。其中重要的是第一個參數,為音頻焦點變化的回調函數,在其中可以設定如果音頻焦點變換了,當前應用如何管理MediaPlayer,第二個參數為媒體流的類型,第三個參數為持續的狀態。
AudioManager.OnAudioFocusChangeListener為音頻焦點變換的監聽器,其中需要實現一個方法:onAudioFocusChange(int focusChange)在音頻焦點變換的時候回調。它有一個參數,為當前表示音頻焦點對於當前應用的狀態碼,通過這個狀態碼指定對應的操作,有些時候音頻狀態改變了,並不一定需要停止音頻的播放。
focusChange有一下幾種狀態碼:
- audioManager = (AudioManager) getSystemService(this.AUDIO_SERVICE);
- int result = audioManager.requestAudioFocus(
- new OnAudioFocusChangeListener() {
- @Override
- public void onAudioFocusChange(int focusChange) {
- switch (focusChange) {
- case AudioManager.AUDIOFOCUS_GAIN:
- // 獲得音頻焦點
- if (!mediaPlayer.isPlaying()) {
- mediaPlayer.start();
- }
- // 還原音量
- mediaPlayer.setVolume(1.0f, 1.0f);
- break;
- case AudioManager.AUDIOFOCUS_LOSS:
- // 長久的失去音頻焦點,釋放MediaPlayer
- if (mediaPlayer.isPlaying())
- mediaPlayer.stop();
- mediaPlayer.release();
- mediaPlayer = null;
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
- // 展示失去音頻焦點,暫停播放等待重新獲得音頻焦點
- if (mediaPlayer.isPlaying())
- mediaPlayer.pause();
- break;
- case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
- // 失去音頻焦點,無需停止播放,降低聲音即可
- if (mediaPlayer.isPlaying()) {
- mediaPlayer.setVolume(0.1f, 0.1f);
- }
- break;
- }
- }
- }, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
源碼下載
總結
以上就講解了MediaPlayer的一些高級的內容,在掌握了MediaPlayer的使用之後,開發有關音樂播放類的應用的時候就可以得心應手了。從用戶體驗的方面出發,如果真實開發一款播放器類的軟件,需要監聽AUDIO_BECOMING_NOISY的廣播,它會在音頻輸出源從其他輸出源變換到設備揚聲器的時候發出此廣播,監聽廣播在音頻輸出源改變到設備揚聲器的時候,停止播放,這樣確保在耳機或額外的音頻輸出硬件與設備斷開連接的時候,不至於重新從揚聲器繼續輸出音頻播放。
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個
相信每個項目都會有用戶反饋建議等功能,這個實現的方法很多,下面是我實現的方法,供大家交流。首先看具體界面,三個字段。名字,郵箱為選填,可以為空,建議不能為空。如有
TCP和UDP在網絡傳輸中非常重要,在Android開發中同樣重要。 首先我們來看一下什麼是TCP和UDP。 什麼是TCP? TCP:Transmission C
JSON代表JavaScript對象符號。它是一個獨立的數據交換格式,是XML的最佳替代品。本章介紹了如何解析JSON文件,並從中提取所需的信息。Android提供了四個