Android蓝牙低能耗代码兼容API> = 21 AND API< 21

时间:2015-12-10 23:56:57

标签: android bluetooth bluetooth-lowenergy android-bluetooth

我正在开发一个必须与BLE设备连接的应用程序,在我的代码中,我想使用从API 21(Android 5)实现的新的Scan和ScanCallback for BLE,但我必须保持与Android 4.3的兼容性以及。

所以我以这种方式编写了代码:

        if (Build.VERSION.SDK_INT >= 21) {
            mLEScanner.startScan(filters, settings, mScanCallback);
        } else {
            btAdapter.startLeScan(leScanCallback);
        }

我已经定义了2个回调,一个用于API 21及更高版本,另一个用于API 18到20:

    //API 21
private ScanCallback mScanCallback = 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);
                   }
               }
         }
    };


//API 18 to 20
        private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) {

        btAdapter.stopLeScan(leScanCallback);
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mBluetoothGatt = device.connectGatt(context, false, btleGattCallback);
            }
        });


    }
};

我还添加了注释

@TargetApi(21)

但是当我在Android 4.x上启动应用程序时,它立即崩溃,报告无法找到类ScanCallback的错误(仅适用于Android 5及更高版本的那个)。

我该如何解决这个问题?

非常感谢你。 丹尼尔。

4 个答案:

答案 0 :(得分:5)

在阅读了几篇文章后,我做了以下几点。以防万一,以下是关于BluetoothLe

的Android文档

<强>第一

创建两个方法scanLeDevice21scanLeDevice18。在scanLeDevice21上添加注释@RequiresApi(21),其中包含:

  

表示只应在给定的API级别或更高级别调用带注释的元素。   这与旧的@TargetApi注释的目的相似,但更清楚地表明这是对调用者的要求,而不是习惯于&#34;抑制&#34;方法中超过minSdkVersion的警告。

<强>第二

实施每种方法,这是我的代码。

@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);
    }

}

<强>第三

每当您需要扫描设备时,您都会围绕代码询问您的版本。例如,我有一个RefreshLayout来显示设备列表。结果如下:

  /**
 * 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);
        }
    }
}

就是这样。

忘记扩展,继承你真正需要的课程,如ulusoyca的回答。

答案 1 :(得分:4)

  1. 创建AbstractBluetoothLe类和IBleScanCallback界面。 IBleScanCallback接口是标记接口。换句话说,一个没有方法的界面。如果需要,您还可以添加接口方法。这些方法将对所有类型的scanCallbacks执行相同的功能,即getListOfFoundBleDevices(),clearListOfFoundBleDevices()等。
  2. 创建扩展BluetootLeLollipop类的BluetoothLeJellyBeanAbstractBluetoothLe个类。同时创建扩展BluetootLeMarshmallow类的BluetootLeLollipop类。 AbstractBluetoothLe类具有受保护的字段mIBleScanCallback,这是IBleScanCallback个对象。
  3. 创建实现BleScanCallbackBase。{/ li>的IBleScanCallback
  4. 创建扩展LollipopScanCallback类并实现ScanCallback接口的IBleScanCallback类。这个类有一个受保护的字段scanCallback,它将被实例化为BleScanCallbackBase个对象。同时创建扩展MarshmallowScanCallback类的LollipopScanCallback类。
  5. 创建扩展JellyBeanScanCallback并实施BleScanCallbackBase
  6. BluetoothAdapter.LeScanCallback
  7. BleScanCallbackBase中覆盖方法:onScanCallback(...)
  8. LollipoScanCallback覆盖onScanResult(int callbackType, ScanResult result)并在此方法内部调用onScanCallback(...)对象的方法scanCallback
  9. JellyBeanScanCallback覆盖onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)并在此方法内部调用onScanCallback(...)
  10. 最后,在onScanCallback(...)BleScanCallbackBase方法中找到设备时,需要做任何事情。
  11. 简而言之,请阅读有关继承的构成 - 我知道这不是你问题的答案,但这是你想要达到目标的一种巧妙方式。这是类图:enter image description here

答案 2 :(得分:0)

您的代码崩溃了,因为它正在创建匿名内部类。因此在运行时它没有找到sCanCallback类。

尝试以下方式并分享结果。在您尝试此操作之前,请确保您对回调(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);
        }

答案 3 :(得分:0)

我想分享我的想法,以解决API 21以下设备上的崩溃问题。ScanCallback类具有API 21的支持。因此,当您编写代码以在扫描代码中实现ScanCallback时,它将导致情人API崩溃。我已通过以下方式对其进行了修复:

我创建了一个新的抽象类,它扩展了ScanCallback类,如下所示:

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

现在我在我的BluetoothService类中使用该类实例,如下所示:

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

并按如下所示使用此变量:

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);
        }

    }
}

并停止扫描

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

希望它会对您有所帮助。