Android 的BLE蓝牙开发
一:定义
传统蓝牙适用于电池使用强度较大的操作,例如 Android 设备之间的流传输和通信等。针对具有低功耗要求的蓝牙设备,Android 4.3(API 18)中引入了面向低功耗蓝牙的 API 支持
应用程序通过这些 API 扫描蓝牙设备、查询 services、读写设备的 characteristics(属性特征)等操作。
Android BLE 使用的蓝牙协议是 GATT 协议
二:Android BLE API 简介
1.BluetoothAdapter
BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。
2.BluetoothDevice
代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。
3.BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。
4.BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。
5.BluetoothGattCharacteristic
这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。
三:Android 蓝牙开发示例
第一步:添加权限
第二步:连接蓝牙前的初始化工作
在建立蓝牙连接之前,需要确认设备支持 BLE。如果支持,再确认蓝牙是否开启。如果蓝牙没有开启,可以使用 BLuetoothAdapter 类来开启蓝牙。
1.获取 BluetoothAdapter
private BluetoothAdapter mBluetoothAdapter;
/**
* 初始化蓝牙配置
*/
private void initBlueTooth() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取蓝牙适配器
}
获取蓝牙适配器的方式二:
private BluetoothAdapter bluetoothAdapter;
...
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
//通过蓝牙管理器来获取适配器
bluetoothAdapter = bluetoothManager.getAdapter();
BluetoothAdapter 代表本地设备的蓝牙适配器。该BluetoothAdapter可以执行基本的蓝牙任务,例如启
动设备发现,查询配对的设备列表,使用已知的MAC地址实例化一个BluetoothDevice类,并创建一个
BluetoothServerSocket监听来自其他设备的连接请求。
查看源码:
public final class BluetoothAdapter {
//获取蓝牙的适配器
public static synchronized BluetoothAdapter getDefaultAdapter() {}
//传入地址获取蓝牙的设备
public BluetoothDevice getRemoteDevice(String address){}
//获取蓝牙的状态
public int getState() {}
//蓝牙是否可用
public boolean isEnabled() {}
//获取本机蓝牙名称
public String getName() {}
//获取本机蓝牙地址
public String getAddress() {}
//开始扫描搜索
public boolean startDiscovery() {}
//停止扫描搜索
public boolean cancelDiscovery() {}
//获取已配对蓝牙设备
public Set getBondedDevices() {}
//开启蓝牙扫描
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
// 在 BluetoothAdapter 中,我们可以看到有两个扫描蓝牙的方法。第一个方法可以指定只扫描含有特定 UUID Service 的蓝牙设备,第二个方法则是扫描全部蓝牙设备。
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback) {}
等功能
2.如果检测到蓝牙没有开启,尝试开启蓝牙
if (bluetoothAdapter != null) {//是否支持蓝牙
if (bluetoothAdapter.isEnabled()) {//打开
//开始扫描周围的蓝牙设备,如果扫描到蓝牙设备,通过广播接收器发送广播
if (mAdapter != null) {//当适配器不为空时,这时就说明已经有数据了,所以清除列表数据,再进行扫描
list.clear();
mAdapter.notifyDataSetChanged();
}
bluetoothAdapter.startDiscovery();
} else {//未打开
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(intent, REQUEST_ENABLE_BLUETOOTH);
}
} else {
showMsg("你的设备不支持蓝牙");
}
监听搜索设备:
我们可以自定义一个广播接收这些系统广播
通过广播接收者查看扫描到的蓝牙设备,每扫描到一个设备,系统都会发送此广播(BluetoothDevice.ACTION_FOUND)。其中参数intent可以获取蓝牙设备BluetoothDevice。
private class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case BluetoothDevice.ACTION_FOUND://扫描到设备
showDevicesData(context, intent);//数据展示
break;
case BluetoothDevice.ACTION_BOND_STATE_CHANGED://设备绑定状态发生改变
mAdapter.changeBondDevice();//刷新适配器
break;
case BluetoothAdapter.ACTION_DISCOVERY_STARTED://开始扫描
loadingLay.setVisibility(View.VISIBLE);//显示加载布局
break;
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED://扫描结束
loadingLay.setVisibility(View.GONE);//隐藏加载布局
break;
}
}
创建一个监听者广播监听者
private void initBlueTooth() {
IntentFilter intentFilter = new IntentFilter();//创建一个IntentFilter对象
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//获得扫描结果
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//绑定状态变化
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);//开始扫描
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束
bluetoothReceiver = new BluetoothReceiver();//实例化广播接收器
registerReceiver(bluetoothReceiver, intentFilter);//注册广播接收器
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();//获取蓝牙适配器
}
BluetoothDevice代表一个远程蓝牙设备。让你创建一个带有各自设备的BluetoothDevice或者查询其皆如名称、地址、类和连接状态等信息。
源码:
public final class BluetoothDevice implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeString(mAddress);
}
public String getAddress() {}
public String getName() {}
}
第三步:扫描蓝牙设备
外围设备开启蓝牙后,会广播出许多的关于该设备的数据信息,例如 mac 地址,uuid 等等。通过这些数据我们可以筛选出需要的设备。
在 BluetoothAdapter 中,我们可以看到有两个扫描蓝牙的方法。第一个方法可以指定只扫描含有特定 UUID Service 的蓝牙设备,第二个方法则是扫描全部蓝牙设备。
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback){}
public boolean startLeScan(LeScanCallback callback) {
return startLeScan(null, callback);
}
//停止蓝牙扫描
public void stopLeScan(LeScanCallback callback) {}
实现LeScanCallback 回调
private LeDeviceListAdapter leDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback leScanCallback =
new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
leDeviceListAdapter.addDevice(device);
leDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
//开启扫描
mBluetoothAdapter.startLeScan(callback);
总结:
方式一:startLeScan()方法
startLeScan()方法有一个回调参数BluetoothAdapter.LeScanCallback 类,先创建这个类的对象,在扫描找到周围的蓝牙设备时,会返回到 onLeScan 函数,可以在 onLeScan 函数中处理逻辑
startLeScan()方法的特点:在onLeScan() 中不能做耗时操作,特别是周围的BLE设备多的时候,容易导致底层堵塞
方式二:startDiscovery()方法
这个方法用到广播。
所以先定义一个类BluetoothReceiver,让他继承自BroadcastReceiver ,并重写onReceive()方法,在该方法中写扫描到设备时的逻辑。
然后创建BluetoothReceiver对象,注册广播,再执行startDiscovery()方法
对于经典蓝牙设备,扫描是通过调用startDiscovery接口,返回的结果是通过BroadcastReceiver接收的,可以获取设备MAC地址,名称以及RSSI。startDiscovery是个异步调用,会立即返回。如果不调用cancelDiscovery主动停止扫描的话,最多扫描12s。广播主要监听以下几个Action:BluetoothDevice.ACTION_FOUNDBluetoothAdapter.ACTION_DISCOVERY_STARTEDBluetoothAdapter.ACTION_DISCOVERY_FINISHED另外要注意startDiscovery返回的设备不包括已配对设备,如要获取已配对设备,需要额外调用getBondedDevices。
第四步:连接到 GATT 服务器,连接蓝牙设备
与 BLE 设备交互的第一步便是连接到 GATT 服务器。更具体地说,是连接到设备上的 GATT 服务器。如要连接到 BLE 设备上的 GATT 服务器,请使用 connectGatt() 方法。此方法采用三个参数:一个 Context 对象、autoConnect(布尔值,指示是否在可用时自动连接到 BLE 设备),以及对 BluetoothGattCallback 的引用:
BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)
第二个参数表示是否需要自动连接。如果设置为 true, 表示如果设备断开了,会不断的尝试自动连接。设置为 false 表示只进行一次连接尝试。
第三个参数是连接后进行的一系列操作的回调,例如连接和断开连接的回调,发现服务的回调,成功写入数据,成功读取数据的回调等等。
END:锲而舍之,朽木不折;锲而不舍,金石可镂