android BLE从入门到精通开发

【android BLE从入门到精通开发】目前智能家居都被看成是下一个科技爆发点,而智能家居里面使用的技术,响应最高的就算是BLE了,下面,我们说一下android怎么开发BLE,和要注意的一些问题:
1.首先,得知道,android是从android4.3版本才开始支持BLE的,所以,开发的前提就是要知道系统的支持:

if (android.os.Build.VERSION.SDK_INT < 18) { // 说明sdk不够高版本 }

2.声明权限,BLE,就是低功耗蓝牙的英文缩写,所以,就是要开启蓝牙的权限,在manifest添加:

3.获取适配器,这里需要注意,和经典蓝牙有个点区别(千万别搞错了):
mBluetoothManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = mBluetoothManager.getAdapter();

4.查看手机蓝牙是否开启,如果未开启,则需要主动开启(不推荐),或者提示用户开启(推荐):
if (!mBluetoothAdapter.isEnabled()) { startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE), 0); // 弹对话框的形式提示用户开启蓝牙 //mBluetoothAdapter.enable(); // 强制开启,不推荐使用 }// 注册个广播监听这个值,就可以获取到蓝牙的开关状态 if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { // 蓝牙开关发生变化 // 这里可以直接使用mBluetoothAdapter.isEnabled()来判断当前蓝牙状态 return; }

5.接下来就是扫描BLE,这里有2个部分,android4.3-5.0的版本,和5.0+的版本:
// 4.3-5.0版本 mBluetoothAdapter.stopLeScan(lescancallback); // 停止扫描 mBluetoothAdapter.startLeScan(lescancallback); // 开始扫描 private LeScanCallback lescancallback = new LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) { // device 就是扫描到的蓝牙对象,里面各种跟蓝牙有关的信息 // rssi信号强度,这个值是个负数,范围一般为0到-100,负数越大,代表信号越弱,一般如果超过-90,连接会出现不理想的情况 // scanRecord广播数据,里面的数据就是蓝牙设备希望手机在连接它之前,让手机知道的信息(稍后的篇章讲解广播数据的组成格式,并且如何解析) } }; // 5.0+版本 BluetoothLeScanner scaner = mBluetoothAdapter.getBluetoothLeScanner(); // android5.0把扫描方法单独弄成一个对象了 scaner.stopScan(mScanCallback); // 停止扫描 scaner.startScan(mScanCallback); // 开始扫描 private ScanCallback mScanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); // callbackType:确定这个回调是如何触发的 // result:包括4.3版本的蓝牙信息,信号强度rssi,和广播数据scanRecord } @Override public void onBatchScanResults(List results) { super.onBatchScanResults(results); // 批量回调,一般不推荐使用,使用上面那个会更灵活 } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); // 扫描失败,并且失败原因 } };

因为有2种回调方法,最好在开发的时候,重新打包封装成一个类,根据系统不同,而分别调用不同的方法,因为google会想到推出新方法,那固然会在以后的某个版本把老方法取消。
需要注意的是:
  • 在扫描前,最好先调用一次停止扫描
  • 而且扫描和停止扫描里面的参数对象必须是同一个,所以,这里不能用匿名的方式来创建扫描回调
  • 不要长时间开始扫描,停止扫描,开始扫描,这样的循环,扫描是很耗电的,部分机型这样会导致蓝牙死机
  • 当扫描到自己需要的设备时,停止扫描。
6.连接BLE,大部分手机可以通过MAC来直接连接,不需要扫描,小部分一定要先扫描到才可以进行连接(目前所知型号红米1s):
mBluetoothAdapter.stopLeScan(lescancallback); // 停止扫描,连接前,必须停止扫描,不然,失败率很高 device = mBluetoothAdapter.getRemoteDevice(mac); // 通过mac地址获取蓝牙对象,或者可以直接用扫描到的对象,效果都是一样 mBluetoothGatt = device.connectGatt(context, false, mGattCallback); // BLE连接回调 private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, final int status, final int newState) { super.onConnectionStateChange(gatt, status, newState); handl.post(new Runnable() { public void run() { if (status != BluetoothGatt.GATT_SUCCESS) { // 连接失败判断 return; } if (newState == BluetoothProfile.STATE_CONNECTED) { // 连接成功判断 mBluetoothGatt.discoverServices(); // 发现服务 return; } if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 连接断开判断 return; } }@Override public void onServicesDiscovered(BluetoothGatt gatt, final int status) { super.onServicesDiscovered(gatt, status); if (status != BluetoothGatt.GATT_SUCCESS) { // 发现服务失败 return; } //不是失败的情况就是成功 }@Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, final int status) { super.onDescriptorWrite(gatt, descriptor, status); if (status != BluetoothGatt.GATT_SUCCESS) {// 写Descriptor失败 return; } //不是失败的情况就是成功 }@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); //BLE设备主动向手机发送的数据时收到的数据回调 characteristic.getValue(); // 通过这个方法来提取收到的数据 } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); if (status != BluetoothGatt.GATT_SUCCESS) {// 写数据失败 return; } } // 还有很多其他回调方法,这里就不一一介绍了 };

需要注意的是:
  • BLE连接成功并不意味着可以通信,需要发现服务后,才可以正常通信,不要问我为什么,因为这就是BLE协议
  • 以手机开启蓝牙为起始点,对某个设备第一次连接,速度会比较慢,这个速度主要取决于BLE设备的广播频率(不懂广播频率?就问你们固件工程师吧),第二次以后,会快很多,因为手机会缓存很多信息,比如虽然需要重新发现服务,但是实际上,手机并没有去发现服务,而是用了缓存的服务。所以,会比较快
  • 中间可能会有回调很多错误,记住一点,不要去深究是什么原因,因为android文档上面没有说,所以,最好的解决方法就是,一旦出现错误,就断开,重新连接,因为BLE底层会对失败进行多次重试,如果告诉你出错误了,也就是说其实底层已经重试了很多次了,还是错误。所以,直接断开,重连就行了
7.获取通信特征值:BLE连接成功后,就需要发现服务,发现服务后,就需要发现特征值,这个特征值才是真正BLE通信需要用到的东西。发现服务后,服务里面有很多BluetoothGattService,这个BluetoothGattService,你可以看做是一个个的文件夹,里面包含了很多文件(特征值BluetoothGattCharacteristic)
private static final String DATA_SERVICE_UUID = "0000f1f0-0000-1000-8000-00805f9b34fb"; BluetoothGattService data_service = mBluetoothGatt.getService(UUID.fromString(DATA_SERVICE_UUID)); // 先获取BluetoothGattService BluetoothGattCharacteristic txd_charact = data_service.getCharacteristic(UUID.fromString(TXD_CHARACT_UUID)); // 再通过BluetoothGattService获取BluetoothGattCharacteristic特征值

8.手机往BLE设备写入数据:获取到特征值后,就可以正式的进行通信了,手机往设备写数据:
txd_charact.setValue(senddatas); // 往通道写数据

注意:因为大部分BLE只支持最大20byte的通信,所以,如果有大于20byte的数据需要发送,请拆成多个20byte以内的数组,进行分包发送(当然后面我们会讲如何一次性发送超过20byte的方法)
9.BLE设备主动往手机写数据:这个功能叫做notify、indecation(用这种方式的比较少,微信BLE就是用这种方式),汉语叫可通知属性(不是所有的通道都有这个属性),要主动去开启(有些不规范的BLE设备已经默认开启,可以省略这步)。成功与否,需要看回调(看上面第6点)
if (0 != (charact.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY)) { // 查看是否带有可通知属性notify mBluetoothGatt.setCharacteristicNotification(charact, true); BluetoothGattDescriptor descriptor = charact.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); // 这包数据什么意思,可以不用管,反正是固定这包数据就是了。 descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); } else if (0 != (charact.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE)) {// 查看是否带有indecation属性 mBluetoothGatt.setCharacteristicNotification(charact, true); BluetoothGattDescriptor descriptor = charact.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE); mBluetoothGatt.writeDescriptor(descriptor); }

到这里还不够,上面的只是设置BLE设备可以主动向手机发送数据,手机还需要开启是否接收BLE设备发来的数据:这里设置成功与否没有回调,运行了这段代码,就设置成功了
mBluetoothGatt.setCharacteristicNotification(charact, enable); // 设置手机是否接收BLE设备发来的数据

10.断开连接:也有相应的回调(看上面第6点)
mBluetoothGatt.disconnect();

需要注意的是:android的BLE有很多问题,如果一定时间(一般5秒,根据BLE设备不同而不同)内没有回调断开,则需要强制关一下手机蓝牙,至于是强制开是强制开,还是用提示用户的方式,根据自己的需求而定

    推荐阅读