有不少帖子都在说Android蓝牙开发的方法,可是对于其中的概念以及做用时间一直没有不是很清楚,下边整理一下相关概念性的东西,记录一下。android
蓝牙链接传输数据的过程当中,会用到如下几个概念:服务,特性,描述。一个蓝牙设备会有多个服务,每个服务都是一类操做;在这类操做下会存在几个不一样的值须要读写或者通知,每个值对应惟一一个标记,该标记便是特征值(特性characteristic),个人理解是键值(key);每个特征值又有多个不一样的属性c#
描述(descriptor): 描述是某个特征值的一个属性。每一个特征值有其指定的属性,例如长度(size);权限(permission):属性访问权限,通常有Read、Write、Notifications、Indications;值(value):属性的值,通常是咱们向设备写入的数据或者设备通知出来的数据;描述(descriptor);数组
特性(characteristic): 开发过程当中定义的一种参数,能够对其进行读、写、通知操做,对应的就是咱们须要的数据;bash
服务(service): 在存在多个特性值的状况下,通常会对其进行分类,每个分类便是一个服务(service)app
以上三个概念每个都使用惟一的UUID指定。异步
属性定义以下这般: ide
Type:属性的类型,即UUID,蓝牙标准组织对UUID进行了分类,以下:ui
0x1800 - 0x26FF 用做服务类通用惟一识别码
0x2700 - 0x27FF 用于标识计量单位
0x2800 - 0x28FF 用于区分属性类型
0x2900 - 0x29FF 用做特性描述
0x2A00 - 0x7FFF 用于区分特性类型
复制代码
作蓝牙BLE开发过程当中有如下类须要使用spa
BluetoothGatt: 通用属性协议。定义BLE通信的基本规则,对通信过程最上层的封装,例如从新链接蓝牙设备,发现蓝牙设备的Service等;线程
BluetoothGattService: 服务。经过BluetoothGatt实例调用getService(UUID)获取,即前面说的对特性分组的服务;用于获取和管理其包含的特性;
BluetoothGattCharacteristic: 特性。经过BluetoothGattService实例调用getCharacteristic(UUID)获取,是GATT通信中的最小数据单元;咱们想蓝牙设备发送数据、接收蓝牙设备的通知都须要用到,是进行通信的最小的操做对象;
BluetoothGattDescriptor: 特性描述符。对特性的额外描述,包括但不只限于特征的单位、属性等。
BluetoothDevice: 表明一个远程蓝牙设备。这个类可使咱们链接其表明的蓝牙设备或者获取一些有关它的信息,例如它的名字、地址和绑定状态等;
BluetoothAdapter: Android中的蓝牙适配器。用于操做蓝牙硬件,例如开启蓝牙扫描,根据已知MAC地址实例化一个BluetoothDevice用于链接蓝牙设备的操做等。
以上就是在蓝牙BLE通讯过程当中须要用到的一些类和概念。
具体看JBD的Android BLE 蓝牙开发入门
<uses-permission android:name="android.permission.BLUETOOTH"/> 使用蓝牙所须要的权限
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> 使用扫描和设置蓝牙的权限(申明这一个权限必须申明上面一个权限)
复制代码
以上BLUETOOTH和BLUETOOTH_ADMIN两个权限是蓝牙开发必须的,同时不一样的SDK版本还会要求其余的权限。在Android 5.0 以后,须要在manifest中声明GPS硬件模块功能的使用。
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
复制代码
在Android 6.0及以上,还须要打开位置权限。若是应用没有位置权限,蓝牙扫描功能不能使用(其它蓝牙操做例如链接蓝牙设备和写入数据不受影响)。同时位置权限是敏感权限,须要进行权限动态申请及判断。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
复制代码
外围设备开启蓝牙后,会广播出许多的关于该设备的数据信息,例如 mac 地址,uuid 等等。经过这些数据咱们能够筛选出须要的设备。使用BluetoothAdapter实例扫描蓝牙设备,低版本SDK中有两个方法能够调用
//扫描含有特定UUID Service的蓝牙设备
boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
//扫描所有蓝牙设备
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
复制代码
在SDK>=21时这两个方法已经被废弃,官方建议使用BluetoothLeScanner中的如下方法。
//该方法是在API版本26开始添加的,能够在主程序再也不运行的时候再后台进行扫描操做,当发现符合过滤条件的设备时会向PendingIntent指定的操做发送通知,进行后续操做。
int startScan (List<ScanFilter> filters,ScanSettings settings,PendingIntent callbackIntent)
//从API版本21开始添加
//搜索所有蓝牙设备,使用默认扫描设置,而且没有扫描过滤器
void startScan (ScanCallback callback)
//从API版本21开始添加
//搜索符合过滤条件的设备
void startScan (List<ScanFilter> filters,ScanSettings settings,ScanCallback callback)
复制代码
ScanFilter: 是一个蓝牙扫描过滤器,能够指定扫描条件,该参数能够为空,具体支持以下,后边两个应该不常使用,翻译可能不太准确:
ScanSettings: 能够配置扫描设置,采用构造器模式。能够设置
等,具体能够查看源代码或者Google官方文档。
BluetoothAdapter.LeScanCallback: 是扫描结果回调,第一个参数是表明蓝牙设备的类;第二个参数是蓝牙的信号强弱指标,经过蓝牙的信号指标,咱们能够大概计算出蓝牙设备离手机的距离。计算公式为:d = 10^((abs(RSSI) - A) / (10 * n));第三个参数是蓝牙广播出来的广播数据。
ScanCallback: 是startScan的扫描回调,自己是一个抽象类;有三个方法可选实现方法;
//扫描结束是返回全部匹配的设备列表
void onBatchScanResults(List<ScanResult> results)
//当发现设备广播时回调,第一个参数是回调类型即前边说的ScanSettings中设置的回调类型,第二个参数是扫描到的蓝牙设备信息
void onScanResult(int callbackType, ScanResult result)
//当启动扫描失败时调用该方法
void onScanFailed(int errorCode)
复制代码
而后就能够调用startLeScan或者startScan开始搜索设备了。若想中止扫描须要根据开始扫描方法肯定调用stopLeScan或者stopScan;出如今回调中的设备会重复出现,因此须要手动过滤掉已经发现的设备。另外须要强调的是终止扫描时传入的回调必须是开始扫描时传入的回调,不然扫描动做不会中止。
链接蓝牙设备能够经过 BluetoothDevice#ConnectGatt 方法链接,也能够经过 BluetoothGatt#connect 方法进行从新链接。
//BluetoothDevice#connectGatt
//第二个参数表明是否须要自动链接,true-表示若是设备断开了,会不断的尝试自动链接,false-表示只进行一次链接尝试
BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
复制代码
当调用蓝牙的链接方法以后,蓝牙会异步执行蓝牙链接的操做,若是链接成功会回调 BluetoothGattCalbackl#onConnectionStateChange 方法。这个方法运行的线程是一个 Binder 线程,因此不建议直接在这个线程处理耗时的任务,由于这可能致使蓝牙相关的线程被阻塞。
void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
复制代码
这一个方法有三个参数,第一个就蓝牙设备的Gatt服务链接类。第二个参数表明是否成功执行了链接操做,若是为BluetoothGatt.GATT_SUCCESS表示成功执行链接操做,第三个参数才有效,不然说明此次链接尝试不成功。有时候,咱们会遇到 status == 133 的状况,根据网上大部分人的说法,这是由于Android最多支持链接6到7个左右的蓝牙设备,若是超出了这个数量就没法再链接了。因此当咱们断开蓝牙设备的链接时,还必须调用 BluetoothGatt#close方法释放链接资源。不然,在屡次尝试链接蓝牙设备以后很快就会超出这一个限制,致使出现这一个错误再也没法链接蓝牙设备。 第三个参数表明当前设备的链接状态,若是newState==BluetoothProfile.STATE_CONNECTED说明设备已经链接,能够进行下一步的操做了(发现蓝牙服务,也就是Service)。当蓝牙设备断开链接时,这一个方法也会被回调,其中的newState==BluetoothProfile.STATE_DISCONNECTED。
在成功链接到蓝牙设备以后才能进行这一个步骤,也就是说在BluetoothGattCalbackl#onConnectionStateChang方法被成功回调且表示成功链接以后调用BluetoothGatt#discoverService 这一个方法。当这一个方法被调用以后,系统会异步执行发现服务的过程,直到 BluetoothGattCallback#onServicesDiscovered被系统回调以后,手机设备和蓝牙设备才算是真正创建了可通讯的链接。
到这一步,咱们已经成功和蓝牙设备创建了可通讯的链接,接下来就能够执行相应的蓝牙通讯操做了,例如写入数据,读取蓝牙设备的数据等等。
当咱们发现服务以后就能够经过BluetoothGatt#getService获取BluetoothGattService,接着经过 BluetoothGattService#getCharactristic获取BluetoothGattCharactristic。经过BluetoothGattCharactristic#readCharacteristic方法能够通知系统去读取特定的数据。若是系统读取到了蓝牙设备发送过来的数据就会调用BluetoothGattCallback#onCharacteristicRead方法。经过BluetoothGattCharacteristic#getValue能够读取到蓝牙设备的数据。
和读取数据同样,在执行写入数据前须要获取到BluetoothGattCharactristic。接着执行如下步骤:
BLE app一般须要获取设备中characteristic 变化的通知。以下为一个Characteristic设置一个监听:
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
//其中的参数为00002902-0000-1000-8000-00805f9b34fb
BluetoothGattDescriptor descriptor=characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
复制代码
以上代码,除了经过 BluetoothGatt#setCharacteristicNotification开启Android端接收通知的开关,还须要往Characteristic的Descriptor属性写入开启通知的数据开关使得当硬件的数据改变时,主动往手机发送数据。
蓝牙接收数据的特性和蓝牙发送通知数据的特性不能相同,不然在高版本系统上没法接收到通知的数据
当咱们链接蓝牙设备完成一系列的蓝牙操做以后就能够断开蓝牙设备的链接了。经过 BluetoothGatt#disconnect能够断开正在链接的蓝牙设备。当这一个方法被调用以后,系统会异步回调 BluetoothGattCallback#onConnectionStateChange方法。经过这个方法的 newState 参数能够判断是链接成功仍是断开成功的回调。
因为 Android 蓝牙链接设备的资源有限,当咱们执行断开蓝牙操做以后必须执行 BluetoothGatt#close 方法释放资源。须要注意的是经过 BluetoothGatt#close 方法也能够执行断开蓝牙的操做,不过BluetoothGattCallback#onConnectionStateChange 将不会收到任何回调。此时若是执行 BluetoothGatt#connect方法会获得一个蓝牙 API 的空指针异常。因此,咱们推荐的写法是当蓝牙成功链接以后,经过BluetoothGatt#disconnect 断开蓝牙的链接,紧接着在BluetoothGattCallback#onConnectionStateChange 执行 BluetoothGatt#close 方法释放资源。