关于React-Native使用intent模式实现pda的一些思路

近日在工作中接到一个需求,要求使用RN来实现pda扫码功能。这里记录一下我的工作思路和流程,仅供参考。

pda应用的一个主要场景是使用设备自身的红外线扫描仪进行扫描,解码后把结果传输到应用中,在应用中接收到扫描结果后进行业务逻辑处理。那pda设备是如何把结果传输给我们的应用的呢?有两种模式:模拟输入模式和广播模式

  • 模拟输入模式: 模拟用户的键盘输入,当设备扫描到结果时,会把结果自动填充到当前页面中聚焦的输入框中。这种模式的优点是通用性强,不需要额外的开发。但缺点也很明显,就是每次扫描前都必须要让input框聚焦,而且软键盘弹出也会遮挡布局。

  • 广播模式: 是更高级的扫描结果传递方式。扫描器通过 Android 的广播机制(Intent)将扫描结果发送给应用程序,应用程序可以通过接收广播来获取数据。这种模式的优点是灵活性高,可以接收多种数据,且不依赖输入框的焦点状态。但缺点是需要进行适配,不同的设备型号都有各自的广播名称和传输字段名称

以下是广播模式在RN上的实现步骤

1、Android原生端添加广播接收器

1.1、新增ScannerBroadcastReceiver.java文件, 创建BroadcastReceiver 来接收扫描结果
java 复制代码
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 {
    /**
     * 接收扫描器发送的广播
     */
    @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中注册广播接收器
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;
        }
    }

  // 其他初始代码
}

2、Android与React通信,传输扫描内容

2.1、获取React上下文

与RN通讯需要在Android中获取React的上下文,通过React上下文向React发送消息。

java 复制代码
  @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初始化完成后才能拿到

2.2、改造ScannerBroadcastReceiver.java,在ScannerBroadcastReceiver类中处理接收到广播后与React的通信
java 复制代码
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为空");
      }
    }

    /**
     * 发送事件
     */
    private void sendEvent(String eventName, WritableMap params) {
        reactContext
            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
            .emit(eventName, params);
    }
}
2.3、改造MainActivity.java,传递React上下文给ScannerBroadcastReceiver
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 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;  // 更新状态为已注册
    }
  }

  // 其他原始代码
}

3、RN接收Android发送来的消息

tsx 复制代码
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端的一些调试技巧:

通过Log.d输出日志,确保连接adb后,真机运行启动,在终端输入

perl 复制代码
adb logcat | grep "IntentLog"

就可以查看实时日志,grep "IntentLog"是筛选关键词为IntentLog的日志

完整代码:github.com/DamonPandm/...

相关推荐
low神1 天前
Flutter入门,Flutter基础知识总结。
前端·javascript·flutter·react native·uni-app·dart
爱lv行4 天前
Redux 在 React Native 中的使用流程
javascript·react native·react.js
爱lv行4 天前
React Native中的核心技术概念
javascript·react native·react.js
qlj2246 天前
react-native键盘遮盖底部输入框问题修复
javascript·react native·react.js
我是前端小学生10 天前
React Native 中的 View 组件:全面解析
react native
wakangda10 天前
React Native 集成 iOS 原生功能
react native·ios·cocoa
SuperHeroWu710 天前
【HarmonyOS】HarmonyOS和React Native混合开发 (一)之环境安装
react native·harmonyos·鸿蒙·开发环境·环境安装·rn·混合开发
wakangda10 天前
React Native 集成原生Android功能
javascript·react native·react.js
爱lv行14 天前
生态:React Native
javascript·react native·react.js