回應媒體按鈕事件

媒體按鈕是 Android 裝置和其他週邊裝置的硬體按鈕,例如藍牙耳機上的暫停/播放按鈕。當使用者按下媒體按鈕時,Android 會產生 KeyEvent,其中包含用於識別按鈕的按鍵碼。媒體按鈕 KeyEvents 的按鍵碼是開頭為 KEYCODE_MEDIA (例如 KEYCODE_MEDIA_PLAY) 的常數。

應用程式應該要能依照下列順序,在三種情況下處理媒體按鈕事件 優先順序:

  • 當應用程式的 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,並將傳入的 Intent 轉譯為 適當的 MediaSessionCompat.Callback 方法呼叫

MediaButtonReceiver 是短期的 BroadcastReceiver。它會轉寄傳入的 呼叫及管理媒體工作階段的服務。如要使用 如果是 Android 5.0 以下版本的系統,請務必加入媒體按鈕, 透過 MEDIA_BUTTON 意圖篩選器,取得資訊清單中的 MediaButtonReceiver

<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() 方法後,使用 KeyEvent 擷取 intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT), 自行處理事件,並傳回 true

摘要

如要正確處理所有 Android 版本的媒體按鈕事件,您必須 指定FLAG_HANDLES_MEDIA_BUTTONS 即可啟用個別媒體功能

此外,視您要支援的 Android 版本而定, 您還必須符合下列規定:

在 Android 5.0 以上版本中執行時:

  • 從媒體控制器 onConnected() 回呼呼叫 MediaControllerCompat.setMediaController()
  • 如要允許媒體按鈕重新啟動閒置的工作階段,請呼叫以下程式碼,以動態方式建立 MediaButtonReceiversetMediaButtonReceiver() 並向其傳遞 PendingIntent

在 Android 5.0 以下版本的系統中執行時:

  • 覆寫活動的 onKeyDown() 來處理媒體按鈕
  • 以靜態方式建立 MediaButtonReceiver,方法是將其新增至應用程式資訊清單