Android:BLE智能开发
目录
- 前言
- BLE是个什么
- BLE中的角色分工
- 主要的关键词和概念
- GATT(Generic Attribute Profile )
- Characteristic
- Service
- Android如何使用BLE
- 蓝牙权限
- APP和BLE外设交互流程
- 后记
前言
前些年,智能硬件炒的挺火的,那今天,咱就来说说智能硬件那些事。BLE是智能硬件的一种通讯方式,通过BLE连接,iOS & Android手机和智能硬件就可以进行自定义的交互了。交互的体验如何,很大程度上取决于智能硬件的驱动工程师驱动写的好不好,以及App的代码质量如何。
笔者曾参与过多款BLE智能硬件的开发,许久不用,怕忘了,把自己的整理的一些知识记录与此,同时也希望能够给一些同学带来帮助。本文将尽力向读者讲清楚BLE是什么,以及在实际Android开发中该如何使用BLE。
Android如何使用BLE
蓝牙权限
使用BLE需要两个权限
<uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
如果你想要APP只适配具备BLE的手机,那个可以再添加一个硬件权限特性
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
APP和BLE外设交互流程
APP和BLE外设交互的一个大概流程就是:
- BLE外设打开电源
- APP初始化蓝牙
- APP扫描周边BLE外设
- APP连接到周边BLE外设
- APP读写BLE外设
- 交互完成,APP向BLE外设写入关机/待机指令(可选)
- BLE外设关机
- APP关闭本地蓝牙连接
以下将逐步利用代码进行讲解APP和BLE外设交互.
初始化BLE
Java代码判断当前手机是否支持BLE低功耗蓝牙
// 判断手机是否支持BLE
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();// 如果手机不支持BLE就关闭程序,仅供参考
}
初始化蓝牙管理者和适配器,这2个对象是ble通讯的基石.
// 初始化蓝牙管理者和适配器,这2个对象是ble通讯的基石.
private BluetoothAdapter mBluetoothAdapter;
...
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
跳转到系统蓝牙设置界面
private BluetoothAdapter mBluetoothAdapter;
...
// 验证蓝牙是否已打开,如果没打开就提示用户跳转打开.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
APP扫描周边BLE外设
需要实现一个BluetoothAdapter.LeScanCallback回调接口,得到扫描结果。该接口只有一个回调方法:
/** * @param device 被手机蓝牙扫描到的BLE外设实体对象 * @param rssi 大概就是表示BLE外设的信号强度,如果为0,则表示BLE外设不可连接。 * @param scanRecord 被扫描到的BLE外围设备提供的扫描记录,一般没什么用 */ public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord)
由于扫描BLE设备比较消耗资源,官方推荐间歇性扫描,示例代码如下
private BluetoothAdapter mBluetoothAdapter;
private boolean mScanning;
private Handler mHandler;
// 每扫描10s休息一下
private static final long SCAN_PERIOD = 10000;
private BluetoothAdapter.LeScanCallback mLeScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
// TODO 这里可以进行连接操作,连接操作见下一小节
if (device != null && device.getName() != null && device.getName().contain("你的产品名称")){
// 连接设备
connectDevice(device);
// 停止扫描
scanLeDevice(false);
}
}
});
}
};
...
/**
* @param enable 是否进行扫描,false则停止扫描
*/
private void scanLeDevice(final boolean enable) {
if (enable) {
// 利用Handler进行间歇性扫描,每次扫描时间:10s
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
// 停止扫描
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
APP连接周边BLE外设
连接操作是进行手机和BLE外设交互的基础,请看下面connectDevice(BluetoothDevice)方法实现。
分两步走:
1. 判断该设备是否连接过,连接过则首先尝试直接连接:BluetoothGatt.connect()
2. 首次连接或者直连失败使用:BluetoothDevice.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
public boolean connectDevice(final BluetoothDevice device) {
if (mBluetoothAdapter == null || device == null) {
Log.w(TAG,
"BluetoothAdapter not initialized or unspecified address.");
return false;
}
String address = device.getAddress();
// 之前连接过的设备,尝试直接连接。mBluetoothDeviceAddress表示刚才连接过的设备地址
if (mBluetoothDeviceAddress != null
&& address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
Log.d(TAG,
"Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {// 连接成功
// 修改连接状态变量
mConnectionState = STATE_CONNECTING;
return true;
} else {
return false;
}
}
final BluetoothDevice remoteDevice = mBluetoothAdapter.getRemoteDevice(address);
if (remoteDevice == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
mBluetoothGatt = remoteDevice.connectGatt(context, false, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
// 将当前连接上的设备地址赋值给连接过的设备地址变量
mBluetoothDeviceAddress = address;
// 改变连接状态变量
mConnectionState = STATE_CONNECTING;
return true;
}
连接BEL外设时,需要一个实现回调接口以得到连接状态,BluetoothGattCallback大概实现如下:
private final BluetoothGattCallback mGattCallback =
new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
// 该方法在连接状态改变时回调,newState即代表当前连接状态
String intentAction;
// 连接上了
if (newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
// 改变蓝牙连接状态变量
mConnectionState = STATE_CONNECTED;
// 发送自定义广播:连接上了
broadcastUpdate(intentAction);
// 当前外设相当于前面章节提到的Server角色:提供数据被手机读取
Log.i(TAG, "Connected to GATT server.");
// 获取读/写服务:Service。该方法会触发下面的onServicesDiscovered()回调
mBluetoothGatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {// 断开连接了
intentAction = ACTION_GATT_DISCONNECTED;
mConnectionState = STATE_DISCONNECTED;
Log.i(TAG, "Disconnected from GATT server.");
// 发送自定义广播:断开了连接
broadcastUpdate(intentAction);
}
}
@Override
// 该方法在蓝牙服务被发现时回调。由上述的mBluetoothGatt.discoverServices()触发结果。
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
// 发现服务。status表示发现服务的结果码
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
// TODO 从发现的Service来找出读数据用的BluetoothGattCharacteristic和写数据用的BluetoothGattCharacteristic。
initReadAndWriteCharacteristic(gatt.getServices());
} else {// 未发现服务
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
// 读取操作的回调结果
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
@Override
// 写入操作的回调结果
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
};
...
};
...
找出读写”数据包”的”搬运工”
下面是找出读写”搬运工”BluetoothGattCharacteristic的initReadAndWriteCharacteristic()代码实现
BluetoothGattCharacteristic mReadCharacteristic;
BluetoothGattCharacteristic mWriteCharacteristic;
public void initReadAndWriteCharacteristic(
List<BluetoothGattService> gattServices) {
if (gattServices == null)
return;
// 遍历所有的 GATT Services.
for (BluetoothGattService gattService : gattServices) {
if (!gattService.getUuid().toString().trim().equalsIgnoreCase("这里是你期望的Service的uuid,由你司智能外色的驱动工程师决定"))
continue;
List<BluetoothGattCharacteristic> gattCharacteristics = gattService.getCharacteristics();
// 遍历当前Service中所有的Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
if (gattCharacteristic.getUuid().toString().trim().equalsIgnoreCase(""这里是你期望的写数据的uuid,由你司驱动工程师决定"")) {
mWriteCharacteristic = gattCharacteristic;
} else if (gattCharacteristic.getUuid().toString().trim().equalsIgnoreCase("这里是你期望的读数据的uuid,由你司驱动工程师决定")) {
mReadCharacteristic = gattCharacteristic;
}
}
}
}
至此,我们就拿到了可携带读写数据的“搬运工”-『mReadCharacteristic & mWriteCharacteristic』,下面就可以和智能硬件进行交互了。
APP读取BLE外设蓝牙数据
想要读取BLE外设的数据时,比如:心跳速率,电量等等。可通过下面方式。
// 告诉”搬运工“我想知道BLE外设当前数据,将回调BluetoothGattCallback接口的onCharacteristicRead()方法
mBluetoothGatt.readCharacteristic(mReadCharacteristic);
// 读取BLE蓝牙数据操作的回调方法
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
// ”搬运工“把”数据包“搬出来了
byte[] data = characteristic.getValue();
// 根据驱动工程师给的协议文档,解析该数组,该处假设数组0位上表示心跳速率
int heartRateR = data[0];// 得到心跳速率,做相应UI更新和操作
}
}
}
APP向BLE外设写入数据
比如说你想告诉BLE外设让他锁屏,或者进行某个动作,APP向操纵BLE外设时可通过以下方式
// 根据驱动工程师给的协议文档,组织一个数组命令
byte[] data = getData();
// 将该条命令“数据包”给“搬运工"
mWriteCharacteristic.setValue(data);
// ”搬运工“将数据搬到BLE外设里面了,将回调BluetoothGattCallback接口的onCharacteristicWrite()方法
mBluetoothGatt.writeCharacteristic(characteristic);
// 向BLE蓝牙外设写入数据操作的回调方法
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
if(status == BluetoothGatt.GATT_SUCCESS) {
// 命令写入成功,数据包成功写入BLE外设中
}
};
APP关闭蓝牙连接
交互完了,不需要了,还是把APP蓝牙连接给断掉吧
public void close() {
if (mBluetoothGatt == null) {
return;
}
mBluetoothGatt.close();
mBluetoothGatt = null;
}
注意事项
在Android 6.0+搜索蓝牙是需要定位权限的,还有BLE搜索在Android 5.0以前和以后是不一样的。最后你还会发现使用官方这套搜索在一些手机型号上也是搜不到的!只能通过传统蓝牙(非BLE方式)搜索然后过滤出BLE设备。
本文作者MichaelX,博客地址:http://blog.csdn.net/xiong_it 转载请注明来源

浙公网安备 33010602011771号