史上最強Android 的低功耗藍牙BLE開發實踐

前言

隨着可穿戴設備的越來越火爆,可移動終端設備上的應用也隨之熱門起來,現在主流的ios和android系統中,對於BLE藍牙的開發有些差別,ios由於其藍牙控制的穩定性,開發起來相對穩定簡單,而android上面對藍牙開發可就沒那麼簡單穩定了,在api19後換上新的Bluedroid協議棧後開始支持BLE4.0低功耗藍牙設備通信,系統對藍牙支持也差強人意,加上android系統開放性,國內各手機廠商對系統所謂的優化定製後,手機系統對藍牙的支持就不太穩定了,大部分情況都要對不同手機進行適配處理,導致android開發難度大大增加…

1.BLE藍牙的特點

  • 近距離通信,典型距離是 10 米以內(BLE4.0)
  • 速度最高可達 24 Mbps
  • 低功耗 (峯值電流15mA,休眠電流 1uA)
  • 低成本
  • 低延遲連接 (3ms)
  • 高可彈性 (多種模式)
  • 高安全性 (128bit AES加密)

2.BLE 名稱解釋

2.1 GATT/ATT

GATT,全稱叫做通用屬性配置文件,它是建立在前面說的 ATT 的基礎上,對 ATT 進行進一步的邏輯封裝,定義數據的交互方式和含義。這是我們做 BLE 開發的時候直接接觸的概念。GATT 按照層級定義了三個概念:服務(Service)、特徵(Characteristic)和描述(Descriptor)。他們的包含關係如右邊這個圖所表示的:一個 Service 包含若干個 Characteristic,一個 Characteristic 可以包含若干 Descriptor。而 Characteristic 定義了數值和操作。Characteristic 的操作這幾種權限:讀、寫、通知等權限。我們說的 BLE 通信,其實就是對 Characteristic 的讀寫或者訂閱通知。還有最外面一層,Profile配置文件,把若干個相關的 Service 組合在一起,就成爲了一個 Profile,Profile 就是定義了一個實際的應用場景。

2.2 UUID

Service、Characteristic 還有 Descriptor 都是使用 UUID 唯一標示的。具體的表現形式,我們在後面會講到。我們先來說說 UUID 是什麼? UUID 是全局唯一標識,它是 128bit 的值,爲了便於識別和閱讀,一般標示程如下的形式,8-4-4-12 的16進制標示。

關於 UUID 有一些規範,爲了避免衝突,一般都不會自己手動去定義。例如 Android 中提供了 UUID.randomUUID() 來生成一個隨機的 UUID。我們也看到,UUID 有點太長了,在低功耗藍牙中這種數據長度非常受限的情況下,使用起來肯定不方便,所以藍牙又使用了所謂的 16 bit 或者 32 bit 的 UUID。其實本質上並沒有什麼 16bit 或者 32 bit UUID,藍牙 SIG 定義了一個基礎的UUID(Bluetooth Base UUID),形式如下。除了 XXXX 那幾位意外,其他都是固定,所以說,其實 16 bit UUID 是對應了一個 128 bit 的 UUID。這樣一來,UUID 就大幅減少了,例如 16 bit uuid 只有有限的 65536 個,所以 16 bit UUID 並不能隨便使用。SIG 已經預先定義了一些 UUID,如果你想添加一些自己的 16 bit 的 UUID,可以花錢買。

2.3 GAP

GAP是通用訪問控制配置文件 ,由名字可以看出,它定義了 BLE 整個通信過程中的流程,例如廣播、掃描、連接等流程。還定義了參與通信的設備角色,以及他們各自的職能,例如廣播數據的 Broadcaster,接收廣播的 Observer,還有被連接的“外設” Peripheral 和發起連接的“中心設備” Central。可以看到,參與交互的設備角色都不是對等。詳細的交互流程,我們放到後面講。

3.BLE 藍牙棧結構

3.1 Arch

這裏寫圖片描述
作爲 Android 開發者,我們不必理解 BLE 的協議棧每個細節,這裏大概介紹一下協議架構。我們都知道,協議一般都是分層設計的。BLE 協議棧也不例外。我們來看一下這個圖。整個協議棧大致分爲三部分,從下到上分別爲,控制器(Controller)→主機(Host)→應用(Applications)。

控制器:它是協議棧的底層的實現,直接與硬件相關,一般直接集成在 SoC 中,由芯片廠商實現,包括物理層和鏈路層。 主機:這是協議棧的上層實現,是硬件的抽象,與具體的硬件和廠家無關。 應用層:就是使用 Host 層提供的 API,開發的應用。

3.2 Controller

這裏寫圖片描述
首先是物理層。藍牙是工作在 2.4GHz 附近,這是工業、科學、醫療 ISM 頻段。可以看到它和 WiFi 工作在同一個頻段。藍牙把頻段切分爲 40 個通道,3 個廣播通道,37 個數據通道,按照一定規律跳頻通信(高斯頻移鍵控 GFSK)。

在 Host 層和 Controller 之間有一個接口層,簡稱爲 HCI。主機和控制器之間就是通過 HCI 命令和事件交互的。HCI 這一層是協議棧中是可選的,例如在一些簡單小型的設備上可能就沒有,但是所有的 Android 設備上肯定是有。這是藍牙上層應用和芯片的交互的必經之路。後面我們會講到,這一層的 log,能夠很好的幫助我們分析和調試問題。

3.3 Host

這裏寫圖片描述
在 Host 部分,協議結構要複雜一些,有邏輯鏈路控制和適配層,安全管理模塊等等。我們重點來看屬性協議,簡稱爲 ATT,它是 BLE 通信的基礎。ATT 把數據封裝,向外暴露爲“屬性”,提供“屬性”的爲服務端,獲取“屬性”的爲客戶端。ATT 是專門爲低功耗藍牙設計的,結構非常簡單,數據長度很短。

3.Android BLE

3.1 Android Ble 開發

這裏寫圖片描述
從 Android 4.3 Jelly Bean,也就是 API 18 纔開始支持低功耗藍牙。這時支持 BLE 的 Central 模式,也就是我們在上面 GAP 中說的,Android 設備只能作爲中心設備去連接其他設備。從 Android 5.0 開始才支持外設模式。

Android SDK 中 BLE 相關的 API 都在 android.bluetooth.* 下面,同時在 Android 5.0 也引入了一些也需要用到 android.bluetooth.le* 下面的 API。

例如,在前面介紹的 GATT 定義的一些概念,都有對應的類,例如 BluetoothGatt/BluetoothGattService/BluetoothGattCharacteristic 等。有了前面的介紹,我們就能很容易的理解這些類的作用。

另外,要在 APP 中使用藍牙功能,需要在 Manifest 中申請藍牙相關的權限。在 Android 6.0 及以上平臺中,還需要申請定位權限。爲什麼會這樣?因爲 BLE 確實有定位的能力,我們後面會講到。

3.2 Android Ble 底層架構

這裏寫圖片描述
Android 上藍牙實現的架構。我們來看右邊這個圖,這是 Android 上一個非常經典的層級結構。最下面硬件部分,可以使用各廠家的具體實現,通過硬件抽象層(HAL)接口統一連接到 Android AOSP 中,通過 JNI 提供 Java 訪問接口。藍牙服務運行在 com.android.bluetooth 進程中,最後通過 Binder 機制向客戶端,也就是 APP 提供相關的 API。

關於藍牙協議棧,這裏多說一點。Android 4.2 以前用的是老牌協議棧實現 BlueZ,4.2 開始換成了由 Google 和 Broadcom(博通)聯合開發的 BlueDroid,專門在 Android 平臺使用。BlueDroid 作爲全新的實現,功能不是完善,我們可以看到在 4.3 以後才支持 BLE,在 5.0 以後才支持外設模式,到目前爲止,功能其實還是不是很完善。也有一些 Bug。
這裏寫圖片描述
介紹一下 Android 中 BLE 操作的過程,APP 發起一個 BLE 操作,然後理解返回,操作結果通過回調上報。操作被封裝爲一個消息,然後放到協議棧的消息隊列中,有一個獨立的線程獲取消息進行處理,這裏非常類似於我們熟知的 Looper 和 Handler 機制。

因爲是使用消息機制,回調的時候必須知道通知哪個客戶端?客戶端發起請求之前,首先要向協議棧註冊客戶端,註冊成功以後,返回一個 clientIf,這是一個整型,是客戶端在協議棧的一個句柄,客戶端的後續操作,都只需要帶上這個 clientIf 句柄即可。

在操作完成的時候,一般都有一個顯式的停止操作,用來釋放前面的申請的 clientIf 和資源。如果不能正確的釋放,不僅會造成內存泄漏,而且可能會導致後續所有的 BLE 操作都是不能做了。因爲這個 clientIf 是有限,在現在藍牙協議棧中只有 32 個,而且是Android 上所有 APP 共用的。當這些資源用完以後,只有通過殺掉對應的 APP 或者重啓藍牙才能恢復。

3.3 Ble 藍牙廣播

這裏寫圖片描述
我們所說的廣播數據其實包含兩部分:Advertising Data(廣播數據) 和 Scan Response Data(掃描響應數據)。通常情況下,廣播的一方,按照一定的間隔,往空中廣播 Advertising Data,當某個監聽設備監聽到這個廣播數據時候,會通過發送 Scan Response Request,請求廣播方發送掃描響應數據數據。這兩部分數據的長度都是固定的 31 字節。在 Android 中,系統會把這兩個數據拼接在一起,返回一個 62 字節的數組。

廣播數據包的結構如這個圖所示(動畫)。廣播包中是包含一個一個的小 AD structure,每個 AD structure 是一個完整的數據,它的結構是:第一個字節表示長度 n,後面緊接 n 個字節的數據。數據部分第一個字節表示數據類型,也就是後面的數據含義,後面 n - 1 個字節表示真實數據。例如 0x08 是設備的名字,後面的數據就是設備名字的 UTF-8 編碼。

這些廣播數據可以自己手動去解析,在 Android 5.0 也提供 ScanRecord 幫你解析,直接可以通過這個類獲得有意義的數據。

廣播中可以有哪些數據類型呢?設備連接屬性,標識設備支持的 BLE 模式,這個是必須的。設備名字,設備包含的關鍵 GATT service,或者 Service data,廠商自定義數據等等。

這裏我們也可以看到,廣播數據只有最多62字節,所以廣播數據空間每個直接都非常珍貴。一般都把哪些數據放到廣播中呢?原則上是廣播中要放一些能夠表達設備身份的數據,還有一些需要暴露的必要數據。因爲空間的限制,所以基於廣播做不了太複雜的應用,到了 Bluetooth 5.0,據說要大幅擴展廣播數據的容量,擴展到 512 字節,這時想象空間就比較大了。

另外,再順帶提一下,無線通信中基本都有信號強度的概念 — 也就是 RSSI,RSSI 單位是 dB,通過 RSSI 能夠大致推測出距離的遠近。但是這個在 Android 設備上非常不靠譜,RSSI 的值波動很大,跟環境和手機的角度關係很大。
這裏寫圖片描述
我們來直觀看一下掃描到的結果是什麼樣。例如我們使用一個 APP 掃描,掃到一個設備,這些內容都是從廣播數據中解析出來的,例如設備名字,設備類型,GATT 服務數據,產商自定義數據等。原始數據是這樣的(動畫),這裏是一個 62 字節的16進制標示,下面這個表格中,每一行就是一個 AD Structure。

3.4 android 藍牙廣播掃描

我們來看一下 Android 作爲接收者怎麼接收廣播數據,掃描設備。代碼其實很簡單,首先創建一個 LeScanCallback,用來接收收到廣播以後,回調上報數據。然後會用 BluetoothAdapter 的 startLeScan 來開始掃描,需要停止掃描的時候,使用 stopLeScan 來停止。

我麼來詳細看一下這個回調函數,onLeScan,有 BluetoothDevice 這個參數,代表掃描到的設備,關鍵是設備的的 MAC 地址信息。然後就是 RSSI,表示掃描到的設備的信號強度,接下來 scanRecord 就是我們前面介紹的廣播數據,這個數據的長度是62字節。值得提的一點是,BLE 所有回調函數都不是在主線程中的。

這裏有幾點需要注意,這裏在不需要掃描以後,一定要 stopLeScan,而且 start 和 stop 中傳入的 LeScanCallback 一定要是同一個,因爲 LeScanCallback 就是我們客戶端的標識。否者就會出現我們前面說的 clientIf 不釋放的問題。在 Android 開發中,我們經常會使用匿名內部類來做參數,在這裏就千萬不要這麼做。

在 Android 5.0 中,提供了全新的掃描 API — BluetoothLeScanner,它提供了對掃描更加精細的控制。

除了這種方法,還可以使用經典藍牙掃描的方式,BluetoothAdapter 的 startDiscovery(),然後通過 BroadcastReceiver 來接收收到的廣播。如果只是做 BLE 的開發,不建議使用這個方法,這是一個非常重的操作,靈活性非常差。
這裏寫圖片描述
接下來我們來看一下掃描的工作流程。首先 APP 發起掃描請求,通過藍牙的 Service 發送請求給藍牙芯片。藍牙芯片開始掃描,掃描到了設備,就通過回調上報。我們知道,掃描真正執行實在 BT 芯片中,只要 APP 發送了請求下去以後,Android 系統就可以休眠了,也就是我們常說的 AP (Application Processor),等掃描到了設備以後,底層 BP (Baseband Processor)就會喚醒上層 AP,執行回調通知到 APP,(動畫)就像我們圖中紅色框標出的這樣。這裏有一個問題,隨着我們周圍的 BLE 設備逐漸增多,頻繁掃描到設備,系統就會被頻繁的喚醒,甚至睡眠不下去,從而導致耗電嚴重。

爲了避免這種問題,耗電的問題。我們需要儘可能少的使用掃描。即使需要掃描,我們也希望儘可能少的上報掃描到的設備。這裏就可以使用 Android 5.0 上提供的新接口,設置 ScanFilter,通過一定的規則過濾,只有掃描到了符合我們的規則的設備才上報,或者通過設置延遲上報,從而減少喚醒系統的次數。

這裏總結一下掃描中一些建議。1、首先,儘可能使用新的 API,功能更強大;2、儘可能少地掃描,因爲畢竟掃描是一個比較重的操作,耗電,也會減慢 BLE 連接速度;3、掃描的時候,儘量設置 ScanFilter,只掃描那些你感興趣的設備,而不是全盤掃描;4、正確使用 API,特別是合理停止掃描,防止資源泄漏。

3.3 Ble 藍牙連接

這裏寫圖片描述
BLE 連接的建立是通過 GAP 來協商和創建連接。Central 設備發起連接,外設接收連接請求,並協商連接參數。

前面我們介紹了 GATT,GATT 核心內容就是 Service、Characteristic 以及 Descriptor。每個 BLE 外設,根據自己的功能,向外暴露 Service 等。其實最重要的獲取 Service 中的 Characteristic,Characteristic 可以被讀、寫、還有變化的時候有通知,這樣就實現了雙向的通信。

未完待續。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章