Android系統編程入門系列之硬件交互——通信硬件USB
- 2022 年 1 月 7 日
- 筆記
- Android_Home
在硬件交互的首篇對設備硬件的分類中,互聯通信系列硬件主要用來與其他設備進行數據交互。從本文開始,將重點介紹該系列相關硬件。
互聯通信系列硬件
根據硬件的可通信距離,由近及遠分為USB、NFC、藍牙、WLAN,SIM卡槽,這些硬件之間的功能原理及關係可以查找其他資料詳細學習。總之,他們為當前設備與其他設備的交互搭建了橋樑,只要雙方設備均遵循該系列硬件的協議,就可以在硬件層互相通信,而設備上的Android操作系統便會將硬件層的數據轉換為應用層數據,進而與應用程序交互。這樣也就實現了兩個不同設備上的應用程序間的交互方案。理論上這個方案是可行的,那實際各硬件的使用方式分別是怎麼樣的呢?
USB接口
在應用程序中與USB硬件的交互,系統提供了兩種方式,包括將該應用程序所在設備的USB接口作為主機模式,和該應用程序所在設備的USB接口作為配件模式。在主機模式下,該應用程序所在設備通過USB接口為其他接入的USB設備供電,通常連接沒有自帶電源的設備(比如U盤)時啟用此種模式;反之在配件模式下,是該應用程序所在設備接收通過USB接口接入的其他USB設備的電源提供,通常在連接有電源的設備(比如筆記本電腦)時啟用此模式。理論上這兩種模式只是針對USB硬件的供電方不同而區分,均可以在USB接口連接的兩個設備之間的數據傳輸。
主機模式
權限聲明
在應用程序的清單文件中,需要聲明<uses-feature />
標籤,並設置其屬性android:name
值為"android.hardware.usb.host"
。該標籤設置並不是通過應用程序向系統申請權限,而是聲明應用程序需要使用USB主機模式。
使用流程
獲取USB主機設備
主機模式下,其中一種常用情況,應用程序可以監聽插入USB接口的設備,此時可以在需要監聽的界面Activity
對應的清單文件註冊信息中增加指定的接收意圖,意圖值為android.hardware.usb.action.USB_DEVICE_ATTACHED
。這樣在USB設備接入後,系統會發送上述意圖值的廣播,從而啟動當前界面Activity
。
在啟動後的界面Activity
中,可以調用getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE)
系列方法,獲取android.hardware.usb.UsbDevice USB設備類的對象。
主機模式下另外一種情況,是直接獲取已連接的USB設備。在能獲取上下文環境Context
對象的地方,調用該對象的getSystemService(String name)
,並指定參數值 name 為Context.USB_SERVICE="usb"
,獲得android.hardware.usb.UsbManagerUSB管理類的對象。調用該對象的getDeviceList()
方法,返回List<UsbDevice>
,同樣能獲取到當前設備連接的所有USB硬件對象的列表數據。
在直接獲取已連接USB設備的情況下,應用程序需要主動向用戶申請設備通信權限。通過
USBManager
設備管理類對象的requestPermission(UsbAccessory accessory, PendingIntent pi)
方法,在用戶同意授權後,才能繼續後續設備通信操作。
USB設備通信
在拿到UsbDevice
類型的USB設備類對象,和UsbManager
管理類對象之後,調用USB管理類對象的openDevice(UsbDevice device)
方法,得到android.hardware.usb.UsbDeviceConnection USB設備連接對象,其參數 device 便是要通信的USB設備對象。
建立UsbDeviceConnection
設備連接對象後,通信過程中還需要分別藉助android.hardware.usb.UsbInterface USB接口類和android.hardware.usb.UsbEndpoint USB端點類。
在獲取UsbDevice
類型的設備類對象後,調用其getInterface(int index)
方法獲取UsbInterface
USB接口類對象,其中參數 index 是當前USB設備可獲取的所有接口數量中的索引值,而獲取UsbDevice
的所有接口數量,可根據另外一個方法getInterfaceCount()
查看。
在獲取UsbInterface
類型的接口對象後,調用其getEndpoint(int index)
方法獲取UsbEndpoint
USB端點類對象,其中參數 index 是當前USB接口中可獲取的所有斷點數量中的索引值,而獲取UsbInterface
的所有斷點數量,可根據另外一個方法getEndpointCount()
查看。
通過梅開二度獲取USB接口對象和USB端點對象之後,首先要佔用USB設備資源。繼續調用UsbDeviceConnection
連接對象的claimInterface(UsbInterface intf, boolean force)
方法,其中參數 intf 便是上文獲取UsbInterface
接口類對象,參數 force 標明是否強制佔用。返回boolean
類型的結果表示佔用是否成功。
在佔用USB設備資源之後,就可以接收USB設備的通信數據了。調用UsbDeviceConnection
連接對象的bulkTransfer (UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout)
方法。參數 endpoint 便是上文獲取UsbEndpoint
端點類對象;參數 buffer 用以存儲通信中的二進制數組;參數 offset 可選項,默認值為0
,用以標記存放數組 buffer 的起始位置;參數 length 用以標記存放數組的長度;參數 timeout 作為通信連接的最大時長。返回通信過程中實際傳輸的數據長度。
通信結束後,只需要關閉連接並釋放佔用的設備資源。調用UsbDeviceConnection
連接對象的close()
方法可以關閉通信連接。而調用該對象的releaseInterface(UsbInterface intf)
方法可以釋放佔用的UsbInterface
設備接口類型的參數 intf 對象。
配件模式
權限聲明
在應用程序的清單文件中,需要聲明<uses-feature />
標籤,並設置其屬性android:name
值為"android.hardware.usb.accessory"
。該標籤設置並不是通過應用程序向系統申請權限,而是聲明應用程序需要使用USB配件模式。
使用流程
獲取USB配件
配件模式下,其中一種常用情況,應用程序可以監聽插入USB接口的設備,此時可以在需要監聽的界面Activity
對應的清單文件註冊信息中增加指定的接收意圖,意圖值為android.hardware.usb.action.USB_ACCESSORY_ATTACHED
。這樣在USB設備接入後,系統會發送上述意圖值的廣播,從而啟動當前界面Activity
。
在啟動後的界面Activity
中,可以調用getIntent().getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
系列方法,獲取android.hardware.usb.UsbAccessory USB配件類的對象。
同樣在配件模式下另外一種情況,是直接獲取已連接的USB配件。在能獲取上下文環境Context
對象的地方,調用該對象的getSystemService(String name)
,並指定參數值 name 為Context.USB_SERVICE="usb"
,獲得android.hardware.usb.UsbManagerUSB管理類的對象。調用該對象的getAccessoryList()
方法,返回List<UsbAccessory>
,同樣能獲取到當前設備連接的所有USB配件對象的列表數據。
與主機模式類似,在直接獲取已連接USB配件的情況下,應用程序需要主動向用戶申請設備通信權限。通過
USBManager
設備管理類對象的requestPermission(UsbAccessory accessory, PendingIntent pi)
方法,在用戶同意授權後,才能繼續後續設備通信操作。
USB設備通信
在拿到UsbAccessory
類型的USB配件類對象,和UsbManager
管理類對象之後,調用USB管理類對象的openAccessory(UsbAccessory accessory)
方法,得到android.os.ParcelFileDescriptor 數據流化的文件描述符類的對象,其參數 accessory 便是要通信的USB配件對象。
在獲取ParcelFileDescriptor
數據流化的文件描述符類的對象後,調用其getFileDescriptor()
方法得到java.io.FileDescriptor普通的文件描述符類型的對象,之後通過該對象創建基本的文件輸入流java.io.FileInputStream對象以讀取USB配件中的數據,或者創建基本的文件輸出流java.io.FileOutputStream對象以將數據寫入USB配件中。
通信結束後,只需要關閉數據流化的文件描述符的佔用即可。通過調用ParcelFileDescriptor
對象的close()
方法以實現該操作。
主機模式與配件模式的區別
代碼軟件層
主機模式下,主要使用UsbDevice
類,可以獲取所連接USB設備的詳細信息。
配件模式下,只能使用UsbAccessory
類,只能獲取連接USB設備的基本信息。
硬件層
主機模式下,由應用程序所在的設備向主線供電,並向連接的USB設備供電。
配件模式下,由連接的USB設備作為主機向主線供電,並嚮應用程序所在的設備供電。