做蓝牙BLE已经有段时间了,从最初的看文档到项目中的使用,再到后面尝试着去实现一个简单的module,整个过程想想都感觉心累。自己做下简单的总结,也是重新整理下思路,再度回顾下踩过的各种坑。
查看Android API是了解蓝牙BLE的第一步,蓝牙API让我非常激动,因为是中文的,但是蓝牙低功耗依然是英文的,英语渣流着泪翻着有道,默默看。。。
1、权限
首先是权限,使用蓝牙不能忘了在Manifest中配置权限,BLUETOOTH和BLUETOOTH_ADMIN是必备的,在Android5.0之后还需要位置权限,原因在Android API的蓝牙低功耗中的
Requesting User Permissions有说到。这里要感谢我们非常智能的Android Studio,我们在写代码时他会在需要使用权限的地方提醒我们在Manifest中配置权限。
<!--需要硬件支持低功耗蓝牙-->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<!--蓝牙权限-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--5.0之后蓝牙还需要位置权限-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
2、打开蓝牙
使用蓝牙前需要判断设备是否支持蓝牙,可以通过BluetoothAdapter是否为null来判断。
获取BluetoothAdapter的方法是通过BluetoothManager来获取
private BluetoothAdapter mBluetoothAdapter;
...
// Initializes Bluetooth adapter.
final BluetoothManager bluetoothManager =
(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();为了偷点懒,我们也可以使用BluetoothAdapter的静态方法getDefaultAdapter(),他会获取一个本地蓝牙的适配器,如果设备不支持,他会返回null。
public static synchronized BluetoothAdapter getDefaultAdapter() {
if (sAdapter == null) {
IBinder b = ServiceManager.getService(BLUETOOTH_MANAGER_SERVICE);
if (b != null) {
IBluetoothManager managerService = IBluetoothManager.Stub.asInterface(b);
sAdapter = new BluetoothAdapter(managerService);
} else {
Log.e(TAG, "Bluetooth binder is null");
}
}
return sAdapter;
}下面是几个是我们在使用蓝牙前的一些检测及打开蓝牙的方法。
/**
* 是否支持蓝牙
*
* @return
*/
public boolean isSupportBluetooth() {
return BluetoothAdapter.getDefaultAdapter() != null;
}
/**
* 是否支持BLE
*
* @return
*/
public boolean isSupportBLE() {
return application.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
/**
* 蓝牙是否可用
*
* @return
*/
public boolean isBluetoothEnable() {
if (isSupportBluetooth()) {
return BluetoothAdapter.getDefaultAdapter().isEnabled();
}
return false;
}
/**
* 开启蓝牙
*/
public void enableBluetooth(Context context) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
注:我们也可以通过BluetoothAdapter#enable()的方法来打开蓝牙,但在API中应该是更推荐使用意图的方式让用户来操作打开而不是让开发者直接去打开的。
3、扫描
在5.0之前我们一般使用的扫描方式是BluetoothAdapter#startLeScan(LeScanCallback),然后再通过BluetoothAdapter#stopLeScan(LeScanCallback)的方式停止设备扫描。
而在5.0之后我们发现这个方法被废弃了,推荐我们使用BluetoothLeScanner#startScan(List, ScanSettings, ScanCallback)来代替扫描,停止扫描则是通过BluetoothLeScanner#stopScan(ScanCallback)的方式。个人感觉是5.0之后职责更清晰了吧,使用单一职责的扫描类BluetoothLeScanner来实现扫描,通过抽象类ScanCallback获取扫描到的设备。
//5.0之前通过蓝牙适配器来扫描设备
bluetoothAdapter.stopLeScan(new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
}
});
//5.0之后通过扫描器来扫描设备
bluetoothLeScanner.startScan(new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
}
});
注:扫描的时候我们要注意两点,第一、一旦我们找到了需要的设备要马上停止扫描,第二、最好设置一个超时时间,否则将一直扫描下去比较费电。
4、连接设备发现服务
扫描到我们想要的设备后我们要进行连接。
BluetoothGatt bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);
获取到的对象
bluetoothGatt 之后我们可以通过这个对象进行设备的重连、断开等操作;
三个参数分别为
context 上下文;
autoConnect 是否重连(在设备可用时重连);
bluetoothGattCallback 这个抽象类非常关键,之后我们知道设备是否已连接,是否找到服务,写特征值,写数据,读数据等等回调都是通过这个抽象类来实现的;
BluetoothGattCallback bluetoothGattCallback=new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
//连接状态改变回调
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
//发现服务回调
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicWrite(gatt, characteristic, status);
//写数据成功
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
//远程特征通知回调
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorWrite(gatt, descriptor, status);
//写特征值成功回调
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
super.onCharacteristicRead(gatt, characteristic, status);
//读取特征值结果回调
}
@Override
public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
super.onDescriptorRead(gatt, descriptor, status);
//读取描述符结果回调
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
super.onReliableWriteCompleted(gatt, status);
//可靠的写事务完成回调
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
//设备信号强度回调
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
super.onMtuChanged(gatt, mtu, status);
//MTU改变回调
}
};连接成功后我们会在onConnectionStateChange中收到连接成功的回调,此时可以通过bluetoothGatt.discoverServices()来查找设备的服务,发现服务后也会收到回调。
5、写特征值,接受通知
查找特定的UUID,声明需要接受通知,有些可能不需要,这个需要根据实际项目的实际需求。
public boolean writeDescriptor(BluetoothGatt gatt, String uuidDescriptorService, String uuidDescriptorCharacteristic, String uuidDescriptor) {
//获取GATT服务
BluetoothGattService bluetoothGattService = gatt.getService(UUID.fromString(uuidDescriptorService));
if (bluetoothGattService == null) {
return false;
}
//获取特性
BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattService.getCharacteristic(UUID.fromString(uuidDescriptorCharacteristic));
if (bluetoothGattCharacteristic == null) {
return false;
}
//设置蓝牙返回数据提醒
BluetoothGattDescriptor bluetoothGattDescriptor = bluetoothGattCharacteristic.getDescriptor(UUID.fromString(uuidDescriptor));
if (bluetoothGattDescriptor == null) {
return false;
}
bluetoothGattDescriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
if (!gatt.setCharacteristicNotification(bluetoothGattCharacteristic, true)) {
return false;
}
if (!gatt.writeDescriptor(bluetoothGattDescriptor)) {
return false;
}
return true;
}
6、写数据
首先根据UUID找到要写入的特征值所在,然后发送数据
public boolean writeData(BluetoothGatt gatt, BLEGattCallback gattCallback, String uuidWriteService, String uuidWriteCharacteristics, byte[] data) {
BluetoothGattService bluetoothGattService = gatt.getService(UUID.fromString(uuidWriteService));
if (bluetoothGattService == null) {
return false;
}
BluetoothGattCharacteristic bluetoothGattCharacteristic = bluetoothGattService.getCharacteristic(UUID.fromString(uuidWriteCharacteristics));
if (bluetoothGattCharacteristic == null) {
return false;
}
if (!bluetoothGattCharacteristic.setValue(data)) {
return false;
}
if (!gatt.writeCharacteristic(bluetoothGattCharacteristic)) {
return false;
}
}
注:这里有要注意的点是蓝牙每次最多发送20个字节的数据,如果数据量过大需要分包发送,或是改变传输的ATT的MTU,需要根据实际情况决定。
以上就是一个简单的蓝牙交互可能需要使用到的一些步骤。