如果 Android 裝置處於 USB 主機模式,就能做為 USB 主機並供電流。 並列舉已連接的 USB 裝置。Android 3.1 以上版本支援 USB 主機模式。
API 總覽
在開始之前,請務必先瞭解您需要使用哪些課程。
下表說明 android.hardware.usb
套件中的 USB 主機 API。
表 1. USB Host API
類別 | 說明 |
---|---|
UsbManager |
可讓您列舉及通訊已連接的 USB 裝置。 |
UsbDevice |
代表連接的 USB 裝置,其中包含存取裝置識別資訊的方法 資訊、介面和端點 |
UsbInterface |
代表 USB 裝置的介面,可定義 裝置。裝置可具有一或多個通訊介面。 |
UsbEndpoint |
代表介面端點,這是這個介面的通訊管道。一個 介面可以有一或多個端點,且通常會有輸入和輸出端點 與裝置進行雙向通訊 |
UsbDeviceConnection |
代表與裝置之間的連線,該裝置會在端點上轉移資料。本課程 以同步或非同步的方式傳送資料。 |
UsbRequest |
代表透過 UsbDeviceConnection 與裝置通訊的非同步要求。 |
UsbConstants |
定義與 Linux/usb/ch9.h 中 linux/usb/ch9.h 定義的 USB 常數 核心。 |
在大部分情況下,您必須使用這些類別 (只有在進行非同步通訊時才需要 UsbRequest
)
與 USB 裝置通訊一般來說,您會取得 UsbManager
來擷取所需的 UsbDevice
。
拿到裝置後,您需要找出適當的 UsbInterface
和其 UsbEndpoint
進行通訊取得正確的端點後,請開啟 UsbDeviceConnection
,與 USB 裝置進行通訊。
Android 資訊清單規定
下列清單說明您必須在應用程式的資訊清單檔案中,加入哪些內容 使用 USB 主機 API:
- 因為不是所有 Android 裝置都保證支援 USB 主機 API,
加入
<uses-feature>
元素,以宣告應用程式使用android.hardware.usb.host
功能。 - 將應用程式的最低 SDK 設為 API 級別 12 以上。USB Host API 預測出的符記
- 如果您希望應用程式在連接 USB 裝置時收到通知,請指定
<intent-filter>
和<meta-data>
元素配對 主要活動中的android.hardware.usb.action.USB_DEVICE_ATTACHED
意圖。<meta-data>
元素指向宣告的外部 XML 資源檔案 與要偵測的裝置識別相關資訊。在 XML 資源檔案中,宣告 USB 的
<usb-device>
元素 。下表說明<usb-device>
。一般來說,如要篩選資料,請使用供應商和產品 ID ,若想篩選群組,請使用類別、子類別和通訊協定 例如大量儲存裝置或數位相機等可以不指定 所有這些屬性指定沒有符合每部 USB 裝置的屬性,因此只需指定 應用程式要求:vendor-id
product-id
class
subclass
protocol
(裝置或介面)
將資源檔案儲存在
res/xml/
目錄中。資源檔案名稱 (不含 .xml 副檔名) 必須與您在<meta-data>
元素。XML 資源檔案格式如下: 範例。
資訊清單和資源檔案範例
以下範例顯示資訊清單和對應的資源檔案:
<manifest ...>
<uses-feature android:name="android.hardware.usb.host" />
<uses-sdk android:minSdkVersion="12" />
...
<application>
<activity ...>
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
</application>
</manifest>
在這種情況下,下列資源檔案應儲存在
res/xml/device_filter.xml
,並指定具有指定
屬性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>
使用裝置
當使用者將 USB 裝置連接到 Android 裝置時,Android 系統可以判斷 表示您的應用程式對連結的裝置感興趣。若是如此,您可以為 與裝置進行通訊為此,應用程式必須:
- 使用意圖篩選器尋找已連接的 USB 裝置,方法是使用意圖篩選器,讓系統在使用者收到通知時傳送通知 連接 USB 裝置,或列舉已連接的 USB 裝置。
- 如果尚未取得 USB 裝置,請要求使用者授予連接 USB 裝置的權限。
- 在適當介面上讀取及寫入資料,與 USB 裝置進行通訊 端點。
探索裝置
應用程式可以使用意圖篩選器偵測 USB 裝置,方法是使用 使用者連接裝置,或列舉已連接的 USB 裝置。使用 如果您想讓應用程式自動偵測輸出內容, 。如果您要取得所有已連接的 USB 裝置清單,請列舉已連接的 USB 裝置 連結的裝置,或是您的應用程式未篩選意圖。
使用意圖篩選器
如要讓應用程式探索特定 USB 裝置,您可以指定意圖篩選器
android.hardware.usb.action.USB_DEVICE_ATTACHED
意圖的篩選。此外,
這個意圖篩選器,需要指定用於指定 USB 屬性的資源檔案
例如產品和供應商 ID使用者連上與裝置相符的裝置時
篩選器,系統會顯示對話方塊,詢問使用者是否要啟動您的應用程式。
如果使用者接受,您的應用程式將自動取得裝置存取權限,直到
裝置的連線已中斷。
以下範例說明如何宣告意圖篩選器:
<activity ...>
...
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
以下範例說明如何宣告對應的資源檔案,指定 您感興趣的 USB 裝置:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" />
</resources>
在活動中,您可以取得代表 UsbDevice
從意圖中附加的裝置:
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
列舉裝置
如果應用程式想要檢查目前連接的所有 USB 裝置
在應用程式執行期間列舉裝置。使用 getDeviceList()
方法取得
讓連接的 USB 裝置雜湊對應會以 USB 裝置的名稱做為索引鍵,也可以用於
或從地圖取得裝置
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
...
val deviceList = manager.getDeviceList()
val device = deviceList.get("deviceName")
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");
如有需要,您也可以從雜湊對應中取得疊代器,然後處理每個裝置 逐一:
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
..
val deviceList: HashMap<String, UsbDevice> = manager.deviceList
deviceList.values.forEach { device ->
// your code
}
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next();
// your code
}
取得與裝置通訊的權限
與 USB 裝置通訊之前,您的應用程式必須取得 使用者。
注意:如果您的應用程式使用 意圖篩選器來尋找連接的 USB 裝置,且該裝置會自動接收 是否允許應用程式處理意圖。如果沒有,您就必須 明確授予其權限。
在某些情況下 (例如 應用程式列舉已連接,且想與 進行通訊的 USB 裝置。 第一。您必須先確認裝置的存取權限,才能與裝置通訊。如果 如果使用者拒絕存取裝置,您會收到執行階段錯誤。
如要明確取得權限,請先建立廣播接收器。這個接收器監聽了以下事件:
您呼叫 requestPermission()
時接收廣播的意圖。呼叫 requestPermission()
會顯示對話方塊
使用者要求連接到裝置的權限。以下程式碼範例說明如何
建立廣播接收器:
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
private val usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ACTION_USB_PERMISSION == intent.action) {
synchronized(this) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
device?.apply {
// call method to set up device communication
}
} else {
Log.d(TAG, "permission denied for device $device")
}
}
}
}
}
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (this) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if(device != null){
// call method to set up device communication
}
}
else {
Log.d(TAG, "permission denied for device " + device);
}
}
}
}
};
如要註冊廣播接收器,請在 onCreate()
方法的
活動:
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"
...
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
...
permissionIntent = PendingIntent.getBroadcast(this, 0,
Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
permissionIntent = PendingIntent.getBroadcast(this, 0,
new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(usbReceiver, filter);
如要顯示要求使用者授予裝置連線權限的對話方塊,請呼叫 requestPermission()
方法:
lateinit var device: UsbDevice
...
usbManager.requestPermission(device, permissionIntent)
UsbDevice device;
...
usbManager.requestPermission(device, permissionIntent);
當使用者回覆對話方塊時,您的廣播接收器會接收包含
額外 EXTRA_PERMISSION_GRANTED
,為布林值
能代表答案請先檢查這個額外項目的值是否為 True,然後再連線至
裝置。
與裝置通訊
與 USB 裝置的通訊可以同步或非同步。無論是哪一種情況,您
應該建立一個新的執行緒來執行所有資料傳輸作業
使用者介面執行緒。如要正確設定與裝置的通訊,需取得適當的
UsbInterface
和UsbEndpoint
要透過 UsbDeviceConnection
在這個端點上通訊及傳送要求的裝置。一般來說,您的程式碼應:
- 查看
UsbDevice
物件的屬性,例如產品 ID、 還是裝置類別,以判斷您是否要與 裝置。 - 如果確定想要與裝置通訊,請尋找適當的
UsbInterface
您要用來與 適當UsbEndpoint
。只有在一個介面下 通常會有雙向輸入和輸出端點 非常適用於內部微服務通訊 - 找到正確的端點後,請開啟
UsbDeviceConnection
這個端點 - 使用
bulkTransfer()
或controlTransfer()
方法在端點提供要傳輸的資料。請 執行這個步驟,以防阻斷主 UI 執行緒。如要 如要進一步瞭解如何在 Android 中使用執行緒,請參閱「處理程序和 執行緒。
下列程式碼片段是執行同步資料移轉作業的簡易方式。你的代碼 應有更多邏輯,即可正確找到要通訊的正確介面和端點 且應在與主 UI 執行緒不同的執行緒中傳輸資料:
private lateinit var bytes: ByteArray
private val TIMEOUT = 0
private val forceClaim = true
...
device?.getInterface(0)?.also { intf ->
intf.getEndpoint(0)?.also { endpoint ->
usbManager.openDevice(device)?.apply {
claimInterface(intf, forceClaim)
bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) //do in another thread
}
}
}
private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;
...
UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = usbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
如要非同步傳送資料,請使用 UsbRequest
類別對 initialize
和 queue
進行非同步要求,然後等待結果
搭配 requestWait()
。
終止與裝置的通訊
完成與裝置通訊或裝置已卸離後,請關閉 UsbInterface
和 UsbDeviceConnection
:
呼叫 releaseInterface()
和
close()
。如要監聽卸離的事件,
建立廣播接收器,如下所示:
var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
device?.apply {
// call your method that cleans up and closes communication with the device
}
}
}
}
BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
// call your method that cleans up and closes communication with the device
}
}
}
};
在應用程式中 (而非資訊清單) 中建立廣播接收器,即可 應用程式,專門處理卸離的事件。如此一來 只會傳送至目前執行中的應用程式,不向所有應用程式廣播。