[英]Unable to scan or discover BT and BLE devices in android
I am a complete novice in Java and Android. I am trying to create a test app to listen for BLE and BT devices nearby.我是 Java 和 Android 的完全新手。我正在尝试创建一个测试应用程序来监听附近的 BLE 和 BT 设备。 I have another device where I wrote some logic to broadcast its BLE beacons.
我有另一个设备,我在其中编写了一些逻辑来广播其 BLE 信标。 I verified it using a playstore app.
我使用 Playstore 应用验证了它。 Now I am trying to write my own app on Android. I have been reading the Android developer pages for guidance.
现在我正在尝试在 Android 上编写自己的应用程序。我一直在阅读 Android 开发人员页面以获取指导。 I have literally followed every step of the following pages
我确实遵循了以下页面的每一步
https://developer.android.com/guide/topics/connectivity/bluetooth/setup 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/permissions
https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices https://developer.android.com/guide/topics/connectivity/bluetooth/find-bluetooth-devices
https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices https://developer.android.com/guide/topics/connectivity/bluetooth/find-ble-devices
Also, Note that I have used BARE MINIMUM CODE from the Android Developers page So here is what I have done.另外,请注意,我使用了 Android 开发者页面中的 BARE MINIMUM CODE所以这就是我所做的。
1. First off I have added my permissions under AndroidManifest 1. 首先我在 AndroidManifest 下添加了我的权限
Note1: I am deploying this app to My phone running Android 11注意 1:我正在将此应用程序部署到运行 Android 11 的手机
Note2: All this code is written inside MainActivity.注 2:所有这些代码都写在 MainActivity 中。 I have not created any other activity class
我还没有创建任何其他活动 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. Next I check if my BT is enabled. 2. 接下来我检查我的 BT 是否启用。
if (bluetoothAdapter == null) {
blefinder.append("\nDEVICE DOES NOT SUPPORT BLUETOOTH");
}
else {
blefinder.append("\nDEVICE SUPPORTS BLUETOOTH");
}
I get the success message that BT is of course enabled我收到了 BT 当然已启用的成功消息
3. Next I check if my device supports BLE 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 : ");
}
I get the message that BLE is supported我收到支持 BLE 的消息
4. Next I list my already paired/bonded devices 4.接下来我列出我已经配对/绑定的设备
For this I call ListPairedAndBondedDevices();
为此,我调用
ListPairedAndBondedDevices();
in onCreate() itself right after the above steps.在上述步骤之后的 onCreate() 本身。 Function Definition Below.
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());
}
}
}
This also works like a charm and prints out my paired devices.这也很有效,可以打印出我配对的设备。 The next 2 parts is where I face the problem.
接下来的两部分是我面临的问题。
5. The Problem Step | 5. 问题步骤 | Part 1:
第1部分:
Here I register a Broadcast receiver to discover all BT devices in the vicinity.在这里,我注册了一个广播接收器来发现附近的所有 BT 设备。 I've unbonded my BT headphones and kept it in pairing mode to verify this.
我已经取消绑定我的 BT 耳机并将其保持在配对模式以验证这一点。
ListPairedAndBondedDevices(); // From previous code snippet
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); // New code statement
registerReceiver(BTReceiver, filter);// New code statement
Broadcast Receiver implementation广播接收器实现
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());
}
}
};
So This part didn't Work:(所以这部分没有工作:(
If you see above, I am registering the BTReceiver
in onCreate
right after listing the already paired devices (by calling ListPairedAndBondedDevices()
).如果您在上面看到,我在列出已经配对的设备(通过调用
ListPairedAndBondedDevices()
)后立即在onCreate
中注册BTReceiver
。
When I ran the debugger, this broadcast receiver never gets called.当我运行调试器时,这个广播接收器永远不会被调用。
6. The Problem Step | 6.问题步骤| Part 2:
第2部分:
Right after this I try to scan for BLE Devices as well by callin scanLeDevice()
在此之后,我也尝试通过
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
Implementation of scanLeDevice() 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");
}
}
Unfortunately this also fails.不幸的是,这也失败了。 The debugger tells me that this part of the code is getting called.
调试器告诉我这部分代码正在被调用。 And after 30 seconds of
SCAN_PERIOD
(The TIMEOUT that I've set), I get the message that the scanning has stopped ( STOPPING BLE SCAN
)在
SCAN_PERIOD
(我设置的超时)30 秒后,我收到扫描已停止的消息( STOPPING BLE SCAN
)
Now I have implemented the leScanCallback
as well (ie the Device Scan Callback)现在我也实现了
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();
}
};
Notice that I am not using a ListAdapter
since I have no idea about that concept.请注意,我没有使用
ListAdapter
,因为我不知道这个概念。 Hence for starters I am just trying to dump the results in a TextView represented by blefinder
.因此,对于初学者来说,我只是想将结果转储到由 blefinder 表示的
blefinder
中。 This blefinder
prints all the other texts so there is nothing wrong with that TextView variable.这个
blefinder
打印了所有其他文本,因此 TextView 变量没有任何问题。 When I ran using the, debugger, it is not entering into the leScanCallback
piece of code definition at all, even after 30 seconds, after scanLeDevice()
function is executed.当我使用调试器运行时,它根本没有进入
leScanCallback
代码定义,即使在 30 秒后,在执行scanLeDevice()
function 之后也是如此。
I am a little lost here.我在这里有点迷路了。 Is there something I may be missing or doing wrong.
有什么我可能遗漏或做错的事情吗? It is supposed to be a simple, list the ble/bt devices around my vicinity.
它应该很简单,列出我附近的 ble/bt 设备。
I am happy to share any further information if I have missed.如果我错过了,我很乐意分享任何进一步的信息。 Just let me know in the comments.
请在评论中告诉我。
First you should check if your app was granted the location permission(s) in the Settings app > Apps <your_app> > permissions.首先,您应该检查您的应用程序是否在设置应用程序 > 应用程序 <your_app> > 权限中获得了位置权限。 Some permissions (like ACCESS_*_LOCATION and BLUETOOTH_ADMIN) need to be requested at runtime and granted by the user through a popup.
一些权限(如 ACCESS_*_LOCATION 和 BLUETOOTH_ADMIN)需要在运行时请求并由用户通过弹出窗口授予。 Normally you should get a SecurityException or a logcat warning when trying to execute code requiring permissions which your app doesn't have, but it's not uncommon for android to skip over error handling.
通常,在尝试执行需要您的应用程序没有的权限的代码时,您应该会收到 SecurityException 或 logcat 警告,但 android 跳过错误处理的情况并不少见。
Consider using this method to start the scan in order check its result code for potential additional info about what is (not) going on.考虑使用此方法开始扫描,以便检查其结果代码以获取有关正在(未)发生的事情的潜在附加信息。
You might also get some clues by logging all actions received in BTReceiver.onReceive(), not just action found.您还可以通过记录 BTReceiver.onReceive() 中收到的所有操作来获得一些线索,而不仅仅是找到的操作。
Lastly check if the location settings on your device to ensure that bluetooth scanning is turned on (Settings app > location > wifi and bluetooth scanning )最后检查您设备上的位置设置是否已打开蓝牙扫描(设置应用>位置> wifi和蓝牙扫描)
Assuming you've done with the permissions that I've mentioned in the comments, we can implement a clean bluetooth LE scanner object and then use it in the UI.假设您已完成我在评论中提到的权限,我们可以实现一个干净的蓝牙 LE 扫描器 object,然后在 UI 中使用它。
First we implement a result consumer interface in order to deliver the results to the consumers which call the BleScanner.scan()
method.首先我们实现一个结果消费者接口,以便将结果传递给调用
BleScanner.scan()
方法的消费者。
public interface ScanResultConsumer {
public void onDeviceFound(BluetoothDevice device, byte[] scanRecord, int rssi);
public void onScanningStarted();
public void onScanningStopped();
}
Now we need to implement the scanner object that manages the scanning events:现在我们需要实现管理扫描事件的扫描仪 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();
}
});
}
}
Finally we can use this clean implementation in anywhere we need.最后,我们可以在任何需要的地方使用这个干净的实现。 But do note that a context must be provided in order to create a
BleScanner
object.但请注意,必须提供上下文才能创建
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.
}
});
}
});
}
}
Note: I written this code in a plain editor not in Android Studio.注意:我在普通编辑器中编写了这段代码,而不是在 Android Studio 中。 So there may be some errors, let me know if any.
所以可能会有一些错误,如果有的话请告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.