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

React-Native调用startDeviceScan报空值TypeError错误求助

错误原因及修复方案

核心错误原因

TypeError: Cannot read property 'startDeviceScan' of null 是因为你在**bleManager状态为null时调用了它的方法**,主要出现在两个场景:

  1. 首次进入应用时权限未通过,bleManager未初始化,后续点击「Start Scanning」按钮直接调用bleManager.startDeviceScan
  2. 组件初始化逻辑存在异步状态更新的疏漏,导致调用方法时实例还未创建

具体修复步骤

1. 所有调用bleManager方法前先做非空检查

不管是启动扫描还是停止扫描,都要先判断bleManager是否存在,避免空指针错误:

修改「Start Scanning」按钮逻辑

<Button
  title="Start Scanning"
  onPress={() => {
    requestBluetoothPermissions((isGranted) => {
      if (isGranted) {
        setDevices([]);
        // 不存在则创建新实例
        let currentManager = bleManager;
        if (!currentManager) {
          currentManager = new BleManager();
          setBleManager(currentManager);
        }
        currentManager.startDeviceScan(
          null, // 传null扫描所有设备,可替换为你的服务UUID数组过滤
          { scanMode: BleManager.ScanMode.LowLatency }, // 修正ScanMode的引用方式
          (error, device) => {
            if (error) {
              console.log(error.message);
              return;
            }
            if (device.name) {
              // 避免重复添加设备
              setDevices(prev => {
                if (!prev.some(d => d.id === device.id)) {
                  return [...prev, device];
                }
                return prev;
              });
            }
          }
        );
        setScanning(true);
      }
    });
  }}
/>

修改「Stop Scanning」按钮逻辑

<Button
  title="Stop Scanning"
  onPress={() => {
    if (bleManager) { // 先检查实例是否存在
      bleManager.stopDeviceScan();
    }
    setScanning(false);
  }}
/>

2. 修复未定义变量问题

代码中serialUUIDs和ScanMode未定义,需修正:

  • serialUUIDs.serviceUUID替换为你的实际蓝牙服务UUID,或传null扫描所有设备
  • ScanMode需从BleManager类中引用,即BleManager.ScanMode.LowLatency

3. 优化组件初始化与销毁逻辑

原useEffect空依赖导致只执行一次,且未处理权限未通过的情况,同时组件卸载时要清理BLE资源:

useEffect(() => {
  // 挂载时先请求权限,初始化BLE实例
  requestBluetoothPermissions((isGranted) => {
    if (isGranted && !bleManager) {
      const manager = new BleManager();
      setBleManager(manager);
    }
  });

  // 卸载时停止扫描并销毁实例
  return () => {
    if (bleManager) {
      bleManager.stopDeviceScan();
      bleManager.destroy();
    }
  };
}, [bleManager]);

4. 修正权限判断逻辑

react-native-permissions返回的权限结果是字符串('granted'/'denied'),而非PermissionsAndroid.RESULTS枚举值,需修正判断:

function requestBluetoothPermissions(cb) {
  if (Platform.OS === 'android') {
    DeviceInfo.getApiLevel().then((apiLevel) => {
      if (apiLevel < 31) {
        PermissionsAndroid.request(
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
          {
            title: '位置权限请求',
            message: '蓝牙低功耗需要位置权限才能扫描设备',
            buttonNeutral: '稍后询问',
            buttonNegative: '取消',
            buttonPositive: '确定',
          },
        ).then((granted) => {
          cb(granted === PermissionsAndroid.RESULTS.GRANTED);
        });
      } else {
        requestMultiple([
          PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
          PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
          PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
        ]).then((result) => {
          const isGranted =
            result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] === 'granted' &&
            result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] === 'granted' &&
            result[PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION] === 'granted';
          cb(isGranted);
        });
      }
    });
  } else {
    cb(true);
  }
}

完整修复代码

import React, { useState, useEffect } from 'react';
import {
  SafeAreaView,
  View,
  Text,
  Button,
  FlatList,
  Platform,
  PermissionsAndroid,
} from 'react-native';
import BleManager from 'react-native-ble-plx';
import { PERMISSIONS, requestMultiple } from 'react-native-permissions';
import DeviceInfo from 'react-native-device-info';

const App = () => {
  const [devices, setDevices] = useState([]);
  const [scanning, setScanning] = useState(false);
  const [bleManager, setBleManager] = useState(null);

  function requestBluetoothPermissions(cb) {
    if (Platform.OS === 'android') {
      DeviceInfo.getApiLevel().then((apiLevel) => {
        if (apiLevel < 31) {
          PermissionsAndroid.request(
            PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
            {
              title: '位置权限请求',
              message: '蓝牙低功耗需要位置权限才能扫描设备',
              buttonNeutral: '稍后询问',
              buttonNegative: '取消',
              buttonPositive: '确定',
            },
          ).then((granted) => {
            cb(granted === PermissionsAndroid.RESULTS.GRANTED);
          });
        } else {
          requestMultiple([
            PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
            PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
            PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
          ]).then((result) => {
            const isGranted =
              result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] === 'granted' &&
              result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] === 'granted' &&
              result[PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION] === 'granted';
            cb(isGranted);
          });
        }
      });
    } else {
      cb(true);
    }
  }

  useEffect(() => {
    requestBluetoothPermissions((isGranted) => {
      if (isGranted && !bleManager) {
        const manager = new BleManager();
        setBleManager(manager);
      }
    });

    return () => {
      if (bleManager) {
        bleManager.stopDeviceScan();
        bleManager.destroy();
      }
    };
  }, [bleManager]);

  const startScanning = () => {
    requestBluetoothPermissions((isGranted) => {
      if (isGranted) {
        setDevices([]);
        let currentManager = bleManager;
        if (!currentManager) {
          currentManager = new BleManager();
          setBleManager(currentManager);
        }
        currentManager.startDeviceScan(
          null,
          { scanMode: BleManager.ScanMode.LowLatency },
          (error, device) => {
            if (error) {
              console.log(error.message);
              return;
            }
            if (device.name) {
              setDevices((prev) => {
                if (!prev.some((d) => d.id === device.id)) {
                  return [...prev, device];
                }
                return prev;
              });
            }
          }
        );
        setScanning(true);
      }
    });
  };

  const stopScanning = () => {
    if (bleManager) {
      bleManager.stopDeviceScan();
    }
    setScanning(false);
  };

  return (
    <SafeAreaView style={{ flex: 1, padding: 16 }}>
      <View style={{ marginBottom: 24 }}>
        {scanning ? (
          <Button title="Stop Scanning" onPress={stopScanning} />
        ) : (
          <Button title="Start Scanning" onPress={startScanning} />
        )}
      </View>
      <FlatList
        data={devices}
        renderItem={({ item }) => (
          <Text style={{ padding: 8, borderBottomWidth: 1, borderBottomColor: '#eee' }}>
            {item.name} ({item.id})
          </Text>
        )}
        keyExtractor={(item) => item.id}
      />
    </SafeAreaView>
  );
};

export default App;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 01:35:26