媒體按鈕是Android設(shè)備和其他外圍設(shè)備上的硬件按鈕,例如藍(lán)牙耳機(jī)上的暫停/播放按鈕。當(dāng)用戶按下媒體按鈕時(shí),Android會(huì)生成一個(gè)KeyEvent,其中包含一個(gè)識(shí)別按鈕的密鑰代碼。media button KeyEvents的關(guān)鍵代碼是以KEYCODE_MEDIA開頭的常量(例如,KEYCODE_MEDIA_PLAY)。
應(yīng)用程序應(yīng)該能夠在三種情況下處理媒體按鈕事件,按優(yōu)先順序排列:
- 當(dāng)應(yīng)用程序的UI活動(dòng)可見時(shí)
- 當(dāng)UI活動(dòng)隱藏且應(yīng)用程序的媒體會(huì)話處于活動(dòng)狀態(tài)時(shí)
- 當(dāng)UI活動(dòng)被隱藏,應(yīng)用程序的媒體會(huì)話不活躍,需要重新啟動(dòng)時(shí)
處理前臺(tái)活動(dòng)中的媒體按鈕
前臺(tái)活動(dòng)在其onKeyDown()方法中接收media button key事件。根據(jù)Android的運(yùn)行版本,系統(tǒng)有兩種方式將事件路由到媒體控制器:
- 如果你運(yùn)行的是安卓5.0 (API level 21)或更高版本,調(diào)用FLAG_HANDLES_MEDIA_BUTTONS
MediaBrowserCompat.ConnectionCallback.onConnected。這將自動(dòng)調(diào)用您的媒體控制器的dispatchMediaButtonEvent(),它將關(guān)鍵代碼轉(zhuǎn)換為媒體會(huì)話回調(diào)。 - 在Android 5.0 (API level 21)之前,您需要修改onKeyDown()來自己處理事件。(有關(guān)詳細(xì)信息,請(qǐng)參閱Handling media buttons in an active media session。)下面的代碼片段展示了如何截獲關(guān)鍵代碼并調(diào)用dispatchMediaButtonEvent()。確保返回true以指示事件已處理:
@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);
}
尋找媒體會(huì)話
如果前臺(tái)活動(dòng)不處理事件,Android將嘗試找到一個(gè)可以處理它的媒體會(huì)話。同樣,根據(jù)Android的運(yùn)行版本,有兩種方法可以搜索媒體會(huì)話:
-
如果你運(yùn)行的是安卓8.0 (API level 26)或更高版本,系統(tǒng)會(huì)試圖找到最后一個(gè)帶有本地播放音頻的MediaSession的應(yīng)用。如果會(huì)話仍然是活動(dòng)的,Android會(huì)將事件直接發(fā)送到它。否則,如果會(huì)話不是活動(dòng)的,并且它有一個(gè)mediabutton接收器,Android將事件發(fā)送給接收器,接收器將重新啟動(dòng)會(huì)話,因此它可以接收事件。(有關(guān)詳細(xì)信息,請(qǐng)參閱Using media buttons to restart an inactive media session 。)如果會(huì)話沒有媒體按鈕接收器,系統(tǒng)將丟棄媒體按鈕事件,并且什么也不會(huì)發(fā)生。邏輯如下圖所示:
image - 在Android 8.0 (API級(jí)別26)之前,系統(tǒng)嘗試將事件發(fā)送到活動(dòng)媒體會(huì)話。如果有多個(gè)活動(dòng)媒體會(huì)話,Android會(huì)嘗試選擇準(zhǔn)備播放(緩沖/連接)、播放或暫停的媒體會(huì)話,而不是停止的。(有關(guān)更多細(xì)節(jié),請(qǐng)參見 Handling media buttons in an active media session。)如果沒有活動(dòng)會(huì)話,Android會(huì)嘗試將事件發(fā)送到最近的活動(dòng)會(huì)話。(有關(guān)詳細(xì)信息,請(qǐng)參閱使用Using media buttons to restart an inactive media session。)邏輯如下圖所示:
image
處理活動(dòng)媒體會(huì)話中的媒體按鈕
在Android 5.0 (API level 21)及更高版本上,Android通過調(diào)用onMediaButtonEvent()自動(dòng)將媒體按鈕事件分派給您的活動(dòng)媒體會(huì)話。默認(rèn)情況下,此回調(diào)將KeyEvent轉(zhuǎn)換為與密鑰代碼匹配的適當(dāng)?shù)拿襟w會(huì)話回調(diào)方法。
在Android 5.0 (API level 21)之前,Android通過使用ACTION_MEDIA_BUTTON操作廣播一個(gè)意圖來處理媒體按鈕事件。你的應(yīng)用程序必須注冊(cè)一個(gè)廣播接收器來攔截這些意圖。MediaButtonReceiver類是專門為此目的設(shè)計(jì)的。它是Android media-compat庫中的一個(gè)方便類,它處理ACTION_MEDIA_BUTTON并將傳入的意圖轉(zhuǎn)換為適當(dāng)?shù)?code>MediaSessionCompat.Callback方法調(diào)用。
MediaButtonReceiver是一個(gè)短命的廣播接收器。它將傳入的意圖轉(zhuǎn)發(fā)給管理媒體會(huì)話的服務(wù)。如果你想在Android 5.0之前在系統(tǒng)中使用媒體按鈕,你必須在清單中包括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>
廣播接收器將意圖轉(zhuǎn)發(fā)給您的服務(wù)。要解析意圖并生成對(duì)媒體會(huì)話的回調(diào),請(qǐng)?jiān)诜?wù)的onStartCommand()中包含MediaButtonReceiver.handleIntent()方法。這將把關(guān)鍵代碼轉(zhuǎn)換為適當(dāng)?shù)臅?huì)話回調(diào)方法。
private MediaSessionCompat mMediaSessionCompat = ...;
public int onStartCommand(Intent intent, int flags, int startId) {
MediaButtonReceiver.handleIntent(mMediaSessionCompat, intent);
return super.onStartCommand(intent, flags, startId);
}
注意:如果您沒有
MediaBrowserServiceCompat,您還可以向任何服務(wù)添加ACTION_MEDIA_BUTTON意圖過濾器。有關(guān)更多信息,請(qǐng)參閱MediaButtonReceiver文檔。
使用媒體按鈕重新啟動(dòng)非活動(dòng)的媒體會(huì)話
如果Android能夠識(shí)別最后一個(gè)活動(dòng)媒體會(huì)話,它會(huì)試圖通過發(fā)送一個(gè)ACTION_MEDIA_BUTTON意圖到一個(gè)聲明注冊(cè)的組件(如服務(wù)或廣播接收器)來重新啟動(dòng)會(huì)話。
這可以讓你的應(yīng)用在UI不可見時(shí)重啟回放,這是大多數(shù)音頻應(yīng)用的情況。
當(dāng)您使用MediaSessionCompat時(shí),將自動(dòng)啟用此行為。如果你使用Android框架的MediaSession或者Support Library 24.0.0到25.1.1,你必須調(diào)用setMediaButtonReceiver,讓媒體按鈕重新啟動(dòng)一個(gè)非活動(dòng)的媒體會(huì)話。
你可以通過設(shè)置一個(gè)空媒體按鈕接收器,在Android 5.0 (API級(jí)別21)或更高版本中禁用此行為:
// Create a MediaSessionCompat
mMediaSession = new MediaSessionCompat(context, LOG_TAG);
mMediaSession.setMediaButtonReceiver(null);
注意:對(duì)于在Android 5.0 (API level 21)之前的系統(tǒng)中運(yùn)行的應(yīng)用程序,您注冊(cè)以處理活動(dòng)會(huì)話的媒體按鈕的
MediaButtonReceiver也會(huì)在會(huì)話不活動(dòng)時(shí)接收媒體按鈕事件。無法禁用此行為。
定制媒體按鈕處理程序
onMediaButtonEvent()的默認(rèn)行為提取關(guān)鍵代碼并使用媒體會(huì)話的當(dāng)前狀態(tài)和支持的操作列表來確定調(diào)用哪個(gè)方法。例如,KEYCODE_MEDIA_PLAY調(diào)用onPlay()。
為了在所有應(yīng)用程序中提供一致的媒體按鈕體驗(yàn),您應(yīng)該使用默認(rèn)行為,并且只為了特定的目的而偏離。如果媒體按鈕需要自定義處理,重寫回調(diào)函數(shù)的onMediaButtonEvent()方法,使用intent.getparcelableextra (Intent.EXTRA_KEY_EVENT)提取KeyEvent,自己處理事件,并返回true。
總結(jié)
要正確處理Android所有版本中的媒體按鈕事件,必須在創(chuàng)建媒體會(huì)話時(shí)指定FLAG_HANDLES_MEDIA_BUTTONS。
此外,根據(jù)您計(jì)劃支持的Android版本,您還必須滿足以下要求:
運(yùn)行Android 5.0或更高版本時(shí):
- 從媒體控制器
onConnected()回調(diào)中調(diào)用MediaControllerCompat.setMediaController() - 要允許媒體按鈕重新啟動(dòng)非活動(dòng)會(huì)話,可以通過調(diào)用
setMediaButtonReceiver()并傳遞PendingIntent動(dòng)態(tài)創(chuàng)建MediaButtonReceiver
當(dāng)系統(tǒng)運(yùn)行時(shí)間早于Android 5.0時(shí):
- 重寫活動(dòng)的onKeyDown()以處理媒體按鈕
- 靜態(tài)創(chuàng)建一個(gè)
MediaButtonReceiver通過添加它到應(yīng)用程序的清單

