You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android监听MediaNotification/PiP按键事件的实现方法

实现方案

你之前尝试的所有方案失效,核心原因是没有卡准媒体通知、PiP控件的事件分发入口,靠全局回调、播放器状态回调捞事件本来就无法区分来源,直接按下面的方式实现即可,不需要改动现有播放核心逻辑:

媒体通知(MediaNotification)按钮点击追踪

MediaControllerCompat.Callback、MediaSessionConnector.setMediaButtonEventHandler 这类方案只能监听到走MediaSession分发的硬件媒体按键事件(比如线控、蓝牙耳机按键),ExoPlayer自带的PlayerNotificationManager实现的通知栏按钮,是直接通过绑定的PendingIntent把事件转发给播放器,根本不会经过MediaSession的分发链路,所以你收不到回调。
直接做一层接收器代理即可:

  • 自定义类继承PlayerNotificationManager.MediaButtonReceiver,重写onReceive方法
  • 在方法内先从Intent中解析出对应的按键事件,匹配播放/暂停/上一首/下一首/快进/快退操作,直接上报来源为媒体通知的对应埋点
  • 最后调用super.onReceive()把事件原样转发,不影响原有播放逻辑

核心代码示例:

class TrackedNotificationMediaReceiver : PlayerNotificationManager.MediaButtonReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        // 埋点逻辑先走
        intent?.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)?.takeIf { 
            it.action == KeyEvent.ACTION_UP 
        }?.let { keyEvent ->
            val trackAction = when(keyEvent.keyCode) {
                KeyEvent.KEYCODE_MEDIA_PLAY -> "play"
                KeyEvent.KEYCODE_MEDIA_PAUSE -> "pause"
                KeyEvent.KEYCODE_MEDIA_NEXT -> "next_track"
                KeyEvent.KEYCODE_MEDIA_PREVIOUS -> "prev_track"
                KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> "seek_forward"
                KeyEvent.KEYCODE_MEDIA_REWIND -> "seek_backward"
                else -> return@let
            }
            // 上报埋点:触发来源=媒体通知栏,操作=trackAction
            trackEvent(source = "media_notification", action = trackAction)
        }
        // 原有播放逻辑正常执行
        super.onReceive(context, intent)
    }
}

最后把AndroidManifest里原来注册的MediaButtonReceiver替换成这个自定义类,初始化PlayerNotificationManager时传入对应接收器class即可。

PiP画中画按钮点击追踪

PiP控件的事件完全不走媒体按钮分发链路,所有按钮点击都是通过你初始化PiP参数时绑定的PendingIntent直接回调,只要在构建PendingIntent时加一层埋点代理就行:

  • 不要直接把控制播放器的逻辑绑到PiP按钮的PendingIntent上
  • 所有PiP按钮的PendingIntent都指向你自定义的广播接收器,接收器收到对应Action后,先上报来源为PiP的操作埋点,再调用播放器的对应控制方法

示例代码(构建暂停按钮):

// 定义PiP操作广播Action
const val ACTION_PIP_PLAY = "tracked_pip_play"
const val ACTION_PIP_PAUSE = "tracked_pip_pause"
const val ACTION_PIP_NEXT = "tracked_pip_next"
const val ACTION_PIP_PREV = "tracked_pip_prev"

// 构建暂停按钮对应的PendingIntent
val pausePi = PendingIntent.getBroadcast(
    context,
    0,
    Intent(context, TrackedPipReceiver::class.java).apply { action = ACTION_PIP_PAUSE },
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
// 把pausePi作为操作Intent传入RemoteAction,绑定到PiP参数中
val pauseRemoteAction = RemoteAction(
    Icon.createWithResource(context, R.drawable.ic_pause),
    "暂停",
    "暂停播放",
    pausePi
)

在TrackedPipReceiver的onReceive里匹配对应Action上报埋点后,直接调用player.play()/player.pause()/player.seekTo()等对应方法即可。

避坑说明

不要尝试在ExoPlayer的onEvents回调里做来源判断,这个回调是播放器状态变更后的全局通知,不管是应用内操作、通知点击、PiP点击、线控触发的状态变化都会走到这里,回调参数里没有任何字段可以标记事件来源,硬加临时状态位判断很容易出现埋点错报漏报,后续维护成本极高。
你之前自定义MediaButtonReceiver不生效,本质是没有替换掉PlayerNotificationManager默认注册的接收器,系统会优先把媒体按钮广播分发给默认接收器,你自定义的接收器根本拿不到事件。


内容的提问来源于stack exchange,提问作者Rastimir Orlic

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 13:01:01