You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native中重写onKeyDown()方法无法捕获蓝牙耳机媒体按钮事件的问题

问题分析

蓝牙耳机的播放/暂停按键属于媒体按键,和有线耳机的普通按键事件传递机制完全不同。Android系统会将这类按键事件以ACTION_MEDIA_BUTTON广播的形式发送,而非直接通过Activity的onKeyDown方法传递——这就是你用react-native-keyevent只能捕获有线耳机事件的核心原因。

解决方案

要捕获蓝牙耳机的媒体按键,需要通过注册广播接收器监听系统发送的媒体按键广播,再将事件传递到React Native的JS层。以下是具体实现步骤:

1. 更新AndroidManifest.xml配置

首先补充Android 12+所需的蓝牙连接权限,并注册媒体按键广播接收器:

<!-- Android 12+ 蓝牙连接权限 -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<application ...>
  <!-- 保留原有配置 -->
  <!-- 注册媒体按键广播接收器 -->
  <receiver android:name=".MediaButtonReceiver">
    <intent-filter>
      <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
  </receiver>
</application>

2. 创建MediaButtonReceiver广播接收器

在Android项目的MainApplication同目录下新建MediaButtonReceiver.java文件,用于接收媒体按键广播并转发到JS层:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.view.KeyEvent;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class MediaButtonReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
            KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            // 只处理按键按下事件,避免重复触发
            if (event != null && event.getAction() == KeyEvent.ACTION_DOWN) {
                int keyCode = event.getKeyCode();
                // 获取React上下文,将事件发送到JS层
                ReactApplicationContext reactContext = 
                    ((MainApplication) context.getApplicationContext())
                    .getReactNativeHost().getReactInstanceManager()
                    .getCurrentReactContext();
                
                if (reactContext != null) {
                    WritableMap params = com.facebook.react.bridge.Arguments.createMap();
                    params.putInt("keyCode", keyCode);
                    reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                        .emit("mediaButtonPressed", params);
                }
            }
        }
    }
}

3. 在JS层监听自定义事件

修改React Native组件代码,同时保留原有有线耳机的监听逻辑,新增蓝牙媒体按键的事件监听:

import { NativeEventEmitter, NativeModules } from 'react-native';

class RecordingComponent extends React.Component {
  componentDidMount() {
    // 原有有线耳机按键监听
    KeyEvent.onKeyDownListener((keyEvent) => {
      console.log(`Wired headset keyCode: ${keyEvent.keyCode}`);
      this.toggleRecording();
    });

    // 监听蓝牙媒体按键广播事件
    const eventEmitter = new NativeEventEmitter(NativeModules.DeviceEventManagerModule);
    this.mediaButtonSubscription = eventEmitter.addListener(
      'mediaButtonPressed',
      (event) => {
        console.log(`Bluetooth media keyCode: ${event.keyCode}`);
        // 播放/暂停键对应的keyCode是127(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE)
        if (event.keyCode === 127) {
          this.toggleRecording();
        }
      }
    );
  }

  componentWillUnmount() {
    // 清理监听资源
    KeyEvent.removeKeyDownListener();
    this.mediaButtonSubscription?.remove();
  }

  toggleRecording = () => {
    // 你的录制启停逻辑
    console.log("Toggle recording state");
  }

  // 其他组件代码...
}

4. Android 12+ 权限处理

如果APP目标SDK为31及以上,需要在运行时请求BLUETOOTH_CONNECT权限。可以借助react-native-permissions库简化流程:

import { check, request, PERMISSIONS, RESULTS } from 'react-native-permissions';

async function checkBluetoothPermission() {
  const result = await check(PERMISSIONS.ANDROID.BLUETOOTH_CONNECT);
  if (result === RESULTS.DENIED) {
    const requestResult = await request(PERMISSIONS.ANDROID.BLUETOOTH_CONNECT);
    // 根据请求结果处理后续逻辑
  }
}

// 在componentDidMount中调用权限检查
componentDidMount() {
  checkBluetoothPermission();
  // 其他初始化逻辑...
}

关键注意点

  • 媒体按键对应keyCode:播放/暂停键是127,你可以根据需求扩展处理上一曲(87)、下一曲(88)等其他媒体按键。
  • 广播优先级:如果有其他APP同时监听媒体按键,可在广播接收器的intent-filter中添加android:priority="1000"提升优先级,确保你的APP优先捕获事件。

内容的提问来源于stack exchange,提问作者Aayush Rajput

火山引擎 最新活动