Android Bluetooth Low Energy code compatible with API>=21 AND API<21

后端 未结 4 981
灰色年华
灰色年华 2020-12-10 17:24

I\'m developing an app that have to connect with a BLE device, in my code I want to use the new Scan and ScanCallback for BLE implemented from API 21 (Android 5) but I have

相关标签:
4条回答
  • 2020-12-10 18:04

    Your code is crashing because it is creating anonymous inner class. Hence at run time it doesn't find that sCanCallback class.

    Try below way and share the outcome. Before you try this, make sure you comment the callback(ScanCallback).

     if (Build.VERSION.SDK_INT >= 21) {
                mLEScanner.startScan(filters, settings, new ScanCallback() {
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
                  BluetoothDevice btDevice = result.getDevice();
                  connectToDevice(btDevice);
             }
             public void connectToDevice(BluetoothDevice device) {
                  if (mGatt == null) {
                       mGatt = device.connectGatt(context, false, btleGattCallback);
                       if (Build.VERSION.SDK_INT < 21) {
                            btAdapter.stopLeScan(leScanCallback);
                       } else {
                            mLEScanner.stopScan(mScanCallback);
                       }
                   }
             }
        };
            } else {
                btAdapter.startLeScan(leScanCallback);
            }
    
    0 讨论(0)
  • 2020-12-10 18:12

    After reading several posts I did the following. Just in case, here is the documentation of Android about BluetoothLe

    First

    Create two methods one scanLeDevice21 and scanLeDevice18. On scanLeDevice21 add the annotation @RequiresApi(21) that says:

    Denotes that the annotated element should only be called on the given API level or higher. This is similar in purpose to the older @TargetApi annotation, but more clearly expresses that this is a requirement on the caller, rather than being used to "suppress" warnings within the method that exceed the minSdkVersion.

    Second

    Implement each method, here's my code.

    @RequiresApi(21)
    private void scanLeDevice21(final boolean enable) {
    
        ScanCallback mLeScanCallback = new ScanCallback() {
    
            @Override
            public void onScanResult(int callbackType, ScanResult result) {
    
                super.onScanResult(callbackType, result);
    
                BluetoothDevice bluetoothDevice = result.getDevice();
    
                if (!bluetoothDeviceList.contains(bluetoothDevice)) {
                    Log.d("DEVICE", bluetoothDevice.getName() + "[" + bluetoothDevice.getAddress() + "]");
                    bluetoothDeviceArrayAdapter.add(bluetoothDevice);
                    bluetoothDeviceArrayAdapter.notifyDataSetChanged();
                }
            }
    
            @Override
            public void onBatchScanResults(List<ScanResult> results) {
                super.onBatchScanResults(results);
    
            }
    
            @Override
            public void onScanFailed(int errorCode) {
                super.onScanFailed(errorCode);
            }
        };
    
        final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
    
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(() -> {
                mScanning = false;
                swipeRefreshLayout.setRefreshing(false);
                bluetoothLeScanner.stopScan(mLeScanCallback);
            }, SCAN_PERIOD);
    
            mScanning = true;
            bluetoothLeScanner.startScan(mLeScanCallback);
        } else {
            mScanning = false;
            bluetoothLeScanner.stopScan(mLeScanCallback);
        }
    }
    
    
     /**
     * Scan BLE devices on Android API 18 to 20
     *
     * @param enable Enable scan
     */
    private void scanLeDevice18(boolean enable) {
    
        BluetoothAdapter.LeScanCallback mLeScanCallback =
                new BluetoothAdapter.LeScanCallback() {
                    @Override
                    public void onLeScan(final BluetoothDevice bluetoothDevice, int rssi,
                                         byte[] scanRecord) {
                        getActivity().runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bluetoothDeviceArrayAdapter.add(bluetoothDevice);
                                bluetoothDeviceArrayAdapter.notifyDataSetChanged();
                            }
                        });
                    }
                };
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(() -> {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }, SCAN_PERIOD);
    
            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    
    }
    

    Third

    Every time you need to scan devices you surround your code asking which version you are. For instance, I have a RefreshLayout to display the list of devices. Here's the result:

      /**
     * Refresh listener
     */
    private void refreshScan() {
        if (!hasFineLocationPermissions()) {
            swipeRefreshLayout.setRefreshing(false);
    
    
            //Up to marshmallow you need location permissions to scan bluetooth devices, this method is not here since is up to you to implement it and it is out of scope of this question. 
            requestFineLocationPermission();
    
        } else {
            swipeRefreshLayout.setRefreshing(true);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                scanLeDevice21(true);
            } else {
                scanLeDevice18(true);
            }
        }
    }
    

    And that's it.

    Forget about extending, subclassing classes you don't really need like ulusoyca answer.

    0 讨论(0)
  • 2020-12-10 18:14
    1. Create AbstractBluetoothLe class and IBleScanCallback interface. IBleScanCallback interface is a Marker Interface. In other saying, an interface with no methods. You can also add methods to interface if you need. These methods will do the same functionality for all type of scanCallbacks i.e. getListOfFoundBleDevices(), clearListOfFoundBleDevices() etc.
    2. Create BluetootLeLollipop, and BluetoothLeJellyBean classes which extend AbstractBluetoothLe class. Create also BluetootLeMarshmallow class which extends BluetootLeLollipopclass. AbstractBluetoothLe class has protected field mIBleScanCallback which is an IBleScanCallback object.
    3. Create BleScanCallbackBase class which implements IBleScanCallback.
    4. Create LollipopScanCallback class which extends ScanCallback class and implements IBleScanCallback interface. .This class has a protected field scanCallback which will be instantiated as BleScanCallbackBase object. Create also MarshmallowScanCallback class which extends LollipopScanCallback class.
    5. Create JellyBeanScanCallback class which extends BleScanCallbackBase and implements BluetoothAdapter.LeScanCallback
    6. In BleScanCallbackBase override the method: onScanCallback(...)
    7. In LollipoScanCallback override onScanResult(int callbackType, ScanResult result) and inside this method call the method onScanCallback(...) of the scanCallback object.
    8. In JellyBeanScanCallback override onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) and inside this method call onScanCallback(...)
    9. Finally, do whatever needs to be done when a device is found in onScanCallback(...)method of BleScanCallbackBase class.

    In short, read about composition over inheritance- I know this is not an answer to your question but this is a neat way of what you want to achieve in the end. Here is the class diagram:

    0 讨论(0)
  • 2020-12-10 18:19

    I would like to share my idea to overcome this crash on devices below API 21. The ScanCallback class has support from API 21. So when you write a code to implement ScanCallback in your scan code it will cause a crash on lover APIs. I have fixed it the following way:

    I created one new abstract class which extending ScanCallback class as follows:

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)  
    public abstract class AppScanCallback extends ScanCallback {}
    

    Now I am using this class instance in my BluetoothService class as follow:

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private AppScanCallback mScanCallback;
    

    And using this variable as follow:

    public void startBleScan() {
        if (isEnabled()) {
            if (Build.VERSION.SDK_INT < 21) {
                _bluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                mLEScanner = _bluetoothAdapter.getBluetoothLeScanner();
                settings = new ScanSettings.Builder()
                        .build();
                filters = new ArrayList<>();
    
                mScanCallback = new AppScanCallback() {
                    @Override
                    public void onScanResult(int callbackType, ScanResult result) {
                        if (Build.VERSION.SDK_INT >= 21) {
                            BluetoothDevice btDevice = result.getDevice();
                            onLeScanResult(btDevice, result.getScanRecord().getBytes());
                        }
                    }
    
                    @Override
                    public void onBatchScanResults(List<ScanResult> results) {
                        for (ScanResult sr : results) {
                            Log.i("ScanResult - Results", sr.toString());
                        }
                    }
                    @Override
                    public void onScanFailed(int errorCode) {
                        Log.e("Scan Failed", "Error Code: " + errorCode);
                    }
                };
                mLEScanner.startScan(filters, settings, mScanCallback);
            }
    
        }
    }
    

    and to stop scan

    public void stopBLEScan() {;
            if (Build.VERSION.SDK_INT < 21) {
                _bluetoothAdapter.stopLeScan(mLeScanCallback);
            } else {
                mLEScanner.stopScan(mScanCallback);
            }
    }
    

    Hope it will help you out.

    0 讨论(0)
提交回复
热议问题