在 android 13(api 33)及以上版本中,`intent.getparcelableextra(string)` 已被弃用,导致蓝牙耳机/手柄的媒体按键(如播放/暂停、音量键)事件始终返回 null,需改用带泛型类型参数的新 api 才能正确解析 keyevent。
在 Xamarin.Android 开发中,监听蓝牙设备媒体按键(如耳机上的播放/暂停、上一首/下一首按钮)依赖 Intent.ActionMediaButton 广播与 KeyEvent 解析。但自 Android 13(API level 33)起,Intent.GetParcelableExtra(string) 方法被标记为 obsolete,其内部实现不再保证跨进程传递 KeyEvent 的完整性——尤其在后台服务或广播接收器中,直接调用 intent.GetParcelableExtra(Intent.ExtraKeyEvent) 将始终返回 null,即使物理按键已触发广播。
需将原代码中:
var keyEvent = (KeyEvent)intent.GetParcelableExtra(Intent.ExtraKeyEvent);
替换为支持泛型推导的强类型方法(Xamarin.Android 13.0+ SDK 提供):
var keyEvent = intent.GetParcelableExtra(Intent.ExtraKeyEvent);
该重载方法会显式指定目标类型 KeyEvent,绕过已废弃的反射式反序列化逻辑,确保从 Intent 中正确还原 KeyEvent 实例。
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionMediaButton, Intent.ActionHeadsetPlug, AudioManager.ActionAudioBecomingNoisy })]
public class MyMediaButtonBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action != Intent.ActionMediaButton) return;
// ✅ 关键修复:使用泛型版本,避免返回 null
var keyEvent = intent.GetParcelableExtra(Intent.ExtraKeyEvent);
if (keyEvent == null)
{
Log.Warn("MyMediaButton", "KeyEvent is null — check target SDK & manifest permissions");
return;
}
// 处理按键事件(注意:Down 事件才代表真实按下)
if (keyEvent.Action == KeyEventActions.Down)
{
switch (keyEvent.KeyCode)
{
case Keycode.MediaPlayPause:
HandlePlayPause(context);
break;
case Keycode.MediaNext:
HandleNextTrack(context);
break;
case Keycode.MediaPrevious:
HandlePrevTrack(context);
break;
case Keycode.Headsethook: // 传统有线耳机单击
HandleHeadsetHook(context);
break;
default:
Log.Debug("MyMediaButton", $"Unknown key code: {keyEvent.KeyCode}");
break;
}
}
}
private void HandlePlayPause(Context context) { /* 实现播放/暂停逻辑 */ }
private void HandleNextTrack(Context context) { /* 实现下一曲逻辑 */ }
private void HandlePrevTrack(Context context) { /* 实现上一曲逻辑 */ }
private void HandleHeadsetHook(Context context) { /* 兼容旧设备 */ }
} GetParcelableExtra