Android在工业物联网或手持机终端上的应用开发中经常会遇到蓝牙相关功能需求,蓝牙4.0之后开始分为低功耗蓝牙(BLE)和经典蓝牙。这里简单介绍一下低功耗蓝牙的基本开发流程。
一、权限处理
应用中需要使用蓝牙功能,必须声明BLUETOOTH蓝牙权限。AndroidManifest文件中进行如下声明:
<!-- 使用蓝牙的权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<!-- 扫描蓝牙设备或者操作蓝牙设置 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
蓝牙通信经常与位置相关联,还必须声明定位相关权限。(注意:需要做动态权限处理)
<!-- 模糊定位权限,仅作用于6.0+ -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 精准定位权限,仅作用于6.0+ -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
二、初始化蓝牙
获取本地蓝牙适配器,同时需要判断本地设备是否支持BLE。
//设置为全局变量,方便后续使用。
private BluetoothAdapter mBtAdapter;
//1、获取本地蓝牙适配器。
mBtAdapter = BluetoothAdapter.getDefaultAdapter();
//2、判断设备是否支持蓝牙。
if (mBtAdapter == null) {
Log.i(TAG, "openBluetooth: 该设备不支持蓝牙");
} else {
//3、启用蓝牙。
if (!mBtAdapter.isEnabled()) {
mBtAdapter.enable(); //打开蓝牙
}
}
三、扫描蓝牙设备
使用startLeScan()方法查找,并在BluetoothAdapter.LeScanCallback回调中获得扫描结果。(注意:扫描这个过程非常耗电,找到所需设备,立即停止扫描)
//扫描蓝牙。
mBtAdapter.startLeScan(mBtLeScanCallback);
//扫描结果回调。
private final BluetoothAdapter.LeScanCallback mBtLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@SuppressLint("MissingPermission")
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//针对扫描结果的一些操作。
device.getName(); //获取蓝牙名称
device.getAddress(); //获取蓝牙MAC地址
}
};
//停止扫描。
mBtAdapter.stopLeScan(mBtLeScanCallback);
四、连接蓝牙
扫描结果回调中获取的蓝牙设备(device)调用connectGatt()连接方法连接蓝牙。 connectGatt(Context context, boolean autoConnect,BluetoothGattCallback callback)连接方法参数解释:context上下文对象,autoConnect布尔值,是否在可用时自动连接,BluetoothGattCallback蓝牙连接回调引用。
//将BluetoothGatt设置为全局变量,方便后续调用。
private BluetoothGatt mBtGatt;
//连接蓝牙。
mBtGatt = device.connectGatt(context, false, mBtGattCallback);
五、建立通信
连接成功后建立设备间通信主要是依靠BluetoothGattCallback回调方法,其中UUID的配置最为重要。 UUID相关信息在开发时通常蓝牙设备厂商会提供,但是在未知的情况下就必须去自动配置服务、写入、监听等UUID信息。 (UUID相关概念可自行搜索,理解更深入)
//服务、写入、监听UUID配置相关全局变量。
private BluetoothGattService mBtGattService;
private BluetoothGattCharacteristic mWriteBtGattCharacteristic;
private BluetoothGattCharacteristic mNotifyBtGattCharacteristic;
//UUID全局变量。
private UUID UUID_WRITE_SERVICES;
private UUID UUID_WRITE_CHARA;
private UUID UUID_NOTIFY_CHARA;
//连接到GATT服务器,建立通信。
private final BluetoothGattCallback mBtGattCallback = new BluetoothGattCallback() {
//成功连接到设备将回调此方法。
@SuppressLint("MissingPermission")
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
//进行蓝牙连接状态判断。
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices(); //开始发现设备的服务
Log.i(TAG, "onConnectionStateChange: 连接成功");
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "onConnectionStateChange: 连接失败");
}
}
//发现服务,一般在设备连接成功后调用,扫描到设备服务后回调此方法。
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
//判断回调服务是否成功。
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onServicesDiscovered: 回调服务连接成功");
} else {
Log.i(TAG, "onServicesDiscovered: 回调服务连接失败" + status);
}
//配置UUID。(整个BLE开发中最重要的地方,关系着通信是否成功)
//创建集合,获取服务UUID。
List<BluetoothGattService> gattServices = gatt.getServices();
//遍历服务UUID集合。
for (BluetoothGattService service : gattServices) {
//创建集合,获取特征UUID。
List<BluetoothGattCharacteristic> characteristics = service.getCharacteristics();
//遍历特征UUID。
for (BluetoothGattCharacteristic characteristic : characteristics) {
//查找并识别出监听、写入等特征UUID和对应的服务UUID。
int charaProp = characteristic.getProperties();
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_WRITE) > 0) {
UUID_WRITE_CHARA = characteristic.getUuid(); //获取写入特征UUID
UUID_WRITE_SERVICES = service.getUuid(); //获取写入服务UUID
}
if ((charaProp & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
UUID_NOTIFY_CHARA = characteristic.getUuid(); //获取监听特征UUID
}
}
}
//设置服务UUID。
mBtGattService = mBtGatt.getService(UUID_WRITE_SERVICES);
//设置写入特征UUID。
mWriteBtGattCharacteristic = mBtGattService.getCharacteristic(UUID_WRITE_CHARA);
//设置监听特征UUID。
mNotifyBtGattCharacteristic = mBtGattService.getCharacteristic(UUID_NOTIFY_CHARA);
//开启监听。
gatt.setCharacteristicNotification(mNotifyBtGattCharacteristic, true);
}
//开启监听,即建立与设备的通信的首发数据通道,BLE开发中只有当上位机成功开启监听后才能与下位机收发数据.开启监听成功则会回调此方法。
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
//判断监听开启状态。
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onDescriptorWrite: 开启监听成功,可以向设备写入命令了");
}
}
//写入数据,若写入指令成功则回调此方法,说明将数据已经发送给下位机。
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
//判断数据发送状态。
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.i(TAG, "onCharacteristicWrite: 数据发送成功");
}
}
//接收数据,若发送的数据符合通信协议,则下位机会向上位机回复相应的数据。发送的数据通过回调此方法获取。
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
//value为设备发送的数据,根据数据协议进行解析。
byte[] value = characteristic.getValue();
}
};
六、发送数据
通过BluetoothGattCharacteristic(GATT特征)配置发送的数据和写入UUID,再通过BluetoothGatt(GATT功能)去实现通信。 蓝牙间的通信通常采用16进制,所以写入写出的数据需要进行进制的转换。
//发送数据。
//设置写入,setValue(发送的数据)。
mWriteBtGattCharacteristic.setValue(data);
//设置写入特征UUID。
mWriteBtGattCharacteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
//向设备写入指令。
mBtGatt.writeCharacteristic(mWriteBtGattCharacteristic);
//进制转换工具方法。
//将字节数组byte[]转换为16进制字符串。
private String arrayToHex(byte[] arr) {
if (arr != null) {
StringBuilder sb = new StringBuilder();
for (byte b : arr)
sb.append(String.format("%02x ", b));
return sb.toString().trim();
}
return "";
}
//字符串转字节数组byte[]。
private static byte[] stringToBytes(String str) {
if (str == null || str.trim().equals("")) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for (int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
七、断开蓝牙连接
断开蓝牙连接时容易出现空引用异常, 最好是在建立通信判断回调服务成功处增加一个判断的标志位, 然后再通过判断回调服务是否成功来决定断开蓝牙连接。
//断开连接。
mBtGatt.disconnect();
总结
以上对BLE低功耗蓝牙的开发进行了简单的介绍,对于其使用仅是流于表面,此示例主要是为抛砖引玉。如有纰漏,请您给出建议,在此表示感谢!