蓝牙开发(二)扫描设备

一、申请位置权限

在Android6.0以后要扫描蓝牙设备,还需要请求位置权限:

    
    

位置权限属于危险权限,因此需要动态获取:

//判断是否有权限
if (ContextCompat.checkSelfPermission(this,
        Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//请求权限
      ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION);
    //判断是否需要 向用户解释,为什么要申请该权限
    if(ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.READ_CONTACTS)) {
                Toast.makeText(this,"shouldShowRequestPermissionRationale",
Toast.LENGTH_SHORT).show();
    }
}
//权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[]
      grantResults) {
  super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

在实际开发的过程中你也可以设置只有支持蓝牙的设备才能运行该APP:

这里设置required为true就是只有在支持蓝牙的设备上才能运行,但是如果想让你的app提供给那些不支持BLE的设备,需要在manifest中包括上面代码并设置required="false",然后在运行时可以通过使用PackageManager.hasSystemFeature()确定BLE的可用性。

// 使用此检查确定BLE是否支持在设备上,然后你可以有选择性禁用BLE相关的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

二、扫描方式

Android官方提供的蓝牙扫描方式有三种,分别是:

  1. BluetoothAdapter.startDiscovery()//可以扫描经典蓝牙和ble蓝牙两种
  2. BluetoothAdapter.startLeScan()//扫描低功耗蓝牙,在api21已经弃用,不过还是可以使用
  3. BluetoothLeScanner.startScan()//新的ble扫描方法

startDiscovery()方法在大多数手机上是可以同时发现经典蓝牙和低功耗蓝牙(BLE)的,但是startDiscovery()的回调无法返回BLE的广播,所以无法通过广播识别设备,而且startDiscovery()扫描BLE效率比startLeScan()低很多。因此需要根据具体的需求去做适配,才能更高效的搜寻蓝牙。PS: startLeScan()和startScan()有重载方法可以指定规则,参数去搜索。

市面上的蓝牙设备,现在一般都是低功耗的蓝牙设备,因此只需要用下面两种扫描方式就OK了

2.1.startLeScan方式

官方提供的事例代码正是使用了这种方式,startLeScan方法有两个:

public boolean startLeScan(LeScanCallback callback)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)

查看源码会发现一个参数的方法是调用了两个参数的方法,只是serviceuuid[]传入为null,也就是过滤条件为空。这里我使用的是没有过滤条件的方法:

// Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }

// Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            //扫描到的设备信息,将字符数组转成16进制字符串
            String scanRecordStr = SysConvert.bytesToHex(scanRecord);
            //在这里可以做一下简单的操作,避免多操作造成性能问题
        }
    };

因为在扫描蓝牙是一个非常非常耗能的操作,因此一定要扫描一段时间就关闭,如果需要循环扫描,可以通过定时器实现扫描的开关操作

注意:打开和关闭所使用的回调必须是同一个,虽然关闭一个没有打开的回调没有错误返回。

2.2.startScan方式

我在项目中是使用startScan()扫描的,因为可以通过设置一些参数来灵活控制扫描的频率等。

同样的有三个重载方法:

public void startScan(final ScanCallback callback)

public void startScan(List filters, ScanSettings settings,final ScanCallback callback) 

public int startScan(@Nullable List filters, @Nullable ScanSettings settings,
        @NonNull PendingIntent callbackIntent) 

这里我以第二种为例:

/**
     * 扫描设备
     */
    public void scanDevices() {
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mProgressDialog.dismiss();
                if (mBluetoothLeScanner != null) {
                    mBluetoothLeScanner.stopScan(scanCallback);
                }
            }
        },  1000);
        mBluetoothLeScanner.startScan(null, createScanSetting(), scanCallback);
    } 
/**
     * 扫描广播数据设置
     *
     * @return
     */
    public ScanSettings createScanSetting() {
        ScanSettings.Builder builder = new ScanSettings.Builder();
        builder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
        //builder.setReportDelay(100);//设置延迟返回时间
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
        }
        return builder.build();
    }
/**
* 回调
*/
private ScanCallback scanCallback=new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            byte[] scanData=result.getScanRecord().getBytes();
            //把byte数组转成16进制字符串,方便查看

        }
        @Override
        public void onBatchScanResults(List results) {
            super.onBatchScanResults(results);
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
        }
    };

ScanSettings :

setScanMode:有三种模式分别是

SCAN_MODE_LOW_POWER--------耗电最少,扫描时间间隔最短

SCAN_MODE_BALANCED---------平衡模式,耗电适中,扫描时间间隔一般,我使用这种模式来更新设备状态

SCAN_MODE_LOW_LATENCY---------最耗电,扫描延迟时间短,打开扫描需要立马返回结果可以使用

setReportDelay:设置扫描返回延迟时间,一般是大于零的毫秒值

如果设置了该属性,扫描结果会以list形式在onBatchScanResults方法返回,在onScanResult中没有返回,如果不设置则在onScanResult中返回,返回的信息包含在result中,可以通过getBytes返回每一个设备信息的字符数组,getDevice获取BluetoothDevice设备,在通过字符数组和蓝牙设备对象获取你想要的信息

 

本人也是一个蓝牙开发新手,如果上面有什么地方有问题,请指出,共同探讨,共同进步,不胜感激!

你可能感兴趣的:(Android开发,Android蓝牙开发,Android蓝牙扫描,ble扫描设备)