一、蓝牙开发必须知道的概念
central和peripheral
中文就是中心和外设。比如手机去连接智能设备,那手机就是central,智能设备就是peripheral。大多时候都是central去连接peripheral的场景
广播和连接
peripheral会发出广播(advertisement:ædvɚ’taɪzmənt),central扫描到广播后,可以对设备进行连接,发出connect请求,peripheral接收到请求后,同意连接后,central和peripheral就建立了连接。
蓝牙应用的一般开发流程
已iOS为例,android也和这个是类似的。
建立中心角色
扫描外设(discover)
连接外设(connect)
扫描外设中的服务和特征(discover)
4.1 获取外设的services
4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值
与外设做数据交互(explore and interact)
订阅Characteristic的通知
断开连接(disconnect)
二、蓝牙开发中的常见的问题和坑
应用如何做自动重连
其实自动重连比想象的要简单许多,无论是android还是ios端,只需要在设备断开连接的委托方法中,重新调用gatt.connet或者是centralManager.connet方法就可以了,无论当时设备是否有点,是否在周围,当设备再次开会或者连接到可连接范围内,都会自动被连上,就是这么简单。
连接失败处理
分两个平台来说,iOS端也有连接失败的委托,但是好像几乎不会发生这种情况,至少我从来没遇见过,而对于同款设备,android常常会出现连接失败的情况,status != BluetoothGatt.GATT_SUCCESS ,android端开发请不要把连接失败和断开连接放在一块处理,因为断开连接可以直接尝试重新连接,而连接失败后尝试重新连接,需要加一些延时,并且需要gatt.close,清空一下状态,否则会把gatt阻塞导致手机不重启蓝牙就再也无法连接任何设备的情况。
后台运行
iOS后来运行,需要设备中info.Plist权限,key:Required background modes ,value: bluetooth-central(手机作为central) , bluetooth-peripheral(手机作为外设) 参考链接
同时连接多个设备
android很简单,创建多个gattCallback,每个gattCallback单独管理设备连接后的操作,而iOS也最好不要创建多个CBCentralManager,多个CBCentralManager理论上可以用,但是会存在多个手机版本存在不同的行为,还有一些很容易出错的问题,这块内容不细说了。使用同一个CBCentralManager,通过进入委托的peripheral的identifier区分不同的设备,进行不同的操作和处理。 在阿里的smurfs蓝牙模块中,我使用了一个dispatcher去分发每个连接设备的事件到不同实例中进行处理。
扫描广播包
所有外设,只有在发出广播包的情况下,才能被central发现,绝大多数情况下,外设被连接后就不会发出广播(也有例外),很多人遇到无法找到设备的问题,大多属于这种情况。 重复扫描问题——————
定向扫描
在扫描时候可以传入serviceUUID,这样可以扫描到特定条件的设备,提高扫描的速度,排除干扰。
如何获取mac地址
android可以直接通过getAddress得到mac地址,而iOS出于苹果的安全策略问题,无法直接获得mac地址,只能得到一个mac地址换算出来的identifier。不过在智能设备开发时,一般都会考虑到这个问题,大多数智能设备会把mac地址保存在广播数据中,不同设备可能会存在不同的位置。
Babybluetooth蓝牙库的使用
Babybluetooth是iOS的蓝牙库的封装,iOS蓝牙委托层级特别讨厌,一个委托接着一个委托,比如先进入扫描的委托,在进入链接的委托,在进入连接成功,发现服务,发现特征,读写操作,一套操作被拆分的很散,容易出错,代码不易维护,上手慢等缺点,Babybluetooth对CoreBluetooth进行了封装,把委托回调进行方法调用的方式,改成了链式方法顺序调用,直接调用baby.enjoy()方法,完成一整套操作。简化了上手难度和代码维护成本。现在开源在github上,有2300个star,蓝牙库中排名第一。
————————————————
版权声明:本文为优快云博主「贾维娣」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/u010708922/article/details/80128420
三、蓝牙搜索功能的实现:
1、得到蓝牙适配器:
BluetoothAdapter mBluetoothAdapter= BluetoothAdapter.getDefaultAdapter();
若mBluetoothAdapter为 null,则说明当前手机不支持蓝牙功能(现在几乎所有手机都支持了吧。。。)
2、判断蓝牙是否打开:
值得注意的是,强制打开蓝牙设备的情况有三种:
(1)没有任何提示,直接打开了蓝牙。如Nexus 5 Android 4.4.4 手机。
(2)会弹出提示框,提示安全警告 “ ***应用尝试开启蓝牙”,可以选择“拒绝”或“允许”。大多数手机都是这样的。
(3)强制打开蓝牙失败,并且没有任何提示。
3、注册蓝牙搜索广播接收者:
(1)Android 的广播机制:
(2)注册分为两种:静态注册和动态注册。
- 静态注册就是在AndroidManifest.xml文件中定义,注册的广播接收器必须继承BroadReceiver
- 动态注册就是在程序中使用Context.registerReceiver注册。
动态注册:
//注册设备被发现时的广播
IntentFilter filter=new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver,filter);
//注册一个搜索结束时的广播
IntentFilter filter2=new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
registerReceiver(mReceiver,filter2);
对应的静态注册如下:
<!-- 广播接收 -->
<receiver android:name="包名.类名" >
<intent-filter android:priority="1000">
<action android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED"/>
<action android:name="android.bluetooth.device.action.FOUND" />
</intent-filter>
</receiver>
4、定义广播接收:
自定义的广播接收器对象必须要继承BroadcastReceiver,然后重写onReceive方法,处理接收的数据的代码就写在这个方法里面。
两种方法:
- 自定义一个类实现BroadcastReceiver抽象类,并且实现其onReceiver(Context context, Intent intent )方法。
- 直接new BroadcastReceiver()来搞定。
public class BluetoothReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
...................
}
}
方法2:
//定义广播接收
private BroadcastReceiver mReceiver=new BroadcastReceiver(){
@Override
public void onReceive(Context context, Intent intent) {
.......................
}
};
5、开始广播:
通过 mBluetoothAdapter.startDiscovery( ); 来开始广播。当广播的事件是我们刚刚注册的事件时就会触发广播接收器,并且触发广播接收器中的onReceiver()方法。
6、解除注册:
通过 unregisterReceiver(mReceiver); 来解除刚刚的注册
二、蓝牙自动配对功能实现:
关于蓝牙的自动配对,大家可以参考我的这篇博客:Android蓝牙自动配对Demo,亲测好使!!!
----------------------------------------------------------------------------------------------------------------------------------
GATT协议
BLE连接都是建立在 GATT (Generic Attribute Profile) 协议之上。GATT 是一个在蓝牙连接之上的发送和接收很短的数据段的通用规范,这些很短的数据段被称为属性(Attribute)。它定义两个 BLE 设备通过Service 和 Characteristic 进行通信。GATT 就是使用了 ATT(Attribute Protocol)协议,ATT 协议把 Service, Characteristic以及对应的数据保存在一个查找表中,次查找表使用 16 bit ID 作为每一项的索引。
关于GATT这部分内容会在下面重点讲解。总之,中心设备和外设需要双向通信的话,唯一的方式就是建立 GATT 连接。当连接成功之后,外围设备与中心设备之间就建立起了GATT连接。
上面讲到的connect(BleDevice bleDevice, BleGattCallback bleGattCallback)
方法其实是有返回值的,这个返回值就是BluetoothGatt
。当然还有其他方式可以获取BluetoothGatt
对象,连接成功后,调用:
BluetoothGatt gatt = BleManager.getInstance().getBluetoothGatt(BleDevice bleDevice);
通过BluetoothGatt
对象作为连接桥梁,中心设备可以获取外围设备的很多信息,以及双向通信。
首先,就可以获取这个蓝牙设备所拥有的Service和Characteristic。每一个属性都可以被定义作不同的用途,通过它们来进行协议通信。下面的方法,就是通过BluetoothGatt
,查找出所有的Service和Characteristic的UUID:
List<BluetoothGattService> serviceList = bluetoothGatt.getServices();
for (BluetoothGattService service : serviceList) {
UUID uuid_service = service.getUuid();
List<BluetoothGattCharacteristic> characteristicList= service.getCharacteristics();
for(BluetoothGattCharacteristic characteristic : characteristicList) {
UUID uuid_chara = characteristic.getUuid();
}
}
作者:陈利健
链接:https://www.jianshu.com/p/795bb0a08beb
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。