近日在工作中接到一个需求,要求使用RN来实现pda扫码功能。这里记录一下我的工作思路和流程,仅供参考。
广播模式:
是更高级的扫描结果传递方式。扫描器通过 Android 的广播机制(Intent)将扫描结果发送给应用程序,应用程序可以通过接收广播来获取数据。这种模式的优点是灵活性高,可以接收多种数据,且不依赖输入框的焦点状态。但缺点是需要进行适配,不同的设备型号都有各自的广播名称和传输字段名称
package com.pda; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.Arguments; public class ScannerBroadcastReceiver extends BroadcastReceiver { /** * 堆代码 duidaima.com * 接收扫描器发送的广播 */ @Override public void onReceive(Context context, Intent intent) { if (intent != null && intent.getAction() != null) { String action = intent.getAction(); /** * 检查广播的 Action 是否是扫描器发送 * android.intent.ACTION_DECODE_DATA为pda设备自身的广播名称,通常在pda设备的设置中可以找到 * 以UROVO这个牌子的设备为例,在设置-扫描设置-输出方式-intent输出-广播动作中可以找到名为"android.intent.ACTION_DECODE_DATA"的广播动作 */ if ("android.intent.ACTION_DECODE_DATA".equals(action)) { String scanData = intent.getStringExtra(Constants.PDA_DATA_KEY); if (scanData != null) { // 向RN发送结果 } else { Log.d("onNewIntent", "Scan Data is null"); } } } } }1.2、在MainActivity.java中注册广播接收器
package com.pda; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactActivityDelegate; // 新增以下导入 import android.content.Intent; import android.os.Bundle; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.ReactInstanceManager; import com.facebook.react.bridge.ReactContext; import android.content.IntentFilter; import com.pda.ScannerBroadcastReceiver; import android.util.Log; public class MainActivity extends ReactActivity { private ScannerBroadcastReceiver scannerReceiver; // 扫描器广播接收器 private boolean isReceiverRegistered = false; // 跟踪接收器的状态 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 其他初始化代码 } @Override protected void onResume() { super.onResume(); // 创建并注册广播接收器 scannerReceiver = new ScannerBroadcastReceiver(); IntentFilter filter = new IntentFilter("android.intent.ACTION_DECODE_DATA"); registerReceiver(scannerReceiver, filter); isReceiverRegistered = true; } @Override protected void onPause() { super.onPause(); // 注销广播接收器 if (isReceiverRegistered) { unregisterReceiver(scannerReceiver); isReceiverRegistered = false; } } // 其他初始代码 }二、Android与React通信,传输扫描内容
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager(); reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() { @Override public void onReactContextInitialized(ReactContext context) { Log.d("IntentLog", "初始化完成"); if (context instanceof ReactApplicationContext) { // 获取reactNative上下文,用于通知rn reactContext = (ReactApplicationContext) context; } } }); }
我在这一步卡了很久,原因是以前获取React上下文的方式是直接在MainActivity中通过ReactActivity的getReactApplicationContext()方法就可以拿到,但在0.72版本中该方法已经被移除了。所以只能在onCreate事件中监听React初始化完成后才能拿到。
package com.pda; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; import com.facebook.react.modules.core.DeviceEventManagerModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.WritableMap; import com.facebook.react.bridge.Arguments; public class ScannerBroadcastReceiver extends BroadcastReceiver { private ReactApplicationContext reactContext; // 通过构造函数传入 ReactApplicationContext public ScannerBroadcastReceiver(ReactApplicationContext reactContext) { this.reactContext = reactContext; } /** * 接收扫描器发送的广播 */ @Override public void onReceive(Context context, Intent intent) { if (intent != null && intent.getAction() != null) { String action = intent.getAction(); /** * 检查广播的 Action 是否是扫描器发送 * Constants.PDA_ACTION_NAME为pda设备自身的广播名称,通常在pda设备的设置中可以找到 * 以UROVO这个牌子的设备为例,在设置-扫描设置-输出方式-intent输出-广播动作中可以找到名为"android.intent.ACTION_DECODE_DATA"的广播动作 */ if (Constants.PDA_ACTION_NAME.equals(action)) { String scanData = intent.getStringExtra(Constants.PDA_DATA_KEY); if (scanData != null) { sendScanResultToReactNative(scanData); } else { Log.d("IntentLog", "Scan Data is null"); } } } } /** * 发送扫描结果到 React Native */ private void sendScanResultToReactNative(String scanData) { if (this.reactContext != null) { WritableMap params = Arguments.createMap(); params.putString("scanData", scanData); sendEvent("onBarcodeScanned", params); } else { Log.d("IntentLog", "reactContext为空"); } } /** * 堆代码 duidaima.com * 发送事件 */ private void sendEvent(String eventName, WritableMap params) { reactContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) .emit(eventName, params); } }2.3、改造MainActivity.java,传递React上下文给ScannerBroadcastReceiver
package com.pda; import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; import com.facebook.react.defaults.DefaultReactActivityDelegate; // 新增以下导入 import android.content.Intent; import android.os.Bundle; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.ReactInstanceManager; import com.facebook.react.bridge.ReactContext; import android.content.IntentFilter; import com.pda.ScannerBroadcastReceiver; import android.util.Log; public class MainActivity extends ReactActivity { private ReactApplicationContext reactContext; // reactNative上下文 private ScannerBroadcastReceiver scannerReceiver; // 扫描器广播接收器 private boolean isReceiverRegistered = false; // 跟踪接收器的状态 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager(); reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() { @Override public void onReactContextInitialized(ReactContext context) { Log.d("IntentLog", "初始化完成"); if (context instanceof ReactApplicationContext) { // 获取reactNative上下文,用于通知rn reactContext = (ReactApplicationContext) context; // 创建广播接收器 scannerReceiver = new ScannerBroadcastReceiver(reactContext); // 注册广播接收器 registerScannerReceiver(); } } }); } /** * 应用切换到前台 * 注册广播接收器,监听扫码事件 */ @Override protected void onResume() { super.onResume(); Log.d("IntentLog", "onResume"); // 注册广播接收器 registerScannerReceiver(); } /** * 应用切换到后台 * 注销广播接收器,防止在应用后台时扫码也触发业务逻辑 */ @Override protected void onPause() { super.onPause(); Log.d("IntentLog", "onPause"); // 注销广播接收器 if (scannerReceiver != null && isReceiverRegistered) { unregisterReceiver(scannerReceiver); isReceiverRegistered = false; // 更新状态为未注册 Log.d("IntentLog", scannerReceiver.toString()); } } /** * 注册广播接收器 */ private void registerScannerReceiver() { if (scannerReceiver != null && !isReceiverRegistered) { IntentFilter filter = new IntentFilter(Constants.PDA_ACTION_NAME); registerReceiver(scannerReceiver, filter); isReceiverRegistered = true; // 更新状态为已注册 } } // 其他原始代码 }三、RN接收Android发送来的消息
import React, { useState } from 'react'; import { Text, DeviceEventEmitter, } from 'react-native'; function App(): JSX.Element { const [scanData, setScanData] = useState(''); useEffect(() => { // 监听来自 Android 端的扫描结果事件 const subscription = DeviceEventEmitter.addListener( 'onBarcodeScanned', // 与Android端发送的事件名一致 event => { console.log('接受到广播', event); setScanData(event.scanData); }, ); // 清理监听器 return () => { subscription.remove(); }; }, []); return ( <Text>扫描到的条形码: {scanData}</Text> ); } export default App;Android端的一些调试技巧:
adb logcat | grep "IntentLog"就可以查看实时日志,grep "IntentLog"是筛选关键词为IntentLog的日志。