Android7.0以上BLE扫描返回空的问题

解决安卓7.0BLE(低功耗蓝牙)扫描返回空的问题

Android7.0后之前的采用BluetoothAdapterstartLeScan方法已经无法获取到ibeacon的信息了。
会报权限安全的错误,要求申请下面的权限之一

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

经过查找资料发现,7.0从安全和功耗的角度出发,把BLE扫描跟定位权限绑在一起了(室内定位需要BLE采集数据),因此我们出了上面的静态权限还需要在代码里通过运行时权限获得GPS定位权限再操作。

查看了Android的蓝牙源码发现,Android7.0修改了蓝牙BLE扫描的API,把之前的BlueToothAdapter方法标记为了Deprecated方法,如图1所示
图1 BluetoothAdapter源码
同时在源码的注释里面推荐我们使用BluetoothLeScanner类的startScan方法。搞清楚了原因就动手修改代码了。

先贴上7.0以上版本的核心代码:

BluetoothManager bluetoothManager = (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothLeScanner bleScanner = btAdapter.getBluetoothLeScanner();//用过单例的方式获取实例

开启BLE扫描

 bleScanner.startScan(scanCallback);
 ScanCallback scanCallback = new ScanCallback() {
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            BluetoothDevice device = result.getDevice();
            int rssi = result.getRssi();//获取rssi
            //这里写你自己的逻辑
        }
    };

停止扫描的代码

 bleScanner.stopScan(scanCallback);

此外,不要忘记申请定位权限

if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
                // 申请定位授权
                ActivityCompat.requestPermissions(Objects.requireNonNull(getActivity()),
                        new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_CODE);
            } else {
                isScanning = true;
                bleScanner.startScan(scanCallback);
            }
        }

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 110:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    bleImage.setImageDrawable(getResources().getDrawable(R.drawable.ic_bluetooth_searching_black_24dp));

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        bleScanner.startScan(scanCallback);//开启BLE扫描
                    }
                } else
                    Toast.makeText(getContext(), "没有获取到定位权限", Toast.LENGTH_LONG).show();
                break;
        }
    }

完整的全版本BLE扫描代码如下

    //通过HashMap保存扫描到的蓝牙信息。可以有效防止重复
    private HashMap<String, String> ibeaconAddrHM = new HashMap<>();
    private StringBuilder TextViewMsg = new StringBuilder();
    private int bleScanTimes = 0;

    //Handler.Callback()可以解决编译器Handler.leak的提示
    private Handler handler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 1010:
                    bleText.setText(TextViewMsg.toString() +
                            "\r\n" + "进行了 " + bleScanTimes + " 次扫描"
                    );
                    break;
            }
            return false;
        }
    });
    //region Button Task
    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.image_ble://一个蓝牙的图片按钮
                if (!isScanning) {
                    if (isBluetoothValid()) { //判断设备是否支持Ble蓝牙
                        if (isBluetoothOpen()) { //蓝牙已经打开,则开始进行蓝牙搜索
                            ourBleScan();//开启BLE扫描
                        } else {
                            enableBluetooth();//开启蓝牙
                            ourBleScan();//开启BLE扫描
                        }
                    }
                } else {
                    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
                    //安卓6.0及以下版本BLE操作的代码
                        btAdapter.stopLeScan(mLeScanCallback);
                    } else
                     //安卓7.0及以上版本BLE操作的代码
                        bleScanner.stopScan(scanCallback);
                    bleScanTimes = 0;
                    Toast.makeText(getContext(), "BLE扫描已经关闭", Toast.LENGTH_SHORT).show();
                    isScanning = false;
                }
                break;
        }
    }
    //endregion

    //region 判断是否支持蓝牙设备
    public boolean isBluetoothValid() {
        if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            return false;
        }
        BluetoothManager bluetoothManager = (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE);
        btAdapter = bluetoothManager.getAdapter();

        if (btAdapter == null) {
            return false;
        }
        //
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && bleScanner == null) {
            bleScanner = btAdapter.getBluetoothLeScanner();
        }
        return true;
    }
    //endregion

    //region 蓝牙是否打开
    private boolean isBluetoothOpen() {
        return btAdapter.isEnabled();
    }
    //endregion

    //region 打开蓝牙
    private void enableBluetooth() {
        if (!btAdapter.isEnabled()) {
            btAdapter.enable();
        }
    }
    //endregion

    //region Ble Scan
    private void ourBleScan() {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {//安卓6.0以下的方案
            btAdapter.startLeScan(mLeScanCallback);
            isScanning = true;
        } else {//安卓7.0及以上的方案
            if (ContextCompat.checkSelfPermission(Objects.requireNonNull(getActivity()), Manifest.permission.ACCESS_COARSE_LOCATION)
                    != PackageManager.PERMISSION_GRANTED) {
                // 申请定位授权
                ActivityCompat.requestPermissions(Objects.requireNonNull(getActivity()),
                        new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_CODE);
            } else {
                isScanning = true;
                bleScanner.startScan(scanCallback);
            }
        }
    }
    //endregion


    //region Android M 以下的回调
    BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            // TODO Auto-generated method stub

            bleScanTimes++;//Scan times plus one

            String macAddr = device.getAddress();
            String ibeconInfo = "\r\n" + macAddr + " -- " + rssi + "DB";

            if (macAddr != null) {
                TextViewMsg.delete(0, TextViewMsg.length());//initialize StringBuilder
                ibeaconAddrHM.put(macAddr, ibeconInfo);

                Iterator it = ibeaconAddrHM.keySet().iterator();
                while (it.hasNext()) {
                    TextViewMsg.append(ibeaconAddrHM.get(it.next()));/这里的TextViewMsg是一个TextView
                }
               //写入文件,这个方法可以参考我的另一篇博文 Android/安卓开发两句代码写文件到外部存储
               // WriteToFile.writeToFile(TextViewMsg.toString());
                Message message = new Message();
                message.what = 1010;
                handler.sendMessage(message);
            }
            Log.i(TAG, macAddr);
        }
    };
    //endregion

    //region Android M 以上的回调
    ScanCallback scanCallback = new ScanCallback() {
        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            BluetoothDevice device = result.getDevice();
            int rssi = result.getRssi();

            bleScanTimes++;//Scan times plus one

            String macAddr = device.getAddress();
            String ibeconInfo = "\r\n" + macAddr + " -- " + rssi + "DB";

          if (macAddr != null) {
                TextViewMsg.delete(0, TextViewMsg.length());//initialize StringBuilder
                ibeaconAddrHM.put(macAddr, ibeconInfo);

                Iterator it = ibeaconAddrHM.keySet().iterator();
                while (it.hasNext()) {
                    TextViewMsg.append(ibeaconAddrHM.get(it.next()));/这里的TextViewMsg是一个TextView
                }
               //写入文件,这个方法可以参考我的另一篇博文 Android/安卓开发两句代码写文件到外部存储
               // WriteToFile.writeToFile(TextViewMsg.toString());
                Message message = new Message();
                message.what = 1010;
                handler.sendMessage(message);
            }
            Log.i(TAG, macAddr);
        }
    };
    //endregion

    //region Request Permissions
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        switch (requestCode) {
            case 110:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    isScanning = true;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        bleScanner.startScan(scanCallback);
                    }
                } else
                    Toast.makeText(getContext(), "没有获取到定位权限", Toast.LENGTH_LONG).show();
                break;

        }
    }
    //endregion
}

猜你喜欢

转载自blog.csdn.net/chance00/article/details/80366064