You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native中frameProcessor内调用API致App崩溃求助

React Native vision-camera-code-scanner 扫描后调用API崩溃问题解决

问题分析

  • Worklet线程冲突:frameProcessor运行在Worklet线程(非JS主线程),直接在其中调用Redux的dispatch(主线程操作)会触发跨线程调用冲突,导致应用崩溃。
  • 无JSON解析错误处理:JSON.parse(detectedBarcodes[0].rawValue)未做异常捕获,若二维码内容不是合法JSON,会直接抛出错误引发崩溃。
  • 重复触发API:摄像头帧处理器每秒执行多次,扫描到二维码时会重复调用API,可能引发请求堆积或状态异常。
  • useCallback依赖缺失:validate函数的useCallback未添加依赖项,虽Redux dispatch本身稳定,但不符合Hooks规范,可能导致意外行为。

解决方案

  1. 用runOnJS执行主线程代码:通过Vision Camera提供的runOnJS方法,将主线程函数(如dispatch调用)包装后在Worklet中执行,避免跨线程冲突。
  2. 添加JSON解析异常捕获:用try-catch包裹JSON.parse,处理非法二维码内容的情况。
  3. 控制扫描触发频率:增加状态标记是否已处理过当前二维码,避免重复调用API。
  4. 完善Hooks依赖:为validate的useCallback添加dispatch依赖,符合Hooks规则。

修改后的完整代码

import React, { useState, useCallback, useEffect } from "react";
import { StyleSheet, Text } from "react-native";
import {
  Camera,
  useCameraDevices,
  useFrameProcessor,
  runOnJS,
} from "react-native-vision-camera";
import { useDispatch, useSelector } from "react-redux";
import * as appActions from "../../../redux/app/app.actions";
import { BarcodeFormat, scanBarcodes } from "vision-camera-code-scanner";

interface ScanScreenProps {}

const Scan: React.FC<ScanScreenProps> = () => {
  const [hasPermission, setHasPermission] = useState(false);
  const [isScanned, setIsScanned] = useState(false); // 标记是否已处理当前扫描
  const devices = useCameraDevices();
  const device = devices.back;
  const dispatch = useDispatch();
  const validateQRStatus = useSelector(validationQRSelector);

  // 修正useCallback依赖,确保函数稳定性
  const validate = useCallback((param: string) => {
    dispatch(appActions.validateQR(param));
  }, [dispatch]);

  const frameProcessor = useFrameProcessor((frame) => {
    "worklet";
    if (isScanned) return; // 已处理过则直接返回,避免重复调用

    const detectedBarcodes = scanBarcodes(frame, [BarcodeFormat.QR_CODE], {
      checkInverted: true,
    });
    
    if (detectedBarcodes?.length !== 0) {
      try {
        const resultObj = JSON.parse(detectedBarcodes[0].rawValue);
        const paramData = `token:${Object.values(resultObj)[0]}`;
        // 切换到主线程执行validate函数
        runOnJS(validate)(paramData);
        // 标记为已处理,防止重复扫描
        runOnJS(setIsScanned)(true);

        // 可选:3秒后重置扫描状态,允许再次扫描其他二维码
        setTimeout(() => {
          runOnJS(setIsScanned)(false);
        }, 3000);
      } catch (e) {
        // 处理JSON解析错误,可根据需求添加用户提示
        console.log("二维码内容格式错误:", e);
      }
    }
  }, [isScanned, validate]); // 添加正确的依赖项

  useEffect(() => {
    (async () => {
      const status = await Camera.requestCameraPermission();
      setHasPermission(status === "authorized");
    })();
  }, []);

  return (
    device != null &&
    hasPermission && (
      <>
        <Camera
          style={StyleSheet.absoluteFill}
          device={device}
          isActive={true}
          frameProcessor={frameProcessor}
          frameProcessorFps={5}
        />
        <Text style={styles.barcodeTextURL}>camera</Text>
      </>
    )
  );
};

export default Scan;

const styles = StyleSheet.create({
  barcodeTextURL: {
    fontSize: 20,
    color: "white",
    fontWeight: "bold",
    alignSelf: "center",
  },
});

关键修改说明

  • runOnJS的使用:将主线程的validate函数通过runOnJS包装后执行,彻底解决Worklet线程与主线程的调用冲突。
  • 扫描状态控制:isScanned状态确保同一个二维码仅触发一次API调用,同时通过定时器重置状态,支持连续扫描不同二维码。
  • 错误防护:try-catch捕获JSON解析异常,避免非法格式的二维码直接导致应用崩溃。
  • 依赖规范:完善useCallback和useFrameProcessor的依赖项,符合React Hooks的最佳实践。

内容的提问来源于stack exchange,提问作者Tanmoy Sarker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 17:46:00