媒體按鈕是 Android 裝置和其他週邊裝置提供的硬體按鈕,例如藍牙耳機上的暫停/播放按鈕。當使用者按下媒體按鈕時,Android 會產生 KeyEvent
,其中包含可識別按鈕的按鍵程式碼。媒體按鈕 KeyEvents 的鍵碼是開頭為 KEYCODE_MEDIA
的常數 (例如 KEYCODE_MEDIA_PLAY
)。
應用程式應能以下列優先順序處理三種情況:媒體按鈕事件:
- 當畫面上顯示應用程式的 UI 活動時
- 隱藏 UI 活動,且應用程式的媒體工作階段處於啟用狀態時
- 隱藏 UI 活動,且應用程式的媒體工作階段閒置且需要重新啟動時
處理前景活動中的媒體按鈕
前景活動會在 onKeyDown()
方法中收到媒體按鈕鍵事件。視執行中的 Android 版本而定,系統可透過兩種方式將事件轉送至媒體控制器:
- 如果使用的是 Android 5.0 (API 級別 21) 以上版本,請呼叫
FLAG_HANDLES_MEDIA_BUTTONS
MediaBrowserCompat.ConnectionCallback.onConnected
。系統會自動呼叫媒體控制器的dispatchMediaButtonEvent()
,將按鍵程式碼轉譯為媒體工作階段回呼。 - 在 Android 5.0 (API 級別 21) 之前,您必須修改
onKeyDown()
以便自行處理事件。(詳情請參閱「處理使用中的媒體工作階段中媒體按鈕」)。下列程式碼片段說明如何攔截鍵程式碼並呼叫 dispatchMediaButtonEvent()。請務必傳回true
表示事件已處理:Kotlin
fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return super.onKeyDown(keyCode, event) } when (keyCode) { KeyEvent.KEYCODE_MEDIA_PLAY -> { yourMediaController.dispatchMediaButtonEvent(event) return true } } return super.onKeyDown(keyCode, event) }
Java
@Override boolean onKeyDown(int keyCode, KeyEvent event) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return super.onKeyDown(keyCode, event); } switch (keyCode) { case KeyEvent.KEYCODE_MEDIA_PLAY: yourMediaController.dispatchMediaButtonEvent(event); return true; } return super.onKeyDown(keyCode, event); }
尋找媒體工作階段
如果前景活動未處理事件,Android 會嘗試尋找可處理該事件的媒體工作階段。再次提醒您,視執行中的 Android 版本而定,您可以透過兩種方式搜尋媒體工作階段:
如果執行的是 Android 8.0 (API 級別 26) 以上版本,系統會嘗試使用 MediaSession 在本機播放音訊,來找出最後一個應用程式。如果工作階段仍在進行中,Android 會直接將事件傳送至該工作階段。否則,如果工作階段不在使用中,且具有媒體按鈕接收器,Android 會傳送事件給接收器,接收端不僅會重新啟動工作階段,也能接收事件。(詳情請參閱使用媒體按鈕重新啟動已停用的媒體工作階段)。 如果工作階段沒有媒體按鈕接收器,系統會捨棄媒體按鈕事件,也不會有任何反應。相關邏輯如以下圖表所示:
在 Android 8.0 (API 級別 26) 以下版本中,系統會嘗試將事件傳送至進行中的媒體工作階段。如有多個執行中的媒體工作階段,Android 會嘗試選擇準備播放 (緩衝/連線)、播放或已暫停的媒體工作階段,而不是已停止的媒體工作階段。(詳情請參閱「處理使用中的媒體工作階段中的媒體按鈕」)。如果沒有使用中的工作階段,Android 會嘗試將事件傳送至最近使用的工作階段。(詳情請參閱使用媒體按鈕重新啟動已停用的媒體工作階段)。 相關邏輯如下圖所示:
在播放中的媒體工作階段中處理媒體按鈕
在 Android 5.0 (API 級別 21) 以上版本中,Android 會透過呼叫 onMediaButtonEvent()
自動將媒體按鈕事件分派到使用中的媒體工作階段。根據預設,此回呼會將 KeyEvent 轉譯為與鍵程式碼相符的適當媒體工作階段回呼方法。
在 Android 5.0 (API 級別 21) 之前,Android 會透過 ACTION_MEDIA_BUTTON
動作播送意圖,以處理媒體按鈕事件。您的應用程式必須註冊 BroadcastReceiver,以攔截這些意圖。MediaButtonReceiver
類別是專為這個用途而設計。這是 Android media-compat 程式庫中的便利類別,可處理 ACTION_MEDIA_BUTTON
並將傳入意圖轉譯為適當的 MediaSessionCompat.Callback
方法呼叫。
MediaButtonReceiver
是短期的 BroadcastReceiver。它會將傳入意圖轉送到管理媒體工作階段的服務。如要在 Android 5.0 以下版本的系統中使用媒體按鈕,您必須在資訊清單中加入 MediaButtonReceiver
,並使用 MEDIA_BUTTON
意圖篩選器。:
<receiver android:name="android.support.v4.media.session.MediaButtonReceiver" >
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
BroadcastReceiver
會將意圖轉送至您的服務。如要剖析意圖並產生媒體工作階段的回呼,請在服務的 onStartCommand()
中加入 MediaButtonReceiver.handleIntent()
方法。這會將按鍵程式碼轉譯為適當的工作階段回呼方法。
Kotlin
private val mediaSessionCompat: MediaSessionCompat = ... override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { MediaButtonReceiver.handleIntent(mediaSessionCompat, intent) return super.onStartCommand(intent, flags, startId) }
Java
private MediaSessionCompat mediaSessionCompat = ...; public int onStartCommand(Intent intent, int flags, int startId) { MediaButtonReceiver.handleIntent(mediaSessionCompat, intent); return super.onStartCommand(intent, flags, startId); }
使用媒體按鈕重新啟動已停用的媒體工作階段
如果 Android 可識別上次使用中的媒體工作階段,就會嘗試重新啟動工作階段,方法是將 ACTION_MEDIA_BUTTON
意圖傳送至已註冊資訊清單的元件 (例如服務或 BroadcastReceiver
)。
如此一來,應用程式就能在 UI 看不到時重新啟動播放 (大多數音訊應用程式都是這樣)。
當您使用 MediaSessionCompat
時,系統會自動啟用這項行為。如果您使用 Android 架構的 MediaSession
或支援資料庫 24.0.0 至 25.1.1,則必須呼叫 setMediaButtonReceiver
,才能讓媒體按鈕重新啟動非使用中的媒體工作階段。
您可以在 Android 5.0 (API 級別 21) 以上版本中,設定空值媒體按鈕接收器來停用這項行為:
Kotlin
// Create a MediaSessionCompat mediaSession = MediaSessionCompat(context, LOG_TAG) mediaSession.setMediaButtonReceiver(null)
Java
// Create a MediaSessionCompat mediaSession = new MediaSessionCompat(context, LOG_TAG); mediaSession.setMediaButtonReceiver(null);
自訂媒體按鈕處理常式
onMediaButtonEvent()
的預設行為會擷取按鍵程式碼,並使用媒體工作階段的目前狀態和支援的動作清單,判斷要呼叫的方法。舉例來說,KEYCODE_MEDIA_PLAY
會叫用 onPlay()
。
如要為所有應用程式提供一致的媒體按鈕體驗,您應使用預設行為,且僅針對特定用途。如果媒體按鈕需要自訂處理,請覆寫回呼的 onMediaButtonEvent()
方法,使用 intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
擷取 KeyEvent
,自行處理事件,然後傳回 true
。
摘要
如要妥善處理所有 Android 版本中的媒體按鈕事件,您必須在建立媒體工作階段時指定 FLAG_HANDLES_MEDIA_BUTTONS
。
此外,視您想支援的 Android 版本而定,您還必須符合下列要求:
在 Android 5.0 以上版本中執行時:
- 從媒體控制器
onConnected()
回呼呼叫MediaControllerCompat.setMediaController()
- 如要允許媒體按鈕重新啟動閒置的工作階段,請呼叫
setMediaButtonReceiver()
並傳遞PendingIntent
,以動態建立MediaButtonReceiver
在 Android 5.0 以下版本的系統中執行時:
- 覆寫活動的
onKeyDown()
以處理媒體按鈕 - 將
MediaButtonReceiver
新增至應用程式的資訊清單,以靜態方式建立