學習Android MediaPlayer

Android Media Playback

原文html

The Android multimedia framework includes support for playing variety of common media types, so that you can easily integrate audio, video and images into your applications. You can play audio or video from media files stored in your application's resources (raw resources), from standalone files in the filesystem, or from a data stream arriving over a network connection, all using MediaPlayer APIs.react

Android多媒體框架包含對多種常見媒體類型的支持,因此你能夠容易的在本身的應用中集成音頻,視頻和圖片。你能夠播放應用內的資源文件,或文件系統中獨立的文件,也能夠經過網絡數據流來播放,這些功能都使用MediaPlayer APIs實現。android

Note: You can play back the audio data only to the standard output device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound files in the conversation audio during a call.網絡

注意:你只能經過標準輸出設備播放音頻。當前,這包括移動設備的揚聲器或者藍牙耳機。你不能在用戶打電話時播放音頻。app


The Basics

在Android Framework中,下面兩個類用來播放聲音和視頻:框架

MediaPlayer  此類是播放聲音和視頻的主要API。less

AudioManager 此類管理音頻資源和音頻在設備上的輸出。異步


Manifest Declarations

Before starting development on your application using MediaPlayer, make sure your manifest has the appropriate declarations to allow use of related features.async

在開始使用MediaPlayer以前,確保你的清單文件中聲明瞭與相關特性有關的權限:ide

  • Internet Permission - 若是你使用MediaPlayer播放網絡內容,應用須要網絡訪問權限。

  • Wake Lock Permission - 若是你的應用須要保持屏幕不變暗或者處理器不休眠,或者使用MediaPlayer.setScreenOnWhilePlaying()MediaPlayer.setWakeMode()方法,你須要請求如下權限:


Using MediaPlayer

One of the most important components of the media framework is the MediaPlayer class. An object of this class can fetch, decode, and play both audio and video with minimal setup. It supports several different media sources such as:

媒體框架中最重要的組件之一是MediaPlayer類。該類的對象能夠用最少的步驟獲取,解碼和播放音頻和視頻。它支持多種媒體源,例如:

  • 本地資源

  • 內部URI,例如用於Content Resolver的URI

  • 外部URL(流)

Android支持的媒體類型,見此文檔:Android Supported Media Formats

下面的例子顯示瞭如何播放本地raw資源(應用res/raw目錄下)

MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you

本例中,一個「raw」資源是一個系統不會嘗試用特殊方式去解析的文件。然而,此資源的內容不能是原始音頻。它應該是根據支持的格式恰當編碼和格式化的文件。

下面的例子顯示如何經過本地URI播放:

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

下面的例子顯示如何經過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();

注意:若是你經過URL播放在線媒體文件,該文件必須能夠漸近下載(Progressive download)。

警告:在使用setDataSource()時,必須捕獲或者傳遞IllegalArgumentException和IOException,由於你引用的文件可能不存在。


Asynchronous Preparation

Using MediaPlayer can be straightforward in principle. However, it's important to keep in mind that a few more things are necessary to integrate it correctly with a typical Android application. For example, the call to prepare() can take a long time to execute, because it might involve fetching and decoding media data. So, as is the case with any method that may take long to execute, you should never call it from your application's UI thread.

原則上,使用MediaPlayer是簡單直接的。然而集成在Android應用中時,有幾點須要注意。例如,調用prepare()可能會花費很長時間,由於此方法涉及到媒體數據的獲取和解碼,所以不該該在UI線程調用。

To avoid hanging your UI thread, spawn another thread to prepare the MediaPlayer and notify the main thread when done. However, while you could write the threading logic yourself, this pattern is so common when using MediaPlayer that the framework supplies a convenient way to accomplish this task by using the prepareAsync() method. This method starts preparing the media in the background and returns immediately. When the media is done preparing, the onPrepared() method of the MediaPlayer.OnPreparedListener, configured through setOnPreparedListener() is called.

要避免掛起UI線程,使用另外一個線程來準備MediaPlaer,在完成時通知主線程。然而,雖然你能夠本身寫線程邏輯,此框架提供了prepareAsync方法來簡化這一工做。此方法馬上返回,在後臺執行準備工做,完成後經過回調通知調用者。


Managing State

Another aspect of a MediaPlayer that you should keep in mind is that it's state-based. That is, the MediaPlayer has an internal state that you must always be aware of when writing your code, because certain operations are only valid when then player is in specific states. If you perform an operation while in the wrong state, the system may throw an exception or cause other undesireable behaviors.

關於MediaPlayer的另外一個關注點是它是基於狀態的。也就是說,你寫代碼時必須始終意識到MediaPlayer有內部狀態,由於某些操做只在player處於特定狀態時纔有效。若是你在錯誤的狀態下執行操做,系統可能會拋出異常或者引起其餘不須要的行爲。

MediaPlayer類文檔中展現了MediaPlayer的完整狀態圖。

Releasing the MediaPlayer

A MediaPlayer can consume valuable system resources. Therefore, you should always take extra precautions to make sure you are not hanging on to a MediaPlayer instance longer than necessary. When you are done with it, you should always call release() to make sure any system resources allocated to it are properly released. For example, if you are using a MediaPlayer and your activity receives a call to onStop(), you must release the MediaPlayer, because it makes little sense to hold on to it while your activity is not interacting with the user (unless you are playing media in the background, which is discussed in the next section). When your activity is resumed or restarted, of course, you need to create a new MediaPlayer and prepare it again before resuming playback.

MediaPlayer會消費寶貴的系統資源,因此必須注意不要保持MediaPlayer實例超過須要的時間。當你用完時,應該調用release()方法來確保分配給它的系統資源被合適的釋放了。例如,若是你正在使用MediaPlayer,而Activity收到了onStop()回調,則必須釋放MediaPlayer(除非你在後臺播放)。當Activity 從新得到焦點或者從新開始時,你須要建立一個新的MediaPlayer實例,並在恢復播放前準備它。

將MediaPlayer釋放並制空:

mediaPlayer.release();
mediaPlayer = null;

As an example, consider the problems that could happen if you forgot to release the MediaPlayer when your activity is stopped, but create a new one when the activity starts again. As you may know, when the user changes the screen orientation (or changes the device configuration in another way), the system handles that by restarting the activity (by default), so you might quickly consume all of the system resources as the user rotates the device back and forth between portrait and landscape, because at each orientation change, you create a new MediaPlayer that you never release.

例如,若是你在Activity stop時忘了釋放MediaPlayer,但在Activity create時建立了新的實例,那在用戶反覆旋轉屏幕時,可能會很快就耗盡全部的系統資源,由於每次方向改變,你都建立了新的MediaPlayer對象,但歷來沒有釋放。

Using a Service with MediaPlayer

If you want your media to play in the background even when your application is not onscreen—that is, you want it to continue playing while the user is interacting with other applications—then you must start a Service and control the MediaPlayer instance from there. You should be careful about this setup, because the user and the system have expectations about how an application running a background service should interact with the rest of the system. If your application does not fulfil those expectations, the user may have a bad experience. This section describes the main issues that you should be aware of and offers suggestions about how to approach them.

若是想讓媒體文件在後臺播放,須要啓動一個Service,讓後再Service中控制MediaPlayer實例。用戶和系統對運行後臺服務的應用如何同系統其它部分交互有一些指望,若是你的應用沒法知足這些指望,用戶可能會有糟糕的體驗。本節描述了你須要注意的主要事項以及如何解決的建議。

Running asynchronously

First of all, like an Activity, all work in a Service is done in a single thread by default—in fact, if you're running an activity and a service from the same application, they use the same thread (the "main thread") by default. Therefore, services need to process incoming intents quickly and never perform lengthy computations when responding to them. If any heavy work or blocking calls are expected, you must do those tasks asynchronously: either from another thread you implement yourself, or using the framework's many facilities for asynchronous processing.

首先,同Activity同樣,默認狀況下Service中的全部工做也是在主線程中完成的。所以,服務須要快速的處理傳入的intent,在響應意圖是不能執行長時間操做。若是有大量的工做或者阻塞式的調用,就須要異步的完成。

For instance, when using a MediaPlayer from your main thread, you should call prepareAsync() rather than prepare(), and implement a MediaPlayer.OnPreparedListener in order to be notified when the preparation is complete and you can start playing. For example:

例如,在主線程中使用MediaPlayer時,你應該調用prepareAsync()而不是prepare(),實現MediaPlayer.OnPreparedListener接口來通知準備已經完成。代碼以下:

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
        }
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
    }
}

Handling asynchronous errors

On synchronous operations, errors would normally be signaled with an exception or an error code, but whenever you use asynchronous resources, you should make sure your application is notified of errors appropriately. In the case of a MediaPlayer, you can accomplish this by implementing a MediaPlayer.OnErrorListener and setting it in your MediaPlayer instance:

在同步的調用中,錯誤一般是經過異常或者錯誤碼錶示,但在異步調用時,你應該確保正確的接收到錯誤。對於MediaPlayer來講,你能夠實現MediaPlayer.OnErrorListener接口,設置給MediaPlayer實例:

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!
    }
}

It's important to remember that when an error occurs, the MediaPlayer moves to the Error state (see the documentation for the MediaPlayer class for the full state diagram) and you must reset it before you can use it again.

要記住當錯誤發生時,MediaPlayer轉變爲Error狀態,在你使用它以前須要重置(reset)它。

相關文章
相關標籤/搜索