Anadroid-蓝牙使用以及知识总结
本文先介绍蓝牙4.0的使用 有时间再补上2.0
哪里有问题欢迎大家指出
1. 权限和相关基础知识介绍
1.1 获取蓝牙权限
“android:required=”true”表示apk只有在具有bluetooth_le属性的系统里运行,这个4.3之前android系统没有
文章中的权限相关问题都限于Android6.0以前的版本 6.0之后权限需要动态申请 以后会补上
<uses-featureandroid:name="android.hardware.bluetooth_le"android:required="true"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH"/>
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN"/>
1.2 蓝牙4.0相关协议
bluetooth_le是指BLE( Bluetooth Low Energy )BLE是蓝牙4.0的核心Profile,主打功能是快速搜索,快速连接,超低功耗保持连接和传输数据,弱点是数据传输速率低,由于BLE的低功耗特点,因此普遍用于穿戴设备。Android 4.3才开始支持BLE API,所以请各位客官把 本文代码 运行在蓝牙4.0和Android 4.3及其以上的系统,另外本文所用的BLE终端是一个蓝牙4.0的串口蓝牙模块。
Bluetooth Profile 是蓝牙的一个很重要特性,就是所有的Bluetooth产品都无须实现全部的Bluetooth规范。为了更容易的保持Bluetooth设备之间的兼容,Bluetooth规范中定义了 Profile。Profile定义了设备如何实现一种连接或者应用的一个通用的规范,按照这个规范来收发数据。你可以把Profile理解为连接层或者应用层协议。
GATT (Generic Attribute Profile)
通过BLE连接,读写属性类小数据的Profile通用规范。现在所有的BLE应用Profile都是基于GATT的。
ATT (Attribute Protocol)
GATT是基于ATT Protocol的。ATT针对BLE设备做了专门的优化,具体就是在传输过程中使用尽量少的数据。每个属性都有一个唯一的UUID,属性将以characteristics and services的形式传输。
Service
Characteristic的集合。
Characteristic
Characteristic可以理解为一个数据类型,它包括一个value和0至多个对次value的描述(Descriptor)。
Descriptor
对Characteristic的描述,例如范围、计量单位等。
关系 BLE分为三部分Service、Characteristic、Descriptor,这三部分都由UUID作为唯一标示符。 一个蓝牙4.0的终端可以包含多个Service,一个Service可以包含多个 Characteristic,一个 Characteristic包含一个Value和多个 Descriptor,一个 Descriptor包含一个Value。
1.3 Android中蓝牙4.0相关类
BluetoothGatt
继承BluetoothProfile,通过BluetoothGatt可以连接设备(connect),发现服务(discoverServices),并把相应地属性返回到 BluetoothGattCallback
BluetoothGattCharacteristic
相当于一个数据类型,它包括一个value和0~n个value的描述(BluetoothGattDescriptor)
BluetoothGattDescriptor
描述符,对Characteristic的描述,包括范围、计量单位等
BluetoothGattService
服务,Characteristic的集合。
BluetoothProfile
一个通用的规范,按照这个规范来收发数据。
BluetoothGattCallback
已经连接上设备,对设备的某些操作后返回的结果。这里必须提醒下,已经连接上设备后的才可以返回,没有返回的认真看看有没有连接上设备。
BluetoothAdapter 应用通过一个统一的类BluetoothAdapter(蓝牙本地适配器类)与这些蓝牙设备协议对应的BLUETOOTH API进行交互。
BluetoothAdapter是所有蓝牙对象交互和执行蓝牙操作的入口:包括调用BLUETOOTH profile API,发现其它蓝牙设备、查询配对成功的设备、使用已知的MAC地址实例化蓝牙设备、创建一个BluetoothServerSocket对象来监听其它蓝牙设备以及根据地址实例化蓝牙设备等操作。
获取BluetoothAdapter对象:
android 4.3 以下(API<18)的版本使用如下方式获得BluetoothAdapter单例对象。
BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
android 4.3 以下(API>=18)的版本使用如下方式获得BluetoothAdapter单例对象。
BluetoothManager bluetoothManager =
(BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter mAdpter=bluetoothManager.getAdapter();
2. 开始搜索设备
搜索之前 先检查是否支持BLE
if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this,R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
}
如果蓝牙没打来,还需要打开蓝牙
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
后台打开蓝牙,不做任何提示,这个也可以用来自定义打开蓝牙对话框啦
mBluetoothAdapter.enable();
后台关闭蓝牙
mBluetoothAdapter.disable();
搜索蓝牙的常用方法
private void connect(){
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, 10000);
mBluetoothAdapter.startLeScan(mLeScanCallback);
}
1.10秒的时间内搜索蓝牙startLeScan()方法只能搜索支持BLE的蓝牙设备.
2.mBluetoothAdapter.startLeScan()方法和mBluetoothAdapter.startDiscovery()互斥
3.这里有一个回调,通过它获得蓝牙的信息 方法如下:
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.e("rssi", "RSSI=:"+rssi+""); //接收信号强度(负值)
Log.e("name", device.getName());//蓝牙设备名称
Log.e("andrass",device.getAddress()); //蓝牙MAC地址
Log.e("a","发现蓝牙"+device.getAddress()+"状态"+device.getBondState()+"type"+device.getType()+device.describeContents());
}
});
}
};
此时,如果发现支持BLE的蓝牙设备可以获得一些基本信息,此处可以筛选我们想要连接的蓝牙设备.
扩展:
RSSI - 接收信号强度(负值)
根据rssi可计算蓝牙发射端和蓝牙接收端的距离
计算公式:
d = 10^((abs(RSSI) - A) / (10 * n))
其中:
d - 计算所得距离
A - 发射端和接收端相隔1米时的信号强度
n - 环境衰减因子
3. 开始连接设备
3.1
以给定的MAC地址去创建一个 BluetoothDevice 类实例(代表远程蓝牙实例)。即使该蓝牙地址不可见,也会产生一个BluetoothDevice 类实例。
如果该蓝牙设备MAC地址不能被识别,其蓝牙Name为null。
如果MAC address无效,抛出IllegalArgumentException。
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
直接创建连接
BluetoothGatt mBluetoothGatt = device.connectGatt(context, false, mGattCallback);
在这之前,需要创建一个BluetoothGattCallback的实例,用来接收是否发现服务以及连接状态的变化
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED)
{
// 试图发现服务连接成功后
Log.e(TAG, "启动服务发现:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 试图发现服务连接失败后
Log.e(TAG, "服务器断开");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
Log.e("BluetoothGattCallback", "onServicesDiscovered");
if (status == BluetoothGatt.GATT_SUCCESS) {
//发现服务成功 在此处可得到服务列表
Log.i(TAG, "onServicesDiscovered");
} else {
Log.e(TAG, "onservicesdiscovered收到: " + status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
Log.e("BluetoothGattCallback", "onCharacteristicRead");
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onCharacteristicRead_success");
}
}
/**
* 处理返回数据。
*/
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
Log.e("BluetoothGattCallback", "onCharacteristicChanged");
}
/**
* 获取信号强度
*/
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
Log.e("BluetoothGattCallback", "onReadRemoteRssi");
Log.e("a", "返回读出的值:"+rssi);
};
};
通过device.connectGatt()方法连接蓝牙设备后,无论成功与否都会进入mGattCallback.onConnectionStateChange()方法中,在此处判断连接是否成功.链接成功后可以通过mBluetoothGatt.discoverServices()方法启动发现服务功能.
boolean state = mBluetoothGatt.discoverServices()
别忘了BLE主要的结构就在这里(Service->Characteristic->Descriptor).如果发现服务成功,则会调用mGattCallback.onServicesDiscovered()方法.代码在上面已经给出.
此时,在mGattCallback.onServicesDiscovered()执行时,说明已经可以获取到Service列表
List<BluetoothGattService> gattServices = mBluetoothGatt.getServices()
这里可以根据你的蓝牙设备来查找相应的Service或者Characteristic,常用方法如下:
String serviceUuid = service.getUuid().toString()
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
String characteristicsUuid = characteristic.getUuid().toString();
int property = characteristic.getProperties();
//通过uuid直接得到BluetoothGattService
BluetoothGattService linkLossService =mBluetoothGatt
.getService(UUID.fromString("49535343-fe7d-4ae5-8fa9-9fafd205e455"));
//通过uuid直接得到BluetoothGattCharacteristic
BluetoothGattCharacteristic alertLevel =linkLossService.getCharacteristic(UUID.fromString("49535343-8841-43f4-a8d4-ecbe34729bb3"));
对于Service,Characteristic,Descriptor不太熟悉的可以回到本文前面再看一下.
一般来说,Characteristic是手机与BLE终端交换数据的关键,Characteristic有较多的跟权限相关的字段,例如PERMISSION和PROPERTY,而其中最常用的是PROPERTY.Characteristic的PROPERTY可以通过位运算符组合来设置读写属性,例如READ|WRITE、READ|WRITE_NO_RESPONSE|NOTIFY,因此读取PROPERTY后要分解成所用的组合.
if ((property | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
mBluetoothGatt.readCharacteristic(characteristic);
mBluetoothGatt.setCharacteristicNotification(characteristic, true);
}
获取到characteristic时,通过readCharacteristic()方法可以获取读属性,可以读取数据.通过setCharacteristicNotification()方法启用或禁用通知给特性,可以获取来自蓝牙设备的通知.
readCharacteristic()会回调mGattCallback.onCharacteristicRead()方法.这时传入的characteristic参数可能带有设备的返回值,可自行处理.
基本到这里蓝牙设备已经连接成功,可能不同的设备连接方式会有所出入,这里只是一个大概的流程,总体的思路就是:
开始搜索BLE设备 -> 找到,筛选设备 -> 通过MAC地址创建设备对象 -> 获取gatt连接 -> 获取设备服务列表 -> 获取特征项(连接完成)
4. 传输数据
Android和蓝牙设备连接完成后,取得特征项后将通讯指令写入
注意:在写入数据和接受蓝牙设备传回的数据时要注意数据格式,一般的蓝牙设备的开发文档都会注明.
boolean state = characteristic.setValue(bb);
然后将特征项发送出去
mBluetoothGatt.writeCharacteristic(characteristic);
此时在会回调mGattCallback的onCharacteristicWrite()方法,可以回去写入状态等信息.
当蓝牙设备返回信息时,会调用mGattCallback的onCharacteristicChanged()方法.
最后处理返回结果,或者发送下一条指令.
获取信号强度
mBluetoothGatt.readRemoteRssi()
该方法可以获得当前的信号强度,会回调mGattCallback的onReadRemoteRssi()方法
4. 断开连接
mBluetoothGatt.close();
一句代码就能断开连接
提示
在设计蓝牙功能的时候可以将其封装到Service中,降低代码耦合,提高控制蓝牙的简洁度.