低功耗藍芽(3)

zerob13發表於2014-08-14

在本系列之前的文章中,我們瞭解了 Bluetooth LE(低功耗藍芽,後文簡稱為BLE)的一些背景並且構建了一個簡單的Activity/Service模式的藍芽低功耗框架。在今天的文章裡,我們將更深入的探討BLE的技術細節,並且實現BLE下的“裝置發現”功能。

發現裝置簡單的說,是在藍芽可見範圍內搜尋可用裝置的過程。為了避免一開始就因為缺乏許可權而無法實現搜尋,首先我們需要在AndroidManifest中新增必要的許可權。我們所需要新增的許可權有android.permission.BLUETOOTH以及android.permission.BLUETOOTH_ADMIN。其中第一個許可權是 Android使用藍芽所必要的許可權,而第二個則是一些藍芽附加功能的許可權,比如本次討論的發現裝置功能。

在我們開始一頭鑽入程式碼之前,有必要解釋一下本文中的BleService是以狀態機的形式運作的。BleService可以在不同的狀態下執行不同的任務,所以我們從第一個狀態——SCANNING開始切入。BleService會在收到一條名為MSG_START_SCAN的訊息後進入這個狀態。

然後是開始搜尋的startScan()函式程式碼:

從程式碼中我們不難發現:首先,我們確認了藍芽服務是否已經開啟,如果檢測到使用者關閉了藍芽服務,那麼就需要去提示使用者開啟。這一過程的實現非常簡單,只要先獲取Android藍芽系統服務BluetoothService的例項物件,然後從這個物件中我們又可以獲取到代表藍芽射頻的BluetoothAdapter例項。注意這裡需要做一下非空判斷,接著可以通過這個Adapter例項的isEnabled()函式來判斷藍芽是否開啟並且處於可用狀態了。如果服務是不可用狀態,那麼我們需要定義一個恰當的狀態,並且通知給所有監聽了服務的客戶端,以便於進一步的處理(本文中就是我們的 Activity)。

服務端收到訊息後,為了提醒使用者開啟藍芽,Android系統專門為了這種情況提供了API。我們選擇呼叫這一系統介面,從而保證在不同機型上有較好的原生使用者體驗。當然,我們也可以通過程式碼直接去開啟藍芽,但是更推薦的做法是主動去提示使用者開啟。這樣做帶來的另一個好處是非常簡單的程式碼實現,我們只需要喚起一個特定的Activity去提示使用者操作,而這個Intent Action已經在BluetoothAdapter中定義好了(參考上面程式碼的7-9行)。當使用者操作完成後,我們就會在之前ActivityonActivityResult()方法中,收到處理完成後的訊息。

不難發現,至此我們還沒有做任何BLE特有的步驟,不過這一些步驟均是使用藍芽服務所必要的。

之後就是掃描支援BLE的裝置了。由於Android為此在BluetoothAdapter中封裝了一個名為startLeScan()的方法,而該方法就是用來掃描裝置的!所以我們只需要簡單的呼叫該方法就可以開始掃描裝置。另外,這一方法需要傳入一個BluetoothAdapter.LeScanCallback例項來接受掃描中各種狀態的回撥。

切記,startLeScan()注:原文說是onStartLeScan(),不過Android中並沒有這個函式,從上下文意思看應該指startleScan())僅僅只是發起了搜尋過程,所以我們必須要記得去停止搜尋。在生產使用中,基於不同的需求,一般儘量在找到裝置後儘快停止搜尋,不過在本文的例子中,我們會通過postDelayed()去定時呼叫stopLeScan()來停止服務。

在搜尋過程中,每次藍芽Adapter收到任何來自BLE裝置的廣播資訊都會呼叫BluetoothAdapter.LeScanCallback中的onLeScan()回撥。由於在廣播模式下的BLE裝置會每秒傳送10條廣播資訊,因此在搜尋過程中我們要仔細過濾這些冗餘資訊,保證程式只處理新裝置發來的訊息。為了達到這個目的,這裡通過已發現裝置的MAC地址(使用MAC地址在後續會帶來一些方便)在onLeScan()中去重,然後把資訊儲存到一個Map中去。

除了過濾冗餘,我們也需要過濾掉那些我們並不關心的裝置。通常我們會通過裝置的一些特徵資訊來篩選(後續文章中會詳細講述),不過 SensorTag documentation 建議對於基於 SensorTag 開發的裝置我們只需要簡單的去匹配裝置名為“SensorTag”的裝置就可以,本文就選擇了這個方式。

每當我們發現一個符合條件的新裝置,我們便把這個裝置新增到Map中去,同時也把所有已發現裝置的MAC地址打包成一個String陣列通過MSG_DEVICE_FOUND訊息傳送給Activity

值得注意的是雖然我們服務中的操作都是在UI執行緒中執行的,但我們並不需要去擔心這會導致UI執行緒的阻塞。啟動BLE搜尋的呼叫是非同步執行的,並且會啟用一個後臺Task來回撥onLeScan()。因此,只要我們保證沒有在onLeScan()中進行計算密集型的任務,那麼是不需要擔心這個後臺Task會帶來任何阻塞的問題。

本文中的Activity也是以狀態機的形式來執行,該Activity會根據BleService的狀態去重新整理改變UI。上方的重新整理選單就是根據BleService是否處於SCANNING狀態來切換可用/不可用狀態,同時該狀態也會使Activity切換到展示已發現裝置列表的fragment。 無論何時Activity一收到MSG_DEVICE_FOUND訊息就會去重新整理已發現裝置列表的介面。由於這和BLE並無太大關係,這部分UI重新整理的程式碼文中就不展開說明了,有興趣的同學可以點選 這裡 來訪問詳細程式碼。

現在我們已經可以通過這個demo來發現周圍處於廣播模式的BLE裝置,並且在列表中看到這些裝置。

discovery

至此,我們完成了基本的“發現裝置”功能,之後我們要做的是去連線上其中一個我們發現的裝置,這部分會在下一篇文章中講述。

本文的原始碼可以在這裡下載。

相關文章