![](/img/trans.png)
[英]Android BLE Scan Callback is not called after scanning for BLE devices
[英]Unable to scan or discover BT and BLE devices in android
我是 Java 和 Android 的完全新手。我正在尝试创建一个测试应用程序来监听附近的 BLE 和 BT 设备。 我有另一个设备,我在其中编写了一些逻辑来广播其 BLE 信标。 我使用 Playstore 应用验证了它。 现在我正在尝试在 Android 上编写自己的应用程序。我一直在阅读 Android 开发人员页面以获取指导。 我确实遵循了以下页面的每一步
https://developer.android.com/guide/topics/connectivity/bluetooth/setup
https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
另外,请注意,我使用了 Android 开发者页面中的 BARE MINIMUM CODE所以这就是我所做的。
1. 首先我在 AndroidManifest 下添加了我的权限
注意 1:我正在将此应用程序部署到运行 Android 11 的手机
注 2:所有这些代码都写在 MainActivity 中。 我还没有创建任何其他活动 class
<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_ADVERTISE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2. 接下来我检查我的 BT 是否启用。
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
我收到了 BT 当然已启用的成功消息
3.接下来我检查我的设备是否支持BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
blefinder.append("\nBLE NOT SUPPORTED ON THIS DEVICE : ");
finish();
}
else{
blefinder.append("\nBLE IS SUPPORTED ON THIS DEVICE : ");
}
我收到支持 BLE 的消息
4.接下来我列出我已经配对/绑定的设备
为此,我调用ListPairedAndBondedDevices();
在上述步骤之后的 onCreate() 本身。 Function 定义如下。
private void ListPairedAndBondedDevices(){
@SuppressLint("MissingPermission") Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
// There are paired devices. Get the name and address of each paired device.
blefinder.append("\nPAIRED/BONDED DEVICES");
for (BluetoothDevice device : pairedDevices) {
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
}
这也很有效,可以打印出我配对的设备。 接下来的两部分是我面临的问题。
5. 问题步骤 | 第1部分:
在这里,我注册了一个广播接收器来发现附近的所有 BT 设备。 我已经取消绑定我的 BT 耳机并将其保持在配对模式以验证这一点。
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
广播接收器实现
private final BroadcastReceiver BTReceiver = new BroadcastReceiver() {
@SuppressLint("MissingPermission")
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Discovery has found a device. Get the BluetoothDevice
// object and its info from the Intent.
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
blefinder.append("\n" + device.getName() + " | " + device.getAddress());
}
}
};
所以这部分没有工作:(
如果您在上面看到,我在列出已经配对的设备(通过调用ListPairedAndBondedDevices()
)后立即在onCreate
中注册BTReceiver
。
当我运行调试器时,这个广播接收器永远不会被调用。
6.问题步骤| 第2部分:
在此之后,我也尝试通过scanLeDevice()
来扫描 BLE 设备
ListPairedAndBondedDevices(); // From previous snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // From previous snippet
registerReceiver(BTReceiver, filter);// From previous snippet
scanLeDevice(); // ---------------->>> CALLING THIS FUNCTION TO SCAN FOR BLE DEVICES
scanLeDevice() 的实现
private void scanLeDevice() {
if (!scanning) {
// Stops scanning after a predefined scan period.
handler.postDelayed(new Runnable() {
@Override
public void run() {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN... TIMEOUT REACHED");
}
}, SCAN_PERIOD);
scanning = true;
bluetoothLeScanner.startScan(leScanCallback);
} else {
scanning = false;
bluetoothLeScanner.stopScan(leScanCallback);
blefinder.append("\nSTOPPING BLE SCAN");
}
}
不幸的是,这也失败了。 调试器告诉我这部分代码正在被调用。 在SCAN_PERIOD
(我设置的超时)30 秒后,我收到扫描已停止的消息( STOPPING BLE SCAN
)
现在我也实现了leScanCallback
(即设备扫描回调)
private ScanCallback leScanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
blefinder.append("SOMETHING GOT SCANNED?");
blefinder.append("\n"+result.getDevice().toString());
// leDeviceListAdapter.addDevice(result.getDevice());
// leDeviceListAdapter.notifyDataSetChanged();
}
};
请注意,我没有使用ListAdapter
,因为我不知道这个概念。 因此,对于初学者来说,我只是想将结果转储到由 blefinder 表示的blefinder
中。 这个blefinder
打印了所有其他文本,因此 TextView 变量没有任何问题。 当我使用调试器运行时,它根本没有进入leScanCallback
代码定义,即使在 30 秒后,在执行scanLeDevice()
function 之后也是如此。
我在这里有点迷路了。 有什么我可能遗漏或做错的事情吗? 它应该很简单,列出我附近的 ble/bt 设备。
如果我错过了,我很乐意分享任何进一步的信息。 请在评论中告诉我。
首先,您应该检查您的应用程序是否在设置应用程序 > 应用程序 <your_app> > 权限中获得了位置权限。 一些权限(如 ACCESS_*_LOCATION 和 BLUETOOTH_ADMIN)需要在运行时请求并由用户通过弹出窗口授予。 通常,在尝试执行需要您的应用程序没有的权限的代码时,您应该会收到 SecurityException 或 logcat 警告,但 android 跳过错误处理的情况并不少见。
考虑使用此方法开始扫描,以便检查其结果代码以获取有关正在(未)发生的事情的潜在附加信息。
您还可以通过记录 BTReceiver.onReceive() 中收到的所有操作来获得一些线索,而不仅仅是找到的操作。
最后检查您设备上的位置设置是否已打开蓝牙扫描(设置应用>位置> wifi和蓝牙扫描)
假设您已完成我在评论中提到的权限,我们可以实现一个干净的蓝牙 LE 扫描器 object,然后在 UI 中使用它。
首先我们实现一个结果消费者接口,以便将结果传递给调用BleScanner.scan()
方法的消费者。
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
现在我们需要实现管理扫描事件的扫描仪 object:
public class BleScanner {
private static final String TAG = BleScanner.class.getSimpleName();
private BluetoothLeScanner leScanner = null;
private BluetoothAdapter bleAdapter = null;
private Handler uiHandler = new Handler(Looper.getMainLooper);
private ScanResultConsumer scanResultConsumer;
private boolean scanning = false;
private final ArrayList<BluetoothDevice> foundDeviceList = new ArrayList<>();
public BleScanner(Context context) {
final BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
bleAdapter = bluetoothManager.getAdapter();
if(bleAdapter == null) {
Log.d(TAG, "No bluetooth hardware.");
}
else if(!bleAdapter.isEnabled()){
Log.d(TAG, "Blutooth is off.");
}
}
public void scan(ScanResultConsumer scanResultConsumer, long scanTime){
foundDeviceList.clear();
if (scanning){
Log.d(TAG, "Already scanning.");
return;
}
Log.d(TAG, "Scanning...");
if(leScanner == null){
leScanner = bleAdapter.getBluetoothLeScanner();
}
if(scanTimeMs > 0) {
uiHandler.postDelayed(()-> {
if (scanning) {
Log.d(TAG, "Scanning is stopping.");
if(leScanner != null)
leScanner.stopScan(scanCallBack);
else
Log.d(TAG,"Scanner null");
setScanning(false);
}
}, scanTimeMs);
}
this.scanResultConsumer = scanResultConsumer;
leScanner.startScan(scanCallBack);
setScanning(true);
}
private final ScanCallback scanCallBack = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning){
return;
}
if(foundDeviceList.contains(result.getDevice())) {
// This device has already been found
return;
}
// New device found, add it to the list in order to prevent duplications
foundDeviceList.add(result.getDevice());
if(scanResultConsumer != null) {
uiHandler.post(() -> {
scanResultConsumer.onDeviceFound(result.getDevice(),
result.getScanRecord().getBytes(), result.getRssi());
});
}
}
};
public boolean isScanning(){
return scanning;
}
void setScanning(boolean scanning){
this.scanning = scanning;
uiHandler.post(() -> {
if(scanResultConsumer == null) return;
if(!scanning){
scanResultConsumer.onScanningStopped();
// Nullify the consumer in order to prevent UI crashes
scanResultConsumer = null;
} else{
scanResultConsumer.onScanningStarted();
}
});
}
}
最后,我们可以在任何需要的地方使用这个干净的实现。 但请注意,必须提供上下文才能创建BleScanner
object。
public class MainActivity extends AppCompatActivity {
private BleScanner bleScanner;
private Button buttonScan
// Other codes...
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Other codes...
bleScanner = new BleScanner(getApplicationContext());
// Other codes...
// For example if you want to start scanning on a button press
// Let's say you have a button called buttonScan and initiated it
buttonScan = findViewById(R.id.scan_button);
buttonScan.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bleScanner.scan(new ScanResultConsumer {
@Override
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi) {
// TODO Here you have a newly found device, do something.
}
@Override
q public void onScanningStarted() {
// TODO Scanning has just started, you may want to make some UI changes.
}
@Override
public void onScanningStopped() {
// TODO Scanning has just stopped, you may want to make some UI changes.
}
});
}
});
}
}
注意:我在普通编辑器中编写了这段代码,而不是在 Android Studio 中。 所以可能会有一些错误,如果有的话请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.