Android蓝牙自动连接指定RSSI范围内同APP设备问题排查
核心问题分析
1. 设备无法自发现
Android蓝牙系统默认不会扫描到自身设备,这是系统层面的设计限制,属于正常现象,无需额外处理。
2. 连接逻辑时序错误
在BroadcastReceiver的onReceive方法中,刚调用connectToDevice就立刻启动文件传输,存在两个致命问题:
bluetoothSocket.connect()是阻塞操作,在主线程执行会直接导致ANR(应用无响应)- 未确认连接是否成功就初始化文件传输,大概率会使用未连接的
bluetoothSocket引发空指针或IO异常
3. 服务器监听逻辑不完善
startServer方法中仅执行一次serverSocket.accept(),接受一个连接后线程就终止,无法持续监听后续连接;且未对接收到的客户端连接做任何传输处理,导致客户端发起连接后无响应。
4. 错误使用wait()方法
wait(10000)属于Object的线程等待方法,调用时必须持有该对象的锁,当前代码未加同步锁,会直接抛出IllegalMonitorStateException;且在主线程调用会卡死UI。
5. 清理逻辑触发过早
在onReceive中连接后立刻调用cleanup(),会直接注销广播接收器、关闭服务器Socket,导致后续无法扫描其他设备或接受新连接,同时文件传输可能未完成就被强制中断。
6. 权限隐患
虽然添加了@SuppressLint("MissingPermission"),但Android 12+需要BLUETOOTH_SCAN、BLUETOOTH_CONNECT权限,Android 11及以下需要ACCESS_FINE_LOCATION权限,未动态申请会导致扫描或连接失败。
代码修复建议
1. 异步执行连接与传输逻辑
将连接和文件传输移至子线程,避免阻塞主线程:
private final BroadcastReceiver receiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); Log.d(TAG, "Device found: " + device.getName() + " RSSI: " + rssi); if(rssi > -75) { new Thread(() -> { connectToDevice(device); if (bluetoothSocket != null && bluetoothSocket.isConnected()) { BluetoothFileTransfer bluetoothFileTransfer = new BluetoothFileTransfer(bluetoothSocket, context); File fileToSend = new File(context.getFilesDir(), "contact.json"); File fileToRecieve = new File(context.getFilesDir(), "recievedContact.json"); bluetoothFileTransfer.startFileTransfer(fileToSend,fileToRecieve); // 建议通过BluetoothFileTransfer的回调判断传输完成,再关闭连接 closeConnection(); } }).start(); } } } };
2. 完善服务器持续监听
让服务器线程循环监听连接,每接收到一个连接就启动子线程处理传输:
public void startServer() { new Thread(() -> { try { serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(APP_NAME, APP_UUID); Log.d(TAG, "Server started"); while (!Thread.currentThread().isInterrupted()) { BluetoothSocket clientSocket = serverSocket.accept(); Log.d(TAG, "Server accepted connection"); new Thread(() -> handleClientConnection(clientSocket)).start(); } } catch (IOException e) { Log.e(TAG, "Failed to start server", e); } }).start(); } private void handleClientConnection(BluetoothSocket socket) { try { BluetoothFileTransfer bluetoothFileTransfer = new BluetoothFileTransfer(socket, context); File fileToRecieve = new File(context.getFilesDir(), "recievedContact.json"); File fileToSend = new File(context.getFilesDir(), "contact.json"); bluetoothFileTransfer.startFileTransfer(fileToSend, fileToRecieve); socket.close(); } catch (IOException e) { Log.e(TAG, "Client connection error", e); } }
3. 修正清理逻辑调用时机
将cleanup()移至应用生命周期结束时调用(比如Activity的onDestroy),而非在单次连接后触发:
// 在宿主Activity中 @Override protected void onDestroy() { super.onDestroy(); if (bluetoothConnectionManager != null) { bluetoothConnectionManager.cleanup(); } }
4. 补充权限申请
在Manifest中添加权限:
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
并在运行时动态申请上述权限,确保扫描和连接功能正常。
内容的提问来源于stack exchange,提问作者Ladle

