我已经被困了一个星期,我想制作一个可以通过蓝牙连接到其他安卓设备但无法发现任何设备的应用程序,因为它只返回空数组。
这是我用来发现其他安卓蓝牙设备的代码:
public class BluetoothScannerModule extends ReactContextBaseJavaModule {
private BluetoothAdapter bluetoothAdapter;
private List<BluetoothDevice> discoveredDevices = new ArrayList<>();
public BluetoothScannerModule(ReactApplicationContext reactContext) {
super(reactContext);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
reactContext.registerReceiver(receiver, filter);
}
@NonNull
@Override
public String getName() {
return "BluetoothScannerModule";
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device != null && !discoveredDevices.contains(device)) {
discoveredDevices.add(device);
}
}
}
};
@ReactMethod
public void scanDevices(Callback callback) {
discoveredDevices.clear();
if (bluetoothAdapter == null) {
callback.invoke("Bluetooth not supported");
return;
}
if (!bluetoothAdapter.isEnabled()) {
callback.invoke("Bluetooth not enabled");
return;
}
if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
return;
}
}
bluetoothAdapter.startDiscovery();
try {
Thread.sleep(5000); // scan for 5 seconds
} catch (InterruptedException e) {
e.printStackTrace();
}
bluetoothAdapter.cancelDiscovery();
WritableArray devicesArray = Arguments.createArray();
for (BluetoothDevice device : discoveredDevices) {
WritableMap deviceMap = Arguments.createMap();
deviceMap.putString("name", device.getName());
deviceMap.putString("address", device.getAddress());
deviceMap.putInt("bondState", device.getBondState());
devicesArray.pushMap(deviceMap);
}
callback.invoke(null, devicesArray);
}
}
并像这样登录到 React Native 以查看设备列表:
BluetoothScannerModule.scanDevices((error, devices) => {
if (error) {
console.error(error);
return;
}
console.log(devices);
})
编辑代码
在本机模块中:
@ReactMethod
public void scanDevices(Callback callback) {
discoveredDevices.clear();
if (bluetoothAdapter == null) {
callback.invoke("Bluetooth not supported");
return;
}
if (!bluetoothAdapter.isEnabled()) {
callback.invoke("Bluetooth not enabled");
return;
}
if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
return;
}
}
bluetoothAdapter.startDiscovery();
callback.invoke(null,"Discovery started");
}
@ReactMethod
public void stopScan(Callback callback) {
if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ActivityCompat.requestPermissions(getCurrentActivity(), new String[]{Manifest.permission.BLUETOOTH_SCAN}, 2);
return;
}
}
bluetoothAdapter.cancelDiscovery();
WritableArray devicesArray = Arguments.createArray();
for (BluetoothDevice device : discoveredDevices) {
WritableMap deviceMap = Arguments.createMap();
deviceMap.putString("name", device.getName());
deviceMap.putString("address", device.getAddress());
deviceMap.putInt("bondState", device.getBondState());
devicesArray.pushMap(deviceMap);
}
callback.invoke(null, devicesArray);
}
在 React Native 中:
type Device = {
name: string | null;
address: string;
bondState: number;
};
const [devices, setDevices] = useState<Device[]>([]);
const startDiscovery = () => {
BluetoothScannerModule.scanDevices((error: string | null, result: string) => {
if (error) {
console.log(error);
} else {
console.log(result);
}
});
};
const cancelDiscovery = () => {
BluetoothScannerModule.stopScan((error: string | null, result: Device[]) => {
if (error) {
console.log(error);
} else {
setDevices(result);
}
});
};
const renderDevice = ({ item }: { item: Device }) => (
<View>
<Text>{item.name ?? 'Unnamed device'}</Text>
<Text>{item.address}</Text>
<Text>Bond state: {item.bondState}</Text>
</View>
);
...
<Button
title='Start Scan'
color="#841584"
accessibilityLabel="Learn more about this purple button"
onPress={startDiscovery}
/>
<Button
title='Stop Scan'
color="#841584"
accessibilityLabel="Learn more about this purple button"
onPress={cancelDiscovery}
/>
<FlatList data={devices} renderItem={renderDevice} />
安卓清单
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
Android 文档 说发现过程通常需要 12 秒。但是你在 5 秒后调用
cancelDiscovery
。所以这行不通。
此外,当您在这 5 秒中调用
Thread.sleep(5000)
时,无法调用 onReceive
的 BroadcastReceiver
。因为onReceive
运行在主线程上。你必须删除Thread.sleep(5000)
.
您可能还需要先决条件
ACCESS_FINE_LOCATION
和 BLUETOOTH_ADMIN
。你也可能需要设置usesPermissionFlags="neverForLocation"
。请阅读链接中的文档。
您是否在清单文件中启用/添加了 android 权限?请求授予许可并在清单文件中声明该许可都很重要。 BLUETOOTH_SCAN、BLUETOOTH_ADMIN 等权限和 ACCESS_FINE_LOCATION 等位置权限,位置权限对于发现附近的设备很重要。此外,在开始扫描之前还应启用手机的位置和蓝牙。如果你想连接到一个设备,那么你还需要BLUETOOTH_CONNECT,BLUETOOTH_ADVERTISE。
我建议在按下按钮时停止扫描,因为在大多数应用程序中,您想要扫描设备直到连接到一个设备。并扫描一段时间,例如 10 到 15 秒。
尝试将这些权限放在您的 AndroidManifest.xml 文件中,放在包名称所在行之后的顶部。并保留代码,否则它将无法工作。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>