Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> MediaPlayer和Service用法

MediaPlayer和Service用法

編輯:關於Android編程

Android的多媒體框架包括支持播放多種常見的媒體類型,使您可以輕松地把音頻、視頻和圖像集成到你的應用。你可以播放音頻或視頻媒體文件,這些文件是存儲在你的應用程序的資源文件中的。應用程序的資源文件可以是文件系統中獨立的文件,或通過網絡連接獲取的一個數據流,所有使用MediaPlayer APIS的資源文件。
1.注意:
如果你通過一個URL來獲取一個在線媒體文件,該文件必須能夠支持漸進式下載。
2.警告:
當你使用setDataSource()方法時,必須捕捉或通過非法數據異常和輸入輸出異常,因為正在被你引用的文件可能不存在在.
3.異步准備 - Asynchronous Preparation
使用MediaPlayer 的原則很簡單。然而,重要的是要記住,有必要將更多的一些東西正確地集成到一個典型的Android應用程序。比如,調用prepare()方法可能需要較長的時間來執行,因為它可能涉及獲取和解碼媒體數據。因此,如同任何方法,可能需要很長時間執行,你不應該從應用程序的UI線程調用它。這樣做將導致UI掛到方法返回,這是一個非常糟糕的用戶體驗,也可能會引起應用程序沒有響應的錯誤。 即使你預期你的資源能快速加載,記住,任何超過十分之一秒的反應在界面上會造成明顯的停頓,將導致給用戶的印象是:你的應用程序是緩慢的。

為了避免你的UI線程掛起,產生另一個線程准備MediaPlayer 當完成時通知主線程。你可以寫自己的線程的邏輯,框架提供prepareAsync() 方法,方便的使用MediaPlayer 。該方法在後台開始准備媒體並立即返回。當媒體准備好了,MediaPlayer.OnPreparedListener的onPrepared 方法,通過配置setonpreparedlistener()方法來調用。

4.狀態管理 - Managing State
關於MediaPlayer ,你需要記住的另一點是它的狀態。即,在你編寫自己的代碼的時候,必須時刻意識到MediaPlayer 有一個內部狀態,因為 只有當玩家在特定狀態,某些特定的操作才會有效。如果您在錯誤的狀態執行一個操作,系統可能會拋出一個異常或引起其它令人不快的行為。

MediaPlayer 類裡有文件顯示一個完整的狀態轉換圖,闡明哪些方法可以把MediaPlayer 從一個狀態改變到另一個狀態。例如,當您創建一個新的MediaPlayer ,它就處於閒置狀態。這時,你應該調用android.net.Uri) setDataSource()方法來初始化它,把它設置為初始化狀態。然後,你必須使用prepare()方法或prepareAsync()方法。當MediaPlayer 准備好了,它將進入准備狀態,這就意味著你可以調用start()方法來播放媒體。另外,在狀態轉換圖上闡明了,你可以調用start(),pause()和 seekTo()這些方法在Started,Paused和PlaybackCompleted狀態之間進行轉換。如果您調用stop()方法,這時請注意,你需要再次准備MediaPlayer ,才可以再一次調用start()方法。

在編寫代碼與MediaPlayer 對象交互時,心裡要隨時想著狀態轉換圖,因為從錯誤的狀態調用它的方法,是引起錯誤的常見原因。

釋放MediaPlayer-Releasing the MediaPlayer
MediaPlayer 會消耗寶貴的系統資源。因此 ,你應該經常采取額外的預防措施來確保及時把不需要的MediaPlayer 取消掉。您需要調用release() 方法來確保系統分配給它的資源正確釋放。例如,您正在使用MediaPlayer ,同時,你的活動調用onStop()方法,這時你必須釋放MediaPlayer,因為你的活動並非與用戶交互,留著它沒什麼意義(除非你是在後台播放多媒體,這是下一節中將討論的內容)。當你的活動恢復或者重新啟動,恢復播放之前,您需要創建一個新的MediaPlayer並且重新准備。
下面是釋放和取消你的MediaPlayer的方法:

mediaPlayer.release(); 
mediaPlayer = null;

作為思考題,考慮一下如果當活動停止的時候你忘了釋放MediaPlayer,活動重啟後新建一個MediaPlayer,可能會發生的問題。正如你可能知道的,當用戶更改屏幕的方向(或以另一種方式更改設備配置),該系統通過重啟活動處理(通過默認方式),所以當用戶頻繁在縱向和橫向之間切換時,你可能會很快消耗掉所有的系統資源,原因是你沒有釋放方向變化時各個方向上創建的新MediaPlayer。(更多關於運行時重啟的資料,請查看Handling Runtime Changes)。

你可能會想知道在用戶離開活動時後台繼續播放媒體是如何實現的,采用同樣的方式實現的,如內置的音樂應用程序的行為。在這種情況下,你需要通過一個Service來控制MediaPlayer,所以我們開始學習Using a Service with MediaPlayer。

5.使用服務控制MediaPlayer - Using a Service with MediaPlayer
如果你希望後台播放媒體,你希望用戶操作其他應用時繼續播放,你必須開始一個Service並且從那裡控制MediaPlayer實例。你必須慎重考慮這個設置,因為用戶與系統期望應用程序運行的後台服務應該與系統的其余部分相互作用。如果應用程序不滿足這些預期,就不能有良好的用戶體驗。本節介紹的主要內容是:告訴你相關知識,並提供建議如何接觸它們。

異步運行 - Running asynchronously
首先,如一個Activity,服務裡的所有任務默認在單一線程中完成。如果你從同一個應用程序裡運行一個Activity和一個Service,它們默認使用相同的線程(“主線程”)。因此,Service需要迅速處理傳入的意圖並且響應它們的時候從不執行冗長的計算。如果預計調用一些復雜的任務或阻塞,你必須異步處理這些任務:由另一個線程自己實現自己,或使用框架處理異步。
例如,當你從主要線程使用一個MediaPlayer ,你應該調用prepareAsync()方法而不是prepare() 方法,實現MediaPlayer.OnPreparedListener,以便當你准備工作完畢後,得到可以開始播放的通知。

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.support.annotation.Nullable;

/**
 * Created by ylwang on 2016/5/13.
 */

public class MyService extends Service implements MediaPlayer.OnPreparedListener {
    private static final String ACTION_PLAY = "com.example.action.PLAY";
    MediaPlayer mMediaPlayer = null;

    public int onStartCommand(Intent intent, int flags, int startId) {...
        if (intent.getAction().equals(ACTION_PLAY)) {
            mMediaPlayer =... // initialize it here
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.prepareAsync(); // prepare async to not block main thread
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

處理異步錯誤 - Handling asynchronous errors
在同步操作中,錯誤通常會出現異常或錯誤代碼信息。但當你使用異步資源時,您需要確保您的應用程序有錯誤提示,在MediaPlayer 中,要做到這一點,可以通過實現MediaPlayer.OnErrorListener,並且將它設置在你的MediaPlayer 實體中。

mport android.app.Service;
import android.media.MediaPlayer;


/**
 * Created by ylwang on 2016/5/13.
 */

public class MyService extends Service implements MediaPlayer.OnErrorListener {
    MediaPlayer mMediaPlayer;

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here
        mMediaPlayer.setOnErrorListener(this);
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        // ... react appropriately ... 
        // The MediaPlayer has moved to the Error state, must be reset! 
        return false;
    }

}

請牢記,當出現錯誤,將這個MediaPlayer 設置為錯誤狀態(請參考MediaPlayer 類文檔的完整的狀態關系圖)您再次使用它之前,必須重置這個狀態。

使用喚醒鎖 - Using wake locks

應用程序在後台播放媒體,其服務在運行期間,設備可能會進入休眠狀態。因為Android系統希望在設備休眠時節省電池。系統試圖關閉手機的應用程序,是沒有必要的,包括CPU和WiFi硬件。但是,如果你的服務正在運行或播放著音樂,你希望防止系統干擾你的回放。
為了確保您的服務在這些條件下能繼續運行,你需要使用“wake locks”。喚醒鎖是一種信號系統,它發出信號,顯示:應用程序正在使用或可用的功能,或手機閒置。

注意:你應該盡量少用喚醒鎖,只有在必要時候才使用它們。它們會使設備的電池壽命大大降低。

你MediaPlayer 正在播放時,需要確保CPU持續運行,當初始化你的MediaPlayer時,調用setWakeMode()方法。一旦你這樣做了,當暫停或停止時候,MediaPlayer 持有指定的鎖:

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

在這個例子中獲得喚醒鎖是指在保證CPU在喚醒狀態。當你通過網絡獲取媒體和您正在使用WiFi時,你可能希望有個WifiLock,可以手動獲取並釋放。當你開始通過遠程URL准備MediaPlayer,你應該創建並獲得wi - fi鎖。 代碼如下:

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

 wifiLock.acquire();

當你暫停或停止你的媒體時,或當你不再需要這樣的網絡,你應該釋放該鎖: 代碼如下:

wifiLock.release();

作為前景服務運行 - Running as a foreground service
服務通常用於執行後台任務,例如獲取電子郵件,同步數據,下載內容,或其他。在這些情況下,用戶不會意識到這個服務的執行,甚至可能不會注意到這些服務被打斷,後來重新啟動。毫無疑問,後台播放音樂是一個服務,用戶能意識到,任何中斷都會嚴重影響到用戶體驗。此外,用戶可能會希望在這個服務執行期間作用於它。這種情況,服務應該運行一個“前景服務”。前台服務在系統中持有一個更高水平的重要性,系統幾乎從未將服務扼殺,因為它對用戶有著直接的重要性。當應用在前台運行,該服務還必須提供一個狀態欄來通知用戶意識有服務正在運行同時允許他們打開一個活動,可以與服務進行交互。
為了把你的服務變為前景服務,您必須為狀態欄創建一個Notification,並且從Service調用startForeground()方法。
代碼如下:

String songName; // assign the song name to songName
        PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
        Notification notification = new Notification();
        notification.tickerText = text;
        notification.icon = R.drawable.play0;
        notification.flags |= Notification.FLAG_ONGOING_EVENT;
        notification.setLatestEventInfo(getApplicationContext(), "MusicPlayerSample", "Playing: " + songName, pi);
        startForeground(NOTIFICATION_ID, notification);

通知區域可見的設備告訴你,服務在前台運行。如果用戶選擇了這個通知,系統將調用你提供的PendingIntent。在上面的例子中,它打開了一個Activity。(MainActivity)
實際執行一些用戶能夠意識到的服務時,你應該保留“foreground service”的狀態。相反情況下,你應該調用stopForeground()方法來釋放它。 代碼如下:
stopForeground(true);
更多信息,請參考Service和 Status Bar Notifications的相關文檔。

處理音頻焦點 - Handling audio focus=

在給定的時間盡管只有一個活動可以運行,但Android是一個多任務環境。這對應用程序使用音頻 造成了一個特別大的難度,由於只有一個音頻輸出,可能會有好幾個媒體服務爭奪使用它。Android 2.2之前,沒有內置機制來解決這個問題,這可能在某些情況下導致糟糕的用戶體驗。例如,一個用戶正在聽音樂,同時,另一個應用程序有很重要的事需要通知用戶,由於吵鬧的音樂用戶可能不會聽到提示音。從Android 2.2開始,Android平台為應用程序提供了一個方式來協商設備的音頻輸出。這個機制被稱為音頻焦點

當您的應用程序需要輸出音頻如音樂或一個通知,這時你就必須請求音頻焦點。一旦得到焦點,它就可以自由的使用聲音輸出設備,同時它會不斷監聽焦點的更改。如果它被通知已經失去了音頻焦點,它會要麼立即殺死音頻或立即降低到一個安靜的水平(被稱為“ducking”——有一個標記,指示哪一個是適當的)當它再次接收焦點時,繼續不斷播放。

音頻焦點是自然的合作。應用程序都期望(強烈鼓勵)遵守音頻焦點指南,但規則並不是系統強制執行的。如果應用程序失去音頻焦點後想要播放嘈雜的音樂,在系統中沒有什麼會阻止他。然而,這樣可能會讓用戶有更糟糕的體驗,並可能卸載這運行不當的應用程序。

請求音頻焦點,您必須從AudioManager調用requestAudioFocus()方法,下面展示一個例子:

AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        int result = audioManager.requestAudioFocus(this
                , AudioManager.STREAM_MUSIC
                , AudioManager.AUDIOFOCUS_GAIN);
        if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // could not get audio focus.
             }

requestAudioFocus()的第一個參數是AudioManager.OnAudioFocusChangeListener,每當音頻焦點有變動的時候其onAudioFocusChange()方法被調用。您還應該在你的服務和活動上實現這個接口。
代碼如下:

class MyService extends Service implements AudioManager.OnAudioFocusChangeListener {
        // .... 
        public void onAudioFocusChange(int focusChange) { // Do something based on focus change... 
        }
    }
}

focusChange 參數告訴你音頻焦點是如何改變的,並且可以使用以下的值之一(他們都是在AudioManager中定義常量的):

AUDIOFOCUS_GAIN: 你已經得到了音頻焦點。 AUDIOFOCUS_LOSS: 你已經失去了音頻焦點很長時間了。
你必須停止所有的音頻播放。因為你應該不希望長時間等待焦點返回,這將是你盡可能清除你的資源的一個好地方。例如,你應該釋放MediaPlayer。 AUDIOFOCUS_LOSS_TRANSIENT:
你暫時失去了音頻焦點,但很快會重新得到焦點。你必須停止所有的音頻播放,但是你可以保持你的資源,因為你可能很快會重新獲得焦點。 AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
你暫時失去了音頻焦點,但你可以小聲地繼續播放音頻(低音量)而不是完全扼殺音頻。
下面是一個示例實現:
 public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // resume playback
                if (mMediaPlayer == null) {
                    initMediaPlayer();
                } else if (!mMediaPlayer.isPlaying()) {
                    mMediaPlayer.start();
                }
                mMediaPlayer.setVolume(1.0f, 1.0f);
                break;
            case AudioManager.AUDIOFOCUS_LOSS:
                // Lost focus for an unbounded amount of time: stop playback and release media player 
                if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
                mMediaPlayer.release();
                mMediaPlayer = null;
                break;
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // Lost focus for a short time, but we have to stop // playback. We don't
                break;
        }
    }
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved