最近開發 Android APP 內連接 BLE 設備時,經過一些測試才發現一個問題:當機器端強制斷線,在 App 中重新連上 BLE 之後,接下來設備傳過來的封包都會收到雙份。如果再斷一次重連,甚至會收到三份…
忽然想到,前陣子才在 facebook 看到一篇類似的討論,其中有個回答滿有趣:
所以可能是之前斷開的連結,又偷偷連上,偷偷收資料了對吧 😡
於是,找到 StackOverflow 上這篇 Android BLE (Bluetooth Low Energy) Connect/Disconnect/Reconnect的的回答,建議用這樣的邏輯:
1 | BluetoothDevice Device = stuff(); |
2 | BluetoothGatt Gatt = null; |
3 | |
4 | if (connecting) |
5 | Gatt = Device.ConnectGatt(...); |
6 | else if (disconnecting temporarily) |
7 | Gatt.Disconnect(); |
8 | else if (reconnecting after a temporary disconnection) |
9 | Gatt.Connect(); |
10 | else if (disconnecting permanently) |
11 | { |
12 | Gatt.Disconnect(); |
13 | Gatt.Close(); |
14 | Gatt = null; |
15 | } |
參考以上的邏輯和一些實驗,有以下結論:
device.ConnectGatt()
會產生新的 gatt object。- 只有
gatt.close()
才能完整把連線關掉 (gatt.disconnect()
不行)。
於是我們決定在 BluetoothGattCallback 實作中,在 OnConnectionStateChanged()
裡只要發現 state 變為 STATE_DISCONNECTED
就 call gatt.close()
。因為不管是 App 主動斷線,或是 BLE 設備端強制斷線,都會觸發 OnConnectionStateChanged()
,所以這樣可以確保任何斷線都關閉 gatt。之後如果想重連,再等重新 scan 後,去連上新的 device。
1 | class Gatt : BluetoothGattCallback() { |
2 | |
3 | override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { |
4 | when (newState) { |
5 | BluetoothProfile.STATE_CONNECTED -> { |
6 | // ... |
7 | gatt?.discoverServices() |
8 | } |
9 | BluetoothProfile.STATE_CONNECTING -> { |
10 | // ... |
11 | } |
12 | BluetoothProfile.STATE_DISCONNECTED -> { |
13 | // ... |
14 | gatt?.close() |
15 | } |
16 | } |
17 | } |
18 | } |
順利解決!