Android低功耗藍牙(BLE)開發的一點感受


最近一段時間,因為產品的需要我做了一個基於低功耗藍牙設備的Android應用,其中碰到了一些困難,使我深深體會到Android開發的難處:不同品牌,不同型號和不同版本之間的差異使得Android應用適配成為一個痛點,尤其是跟硬件相關的,每個廠商在實現Android API的時候,或多或少都會有些差別。這些區別,有些是明顯的Bug,有些則是對API理解的差異造成的。

我的開發是基於Android 4.3+ 標准BLE API。Android 4.3之前廠商自己實現的API不在討論之列。Android 5.0對BLE API進行了改進,但由於基於Android 5.0的智能設備還沒有普及,所以我也沒有針對Android 5.0進行適配。希望新的API實現可以不僅僅是語義上的統一,在這背后的行為上也應該是一致的,

在這里我分享一些在Android BLE開發過程中遇到的奇怪的事,希望后來者不要走我走過的彎路......

命令執行的順序

機型 - 紅米 1S, Android 4.4.2   / 華為 榮耀6, Android 4.4.2

在BLE設備連接並且做完Service Discovery之后,下一步要做的就是讀寫設備的Characteristic了,通常在連接后需要讀寫幾個Characteristic。因為我之前做過蘋果的開發,這些對BLE讀寫的命令都是異步,可以並發的,底層實現應該會有一個queue來緩存硬件沒有實際執行的命令。於是我想當然的就按照以前的做法實現了對Characteristic的讀寫,並且在紅米 1S上成功運行。可是當我用華為手機測試的時候出錯了,我要的數據沒有讀出來。Debug后發現底層報錯,在執行第二個命令時,出現了“有命令在執行中”的錯誤。請教了偉大的Stack Overflow后才知道,原來有些Android的實現是沒有底層的Command Queue的。我在應用層實現了一個簡單的Queue,只有當前一個BLE命令執行完成后才進行下一個命令的執行,華為手機的問題就解決了!當然在已經實現了Command Queue的手機上,新的方法也是沒有問題的。

所以,同樣是實現了Android API,不同的廠商對這背后行為的理解是不同的。如果Google能夠不僅對API的語義進行規范,同時也對其行為進行統一,那該多完美!

設備的搜索(Scan)

機型 - Samsung S3 Android 4.3 (機鋒ROM)

Android提供了兩個API做BLE設備的搜索,一個是帶Service UUID過濾,一個不帶過濾。我最初的實現選擇了帶UUID過濾的API,這樣效率應該會稍稍高一些吧。在紅米1S,華為榮耀6以及魅族MX2上都工作正常,可以搜索到我指定的設備。然而三星S3卻無法搜索到任何設備,我百思不得其解,只得再次求助Stack Overflow - 原來不是所有的設備都支持帶UUID過濾的設備搜索(怎么會這樣?)。我只好使用不帶UUID過濾的API了,然后在應用層通過設備名稱來過濾我想要的結果。這樣一切都好了...吧?

紅米1S的奇葩行為

機型 - 紅米 1S, Android 4.4.2

俗話說拆了東牆補西牆,用來形容Android開發再恰當不過了,好不容易為一款手機做完了改動,結果原來工作正常的機型卻又出了問題。上面為三星S3做過的改動就是一個例子:三星倒是OK了,可是原來一切安好的紅米1S卻收不到從設備發來的Notification了(讀寫Characteristic正常)。一開始我很抓狂,不知道為什么紅米突然就不工作了,Debug底層也沒有報錯。萬般無奈,只好一點點回退,最后終於發現問題出現在Scan設備的API使用上:如果我用不帶UUID過濾的API,紅米1S就無法收到從設備發來的Notification!多么奇葩的行為!在紅米上我可以搜尋到設備,可以連接,可以發現Service,可以讀寫Characteristic,卻單單無法收到Notification。當我改成帶Service UUID過濾的API后,一切就都好了!好吧,這個Bug一般人真的很難理解了,就交給小米去解決吧。

可是我該怎么辦呢?只有hack一下,判斷機型和版本號,使用不同的BLE Scan API了。太丑陋了!

三星的連接問題

機型 - Samsung Note 2, Android 4.3

三星手機是我做適配時出問題最多的機型了,可能的原因是三星在很早之前就支持了低功耗藍牙,並且在Android 4.3之前提供了它自己的BLE API。當Android 4.3標准BLE API出來之后,三星做了API的適配,但並不完美。由於條件所限,我們只測試了三星比較老的一些機型,感覺問題還是比較多的,最新的機型以及ROM版本應該會好很多(我們還沒有收到關於S5的問題報告)。除了上面講到的BLE Scan的問題,三星手機對BLE設備的連接也有比較特別的要求:(來自Stack Overflow)某些三星手機在進行BLE連接時,需要在UI thread里調用相應的API。我的應用場景是當手機搜索到設備后自動進行設備連接,在實際的使用中我發現即使是把connect device調用放在UI Thread里,三星手機也是經常不工作的。最后,我在連接設備之前加了一個延時,它就工作了。。。至於是怎么發現的就不啰嗦了,說多了都是淚啊。

Android的BLE API

安卓對於BLE的支持相比iOS來說確實差了許多:API是基於傳統Bluetooth API改進的,不支持BLE peripheral模式,在一些API的行為上沒有iOS友好,等等。Android 5.0對BLE API進行了重構,並且支持了BLE peripheral模式,希望會大大改善BLE在安卓系統上的體驗。

最后貼一段代碼,這是用來設置接收設備Notification的,如果不調用writeDescriptor那段代碼,notification就不工作。可是谷歌你就不能把它封裝在setCharacteristicNotification里么?這個坑不知道害了多少碼農啊。。。

 

[java]  view plain  copy
 
  1. /** 
  2.  * Enables or disables notification on a give characteristic. 
  3.  * 
  4.  * @param characteristic Characteristic to act on. 
  5.  * @param enabled If true, enable notification.  False otherwise. 
  6.  */  
  7. public boolean setCharacteristicNotification(BluetoothGattCharacteristic characteristic,  
  8.                                           boolean enabled) {  
  9.     if (mBluetoothAdapter == null || mBluetoothGatt == null) {  
  10.         Log.w(TAG, "BluetoothAdapter not initialized");  
  11.         return false;  
  12.     }  
  13.   
  14.     mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);  
  15.           
  16.     if (enabled && CHARACT_UUID_BATT_LEVEL.equals(characteristic.getUuid().toString()))  
  17.     {  
  18.         Log.i(TAG, "setCharacteristicNotification");  
  19.         BluetoothGattDescriptor descriptor = characteristic.getDescriptor(  
  20.                 UUID.fromString());  
  21.         descriptor.setValue("00002902-0000-1000-8000-00805f9b34fb");  
  22.         mBluetoothGatt.writeDescriptor(descriptor);  
  23.     }  
  24.       
  25.     return true;  
  26. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM