简体   繁体   English

无法扫描或发现 android 中的 BT 和 BLE 设备

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM