Android BLE 扫描从未找到设备
Android BLE Scan never finds devices
这几天我尝试在我的APP中实现BLE连接。我知道我尝试连接的设备功能齐全,所以问题一定出在我的代码上。
我用的是BluetoothLeScanner.startScan()
方法。
但是永远不会调用回调方法。
public void startScan() {
if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
isScanning = true;
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mainActivityHandler.setMSG("Scan stopped");
isScanning = false;
leScanner.stopScan(scanCallback);
}
}, SCAN_TIME);
mainActivityHandler.setMSG("Start scan");
try {
leScanner.startScan(scanCallback);
} catch (Exception e) {
mainActivityHandler.catchError(e);
}
} else mainActivityHandler.catchError(new Exception("Bluetooth not activated"));
}
我的 CallbackMethod(不知道我是否正确使用 gatt,但这是另一个问题):
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onBatchScanResults(List<ScanResult> results) {
mainActivityHandler.setMSG("Callback");
isScanning = false;
try {
mainActivityHandler.setMSG("Connected to " + results.get(0).getDevice().getName());
gatt = results.get(0).getDevice().connectGatt(mainActivity, true, bluetoothGattCallback);
BluetoothGattDescriptor descriptor;
for (int i = 0; i < charIDs.length; i++) {
gatt.setCharacteristicNotification(gatt.getService(serviceID[0]).getCharacteristic(charIDs[i]), true);
descriptor = gatt.getService(serviceID[0]).getCharacteristic(charIDs[0]).getDescriptor(charIDs[i]);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} catch (Exception e) {
mainActivityHandler.catchError(e);
}
}
};
清单:
你有没有启用位置,确保它是专门为设备启用的 Api 23.Also 有一个免费的应用程序 nrfconnect(我用过的东西)请下载并查看它检测到设备。
我用的也是这个
private void startScan() {
try {
if (!hasPermissions() || mScanning) {
return;
}
List<ScanFilter> filters = new ArrayList<>();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
mScanResults = new ArrayList<>();
mScanCallback = new DeviceScanCallback();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
handleProgress(true, getString(R.string.lbl_scanning));
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
mScanning = true;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (activity == null || !isAdded()) {
return;
}
if (getResources() != null) {
handleProgress(false, getResources().getString(R.string.lbl_scanning));
stopScan();
}
}
}, 20000);
} catch (Exception e) {
Toast.makeText(activity, getString(R.string.scan_failed), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
// Class 利用回调
private class DeviceScanCallback extends ScanCallback {
@Override
public void onScanResult(int callbackType, ScanResult result) {
try {
if (result != null && result.getScanRecord() != null && result.getScanRecord().getDeviceName() != null) {
Log.d("Device name", "" + result.getScanRecord().getDeviceName());
if (result.getScanRecord().getDeviceName().startsWith(ConstantClass.DEVICE_IDENTIFIER)) {
addScanResult(result);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
try {
for (ScanResult result : results) {
Log.i("ScanResult - Results", result.toString() + " " + result.getDevice().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e(TAG, "BLE Scan Failed with code " + errorCode);
}
private void addScanResult(ScanResult result) {
try {
BluetoothDevice device = result.getDevice();
if (mScanResults != null) {
mScanResults.add(device);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
问题
问题是您只是覆盖了 onBatchScanResults
方法而不是 onScanResult
方法。 onBatchScanResults
仅在以下情况下触发:
- 您已使用
ScanSettings.Builder
. 将 ScanSettings
模式设置为 ScanSettings.SCAN_MODE_LOW_POWER
(这是默认设置)
- 您已使用
ScanSettings.Builder
中的 setReportDelay(long reportDelayMillis)
将报告延迟时间设置为 >0 的某个值。
reportDelayMillis - Delay of report in milliseconds. Set to 0 to be notified of results immediately. Values > 0 causes the scan results to be queued up and delivered after the requested delay or when the internal buffers fill up.
例如:
public void startScan(BluetoothLeScanner scanner) {
ScanFilter filter = new ScanFilter.Builder().setDeviceName(null).build();
ArrayList<ScanFilter> filters = new ArrayList<ScanFilter>();
filters.add(filter);
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setReportDelay(1l)
.build();
Log.i(TAG,"The setting are "+settings.getReportDelayMillis());
scanner.startScan(filters,settings,BLEScan);
}
解决方案
但是,您可能只想一次获得结果而不是一批结果。为此,您不必修改 ScanSettings,只需重写 ScanCallback
中的 onScanResult
方法即可。
private ScanCallback mScanCallback =
new ScanCallback() {
public void onScanResult(int callbackType, ScanResult result) {
System.out.println(result.getDevice().getName())
// Do whatever you want
};
...
};
替代方案 - RxAndroidBle
无论如何,我强烈推荐使用库 RxAndroidBle。它维护得很好,它解决了许多开箱即用的 BLE 问题(扫描可能是 BLE 中不太复杂的部分)。
使用该库,可以像这样完成扫描:
Disposable scanSubscription = rxBleClient.scanBleDevices(
new ScanSettings.Builder()
// .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
// .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build()
// add filters if needed
)
.subscribe(
scanResult -> {
// Process scan result here.
},
throwable -> {
// Handle an error here.
}
);
// When done, just dispose.
scanSubscription.dispose();
这几天我尝试在我的APP中实现BLE连接。我知道我尝试连接的设备功能齐全,所以问题一定出在我的代码上。
我用的是BluetoothLeScanner.startScan()
方法。
但是永远不会调用回调方法。
public void startScan() {
if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
isScanning = true;
Handler mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mainActivityHandler.setMSG("Scan stopped");
isScanning = false;
leScanner.stopScan(scanCallback);
}
}, SCAN_TIME);
mainActivityHandler.setMSG("Start scan");
try {
leScanner.startScan(scanCallback);
} catch (Exception e) {
mainActivityHandler.catchError(e);
}
} else mainActivityHandler.catchError(new Exception("Bluetooth not activated"));
}
我的 CallbackMethod(不知道我是否正确使用 gatt,但这是另一个问题):
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onBatchScanResults(List<ScanResult> results) {
mainActivityHandler.setMSG("Callback");
isScanning = false;
try {
mainActivityHandler.setMSG("Connected to " + results.get(0).getDevice().getName());
gatt = results.get(0).getDevice().connectGatt(mainActivity, true, bluetoothGattCallback);
BluetoothGattDescriptor descriptor;
for (int i = 0; i < charIDs.length; i++) {
gatt.setCharacteristicNotification(gatt.getService(serviceID[0]).getCharacteristic(charIDs[i]), true);
descriptor = gatt.getService(serviceID[0]).getCharacteristic(charIDs[0]).getDescriptor(charIDs[i]);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
} catch (Exception e) {
mainActivityHandler.catchError(e);
}
}
};
清单:
你有没有启用位置,确保它是专门为设备启用的 Api 23.Also 有一个免费的应用程序 nrfconnect(我用过的东西)请下载并查看它检测到设备。
我用的也是这个
private void startScan() {
try {
if (!hasPermissions() || mScanning) {
return;
}
List<ScanFilter> filters = new ArrayList<>();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
mScanResults = new ArrayList<>();
mScanCallback = new DeviceScanCallback();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
handleProgress(true, getString(R.string.lbl_scanning));
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
mScanning = true;
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (activity == null || !isAdded()) {
return;
}
if (getResources() != null) {
handleProgress(false, getResources().getString(R.string.lbl_scanning));
stopScan();
}
}
}, 20000);
} catch (Exception e) {
Toast.makeText(activity, getString(R.string.scan_failed), Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
// Class 利用回调
private class DeviceScanCallback extends ScanCallback {
@Override
public void onScanResult(int callbackType, ScanResult result) {
try {
if (result != null && result.getScanRecord() != null && result.getScanRecord().getDeviceName() != null) {
Log.d("Device name", "" + result.getScanRecord().getDeviceName());
if (result.getScanRecord().getDeviceName().startsWith(ConstantClass.DEVICE_IDENTIFIER)) {
addScanResult(result);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
try {
for (ScanResult result : results) {
Log.i("ScanResult - Results", result.toString() + " " + result.getDevice().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e(TAG, "BLE Scan Failed with code " + errorCode);
}
private void addScanResult(ScanResult result) {
try {
BluetoothDevice device = result.getDevice();
if (mScanResults != null) {
mScanResults.add(device);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
问题
问题是您只是覆盖了 onBatchScanResults
方法而不是 onScanResult
方法。 onBatchScanResults
仅在以下情况下触发:
- 您已使用
ScanSettings.Builder
. 将 - 您已使用
ScanSettings.Builder
中的setReportDelay(long reportDelayMillis)
将报告延迟时间设置为 >0 的某个值。
ScanSettings
模式设置为 ScanSettings.SCAN_MODE_LOW_POWER
(这是默认设置)
reportDelayMillis - Delay of report in milliseconds. Set to 0 to be notified of results immediately. Values > 0 causes the scan results to be queued up and delivered after the requested delay or when the internal buffers fill up.
例如:
public void startScan(BluetoothLeScanner scanner) {
ScanFilter filter = new ScanFilter.Builder().setDeviceName(null).build();
ArrayList<ScanFilter> filters = new ArrayList<ScanFilter>();
filters.add(filter);
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.setReportDelay(1l)
.build();
Log.i(TAG,"The setting are "+settings.getReportDelayMillis());
scanner.startScan(filters,settings,BLEScan);
}
解决方案
但是,您可能只想一次获得结果而不是一批结果。为此,您不必修改 ScanSettings,只需重写 ScanCallback
中的 onScanResult
方法即可。
private ScanCallback mScanCallback =
new ScanCallback() {
public void onScanResult(int callbackType, ScanResult result) {
System.out.println(result.getDevice().getName())
// Do whatever you want
};
...
};
替代方案 - RxAndroidBle
无论如何,我强烈推荐使用库 RxAndroidBle。它维护得很好,它解决了许多开箱即用的 BLE 问题(扫描可能是 BLE 中不太复杂的部分)。
使用该库,可以像这样完成扫描:
Disposable scanSubscription = rxBleClient.scanBleDevices(
new ScanSettings.Builder()
// .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
// .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build()
// add filters if needed
)
.subscribe(
scanResult -> {
// Process scan result here.
},
throwable -> {
// Handle an error here.
}
);
// When done, just dispose.
scanSubscription.dispose();