1. 程式人生 > >Android藍芽BLE4.0踩過的坑

Android藍芽BLE4.0踩過的坑

一直都負責專案的藍芽模組,期間踩過很多坑,說出來跟大家分享一下。

1. 從簡單的開始,首先是許可權的問題,在Android6.0以上,部分機型需要開啟定位許可權,部分機型需要同時開啟GPS。所以使用藍芽之前,你可以動態申請定位許可權,或者直接將targetSdkVersion設定為23以下。

2. 藍芽剛開啟的時候,建議間隔1s後再進行搜尋,有些機型初始化很慢,會搜尋不到裝置。

3. 始終無法搜尋裝置,可能是上一次連線殘留的藍芽快取導致的,重啟藍芽試一試。

4. 搜尋方法需要區分Android版本。21以下呼叫.startLeScan(LeScanCallback),21及其以上呼叫:

 ScanSettings mScanSettings = new ScanSettings.Builder()
                            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                            .setReportDelay(0)
                            .build();
                    List<ScanFilter> mFilters = new ArrayList<>();//搜尋藍芽過濾UUID
                    ScanFilter scanFilter = new ScanFilter.Builder()
                            .setServiceUuid(ParcelUuid.fromString("xxxxxxxxxx"))
                            .build();
                    mFilters.add(scanFilter);
                    if (scanner == null) {
                        scanner = getBluetoothAdapter().getBluetoothLeScanner();
                    }
                    scanner.startScan(mFilters, mScanSettings, ScanCallback);

5. 搜尋結束後,最好間隔1s後在連線。部分機型可能會在搜尋後重新整理藍芽快取,導致連線失敗。

6. 連線方法也需要做相應的判斷處理:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                bluetoothGatt = device.connectGatt(mContext, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
            } else {
                bluetoothGatt = device.connectGatt(mContext, false, bluetoothGattCallback);
            }

7. 連線失敗,或者連線斷開後,必須及時關閉bluetoothGatt,具體操作如下:

public void closeGatt(){
    if (bluetoothGatt != null) {
            refreshGattCache(bluetoothGatt);
            bluetoothGatt.disconnect();
            bluetoothGatt.close();
            bluetoothGatt = null;
    }
}
public static boolean refreshGattCache(BluetoothGatt gatt) {
        boolean result = false;
        try {
            if (gatt != null) {
                Method refresh = BluetoothGatt.class.getMethod("refresh");
                if (refresh != null) {
                    refresh.setAccessible(true);
                    result = (boolean) refresh.invoke(gatt, new Object[0]);
                }
            }
        } catch (Exception e) {
        }
        return result;
    }

8. 在onConnectionStateChange檢測到133錯誤,需要關閉gatt。如果返回BluetoothProfile.STATE_CONNECTED,間隔1s後再呼叫gatt.discoverServices(),只有在onServicesDiscovered返回BluetoothGatt.GATT_SUCCESS才能說明裝置連線成功,其他狀態需要關閉gatt,以免下次搜尋連線不上裝置。

9. 執行notify()後,onCharacteristicChanged方法才能接收到裝置返回的資料

//具體的UUID需要參考你們自己的藍芽協議
try {
            BluetoothGattService BGService = bluetoothGatt
                    .getService(UUID.fromString(serviceUUID));
            if (BGService == null) {
                return;
            }
            BluetoothGattCharacteristic characteristic = BGService
                    .getCharacteristic(UUID.fromString(character));
            if (characteristic == null) {
                return;
            }
            int properties = characteristic.getProperties();
            if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == 0) {
                return;
            }
            bluetoothGatt.setCharacteristicNotification(characteristicUUID, true);
            BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID);
            if (descriptor != null) {
                descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                bluetoothGatt.writeDescriptor(descriptor);
            }
        } catch (NullPointerException e) {
            LogUtils.e("getService NullPointerException");
        }

10. 關於連線的穩定性,如果是BLE4.0可能會與手機的4G訊號相互干擾導致中斷,或者是APP長時間未操作藍芽服務被關閉,還有就是手機藍芽的連線引數跟裝置藍芽的連線引數不一致導致穩定性變差。連線引數這個無法在客戶端修改,只能跟硬體裝置協調,但也無法保證所有機型能夠百分百適配。APP長時間未操作可採用程序保活的方式,現在比較正式的是加入電池白名單,但部分機型沒有此類許可權(錘子、360手機等),而且不一定管用;要麼就是加入無聲音樂,APP很耗電,也違背了Android開發原則,但效果不錯。

11. 有時候你明明關閉了gatt,但最後死活搜尋不到該裝置。原因也很簡單,系統並未執行成功,還殘留著該gatt的引用。目前本人除了重啟手機藍芽,想不到有更好的方法。在網上看到有個大神寫過一篇部落格,裡面有詳細介紹,連結參見下面。

但實際使用中會引發一些問題,比如裡面通過反射關閉藍芽和開啟藍芽,實際使用發現關閉藍芽後可能會導致手機無法搜尋到任何裝置,反覆呼叫開啟藍芽才恢復正常(猜測是系統未能對開啟的操作執行成功)。聽說Android9將取消反射的方法,也不知道是真是假。

12. 一段時間內,不要過於頻繁的搜尋裝置。一方面搜尋過程中,不要反覆調用搜索的方法,加標識進行判斷;另一方面,當次搜尋的時長儘可能長一點,比如10s。

當前想到的就只有這些了,後續想到會繼續補充。有想法的小夥伴也可以在評論區留下你的建議。