Android系統編程入門系列之硬件交互——通信硬件Bluetooth


通信硬件NFC的文章,雖然可以在Android系統中通過非直接接觸的形式與支持NFC硬件的設備通信,但是也只能交互一些簡短的標簽內容,對大量的持續性數據,卻並不能很好的支持。因此針對這個弊端,可以考慮使用支持Bluetooth技術的硬件。
Android系統支持傳統的Bluetooth技術,其實現功能不僅可以傳輸數據,還可以傳輸並執行遠程控制指令。在Android4.3 即API 18 及以后的版本中,低功耗的Bluetooth技術(簡稱為BLE)取自傳統Bluetooth的核心功能,可以更省功耗並支持數據傳輸功能。在傳統藍牙技術中,應用程序所持有的藍牙設備可以作為藍牙服務端,開啟藍牙等待處理其他藍牙設備的接入請求;也可以作為藍牙客戶端,開啟藍牙查找附近的藍牙設備並請求接入。而在BLE技術中,應用程序所持有的藍牙設備只能作為GATT客戶端,連接其他類似藍牙耳機等BLE設備。下面應用程序將會以這三種身份分別說明。

權限聲明

使用Bluetooth技術,需要在清單文件中聲明藍牙相關權限。申請權限使用<uses-permission />標簽,並設置其android:name屬性為對應的權限名。藍牙相關權限中主要有三種權限,申請后可分別對應執行不同功能。首先是可以執行藍牙通信的基礎權限,其值為Manifest.permission.BLUETOOTH="android.permission.BLUETOOTH";在需要通過藍牙獲取位置信息的應用程序中,還需要獲取位置權限,其值為Manifest.permission.ACCESS_FINE_LOCATION="android.permission.ACCESS_FINE_LOCATION";另外如果需要使用藍牙的遠程控制功能,需要藍牙管理員權限,其值為Manifest.permission.BLUETOOTH_ADMIN="android.permission.BLUETOOTH_ADMIN"

使用流程

由於一個Android系統的設備上只能使用唯一的一個藍牙硬件,因此應用程序中可以通過android.bluetooth.BluetoothAdapter藍牙適配器操作底層的藍牙硬件。

設置藍牙

調用BluetoothAdapter中的靜態方法getDefaultAdapter()可以獲取到BluetoothAdapter藍牙適配器對象。但是,從Android 12即API 31開始就廢棄了上述方法,取而代之的是借助android.bluetooth.BluetoothManager藍牙管理類。在能獲取到Context上下文環境類對象的位置,調用其getSystemService(String name)方法,傳入參數 name 值為Context.BLUETOOTH_SERVICE="bluetooth",可以獲取到BluetoothManager藍牙管理類的實例化對象,進而調用該對象的getAdapter()方法獲取已經實例化的BluetoothAdapter藍牙適配器對象。

對於不支持藍牙硬件的設備,得到的藍牙適配器對象為null,因此記得在應用程序中做非空判斷的相關操作!

在支持藍牙的設備上得到BluetoothAdapter對象后,還要確保設備已經開啟了藍牙功能。調用該對象的isEnabled()方法,根據返回的boolean類型結果,判斷該設備是否已經正常開啟了藍牙功能;如果藍牙處於未啟用狀態,還要通過調用Context上下文環境對象的startActivityForResult(Intent intent, int requestCode)方法跳轉到藍牙功能開啟界面,而這里傳入的參數 intent 則是標記為藍牙界面的Intent意圖對象,在該對象中設置其 action 值為BluetoothAdapter.ACTION_REQUEST_ENABLE ="android.bluetooth.adapter.action.REQUEST_ENABLE"。在跳轉到藍牙功能界面后,需要由用戶手動選擇開啟藍牙,並返回當前界面后,才能繼續執行后續操作。

藍牙客戶端查找其他藍牙設備

在設置藍牙等操作之后,就是查找該設備附近范圍內其他開啟藍牙功能的設備了。可以直接調用BluetoothAdapter對象的startDiscovery()方法來開啟查找附近設備的功能,但是在發現設備后,系統會通過廣播的形式發送已發現的設備信息給應用程序。所以還需要在某個自定義界面Activity或者服務Service中,注冊BroadcastReceiver以接收該廣播。

注冊廣播的方式可以回顧BroadcastReceiver動態注冊部分內容,這里只是需要創建的IntentFilter意圖過濾對象中的參數 action 值為BluetoothDevice.ACTION_FOUND="android.bluetooth.device.action.FOUND"。同時在自定義的BroadcastReceiver中處理收到的onReceive(Context context, Intent intent)方法中,對參數 intentgetParcelableExtra(String name)方法,並設置 name 值為BluetoothDevice.EXTRA_DEVICE="android.bluetooth.device.extra.DEVICE",可以獲得android.bluetooth.BluetoothDevice藍牙硬件設備類對象,在該對象中可以獲取查找到的藍牙硬件相關信息。

在查找到設備后,要及時調用BluetoothAdapter對象的cancelDiscovery()方法關閉查找功能。否則設備的藍牙硬件會一直占用cpu資源並消耗電量,而且在后續的建立連接及數據傳輸時,也會由於藍牙發現功能的開啟占用大量帶寬而影響后續操作效率。

對於之前已經連接配對過的設備,就不需要通過上述查找藍牙的方式發現設備了,可以通過調用BluetoothAdapter對象的getBondedDevices()方法,直接獲取已配對過的藍牙設備,返回BluetoothDevice對象組成的Set集合。

藍牙服務端等待其他設備連接請求

在設置藍牙操作后,如果想將該設備作為被發現的設備等待連接,需要啟動藍牙的可檢測功能。與開啟藍牙功能界面的方法類似,調用Context上下文環境對象的startActivityForResult(Intent intent, int requestCode)方法跳轉到啟動藍牙可檢測性功能界面,傳入的參數 intent 意圖對象中,設置的 action 值為BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE="android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";另外可以通過putExtra(String name, int value)設置可檢測時間,其參數 name 值為BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION="android.bluetooth.adapter.extra.DISCOVERABLE_DURATION",而參數 value 值為int類型的數值,單位為秒。

同樣在跳轉到藍牙可檢測性功能界面后,需要由用戶手動確認開啟,才能等待被其他藍牙設備查找發現。同時不管用戶確認開啟或取消開啟功能,都會將結果回調到Context上下文環境所代表的原啟動界面Activity中的onActivityResuslt(int requestCode, int resultCode, Intent data)方法中。

藍牙Gatt客戶端查找其他BLE設備

在設置藍牙操作后,如果只是想查找附近的BLE設備並建立連接,則需要先拿到android.bluetooth.le.BluetoothLeScannerBLE掃描類對象。通過BluetoothAdapter對象的getBluetoothLeScanner()方法,可以得到BluetoothLeScannerBLE掃描類對象。之后借助該對象的startScan(ScanCallback callback)等系列方法,開始掃描查找附近的BLE設備,在查找到符合條件的設備后,會回調android.bluetooth.le.ScanCallback類型的參數 callback 對象中的onScanResult(int callbackType, ScanResult result)方法,並將結果保存在android.bluetooth.le.ScanResult掃描結果類型的參數 result 對象中。在ScanResult對象中,可以獲得連接的BLE設備BlutoothDevice對象及連接所使用的協議功能等信息。

掃描查找到BLE設備后,及時調用BluetoothLeScanner對象的stopScan(ScanCallback callback)方法,參數 callback 與上文開始查找附近BLE設備方法中的參數相同,以停止當前藍牙掃描查找功能。

連接藍牙

在上述查找藍牙或藍牙可檢測功能開啟后被其他設備連接后,都會得到對方的BlutetoothDevice藍牙設備類對象。通常,應用程序所在設備查找到其他藍牙設備后,可以主動向設備建立連接請求;而當應用程序所在設備開啟藍牙可檢測功能等待其他設備查找到時,只能被動的開啟連接服務,等待處理其他設備的連接請求。

藍牙客戶端發起連接請求

對於主動發起藍牙連接請求的方式,由得到的BluetoothDevice對象調用createRfcommSocketToServiceRecord(UUID uuid)方法,可以創建遠程socket連接。參數 uuid 是自定義的java.util.UUID唯一索引類對象,該值必須與后文提到的被動等待連接的藍牙設備中的監聽服務中的唯一索引值一致。該方法會返回android.bluetooth.BluetoothSocket類用以保存創建的遠程藍牙socket連接。

在得到BluetoothSocket對象后,直接調用該對象的connect()方法發起連接請求。該方法調用后會一直等待連接的建立,只有建立連接后才會正常返回並執行后續操作,否則,如果請求超時或連接失敗,該方法會拋出java.io.IOException異常。

藍牙服務端等待處理連接

對於被動等待建立連接的方式,由得到的BluetoothDevice對象調用listenUsingRfcommWithServiceRecord(String name, UUID uuid)方法,建立持續監聽服務。參數 name 是為該服務定義的名字;參數 uuid 是監聽的唯一索引值,其值與上文中主動發起藍牙連接請求是創建遠程socket連接所傳入的參數一致。該方法返回android.bluetooth.BluetoothServerSocket藍牙連接的服務端socket類,用以保存在等待建立連接時的socket對象。

在得到BluetoothServerSocket對象后,直接調用該對象的accept()方法監聽連接請求。該方法調用后也會一致等待監聽狀態,只有監聽到匹配的連接請求后,才會返回BluetoothSocket對象,否則,如果連接失敗,該方法同樣會拋出java.io.IOException異常。

對於上述兩種建立連接方式執行之后,便可以通過BluetoothSocket對象在藍牙客戶端和服務端之間傳輸數據。該對象的getInputStream()getOutputStream()方法可以分別獲取socket連接的InputStream數據輸入流對象和OutputStream數據輸出流對象。可以借助InputStream數據輸入流的read()系列方法,從socket連接中讀取數據到應用程序中,反之借助OutputStream數據輸出流的write()系列方法,將應用程序中的數據寫入到socket連接中。同樣地,這里的數據輸入流的讀取系列方法會一直處於等待讀取狀態,直到讀到指定長度數據后才會返回結果,而數據輸出流的寫入系列方法,會在對方數據輸入流讀取慢導致寫入緩存滿時處於一直等待寫入狀態,直到指定數據完全寫入才會返回結果。

BluetoothServerSocketBluetoothSocket對象傳輸數據結束后,需要分別調用他們的close()方法關閉連接,防止遠程socket連接一直處於占用資源狀態。

Gatt客戶端連接BLE設備

對於Gatt客戶端連接方式,由得到的BluetoothDevice對象調用connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback)系列方法連接BLE設備的Gatt服務端,參數 context 是當前應用程序所在的上下文環境對象,參數 autoConnect 標識是否在連接該設備后自動建立Gatt服務連接,參數 callback 是建立Gatt服務連接后的回調android.bluetooth.BluetoothGattCallback抽象類。

在初次連接其他BLE設備的Gatt服務端時,會回調參數 callback 中的onServicesDiscovered(BluetoothGatt gatt, int status) 方法。而當已建立的Gatt連接狀態發生改變時,會回調參數 callback 中的onConnectionStateChange(BluetoothGatt gatt, int status, int newState)方法。其他連接狀態也會對應BluetoothGattCallback類中的相關方法。

BluetoothGattCallback抽象類的回調方法中,Gatt服務連接信息是通過android.bluetooth.BluetoothGatt類的參數 gatt 來傳遞的。通過BluetoothGatt對象的相關方法,可以執行Gatt客戶端的連接及協議控制等操作,具體可需根據應用程序需求實現。只是記得在執行完相關操作后,調用參數 gattclose()方法關閉連接。

————————————————————————————————————————————————————————


免責聲明!

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



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