您好,我是 React Native 的新手,在我的应用程序中,我正在尝试扫描 ble 设备并在屏幕上列出它们的名称。在代码源中,人们最常创建一个蓝牙低功耗管理器为
const manager = new BleManager();
,但是当我尝试此代码(当然还有其他方式)时,它会给出类似的错误
TypeError: null is not an object (evaluating '_BleModule.BleModule.createClient')
This error is located at:
in HelloWorldApp (created by ExpoRoot)
in ExpoRoot
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
at node_modules\react-native\Libraries\LogBox\LogBox.js:149:8 in registerError
at node_modules\react-native\Libraries\LogBox\LogBox.js:60:8 in errorImpl
at node_modules\react-native\Libraries\LogBox\LogBox.js:34:4 in console.error
at node_modules\expo\build\environment\react-native-logs.fx.js:27:4 in error
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:104:6 in reportException
at node_modules\react-native\Libraries\Core\ExceptionsManager.js:172:19 in handleException
at node_modules\react-native\Libraries\Core\ReactFiberErrorDialog.js:43:2 in showErrorDialog
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15792:34 in logCapturedError
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15884:20 in update.callback
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:7199:2 in callCallback
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:7220:20 in commitUpdateQueue
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:16632:25 in commitLifeCycles
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:19216:22 in commitLayoutEffects
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:93:4 in invokeGuardedCallbackProd
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:318:2 in invokeGuardedCallback
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18952:29 in commitRootImpl
at node_modules\scheduler\cjs\scheduler.development.js:468:23 in unstable_runWithPriority
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18791:17 in commitRoot
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18192:12 in performSyncWorkOnRoot
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5911:33 in runWithPriority$argument_1
at node_modules\scheduler\cjs\scheduler.development.js:468:23 in unstable_runWithPriority
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5906:23 in flushSyncCallbackQueueImpl
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:5893:28 in flushSyncCallbackQueue
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:17745:30 in scheduleUpdateOnFiber
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:21484:23 in updateContainer
at node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:22144:17 in render
at node_modules\react-native\Libraries\ReactNative\renderApplication.js:58:4 in renderApplication
at node_modules\react-native\Libraries\ReactNative\AppRegistry.js:117:25 in runnables.appKey.run
at node_modules\react-native\Libraries\ReactNative\AppRegistry.js:202:4 in runApplication
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:414:4 in __callFunction
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:113:6 in __guard$argument_0
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:365:10 in __guard
at node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112:4 in callFunctionReturnFlushedQueue
这是我尝试创建管理器的代码:
import { StatusBar } from 'expo-status-bar';
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { BleManager } from 'react-native-ble-plx';
const HelloWorldApp = () => {
const manager = new BleManager();
我无法解决我的错误在哪里。请问有人可以帮我解决这个问题吗?
注意:我还使用
npm install --save react-native-ble-plx
命令安装了 ble-plx 软件包。
注2:我使用expo创建了我的项目。
在使用蓝牙之前,您可能需要征求用户的许可。将这些添加到您的 android-manifest.xml 中:
<uses-feature android:name="android.hardware.bluetooth" android:required="true"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
您因博览会而收到此错误。 Expo 不支持 ble-manager ble-plx 软件包。您需要通过
创建一个本机项目npx react-native init AwesomeProject
import React, {useState, useEffect, useCallback} from 'react';
import {
View,
Text,
Button,
FlatList,
StyleSheet,
Alert,
TouchableOpacity,
TextInput,
Platform,
PermissionsAndroid,
NativeEventEmitter,
NativeModules,
} from 'react-native';
import BleManager from 'react-native-ble-manager';
import {Buffer} from 'buffer';
const BluetoothScanner = () => {
const [devices, setDevices] = useState([]);
const [connectedDevice, setConnectedDevice] = useState(null);
const [services, setServices] = useState([]);
const [characteristics, setCharacteristics] = useState([]);
const [selectedCharacteristic, setSelectedCharacteristic] = useState(null);
const [readValue, setReadValue] = useState('');
const [writeValue, setWriteValue] = useState('');
useEffect(() => {
BleManager.start({showAlert: false});
const requestPermissions = async () => {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
PermissionsAndroid.PERMISSIONS.BLUETOOTH_SCAN,
PermissionsAndroid.PERMISSIONS.BLUETOOTH_CONNECT,
]);
if (
granted['android.permission.ACCESS_FINE_LOCATION'] !==
PermissionsAndroid.RESULTS.GRANTED ||
granted['android.permission.BLUETOOTH_SCAN'] !==
PermissionsAndroid.RESULTS.GRANTED ||
granted['android.permission.BLUETOOTH_CONNECT'] !==
PermissionsAndroid.RESULTS.GRANTED
) {
Alert.alert(
'Permission Required',
'Please grant all permissions to use Bluetooth features.',
);
}
}
};
requestPermissions();
const handleDiscoverPeripheral = peripheral => {
if (peripheral && peripheral.name) {
setDevices(prevDevices => {
if (!prevDevices.find(d => d.id === peripheral.id)) {
return [...prevDevices, peripheral];
}
return prevDevices;
});
}
};
const handleStopScan = () => {
console.log('Scan is stopped');
};
const handleDisconnectedPeripheral = data => {
Alert.alert(
'Disconnected',
`Disconnected from device ${data.peripheral}`,
);
setConnectedDevice(null);
setServices([]);
setCharacteristics([]);
};
const bleManagerEmitter = new NativeEventEmitter(NativeModules.BleManager);
const discoverListener = bleManagerEmitter.addListener(
'BleManagerDiscoverPeripheral',
handleDiscoverPeripheral,
);
const stopListener = bleManagerEmitter.addListener(
'BleManagerStopScan',
handleStopScan,
);
const disconnectListener = bleManagerEmitter.addListener(
'BleManagerDisconnectPeripheral',
handleDisconnectedPeripheral,
);
return () => {
discoverListener.remove();
stopListener.remove();
disconnectListener.remove();
};
}, []);
const scanAndConnect = useCallback(() => {
console.log('Starting scan');
setDevices([]);
BleManager.scan([], 5, true)
.then(() => {
console.log('Scanning...');
})
.catch(error => {
console.warn('Error during scan:', error);
});
}, []);
const connectToDevice = useCallback(async deviceId => {
try {
await BleManager.connect(deviceId);
console.log('Connected to device:', deviceId);
setConnectedDevice(deviceId);
await BleManager.retrieveServices(deviceId);
fetchServices(deviceId);
Alert.alert('Connected', `Successfully connected to device ${deviceId}`);
} catch (error) {
console.warn('Error connecting to device:', error);
Alert.alert('Error', 'Failed to connect to device.');
}
}, []);
const fetchServices = useCallback(async deviceId => {
try {
const servicesData = await BleManager.retrieveServices(deviceId);
console.log('Fetched services:', servicesData.characteristics);
const filteredCharacteristics = servicesData.characteristics.filter(
char => char.service !== '1800' && char.service !== '1801',
);
setServices(servicesData.services);
setCharacteristics(filteredCharacteristics);
} catch (error) {
console.warn('Error fetching services:', error);
}
}, []);
const handleServiceSelect = useCallback(
serviceUUID => {
const serviceCharacteristics = characteristics.filter(
char => char.service === serviceUUID,
);
setCharacteristics(serviceCharacteristics);
},
[characteristics],
);
const readCharacteristic = useCallback(
async characteristic => {
if (connectedDevice) {
try {
console.log(
`Attempting to read characteristic ${characteristic.characteristic} from service ${characteristic.service}`,
);
// Read the characteristic
const readData = await BleManager.read(
connectedDevice,
characteristic.service,
characteristic.characteristic,
);
console.log('Read data:', readData);
// Convert the read data to Buffer
const buffer = Buffer.from(readData);
// Optionally, process the buffer based on data type
let parsedData;
// Example for different types of data
// 1. Read UInt8
if (characteristic.dataType === 'UInt8') {
parsedData = buffer.readUInt8(0);
}
// 2. Read Int16 (little-endian)
else if (characteristic.dataType === 'Int16') {
parsedData = buffer.readInt16LE(0);
}
// 3. Read Float32 (little-endian)
else if (characteristic.dataType === 'Float32') {
parsedData = buffer.readFloatLE(0);
}
// 4. Read String (assuming UTF-8 encoding)
else if (characteristic.dataType === 'String') {
parsedData = buffer.toString('utf8');
}
// 5. Handle other data types or default case
else {
parsedData = `${readData}`;
}
setReadValue(parsedData.toString());
} catch (error) {
console.warn('Error reading characteristic:', error);
Alert.alert('Error', 'Failed to read characteristic.');
}
} else {
Alert.alert('Error', 'No device connected.');
}
},
[connectedDevice],
);
// Convert a hexadecimal string to an ArrayBuffer
function hexStringToByteArray(hexString) {
const hex = hexString.replace(/\s+/g, ''); // Remove spaces
const length = hex.length / 2;
const byteArray = [];
for (let i = 0; i < length; i++) {
byteArray.push(parseInt(hex.substr(i * 2, 2), 16));
}
return byteArray;
}
// Convert an array of integers to an ArrayBuffer
function intArrayToByteArray(intArray) {
// Ensure all integers are in the 0-255 range
return intArray.map(value => value & 255);
}
// Write data to a characteristic
const writeCharacteristic = useCallback(async () => {
if (selectedCharacteristic && writeValue && connectedDevice) {
try {
let byteArray;
// Check and convert writeValue based on its type
if (/^[0-9A-Fa-f]+$/.test(writeValue)) {
// Hexadecimal string
byteArray = hexStringToByteArray(writeValue);
} else if (
Array.isArray(writeValue) &&
writeValue.every(Number.isInteger)
) {
// Array of integers
byteArray = intArrayToByteArray(writeValue);
} else {
throw new Error('Invalid writeValue format');
}
console.log('Byte Array:', byteArray);
// Write to BLE characteristic
await BleManager.write(
connectedDevice,
selectedCharacteristic.service,
selectedCharacteristic.characteristic,
byteArray,
20, // maxByteSize, optional
);
Alert.alert('Success', 'Value written successfully');
} catch (error) {
console.warn('Error writing characteristic:', error);
Alert.alert('Error', 'Failed to write characteristic.');
}
} else {
Alert.alert('Error', 'No characteristic selected or value not provided.');
}
}, [connectedDevice, selectedCharacteristic, writeValue]);
const disconnectFromDevice = useCallback(async () => {
if (connectedDevice) {
try {
await BleManager.disconnect(connectedDevice);
setConnectedDevice(null);
setServices([]);
setCharacteristics([]);
setWriteValue('');
setReadValue('');
setSelectedCharacteristic(null);
Alert.alert(
'Disconnected',
`Successfully disconnected from device ${connectedDevice}`,
);
} catch (error) {
console.warn('Error disconnecting from device:', error);
Alert.alert('Error', 'Failed to disconnect from device.');
}
}
}, [connectedDevice]);
return (
<View style={styles.container}>
<Text style={styles.header}>Bluetooth Scanner</Text>
<Button title="Scan for Devices" onPress={scanAndConnect} />
<FlatList
data={devices}
renderItem={({item}) => (
<View style={styles.deviceContainer}>
<Text style={styles.device}>{`Device found: ${item.name}`}</Text>
{connectedDevice === item.id ? (
<TouchableOpacity
style={styles.button}
onPress={disconnectFromDevice}>
<Text style={styles.buttonText}>Disconnect</Text>
</TouchableOpacity>
) : (
<TouchableOpacity
style={styles.button}
onPress={() => connectToDevice(item.id)}>
<Text style={styles.buttonText}>Connect</Text>
</TouchableOpacity>
)}
</View>
)}
keyExtractor={item => item.id}
/>
{connectedDevice && (
<>
{/* <Text style={styles.header}>Services</Text> */}
{/* <FlatList
data={services}
renderItem={({item}) => (
<TouchableOpacity
style={styles.button}
onPress={() => handleServiceSelect(item.uuid)}>
<Text style={styles.buttonText}>{`Service: ${item.uuid}`}</Text>
</TouchableOpacity>
)}
keyExtractor={item => item.uuid}
/> */}
<Text style={styles.header}>Characteristics</Text>
<FlatList
data={characteristics}
renderItem={({item}) => (
<View style={styles.characteristicContainer}>
<Text
style={
styles.device
}>{`Characteristic: ${item.characteristic}`}</Text>
{item.properties.Read && (
<TouchableOpacity
style={styles.button}
onPress={() => {
setSelectedCharacteristic(item);
readCharacteristic(item);
}}>
<Text style={styles.buttonText}>Read</Text>
</TouchableOpacity>
)}
{item.properties.Write && (
<TouchableOpacity
style={styles.button}
onPress={() => {
setSelectedCharacteristic(item);
setReadValue(null);
}}>
<Text style={styles.buttonText}>Write</Text>
</TouchableOpacity>
)}
</View>
)}
keyExtractor={item => item.characteristic}
/>
{selectedCharacteristic && readValue === null && (
<>
<Text style={styles.header}>Characteristic Value</Text>
<TextInput
style={styles.input}
placeholderTextColor={'gray'}
placeholder="Enter value to write"
value={writeValue}
onChangeText={setWriteValue}
/>
<Button
title="Write Characteristic"
onPress={writeCharacteristic}
/>
</>
)}
{selectedCharacteristic && readValue && (
<View
style={{
padding: 8,
backgroundColor: 'black',
}}>
<Text
style={{
color: 'white',
}}>{`Read Value: ${readValue}`}</Text>
</View>
)}
</>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
header: {
fontSize: 18,
fontWeight: 'bold',
marginVertical: 8,
color: 'black',
},
deviceContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 8,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
},
device: {
fontSize: 16,
color: 'black',
},
button: {
padding: 8,
backgroundColor: '#007AFF',
borderRadius: 4,
},
buttonText: {
color: '#fff',
},
characteristicContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
padding: 8,
borderBottomWidth: 1,
borderBottomColor: '#ccc',
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 12,
padding: 10,
color: 'black',
},
});
export default BluetoothScanner;
这是为了让 React Native 连接附近的设备并使用广告和蓝牙管理器获取其服务和特性