Android上的蓝牙

Context

如果说Android和iOS一样,一出新版本,每个用户都回去升级,那我就不用写这篇咯

先说说iOS端的蓝牙扫描

@param serviceUUIDs A list of <code>CBUUID</code> objects representing the service(s) to scan for.
//serviceUUIDs是一个过滤NSArray
//根据蓝牙外设的广播信号中的Service UUID来过滤
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;

假定说,我们的app需要过滤两个产品的蓝牙信号.那么只要在serviceUUIDs里面填写他们特有Service UUID.

然后,在代理方法中做区分.

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI{
    // 从广播信号中获取Service UUID 的列表
    NSArray *serviceUUIDs=advertisementData[CBAdvertisementDataServiceUUIDsKey];
    //做一下判断
    if ([serviceUUIDs containsObject:[CBUUID UUIDWithString:@"FFFF"]]){
        //欧耶,这是我们的产品1
    }else if ([serviceUUIDs containsObject:[CBUUID UUIDWithString:@"FFFF"]]){
        //欧耶,这是产品2
        //直接else当然也可以.但是要保险一些嘛
    }
}

方便,真是方便

正题

Android中,貌似没有那么轻松

当然,这就要提起Context里面说的事情了.

BLE是android 4.3引入的,所以最早拥有蓝牙更能的版本,就是API 18.

大概是由于API 18中,蓝牙搜索的方法,有点儿蛋疼,所以,他们改了新的方法吧?

API 21

先看看API 21怎么搜索蓝牙的.

BluetoothAdapter的一个静态方法

void startScan (List<ScanFilter> filters,ScanSettings settings,ScanCallback callback)
参数 参数 解释
filters List ScanFilters for finding exact BLE devices.
settings ScanSettings Settings for the scan.
callback ScanCallback Callback used to deliver scan results.

其中ScanFilter很牛逼

Service UUIDs which identify the bluetooth gatt services running on the device.
Name of remote Bluetooth LE device.
Mac address of the remote device.
Service data which is the data associated with a service.
Manufacturer specific data which is the data associated with a particular manufacturer.

这过滤方法,可比iOS的搜索强多了

然而,这个是API 21才有的……

API 18

由于,咱们要覆盖所有的,拥有BLE功能的手机用户

所以,只能用API 18了.

boolean startLeScan (UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback);

这过滤,和iOS一样了,只能用serviceUUID来过滤.

不过,通过我的实际测试,我发现serviceUuids填写的不能是不同设备的service uuid.

这样做,总是会显示没有match uuid list (有机会再好好研究一下)

那过滤两种设备,可以选择,做两遍这个方法,给不同的callback,好像还省去了用if去判断设备这个工作.

但是,如果有一大排设备需要去过滤呢……

boolean startLeScan (BluetoothAdapter.LeScanCallback callback);

直接全部搜索吧……还过滤啥呀

手动解析广播数据,进行分类,做不同的操作

BluetoothAdapter.LeScanCallback bluetoothScanCallback = new BluetoothAdapter.LeScanCallback() {

    @Override
    public void onLeScan(BluetoothDevice bluetoothDevice, int i, byte[] bytes) {
        //已经搜索过的设备,就不用重新解析啦
        if (locatorList.containsKey(bluetoothDevice.getAddress())){
            LocatorPeripheral peripheral=locatorList.get(bluetoothDevice.getAddress());
            peripheral.updateInformation(i);
        }else if(keyList.containsKey(bluetoothDevice.getAddress())){
            KeyPeripheral peripheral=keyList.get(bluetoothDevice.getAddress());
            peripheral.updateInformation(i);
        }else if(ignoreList.contains(bluetoothDevice)){
            return;
        } else {
            //这里开始重新解析
            VKPeripheral peripheral = VKPeripheral.createPeripheral(i,bytes,bluetoothDevice);
            if (peripheral instanceof LocatorPeripheral){
                //这是设备1
                locatorList.put(bluetoothDevice.getAddress(),(LocatorPeripheral) peripheral);
            }else if (peripheral instanceof KeyPeripheral){
                //这是设备2
                keyList.put(bluetoothDevice.getAddress(),(KeyPeripheral) peripheral);
            }else {
                //这不是咱们的东西
                ignoreList.add(bluetoothDevice);
                return;
            }
            //通知UI

        }
    }
};

我水平有限,所以就是这样写的~

这里有个VKPeripheral 类名也是直接从iOS版移植过来了

用来根据广播信息来新建我自己的蓝牙设备实例.

他生成的KeyPeripheral和LocatorPeripheral都是他的子类.如果不是我们需要的设备,就加到忽略的列表中去.

还有几个小坑

1.从android 6.0开始,蓝牙扫描如果需要得到结果,就必须获得定位权限(你逗我的吧…凭什么呀)

所以要把这个加到Manifest里面去

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

2.还是android 6.0的锅. 由于6.0可以动态获取权限,所以上面这个权限,直接写在Manifest里面,说不定还没用呢…
还要手动的去获取权限

找个Activity加上这一段(不唯一)

if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION},1);
}else if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.BLUETOOTH)!=PackageManager.PERMISSION_GRANTED){
    ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN},1);
}else {
    //你是不是不想用这个App!!
}

End

哎,灵活的Android呀.开放的Android呀.