1. 程式人生 > >Android藍芽BLE的詳細講解

Android藍芽BLE的詳細講解

我今天分享的主題是 Android 上低功耗藍芽的實踐。這個主題比較小眾。我在過去的一年多的時間裡,主要是在做低功耗藍芽相關的開發。接觸過程中發現,BLE 的開發和通常的 Android APP 的開發有點不一樣,這裡需要訪問硬體資源,而且涉及到一些協議相關的內容,而且這方面的資料也比較少。今天我從 Android 開發者的角度,來分享一下低功耗藍芽開發實踐。

Slide 0202今天分享的內容,主要包含如下幾個部分:首先對藍芽和低功耗藍芽做一個簡單的介紹;然後介紹 Android 上對低功耗藍芽的支援;再介紹一下在 Android 平臺上可以開發哪些低功耗藍芽應用;然後是,開發過程中,可以幫助我們除錯的工具;最後,總結一下所謂的 “最佳實踐”,低功耗藍芽開發的一些小經驗。

Slide 0303在介紹低功耗藍芽之前,不得不說一下它的超集—藍芽。藍芽普及率相當的高,相信每個人都知道它,標識是這樣的(圖片)。簡單來說,藍芽其實就是一個近距離無線通訊技術,最早是由愛立信研發出來。藍芽 Bluetooth 這個名字看起來是一個合成詞,藍色的牙齒,實際上不是,這個詞是一個丹麥的國王的綽號,當時研發它的工程師正在看一個關於這個國王的書,就起了這個名字,從此這個國王圖片,就帶上了藍芽耳機。

說到藍芽,就不得不說藍芽技術聯盟(Bluetooth SIG),它負責藍芽規範制定和推廣的國際組織。做藍芽相關產品的,少不了和它打交道。藍芽技術聯盟同時也擁有藍芽商標,如果要使用,需要通過他們的授權。

我們回到藍芽技術本身。藍芽它有哪些特點呢?首先,近距離通訊,典型距離是 10 米以內,傳輸速度最高可達 24 Mbps,支援多連線,安全性高,非常適合用智慧裝置上。

簡單介紹一下藍芽的版本演進。99 年藍芽 1.0 釋出,後來釋出的藍芽 2.1 使用最廣的,現在絕大部分藍芽產品都是這個版本,也是我們所謂的經典藍芽。藍芽 3.0,也就是所謂的高速藍芽,最高傳輸速度可達 24Mbps。藍芽 4.0/4.1 在這個版本中引入了低功耗藍芽。藍芽 5.0 很快也要釋出了,主要是針對物聯網方向的改進。

Slide 0404下面介紹一下低功耗藍芽,它的全稱是 Bluetooth Low Energy,簡稱為 BLE。從名字可以看出,它的最大特點就是低功耗,一些 BLE 裝置可以用一個鈕釦電池使用一兩年。還有一些其他的有點,例如成本低呀,連線速度快呀,安全性高呀。另外,低功耗不是沒有缺點的,低功耗意味著低傳輸速率,它被設計就是用來傳輸少量資料的。總體來說,低功耗藍芽非常適合用在可穿戴裝置或者物聯網上。

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

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

協議棧裡面的模組比較多,我們就簡單介紹幾個和我們開發相關的幾塊簡單介紹一下。

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

在 Host 層和 Controller 之間有一個介面層,簡稱為 HCI。主機和控制器之間就是通過 HCI 命令和事件互動的。HCI 這一層是協議棧中是可選的,例如在一些簡單小型的裝置上可能就沒有,但是所有的 Android 裝置上肯定是有。這是藍芽上層應用和晶片的互動的必經之路。後面我們會講到,這一層的 log,能夠很好的幫助我們分析和除錯問題。

Slide 0707在 Host 部分,協議結構要複雜一些,有邏輯鏈路控制和適配層,安全管理模組等等。我們重點來看屬性協議,簡稱為 ATT,它是 BLE 通訊的基礎。ATT 把資料封裝,向外暴露為“屬性”,提供“屬性”的為服務端,獲取“屬性”的為客戶端。ATT 是專門為低功耗藍芽設計的,結構非常簡單,資料長度很短。

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

Slide 0909這裡還要多說一點,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,可以花錢買。

Slide 1010再往上,是通用訪問控制配置檔案,也就是 GAP,由名字可以看出,它定義了 BLE 整個通訊過程中的流程,例如廣播、掃描、連線等流程。還定義了參與通訊的裝置角色,以及他們各自的職能,例如廣播資料的 Broadcaster,接收廣播的 Observer,還有被連線的“外設” Peripheral 和發起連線的“中心裝置” Central。可以看到,參與互動的裝置角色都不是對等。詳細的互動流程,我們放到後面講。

最後,就是應用層,就是使用 Host 提供的 API 開發的低功耗藍芽應用。 到這裡,我們就把 BLE 的協議棧過了一下,為我們開發 BLE 有了一些理論基礎。

Slide 1111接下來,我們來看一下 Android 平臺上對低功耗藍芽的支援。

Slide 1212從 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 確實有定位的能力,我們後面會講到。

Slide 1313下面我們來看一下,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。

Slide 1414這裡介紹一下 Android 中 BLE 操作的過程,APP 發起一個 BLE 操作,然後理解返回,操作結果通過回撥上報。操作被封裝為一個訊息,然後放到協議棧的訊息佇列中,有一個獨立的執行緒獲取訊息進行處理,這裡非常類似於我們熟知的 Looper 和 Handler 機制。

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

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

Slide 1515Android 上 BLE 的實現我們就講到這裡,接下來介紹一下 Android 上能夠使用 BLE 做一些哪些事情,開發一些什麼應用?

Slide 1616BLE 應用可以分為兩大類:基於非連線的和連線的。

基於非連線的,這種應用就是依賴 BLE 的廣播,也叫作 Beacon。這裡有兩個角色,傳送廣播的一方叫做 Broadcaster,監聽廣播的一方叫 Observer。

基於連線的,就是通過建立 GATT 連線,收發資料。這裡也有兩個角色,發起連線的一方,叫做中心裝置—Central,被連線的裝置,叫做外設—Peripheral。

Slide 1717我們先來看非連線的,也就是基於廣播 Beacon 的應用。它的網路拓撲結構如下。我們知道廣播是單向的,Broadcaster 向外廣播,監聽者接收附近的廣播,整體來說形成一個單向的星型。網路中可以有多個外設,也可以有多個監聽者(動畫)。

完全基於廣播的應用,有大名鼎鼎的 iBeacon,這是蘋果公司定義的基於 BLE 廣播實現的功能,可以實現廣告推送和室內定位。這也說明了,APP 使用 BLE,需要定位許可權。還有前段時間比較火的,手機 QQ 可以“尋找丟失兒童”的專案,其實也是這個原理,在兒童用品中植入可以傳送特定廣播的裝置,手機掃描到了,根據手機的地理位置,就能大致確定範圍。

還有些裝置,可以同時實現兩個角色,既能傳送廣播,也可以接收廣播。一個裝置接收到廣播,可以通過處理,然後再轉發出去,這樣就可以形成一個雙向的網格,這就是藍芽的 Mesh。這樣的網路可以不受藍芽傳輸距離限制了,只要在空間中佈置足夠密集的節點,就能把資訊從網路一點,傳遞到任何一點。這個可以應用在物聯網和智慧家居系統中。

前面在介紹協議棧物理層的時候,我們知道廣播只在37、38、39這三個廣播頻道進行廣播,監聽者也在這三個頻道進行監聽。我們前面介紹了,藍芽通訊是跳頻的,只有雙方裝置在某個時刻跳到同一個頻到上,才能收到廣播,這種傳播資料效率比較低,資料量也有限,不適合大規模的資料傳輸。

Slide 1818接下來我們介紹一下廣播的資料,也就是廣播包。我們所說的廣播資料其實包含兩部分: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 的值波動很大,跟環境和手機的角度關係很大。

Slide 1919我們來直觀看一下掃描到的結果是什麼樣。例如我們使用一個 APP 掃描,掃到一個裝置,這些內容都是從廣播資料中解析出來的,例如裝置名字,裝置型別,GATT 服務資料,產商自定義資料等。原始資料是這樣的(動畫),這裡是一個 62 位元組的16進位制標示,下面這個表格中,每一行就是一個 AD Structure。

Slide 2020我們來看一下 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 的開發,不建議使用這個方法,這是一個非常重的操作,靈活性非常差。

Slide 2121接下來我們來看一下掃描的工作流程。首先 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,特別是合理停止掃描,防止資源洩漏。

Slide 2222下面我們來看一下 Android 作為 Broadcaster 的應用。從 Android 5.0 開始,Android 裝置就可以像外設一樣傳送 BLE 廣播了。這時 Android 裝置之間就可以通過 BLE 來互動資料,或者發現對方裝置了,例如類似 NFC 一樣交換簡單資訊的應用,想象空間還是很大的。

Android 中實現的程式碼如下,通過前面的介紹,我們知道廣播有兩種包:Advertising Data 和 Scan Response Data,我們這裡設定好這兩種包,然後通過 BluetoothLeAdvertier 的 startAdvertising 就可以了。這裡需要注意的點和前面一樣,Start 了,需要注意 Stop。

Slide 2323非連線的應用我們就講到這裡,接下來我們將基於連線的 BLE 應用。它的網路拓撲如圖所示。我們看到,參與通訊的有兩個角色:中心裝置(Central)和外設(Peripheral)。一箇中心裝置可以連線多個外設,一個外設只能被一箇中心裝置連線。中心裝置和外設之間的通訊是雙向的。可以看到這是一個典型的星型結構。其實一箇中心裝置能夠同時連線的裝置數量也是有限,一般最多連線 7 個外設。

Slide 2424BLE 連線的建立是通過 GAP 來協商和建立連線。Central 裝置發起連線,外設接收連線請求,並協商連線引數。

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

Slide 2525我們通過一個例子來簡單看一下 GATT 的結構。在右邊這個圖中,我們連線了一個小米手環2,這裡個列表中每一行就是一個 Service,例如 Generic Access、Device Information 等,還有沒有名字的,直接顯示了一串 UUID。這是怎麼回事呢?前面我們介紹了,藍芽聯盟已經預先定義了一些 Service,都是一些 16bit UUID 的。這些Service有標準的含義和用途。例如 Generic Access 這個 service 就描述了裝置連線相關的屬性。還有 Immediate Alert 服務,標識裝置支援標準的即時提醒功能,例如小米手環作為提醒通知裝置,Hear Rate 表示心率服務。而直接顯示 UUID 的標示廠商自定義的,有 128 bit 的長的,也有 16 bit 短的,短的標示購買的,可以看到廠商購買了 FEE0 這個。自定義 Service 具體怎麼操作,裡面屬性的含義只有廠商自己知道。

這裡也展開了兩個 Service,裡面列出來的是 Characteristic,有些還有 Descriptor。後面標籤標示這個特徵有哪些許可權,例如 W 標示可寫。Character 的 UUID 也是類似,有官方定義的我們直接指導怎麼讀寫,例如我們往 Alert Level 裡面寫入 1,手環就應該能夠發出提醒。直接讀取 Hear Rate 就能獲得到心率。另外也有廠商自定義的,裡面資料的內容,就只有廠商自己知道了。

官方定義了哪些 Service 和 Characteristic,都在藍芽的官網上有定義,可以通過 KeyNote 中的連線去檢視。

Slide 2626這是在 Android 中使用 GATT 連線的具體寫法。可以看到,Android SDK 相關的類都和 GATT 裡面概念一一對應。前面我們也說了,Android 上 BLE 的操作都是非同步的,這裡也要建立一個 BluetoothGattCallback 用來出來回撥,這個類非常重要,所有的 GATT 操作的回撥都在這裡。

通過 BluetoothDevice 的 connectGatt() 方法得到 BluetoothGatt,表示一個連線,用完以後,記得 close() 來釋放資源。

Slide 2727我們接下來看一下 Android 中 GATT 操作的流程。右邊這個圖,APP 是我們的應用,右邊藍芽服務端,從左向右箭頭是 APP 發起的請求,從右向左的箭頭是回撥。我們看到所有的操作都是非同步的完成的。連線過程是,首先使用 gattConnect 發起連線,收到 onConnectionStateChange() 通知連線是否成功,若成功,則進行下一步的 discoverService(),這一步就是發現裝置所有的 GATT Service,若發現成功,通過 onServiceDiscovered() 回撥,這時才算真正的連線成功。然後可以通過 BluetoothGatt 的 getService() 來獲得BluetoothGattService,進而獲得BluetoothGattCharacteristic 等,然後對 Characteristic 進行讀寫。

圖中這是理想狀況。有可能是這樣(動畫),你在嘗試建立一個 GATT 連線,而沒有回撥返回的時候,同時再發起一個 GATT 連線,這個時候有很大概率連線會失敗。所以最好不要同時發起多個連線請求。

另外,在一個 GATT 連線中,你對 Characteristic 發起讀寫操作的時候,如果上一個請求沒有返回之前,不能發起另外一個操作(動畫),否者後一個就會失敗。所以對於一個連線,請求必須順序執行,一個請求完成以後,才能進行下一個請求。

我們預期 request 和 response 是成對出現的,但是現實是,有時候會出現傳送一個 request,而沒有回撥的情況。前面我們說了,在前一個回撥之前,不能發起新的 request,這樣我們是不是就傻等在這裡了呢?為了解決這些問題,下面推薦一個解決方案。

Slide 2828為了實現只有一個連線請求的需求,我們可以建立連線管理類,在這個類中每次只有一個正在連線的裝置,因為連線需要經過多次互動,可以使用狀態機是最合適的解決方案。根據回調出髮狀態轉移,同時也設定一個在某個狀態停留的時間有個閾值,超過這個時間,即使沒有回撥也會發生狀態轉移。把所有的連線請求放到一個佇列中,等前面的連線成功或者失敗以後,才進行下一個連線請求。

接下來,對於已經建立的 GATT 連線,所有的操作都是非同步的,而且需要順序執行。為了保證這樣的邏輯,寫起非常麻煩,想象一下你需要連續讀寫多個 Characteristic 的場景。解決方法是封裝非同步呼叫成為同步呼叫,具體的實現原理是使用 wait/notify 來實現即可。寫起來就非常方便,而且不容易出現狀態錯誤。

Slide 2929在 Android 5.0 以後,我們還可以把 Android 裝置作為外設,被中心裝置連線。這個時候開始,Android 裝置之間可以真正地使用 BLE 進行雙向通訊了,應用場景也非常多。具體的實現與 Central 基本的對應,瞭解了 GATT 原理,這裡實現也就非常簡單了。這裡不詳細說了。

Slide 3030通過前面的介紹,已經講解完了 Android 裝置作為 BLE 應用的所有四個角色:監聽者、廣播者、中心裝置以及外設。也穿插介紹了一些開發中可能遇到的坑,但是實際開發過程中,我們需要怎麼 Debug BLE 相關的問題呢?下面我們介紹一些可以幫助我們的 Debug 工具。

Slide 3131首先,我們可以使用一些 BLE 測試 APP 來驗證一些基本功能,在應用市場上有很多類似的 APP,這裡特別推薦 nRF Connect 這個 App。這個 APP 就像 API demo 一樣,實現了所有 Android 提供的 API 能做的事情,APP 質量很高,用起來也非常方便,前面介紹有些截圖,就是來自這個 APP。

另外,檢視 Log 是最常用的方法。 Logcat 作為 Android 開發者最熟悉不過。Logcat 中可以檢視 com.android.bluetooth 這個程序的 log,可以瞭解 Java 層的一些狀態。

更深入一點,我們可以檢視協議棧的 log。藍芽的協議棧 log 預設沒有列印,我們可以在開發者選擇中開啟。(動畫)。如圖中所示,開啟這兩個開關:

先說下面這個開關,開啟這個開關,在 Logcat 就會中輸出 BlueDroid 中的 Log,通過這些 log 可以排查 BlueDroid 裡面實現的一些問題。

有時候,有時候知道協議棧的log可能還不夠,開啟第一個開關,系統就會抓取 HCI 層的 log,log 檔案一般在 /sdcard/bt_snoop.log。HCI 層我們之前介紹了,Host 和 Controller 互動的必經之路。這個 log 非常類似我們在網絡卡上進行的網路抓包。實際上這個 log 檔案,也可以通過網路抓包軟體 WireShark 開啟,如圖,這裡可以看到所有 HCI 命令和事件,以及對應的資料。通過這個 log 可以排查藍芽晶片以上的問題。

其實還可以更進一步,可以排查藍芽晶片是否正常工作,就是通過一些裝置直接抓取藍芽空中包。這個需要購買特定的硬體和軟體配合使用。

Slide 3232最後我們來總結一下,Android 中 BLE 開發一些所謂的最佳實踐。

Slide 3333最後我們來總結一下,Android 中 BLE 開發一些所謂的最佳實踐。

Slide 3434首先,我們要儘量少幹一些事情。因為藍芽的硬體資源有限,對晶片的請求當然是越少越好。另外,BlueDroid 還很年輕,上層對它的壓力也是越小越好。我們在保證業務的情況下,可以儘量遵循如下一些原則:儘量少的連線數量,儘量短的連線時間,儘量少的掃描,至於儘量少的 ClientIf,如果在 APP 中多個模組中都要掃描或者連線,應該做盡可能的合併。儘量少的廣播資料,儘量少的並行請求,在回撥中要做盡量少的工作。

Slide 3535另外,我們需要儘量多做一些事情。藍芽雖然是一個統一的標準,但是不同廠家實現之間還是有一定的差距,例如 MTK 和高通平臺是有些不一樣的。另外,藍芽這一塊,不同的 ROM 廠家都會有一些修改,需要在不同的 ROM 上都要做一些測試,最後連線的外設,實現也不太一樣,也需要做盡量多的測試。

Bluetooth 還在快速的發展,不管是藍芽規範的制定,還是 Android 平臺對藍芽的支援,給了我們開發者提供了更多的空間和可能性,期待 Android BLE 能夠出現更多好用好玩的應用。

Slide 3636最後,這次分享沒法做到面面俱到,開發過程中還需要更深入的瞭解,這裡有一些參考資料:

  1. 這是藍芽的 4.1 的協議文件,這是最權威的資料了;
  2. 這是 Bluetooth 的官方網站,關於藍芽的各種資料都能從這裡找到;
  3. 這是我關於藍芽寫的一些文章;
  4. 這是 Android 開發者網站關於低功耗藍芽的開發的簡介。