低功耗蓝牙
Android BLE API 简介
BluetoothAdapter
BluetoothAdapter 拥有基本的蓝牙操作,例如开启蓝牙扫描,使用已知的 MAC 地址 (BluetoothAdapter#getRemoteDevice)实例化一个 BluetoothDevice 用于连接蓝牙设备的操作等等。
BluetoothDevice
代表一个远程蓝牙设备。这个类可以让你连接所代表的蓝牙设备或者获取一些有关它的信息,例如它的名字,地址和绑定状态等等。
BluetoothGatt
这个类提供了 Bluetooth GATT 的基本功能。例如重新连接蓝牙设备,发现蓝牙设备的 Service 等等。
BluetoothGattService
这一个类通过 BluetoothGatt#getService 获得,如果当前服务不可见那么将返回一个 null。这一个类对应上面说过的 Service。我们可以通过这个类的 getCharacteristic(UUID uuid) 进一步获取 Characteristic 实现蓝牙数据的双向传输。
BluetoothGattCharacteristic
这个类对应上面提到的 Characteristic。通过这个类定义需要往外围设备写入的数据和读取外围设备发送过来的数据。
- 扫描蓝牙
在 BluetoothAdapter 中,我们可以看到有两个扫描蓝牙的方法。第一个方法可以指定只扫描含有特定 UUID Service 的蓝牙设备,第二个方法则是扫描全部蓝牙设备。
boolean startLeScan(UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
boolean startLeScan(BluetoothAdapter.LeScanCallback callback)
@Override public void onLeScan(BluetoothDevice bluetoothDevice, int rssi, byte[] scanRecord) { }
bluetoothDevice:蓝牙设备的类,可以通过这个类建立蓝牙连接获取关于这一个设备的一系列详细的参数,例如名字,MAC 地址等等。 rssi:蓝牙的信号强弱指标。 scanRecord:蓝牙广播出来的数据。
//停止扫描 void stopLeScan(BluetoothAdapter.LeScanCallback callback)
5.0 系统后添加
public void startScan(final ScanCallback callback)
mBLEScanner = mBluetoothAdapter.getBluetoothLeScanner(); mBLEScanner.startScan(mScanCallback); //停止扫描 mBLEScanner.stopScan(mScanCallback);
private ScanCallback mScanCallback = new ScanCallback() {
@Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); //当发现一个外设时回调此方法。 } @Override public void onBatchScanResults(List results) { super.onBatchScanResults(results); //在此返回一个包含所有扫描结果的列表集,包括以往扫描到的结果。 } @Override public void onScanFailed(int errorCode) { super.onScanFailed(errorCode); //扫描失败后的处理。 } };
- 连接
public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) contex:You know autoConnect:表示是否需要自动连接。如果设置为 true, 表示如果设备断开了,会不断的尝试自动连接。设置为 false 表示只进行一次连接尝试。 callback:连接后进行的一系列操作的回调。
void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
status:嵌入式可以自定义的比如:19 蓝牙门锁主动断开。 newState:2(STATE_CONNECTED)连接成功。0(STATE_DISCONNECTED)连接失败。 只能是 2 或者0
//-------源码部分----------
/**
* Client connection state changed
* @hide
*/
@Override
public void onClientConnectionState(int status, int clientIf,
boolean connected, String address) {
if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
+ " clientIf=" + clientIf + " device=" + address);
if (!address.equals(mDevice.getAddress())) {
return;
}
int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
BluetoothProfile.STATE_DISCONNECTED;
runOrQueueCallback(new Runnable() {
@Override
public void run() {
if (mCallback != null) {
mCallback.onConnectionStateChange(BluetoothGatt.this, status,
profileState);
}
}
});
synchronized(mStateLock) {
if (connected) {
mConnState = CONN_STATE_CONNECTED;
} else {
mConnState = CONN_STATE_IDLE;
}
}
synchronized(mDeviceBusy) {
mDeviceBusy = false;
}
}
- 发现服务
gatt.discoverServices() 连接成功后调用。
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
在蓝牙设备中, 其包含有多个BluetoothGattService, 而每个BluetoothGattService中又包含有多个BluetoothGattCharacteristic。
当onServicesDiscovered()回调的 status == BluetoothGatt.GATT_SUCCESS, 可以进行获取service,根据嵌入式定义的 读写UUID 获取到readCharacteristic。
gatt.setCharacteristicNotification(readCharacteristic, true); 不设置不会接受到数据。 此处可获取到writeCharacteristic 用于发送数据的操作。
-------其他方式获取方法-------- BluetoothGattService getService(UUID uuid) BluetoothGattCharacteristic getCharacteristic(UUID uuid)
- 蓝牙收到数据
app 获取到蓝牙数据的出口
@Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic ch) {}
mReceivedData = ch.getValue(); 此处可以根据gatt 的uuid 来区分不同特征值的数据。
- 发送数据
boolean ret = writeCharacteristic.setValue(tempData);
gatt.writeCharacteristic(writeCharacteristic); 分包20个字节发送
- MTU
mtu20的来源:GATT是基于ATT Protocol的,而它的 core spec里面定义了ATT的默认MTU为23个bytes,除去ATT的opcode一个字节以及ATT的handle2个字节之后,剩下的20个字节便是留给GATT的了。
public boolean requestMtu (int mtu) Added in API level 21 Request an MTU size used for a given connection. When performing a write request operation (write without response), the data sent is truncated to the MTU size. This function may be used to request a larger MTU size to be able to send more data at once. A onMtuChanged(BluetoothGatt, int, int) callback will indicate whether this operation was successful. Requires BLUETOOTH permission. Returns true, if the new MTU value has been requested successfully @Override public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) { super.onMtuChanged(gatt, mtu, status); if (status == BluetoothGatt.GATT_SUCCESS) { } }
由于十分不稳定和兼容问题,很少使用。
- AndroidQ BLE 新增COC, 可以用于传输大数据,比如用于ota升级。
Bluetooth LE Connection Oriented Channels (CoC) Android 10 enables your app to use BLE CoC connections to transfer larger data streams between two BLE devices. This interface abstracts Bluetooth and connectivity mechanics to simplify implementation.
- 断开连接
gatt.disconnect(); gatt.close(); gatt=null;
Read More
https://www.bluetooth.com/
https://developer.android.com/guide/topics/connectivity/bluetooth-
le.html
https://developer.android.google.cn/reference/android/bluetooth/le/
其他摘要:
Dealing with errors
Now that we dealt with successful operations we need to look at the errors. There are a bunch of cases that are actually very normal that will present themselves as ‘errors’:
- The device disconnected itself on purpose. For example, because all data has been transferred and there is nothing else to to. You will receive status 19 (GATT_CONN_TERMINATE_PEER_USER).
- The connection timed out and the device disconnected itself. In this case you’ll get a status 8 (GATT_CONN_TIMEOUT)
- There was an low-level error in the communication which led to the loss of the connection. Typically you would receive a status 133 (GATT_ERROR) or a more specific error code if you are lucky!
- The stack never managed to connect in the first place. In this case you will also receive a status 133 (GATT_ERROR)
- The connection was lost during service discovery or bonding. In this case you will want to investigate why this happened and perhaps retry the connection.
The first two cases are totally normal and there is nothing else to do than call close() and perhaps do some internal cleanup like disposing of the BluetoothGatt object.
In the other cases, you may want to do something like informing other parts of your app or showing something in the UI. If there was a communication error you might be doing something wrong yourself. Alternatively, the device might be doing something wrong. Either way, something to deal with! It is a bit up to you to what extend you want to deal with all possible cases.
Have a look at my version in my Blessed library how I did it.
Status 133 when connecting
It is very common to see a status 133 when trying to connect to a device, especially while you are developing your code. The status 133 can have many causes and some of them you can control:
- Make sure you always call close() when there is a disconnection. If you don’t do this you’ll get a 133 for sure next time you try.
- Make sure you always use TRANSPORT_LE when calling connectGatt()
- Restart your phone if you see this while developing. You may have corrupted the stack by debugging and it is in a state where it doesn’t behave normal again. Restarting your phone may fix things.
- Make sure your device is advertising. The connectGatt with autoconnect set to false times out after 30 seconds and you will receive a 133.
- Change the batteries of your device. Devices typically start behaving erratically when they battery level is very low.
If you have tried all of the above and still see status 133 you need to simply retry the connection! This is one of the Android bugs I never managed to understand or find a workaround for. For some reason, you sometimes get a 133 when connecting to a device but if you call close() and retry it works without a problem! I suspect there is an issue with the Android cache that causes all this and the close() call puts it back in a proper state. But I am really just guessing here…If anybody figures out how to solve this one, let me know!