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

相关推荐
EndingCoder1 天前
跨平台移动开发框架React Native和Flutter性能对比
flutter·react native·react.js
恋猫de小郭3 天前
React Native 前瞻式重大更新 Skia & WebGPU & ThreeJS,未来可期
android·javascript·flutter·react native·react.js·ios
zwjapple3 天前
“ES7+ React/Redux/React-Native snippets“常用快捷前缀
javascript·react native·react.js
程序猿阿伟4 天前
《探索React Native社交应用中WebRTC实现低延迟音视频通话的奥秘》
react native·音视频·webrtc
十步杀一人_千里不留行5 天前
【实战教程】React Native项目集成Google ML Kit实现离线水表OCR识别
react native·react.js·ocr
程序猿阿伟5 天前
《社交应用架构生存战:React Native与Flutter的部署容灾决胜法则》
flutter·react native·架构
流星雨在线6 天前
react naive 网络框架源码解析
网络·react native
老猿阿浪7 天前
突破测试环境文件上传带宽瓶颈!React Native 阿里云 OSS 直传文件格式问题攻克一
react native·阿里云
小妖怪的夏天9 天前
React Native 动态切换主题
javascript·react native·react.js
zhangguo200211 天前
react native和react在跨端架构上有什么区别?
javascript·react native·react.js