关于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/...

相关推荐
EndingCoder18 小时前
React Native 开发环境搭建(全平台详解)
javascript·react native·react.js·前端框架
程序员小张丶21 小时前
React Native在HarmonyOS 5.0阅读类应用开发中的实践
javascript·react native·react.js·阅读·harmonyos5.0
EndingCoder21 小时前
React Native 是什么?为什么学它?
javascript·react native·react.js
程序员小张丶1 天前
基于React Native开发HarmonyOS 5.0主题应用技术方案
javascript·react native·react.js·主题·harmonyos5.0
wen's1 天前
React Native 弹窗组件优化实战:解决 Modal 闪烁与动画卡顿问题
javascript·react native·react.js
程序员小刘2 天前
如何优化React Native应用以适配HarmonyOS5?
javascript·react native·react.js·华为·harmonyos
哼唧唧_3 天前
使用 React Native 开发鸿蒙(HarmonyOS)运动健康类应用的系统化准备工作
react native·react.js·harmonyos·harmony os5·运动健康
哼唧唧_4 天前
React Native开发鸿蒙运动健康类应用的项目实践记录
react native·harmonyos·harmony os5·运动健康
程序员小刘5 天前
鸿蒙【HarmonyOS 5】 (React Native)的实战教程
react native·华为·harmonyos
烈焰晴天5 天前
使用ReactNative加载Svga动画支持三端【Android/IOS/Harmony】
android·react native·ios