Android BLE 開發(fā)過程中遇到的問題記錄
1.斷開連接后出現(xiàn)133錯誤
在斷開連接之后再次連接經常會出現(xiàn)133錯誤,并且難以連接成功,處理方式如下:
首先在重連的時候先將Gatt緩存進行清理:
public boolean refreshDeviceCache() {
if (mBluetoothGatt != null) {
try {
BluetoothGatt localBlueToothGatt = mBluetoothGatt;
Method localMethod = localBlueToothGatt.getClass().getMethod("refresh", new Class[0]);
if (localMethod != null) {
Boolean bool = ((Boolean) localMethod.invoke(localBlueToothGatt, new Object[0])).booleanValue();
return bool;
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e2) {
e2.printStackTrace();
} catch (IllegalAccessException e3) {
e3.printStackTrace();
}
}
return false;
}
然后將Gatt連接進行close操作:
mBluetoothGatt.close();
mBluetoothGatt = null;
再次連接時,使用一個全新的Gatt對象進行連接操作,而且在連接之前再次確認Gatt連接是否已經釋放:
/**
* 連接設備Gatt
*
* @param device 設備
*/
private void connectDevice(final BluetoothDevice device) {
if (mBluetoothAdapter == null) {
Log.d(TAG, "connectDevice: BluetoothAdapter not initialized.");
return;
}
if (!mBluetoothAdapter.isEnabled()) {
Log.d(TAG, "connectDevice: BluetoothAdapter is disabled");
return;
}
if (device == null) {
Log.d(TAG, "connectDevice: Unspecified device.");
return;
}
//防止連接出現(xiàn)133錯誤, 不能發(fā)現(xiàn)Services
if (mBluetoothGatt != null || getState() == State.Connecting) {
Log.d(TAG, "connectDevice: closeGatt");
mBluetoothGatt.disconnect();
close();
}
if (mBluetoothGatt != null && mBluetoothGatt.getDevice() != null && mBluetoothGatt.getDevice().equals(device)) {
Log.d(TAG, "connectDevice: Trying to use an existing mBluetoothGatt for connection.");
mBluetoothGatt.connect();
} else {
Log.d(TAG, "connectDevice: Trying to create a new connection.");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mBluetoothGatt = device.connectGatt(MyApplication.getInstance(), false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
mBluetoothGatt = device.connectGatt(MyApplication.getInstance(), false, mGattCallback);
}
}
}
這種操作下來即使出現(xiàn)133,也能在多次嘗試重連之后連接成功。
其次,出現(xiàn)133也很有可能與硬件有關,筆者使用的硬件硬件在初期經常出現(xiàn)133錯誤,但硬件進行了調優(yōu)之后出現(xiàn)的頻率大大降低了,所以并不是所有的問題都是Android設備的錯。
2.交互時出現(xiàn)128錯誤
開發(fā)過程中命令交互的時候出現(xiàn)128錯誤,連接中止:
這個錯誤出現(xiàn)的原因是因為gatt命令交互并不能并行操作,一旦上一個命令還未反饋結束就再次發(fā)送下一個命令就會出現(xiàn)128錯誤。
3.發(fā)現(xiàn)服務之后立即進行連接,部分機型無法成功
在開發(fā)過程中發(fā)現(xiàn),建立連接過程中,發(fā)現(xiàn)服務之后如果立即書寫特征值,可能出現(xiàn)部分機型無法成功連接的情況:
通過長期測試,目前將每一條特征值的書寫進行了700ms的延遲,暫時可以支持絕大部分機型。
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
mBluetoothGatt = gatt;
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d(TAG, "onServicesDiscovered. status = " + status);
//UUID serviceUuid = UUID.fromString("00001800-0000-1000-8000-00805f9b34fb");
BluetoothGattService service = gatt.getService(mServiceUuid);
if (BuildConfig.DEBUG) Log.d(TAG, "service.getUuid():" + service.getUuid());
if (service != null) {
List characteristics = service.getCharacteristics();
for (int j = 0; j < characteristics.size(); j++) {
final BluetoothGattCharacteristic characteristic = (BluetoothGattCharacteristic) characteristics.get(j);
Log.d(TAG, "characteristic = " + characteristic.getUuid().toString());
Log.d(TAG, String.format("characteristic.properties = %X", characteristic.getProperties()));
if ((characteristic.getProperties() & (BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristi
gatt.setCharacteristicNotification(characteristic, true);
sHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(CLIENT_CHARAC
if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
} else if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_INDICATE) > 0
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);
}
} catch (Exception e) {
reScanDevice();
e.printStackTrace();
}
}
}, 700 * j);
}
}
}
} else {
reScanDevice();
Log.d(TAG, "onServicesDiscovered. status = " + status);
}
}
4.BLE藍牙命令交互確保是有序串行的
由于BLE藍牙命令交互時串行的所以在開發(fā)過程中對此必須嚴格遵守,異常出現(xiàn)的并行命令極有可能造成各種問題,而且不容易定位bug,當然這部分可以使用第三方封裝好的庫來做。
5.各品牌手機省電策略引起的問題
華為: 非常嚴格,如果不開啟任何白名單,息屏后五分鐘之內應用都殺了,簡單開啟后臺任務白名單,息屏不會被殺,但是藍牙連接經常被斷開,后臺無法開啟掃描,點亮屏幕才會正常工作,解決辦法,開啟所有的白名單吧。
小米: 開啟白名單之后就可以正常使用了,但是如果一開始在未開啟白名單的狀態(tài)下使用藍牙,多次掃描后,會報告藍牙掃描錯誤,只有開啟白名單,重啟藍牙才可以。
OV: 這倆手機打開基本的白名單之后還需要額外確認是否開啟的定位權限,閉著用來測試的OV系統(tǒng)版本中定位權限開關很深,所以特別提一下。
魅族:基本的白名單即可;
三星:開啟只能管理器中白名單之后就可以隨意折騰了。
Nexus/Piexl:低版本隨意折騰,7.0以后有一些簡單的電量管理白名單可以開啟。
請善待那些信任你放開手讓你折騰的ROM! Orz.
5.關于與硬件設備之間的連接參數(shù)設置
在與硬件進行調試的過程中發(fā)現(xiàn),Android相比于iOS的BLE連接在每次連接后都會默認去更新連接參數(shù),不知道是否與各家定制的ROM有關。
6.掃描模式參數(shù)與連接模式參數(shù)設置
在藍牙掃描方法中提供了一個掃描模式參數(shù):
final ScanSettings scanSettings = new ScanSettings.Builder()
.setScanMode(mScanMode)
.build();
這里的掃描模式有三種:
ScanSettings.SCAN_MODE_LOW_POWER:節(jié)省功耗優(yōu)先。
ScanSettings.SCAN_MODE_BALANCED:功耗與效率平衡。
ScanSettings.SCAN_MODE_LOW_LATENCY:效率優(yōu)先優(yōu)先。
可根據需求進行選擇
在BLE連接的方法中,提供了一個連接模式參數(shù):
mBluetoothGatt = device.connectGatt(context, false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
這里的模式選擇有三種:
BluetoothDevice.TRANSPORT_AUTO:對于GATT連接到遠程雙模設備無物理傳輸優(yōu)先。
BluetoothDevice.TRANSPORT_BREDR:GATT連接到遠程雙模設備優(yōu)先BR/EDR。
BluetoothDevice.TRANSPORT_LE:GATT連接到遠程雙模設備優(yōu)先BLE。
這里選擇優(yōu)先BLE。