媒体按钮是 Android 设备和其他外围设备上的硬件按钮,例如蓝牙耳机或者线控耳机上的暂停/播放按钮。当用户按下媒体按钮时,Android 会生成 KeyEvent,其中包含用于标识按钮的键码。
可先阅读一下官方的文档,了解下整个流程和机制:
本文是监听播放、上一首、下一首等按键事件的代码实现。
不同版本的实现方式,差异有点大,比较碎片化。本人肤浅地认为Android5.0以下通过广播的方式接收,5.0及以上通过MediaSession,官方也封装了MediaSessionCompat这个类,它里面做了5.0到目前最新Android版本的适配处理。
代码实现
引入androidx的suppor库:
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
新建一个MediaButtonReceiver继承BroadcastReceiver:
class MediaButtonReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent?.action ?: return
when (action) {
Intent.ACTION_MEDIA_BUTTON -> {
val keyEvent =intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT) as? KeyEvent ?: return
when (keyEvent.action) {
KeyEvent.ACTION_DOWN -> {//按键按下
when (keyEvent.keyCode) {
KeyEvent.KEYCODE_MEDIA_PLAY->{//播放按钮
//处理你的逻辑
}
KeyEvent.KEYCODE_MEDIA_NEXT->{//下一首
//处理你的逻辑
}
KeyEvent.KEYCODE_MEDIA_PREVIOUS->{//上一首
//处理你的逻辑
}
}
}
}
}
}
}
}
在配置清单注册:
<receiver android:name=".receiver.MediaButtonReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
ps:MEDIA_BUTTON这个action只支持静态注册的,别想着用动态注册了。
接下来:
private var mMediaPlayer: MediaPlayer? = null//用于抢占线控按键
private var mComponentName: ComponentName? = null
private var mMediaSession: MediaSessionCompat? = null
private fun registerMediaButtonReceiver() {
val audioManager = getSystemService(AUDIO_SERVICE) as? AudioManager ?: return
robKeyEvent()
mComponentName = ComponentName(packageName, MediaButtonReceiver::class.java.name).apply {
packageManager.setComponentEnabledSetting(
this,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP
)
if (Build.VERSION.SDK_INT >= 21) {//5.0以上
mMediaSession =
MediaSessionCompat(this@MainActivity, "mbr", this, null).apply {
//指明支持的按键信息类型
setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
)
setCallback(object : MediaSessionCompat.Callback() {
override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {
//通过Callback返回按键信息,为复用MediaButtonReceiver,直接调用它的onReceive()方法
MediaButtonReceiver().onReceive(this@MainActivity, mediaButtonEvent)
return true
}
}, Handler(Looper.getMainLooper()))
//把MediaSession置为active,这样才能开始接收各种信息
if (!isActive) {
isActive = true
}
}
} else {//5.0以下
audioManager.registerMediaButtonEventReceiver(this)
}
}
}
private fun unRegisterMediaButtonReceiver() {
val audioManager = getSystemService(AUDIO_SERVICE) as? AudioManager ?: return
if (Build.VERSION.SDK_INT >= 21) {//5.0以上
mMediaSession?.let {
it.setCallback(null)
it.release()
}
} else {//5.0以下
mComponentName?.let {
audioManager.unregisterMediaButtonEventReceiver(it)
}
}
}
private fun robKeyEvent() {
if (mMediaPlayer == null) {
mMediaPlayer = MediaPlayer.create(this, R.raw.audiofilename)//随便往raw文件夹放个小的音频文件进去
}
mMediaPlayer?.start()
mMediaPlayer?.stop()
}
使用说明
界面onCreate时调用registerMediaButtonReceiver,onDestroy时调用unRegisterMediaButtonReceiver。
值得一提的是,MediaSession需要先抢占到音频焦点,才能起作用。
音频焦点系统一般都会给在最后播放过的MediaPlayer上,所以我们用了个MediaPlayer来抢占音频焦点。
更多关于音频焦点的研究,比如监听音频失焦、或者通过更优雅的方式来获取音频焦点等,请访问官方文档: