MediaPlayer 總覽

Android 多媒體架構支援播放各種常見的媒體類型,可讓您輕鬆在應用程式中整合音訊、影片和圖片。您可以從儲存在應用程式資源 (原始資源) 的媒體檔案、檔案系統中的獨立檔案,或經由網路連線傳入的資料串流播放音訊或影片,全都使用 MediaPlayer API。

本文說明如何使用 MediaPlayer 編寫與使用者和系統互動的媒體播放應用程式,藉此獲得良好效能和良好的使用者體驗。或者,建議您使用 ExoPlayer,它是可自訂的開放原始碼程式庫,可支援 MediaPlayer 所沒有的高效能功能。

注意:您只能播放標準輸出裝置的音訊資料。也就是行動裝置喇叭或藍牙耳機。通話期間,您無法在對話音訊中播放音效檔案。

基本概念

下列類別可用來在 Android 架構中播放音效和影片:

MediaPlayer
此類別是播放音效和影片的主要 API。
AudioManager
這個類別會管理裝置上的音訊來源和音訊輸出。

資訊清單宣告

使用 MediaPlayer 開始開發應用程式之前,請確認您的資訊清單含有適當的宣告,以允許使用相關功能。

  • 網際網路權限 - 如果您使用 MediaPlayer 串流網路內容,應用程式必須要求網路存取權。
    <uses-permission android:name="android.permission.INTERNET" />
    
  • Wake Lock 權限:如果玩家應用程式需要避免螢幕變暗、處理處理器進入休眠狀態,或是使用 MediaPlayer.setScreenOnWhilePlaying()MediaPlayer.setWakeMode() 方法,則必須要求這項權限。
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    

使用 MediaPlayer

MediaPlayer 類別是媒體架構最重要的元件之一。只需最少的設定,這個類別的物件就能擷取、解碼及播放音訊和影片。這項功能支援多種不同的媒體來源,例如:

  • 本機資源
  • 內部 URI,例如您可以從內容解析器取得的 URI
  • 外部網址 (串流)

如需 Android 支援的媒體格式清單,請參閱「支援的媒體格式」頁面。

以下範例說明如何播放做為本機原始資源 (儲存在應用程式的 res/raw/ 目錄) 中的音訊:

Kotlin

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

Java

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

在此情況下,「原始」資源是系統不嘗試以任何特定方式剖析的檔案。不過,這項資源的內容不得為原始音訊。這個檔案應是正確編碼和格式的媒體檔案,且採用其中一種支援的格式。

以下說明如何從系統本機可用的 URI 播放 (例如您透過內容解析器取得):

Kotlin

val myUri: Uri = .... // initialize Uri here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, myUri)
    prepare()
    start()
}

Java

Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

透過 HTTP 串流從遠端網址播放看起來會像這樣:

Kotlin

val url = "http://........" // your URL here
val mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(url)
    prepare() // might take long! (for buffering, etc)
    start()
}

Java

String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

注意:如要傳遞用於串流線上媒體檔案的網址,該檔案必須能執行漸進式下載。

注意:使用 setDataSource() 時,您必須擷取或傳遞 IllegalArgumentExceptionIOException,因為您參照的檔案可能不存在。

非同步準備

原則上,使用 MediaPlayer 可能相當簡單。不過請注意,如要正確與一般 Android 應用程式整合,還需要執行其他操作。舉例來說,對 prepare() 的呼叫可能會需要很長的時間執行,因為涉及擷取及解碼媒體資料。因此,就像使用任何可能需要長時間執行的方法一樣,您切勿從應用程式的 UI 執行緒呼叫。這樣做會導致使用者介面停止運作,直到方法傳回為止,這樣會導致使用者體驗不佳,並可能引發 ANR (應用程式無回應) 錯誤。即使您預期資源能快速載入,提醒您,只要 UI 回應時間超過一秒的時間,就會造成明顯暫停,並讓使用者覺得應用程式速度變慢。

為避免 UI 執行緒停止運作,請產生另一個執行緒來準備 MediaPlayer,並在完成後通知主要執行緒。不過,雖然您可以自行編寫執行緒邏輯,但這種模式在使用 MediaPlayer 時常見,也就是透過 prepareAsync() 方法提供便利的方法來完成這項工作。這個方法會開始在背景準備媒體並立即傳回。媒體準備完成後,系統會呼叫透過 setOnPreparedListener() 設定的 MediaPlayer.OnPreparedListener onPrepared() 方法。

管理狀態

請留意 MediaPlayer 的另一個面向是採用狀態。也就是說,MediaPlayer 具有您在編寫程式碼時必須注意的內部狀態,因為只有在玩家處於特定狀態時,某些操作才有效。如果您在錯誤狀態下執行作業,系統可能會擲回例外狀況或引發其他不理想的行為。

MediaPlayer 類別的說明文件會顯示完整的狀態圖表,清楚說明哪些方法可將 MediaPlayer 從一個狀態移至其他狀態。例如,當您建立新的 MediaPlayer 時,它處於「閒置」狀態。此時,您應呼叫 setDataSource() 將其初始化,使其進入「初始化」狀態。之後,您必須使用 prepare()prepareAsync() 方法進行準備作業。MediaPlayer 準備完成後,會進入「Prepared」狀態,這表示您可以呼叫 start() 來播放媒體。如圖所示,您可以呼叫 start()pause()seekTo() 等方法,在「Started」、「Pause」 和「PlaybackCompleted」 狀態之間移動。但是當您呼叫 stop() 時,請注意要等到再次準備 MediaPlayer 之後,才能再次呼叫 start()

編寫與 MediaPlayer 物件互動的程式碼時,請一律記住狀態圖表,因為從錯誤狀態呼叫方法是發生錯誤的常見原因。

推出 MediaPlayer

MediaPlayer 可取用重要的系統資源。因此,您應一律採取額外預防措施,確保活動時間不會超過必要的 MediaPlayer。完成後,建議您一律呼叫 release(),以確保分配給其的所有系統資源皆正確發布。舉例來說,如果您使用 MediaPlayer,且活動收到對 onStop() 的呼叫,就必須釋放 MediaPlayer,因為在活動未與使用者互動時,保留 MediaPlayer 並不合理 (除非您正在背景播放媒體,詳情請見下一節)。當然,當活動繼續或重新啟動時,您必須建立新的 MediaPlayer 並再次做好準備,才能繼續播放。

以下說明如何釋出然後空值 MediaPlayer

Kotlin

mediaPlayer?.release()
mediaPlayer = null

Java

mediaPlayer.release();
mediaPlayer = null;

舉例來說,請思考在活動停止時,如果您忘記釋出 MediaPlayer 時可能發生的問題,但在活動再次啟動時就新建一個。如您所知,當使用者變更螢幕方向 (或以其他方式變更裝置設定) 時,系統會重新啟動活動 (預設) 處理此情況,因此您可以在使用者在直向和橫向之間來迴旋轉裝置時,快速消耗所有系統資源,因為每次螢幕方向改變時,您都會建立一個不會釋出的新 MediaPlayer。(如要進一步瞭解執行階段重新啟動,請參閱「處理執行階段變更」)。

您可能會好奇,就算使用者離開活動,您仍想繼續播放「背景媒體」,這和內建音樂應用程式的行為模式大致相同。在這種情況下,您需要由 Service 控管的 MediaPlayer,詳情請見下一節

在服務中使用 MediaPlayer

如要讓媒體即使應用程式並非處於螢幕狀態,也想在背景中播放 (也就是您希望媒體在使用者與其他應用程式互動時持續播放),則必須從服務啟動服務並控制 MediaPlayer 例項。您必須將 MediaPlayer 嵌入 MediaBrowserServiceCompat 服務,並使該播放器與其他活動中的 MediaBrowserCompat 互動。

設定此用戶端/伺服器時,請務必謹慎小心。我們預期在背景服務中執行的玩家如何與系統的其餘部分互動。如果您的應用程式不符合這些期望,使用者可能會享有不愉快的體驗。詳情請參閱建構音訊應用程式

本節說明在服務內實作 MediaPlayer 的特殊指示。

以非同步方式執行

首先,如同 ActivityService 中的所有工作都會預設在單一執行緒中完成。事實上,如果從相同應用程式執行活動和服務,預設會使用相同的執行緒 (「主執行緒」)。因此,服務必須快速處理傳入的意圖,不會在回應意圖時執行冗長的運算。如果預期需要進行繁重的工作或封鎖呼叫,您必須以非同步的方式執行這些工作:您自行實作的另一個執行緒,或使用架構的許多功能進行非同步處理。

舉例來說,從主執行緒使用 MediaPlayer 時,應呼叫 prepareAsync() 而非 prepare(),並實作 MediaPlayer.OnPreparedListener,以便在準備完成時收到通知,且可以開始玩。例如:

Kotlin

private const val ACTION_PLAY: String = "com.example.action.PLAY"

class MyService: Service(), MediaPlayer.OnPreparedListener {

    private var mMediaPlayer: MediaPlayer? = null

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        ...
        val action: String = intent.action
        when(action) {
            ACTION_PLAY -> {
                mMediaPlayer = ... // initialize it here
                mMediaPlayer?.apply {
                    setOnPreparedListener(this@MyService)
                    prepareAsync() // prepare async to not block main thread
                }

            }
        }
        ...
    }

    /** Called when MediaPlayer is ready */
    override fun onPrepared(mediaPlayer: MediaPlayer) {
        mediaPlayer.start()
    }
}

Java

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

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

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

處理非同步錯誤

在同步處理作業中,錯誤通常會因例外狀況或錯誤代碼而發出信號,但在使用非同步資源時,請務必確保應用程式可正確收到錯誤通知。以 MediaPlayer 為例,您可以實作 MediaPlayer.OnErrorListener 並在 MediaPlayer 執行個體中設定,即可完成此操作:

Kotlin

class MyService : Service(), MediaPlayer.OnErrorListener {

    private var mediaPlayer: MediaPlayer? = null

    fun initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer?.setOnErrorListener(this)
    }

    override fun onError(mp: MediaPlayer, what: Int, extra: Int): Boolean {
        // ... react appropriately ...
        // The MediaPlayer has moved to the Error state, must be reset!
    }
}

Java

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

    public void initMediaPlayer() {
        // ...initialize the MediaPlayer here...
        mediaPlayer.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!
    }
}

請注意,發生錯誤時,MediaPlayer 會移至「Error」狀態 (請參閱 MediaPlayer 類別的說明文件,瞭解完整狀態圖表)。您必須先重設該類別,才能再次使用該狀態。

使用 Wake Lock

設計在背景播放媒體的應用程式時,裝置可能會在服務執行期間進入休眠狀態。由於 Android 系統會在裝置休眠時嘗試節省電池電力,因此會嘗試關閉所有非必要的手機功能,包括 CPU 和 Wi-Fi 硬體。但是,如果您的服務正在播放或串流音樂,您必須防止系統幹擾播放。

為了確保您的服務在上述情況下繼續運作,您必須使用「Wake Lock」。Wake Lock 是一種向系統告知系統應用程式正在使用的部分功能,即使手機處於閒置狀態,這些功能也應保持可用。

注意:由於 Wake Lock 會大幅縮短裝置的電池續航力,因此請盡量不要使用 Wake Lock,並只在必要期間保留。

為確保 CPU 在 MediaPlayer 播放時仍能繼續運作,請在初始化 MediaPlayer 時呼叫 setWakeMode() 方法。完成後,MediaPlayer 會在播放時保留指定的鎖定,並在暫停或停止時解除鎖定:

Kotlin

mediaPlayer = MediaPlayer().apply {
    // ... other initialization here ...
    setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
}

Java

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

但是,本範例取得的 Wake Lock 僅可保證 CPU 仍然處於喚醒狀態。如果您是透過網路串流媒體,且使用的是 Wi-Fi,則可能也需要保留 WifiLock,而必須手動取得及釋出該空間。因此,當您開始使用遠端網址準備 MediaPlayer 時,請建立並取得 Wi-Fi 鎖定。例如:

Kotlin

val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiLock: WifiManager.WifiLock =
    wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")

wifiLock.acquire()

Java

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

wifiLock.acquire();

當您暫停或停止媒體,或不再需要網路時,請解除鎖定:

Kotlin

wifiLock.release()

Java

wifiLock.release();

執行清除作業

如前文所述,MediaPlayer 物件可能會耗用大量系統資源,因此建議您只將物件保留下來,並在使用完畢後呼叫 release()。請務必明確呼叫這個清除方法,而不要依賴系統垃圾收集,因為垃圾收集器可能需要一點時間才能收回 MediaPlayer,因為只有記憶體需求會敏感,不會影響其他媒體相關資源。因此,在您使用服務時,建議您一律覆寫 onDestroy() 方法,以確保您推出的 MediaPlayer 是正確的:

Kotlin

class MyService : Service() {

    private var mediaPlayer: MediaPlayer? = null
    // ...

    override fun onDestroy() {
        super.onDestroy()
        mediaPlayer?.release()
    }
}

Java

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

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

您應一律尋找其他釋出 MediaPlayer 的機會,除非是在關閉時將其釋出。舉例來說,假使您預期會長時間無法播放媒體 (例如失去音訊焦點後),請絕對釋出現有的 MediaPlayer,並於稍後重新建立。另一方面,如果您只想要在極短的時間內停止播放,則應該在 MediaPlayer 上保留,避免再次建立和準備的負擔。

數位版權管理 (DRM)

從 Android 8.0 (API 級別 26) 開始,MediaPlayer 包含支援播放受 DRM 保護內容的 API。這類 API 與 MediaDrm 提供的低階 API 類似,但會在較高層級運作,而且不會曝光基礎擷取器、drm 和加密物件。

雖然 MediaPlayer DRM API 不提供 MediaDrm 的完整功能,但可支援最常見的用途。目前的實作方式可處理下列內容類型:

  • 受 Widevine 保護的本機媒體檔案
  • 受 Widevine 保護的遠端/串流媒體檔案

下列程式碼片段示範如何在簡單的同步實作中使用新的 DRM MediaPlayer 方法。

如要管理受 DRM 控制的媒體,您必須加入新方法與 MediaPlayer 呼叫的一般流程,如下所示:

Kotlin

mediaPlayer?.apply {
    setDataSource()
    setOnDrmConfigHelper() // optional, for custom configuration
    prepare()
    drmInfo?.also {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }

    // MediaPlayer is now ready to use
    start()
    // ...play/pause/resume...
    stop()
    releaseDrm()
}

Java

setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();

首先,請初始化 MediaPlayer 物件並使用 setDataSource() 設定其來源。接著,如要使用 DRM,請執行下列步驟:

  1. 如果您希望應用程式執行自訂設定,請定義 OnDrmConfigHelper 介面,並使用 setOnDrmConfigHelper() 將其附加至玩家。
  2. 呼叫 prepare()
  3. 呼叫 getDrmInfo()。如果來源具有 DRM 內容,這個方法會傳回非空值的 MediaPlayer.DrmInfo 值。

如果 MediaPlayer.DrmInfo 存在:

  1. 查看地圖的可用 UUID,從中選擇一個。
  2. 呼叫 prepareDrm() 來準備目前來源的 DRM 設定。
    • 如果您已建立並註冊 OnDrmConfigHelper 回呼,系統會在 prepareDrm() 執行期間呼叫該回呼。這樣可讓您在開啟 DRM 工作階段之前,自訂 DRM 屬性。系統會在名為 prepareDrm() 的執行緒中同步呼叫回呼。如要存取 DRM 屬性,請呼叫 getDrmPropertyString()setDrmPropertyString()。避免執行冗長的作業。
    • 如果裝置尚未佈建完畢,prepareDrm() 也會存取佈建伺服器來佈建裝置。這項作業所需的時間會因網路連線而異。
  3. 如要取得不透明的金鑰要求位元組陣列,並傳送至授權伺服器,請呼叫 getKeyRequest()
  4. 如要通知 DRM 引擎來自授權伺服器收到的金鑰回應,請呼叫 provideKeyResponse()。結果會因金鑰要求類型而有所不同:
    • 如果是離線金鑰要求的回應,結果就會是鍵集 ID。您可以搭配 restoreKeys() 使用這個金鑰組 ID,將金鑰還原至新工作階段。
    • 如果回應是針對串流或發布要求,則結果為空值。

以非同步方式執行 prepareDrm()

根據預設,prepareDrm() 會同步執行,直到準備完成為止。不過,在新裝置首次進行 DRM 準備時,可能也需要由 prepareDrm() 內部處理,而由於涉及的網路作業,可能需要一段時間才能完成。您可以定義及設定 MediaPlayer.OnDrmPreparedListener,避免在 prepareDrm() 上遭到封鎖。

當您設定 OnDrmPreparedListener 時,prepareDrm() 會在背景執行佈建 (如有需要) 和準備作業。佈建和準備完成後,系統就會呼叫事件監聽器。請勿假設呼叫序列或執行事件監聽器的執行緒 (除非使用處理常式執行緒註冊事件監聽器)。可以在 prepareDrm() 回傳之前或之後呼叫事件監聽器。

以非同步方式設定數位版權管理

您可以透過建立並註冊 MediaPlayer.OnDrmInfoListener 進行 DRM 準備,並透過 MediaPlayer.OnDrmPreparedListener 啟動玩家,以非同步方式初始化 DRM。它們會與 prepareAsync() 搭配運作,如下所示:

Kotlin

setOnPreparedListener()
setOnDrmInfoListener()
setDataSource()
prepareAsync()
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
override fun onDrmInfo(mediaPlayer: MediaPlayer, drmInfo: MediaPlayer.DrmInfo) {
    mediaPlayer.apply {
        prepareDrm()
        getKeyRequest()
        provideKeyResponse()
    }
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
override fun onPrepared(mediaPlayer: MediaPlayer) {
    mediaPlayer.start()
}

Java

setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
  prepareDrm();
  getKeyRequest();
  provideKeyResponse();
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {

start();
}

處理加密媒體

從 Android 8.0 (API 級別 26) 開始,MediaPlayer 也可以解密主要串流類型 H.264 和 AAC 的通用加密配置 (CENC) 和 HTTP 即時串流取樣層級加密媒體 (Method=SAMPLE-AES)。先前支援全區隔加密媒體 (METHOD=AES-128)。

從 ContentResolver 擷取媒體

媒體播放器應用程式的另一項實用功能是擷取使用者在裝置上擁有的音樂的功能。方法是透過查詢 ContentResolver 來取得外部媒體:

Kotlin

val resolver: ContentResolver = contentResolver
val uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor: Cursor? = resolver.query(uri, null, null, null, null)
when {
    cursor == null -> {
        // query failed, handle error.
    }
    !cursor.moveToFirst() -> {
        // no media on the device
    }
    else -> {
        val titleColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE)
        val idColumn: Int = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID)
        do {
            val thisId = cursor.getLong(idColumn)
            val thisTitle = cursor.getString(titleColumn)
            // ...process entry...
        } while (cursor.moveToNext())
    }
}
cursor?.close()

Java

ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
    // query failed, handle error.
} else if (!cursor.moveToFirst()) {
    // no media on the device
} else {
    int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
    int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
    do {
       long thisId = cursor.getLong(idColumn);
       String thisTitle = cursor.getString(titleColumn);
       // ...process entry...
    } while (cursor.moveToNext());
}

如要搭配 MediaPlayer 使用,可以執行此操作:

Kotlin

val id: Long = /* retrieve it from somewhere */
val contentUri: Uri =
    ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id )

mediaPlayer = MediaPlayer().apply {
    setAudioAttributes(
        AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .build()
    )
    setDataSource(applicationContext, contentUri)
}

// ...prepare and start...

Java

long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
        android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioAttributes(
    new AudioAttributes.Builder()
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .build()
);
mediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...

瞭解詳情

這些頁面涵蓋與錄製、儲存及播放音訊和視訊相關的主題。