Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android USB 主機(Host)和配件(Accessory)

Android USB 主機(Host)和配件(Accessory)

編輯:關於Android編程

原文地址: https://developer.android.com/guide/topics/connectivity/usb/host.html

Android 支持多種 USB 外設和 Android USB 配件(硬件實現 Android 配件協議),通過兩種模式: USB 配件和 USB 主機。在 USB 配件模式中,外部 USB 硬件擔當 USB 主機。在 USB 主機模式中,Android 設備擔當主機。這樣的設備包括數碼相機、鍵盤、鼠標和游戲控制器等。

\

USB 配件和主機模式在 Android 3.1(API 等級12) 或者更高的版本中支持。

注意: 對於支持 USB 主機和配件模式最終依賴於設備的硬件配置,不管平台的API等級是多少。你可以設置 元素過濾設備是否支持 USB 主機和配件的使用。

USB 主機

當你的 Android 設備在主機模式中,它擔當 USB 主機,提供電源,羅列連接的 USB 設備。 USB 主機模式在 Android 3.1 或更高的版本中支持。

API 概述

在開發之前,去理解開發中將要用到的類是很重要的。下表描述了在 android.hardware.usb 包中的 USB 主機的 API。

類 描述 UsbManager 允許你羅列已連接的 USB 設備並與其通信 UsbDevice 代表一個已連接的 USB 設備,且包含訪問它的識別信息、 interface 和 endpoint 的方法 UsbInterface 代表一個 USB 設備的 interface,設備功能的一個集合。一個連接的設備有一個或多個 interface UsbEndpoint 代表一個 interface 的 endpoint ,它是這個 interface 的通信的信道。一個 interface 可以有一個或多個 endpoint 。通常在雙向通信設備中有輸入和輸出的 endpoint UsbDeviceConnection 代表設備的一個連接,在 endpoint 上傳輸數據。這個類允許你發送和接收同步或異步返回的數據 UsbRequest 代表通過 UsbDeviceConnection 和一個設備通信的異步請求 UsbConstants 定義的 USB 常量和 Linux 內核中 linux/usb/ch9.h 文件中定義的一致

在大多數情況下,和一個 USB 通信時你需要使用以上這些類。通常,你得到一個 UsbManager 去獲取一個期望的 UsbDevice 。你需要去尋找合適的 UsbInterface 以及通信接口的 UsbEndpoint 。一旦你獲得正確的endpoint,就可以打開一個 UsbDeviceConnection 去和 USB 設備通信。

Android Manifest 約束

下面的描述是你在使用 USB 主機 API 開發時需要在你的應用的 Manifest 文件中添加的內容:

因為不是所有的 Android 設備都保證支持 USB 主機 API ,在 元素中聲明你的應用使用 android.hardware.usb.host 特性。

設置應用的最小 SDK 的 API 等級為 12 或者更高。 USB 主機 API 在之前的版本中沒有提出。

如果你想要你的應用在連接上 USB 設備時進行提示,在你的主 activity 中的 和 元素對中添加 android.hardware.usb.action.USB_DEVICE_ATTACHED 意圖。 元素指向一個外部 XML 資源文件,它聲明了你想要監測的設備的識別信息。

在 XML 資源文件中,在 元素中聲明你想要過濾的 USB 設備。下面列表描述了 的屬性。通常,如果你想要過濾一個指定的設備可以使用供應商和產品編號,如果你想要過濾一類 USB 設備,如大量的存儲設備或者數碼相機,可以使用類、子類和協議。你可以不指定或者指定所有的屬性。沒有指定這些屬性會匹配每一個 USB 設備,如果你的應用有需要只要指定這些:

vendor-id product-id class subclass protocol(device or interface)

在 res/xml 目錄中保存這個資源文件。資源文件的名字必須和你在 元素中只指定的一樣。

Manifest 和資源文件示例

下面示例展示了一個 manifest 范例和它相應的資源文件:


    
    
    ...
    
        
            ...
            
                
            

            
        
    

在這種情況下,下面的資源文件應該保存為 res/xml/device-filter.xml ,指定要過濾設備的屬性:




    

設備工作

當用戶的 USB 設備連接到 Android 設備時,Android 系統可以判定你的應用是否對連接的設備感興趣。如果是這樣,你可以和期望的設備建立通信。要這樣做,你的應用必須:

去發現已連接的 USB 設備,可以使用一個 intent 過濾器在用戶連接到一個 USB設備時進行通知,也可以通過枚舉已連接的 USB 設備。

對已連接的 USB 設備請求用戶權限,是否已獲得。

與 USB 設備通信,在合適的 interface 上的 endpoint 的進行數據讀寫。

發現設備

你的應用可以使用 intent 過濾器在用戶連接上一個設備時進行通知或者枚舉已連接的 USB 設備去發現 USB設備。如果你想要你的應用自動去檢測期望的設備,那麼使用 intent 過濾器是非常有用的。如果你想要獲得已連接的設備列表或者如果你的應用沒有 intent 過濾器,則枚舉已連接的 USB 設備是游泳的。

使用 intent 過濾器

想要你的應用發現一個指定的 USB 設備,你可以指定一個 intent 過濾器去進行過濾,添加 android.hardware.usb.action.USB_DEVICE_ATTACHED 。除了這個 intent 過濾器,你需要指定一個資源文件,這個文件指定 USB 設備設備的屬性,如產品和供應商的ID。當用戶連接一個設備匹配你的設備過濾器時,系統會出現一個提示框詢問是否啟動你的應用。如果用戶接受,你的應用自動擁有訪問設備的權限直到設備斷開連接。

下面示例展示如何聲明 intent 過濾器:


...
    
        
    

    

下面的示例展示了如何聲明相應的資源文件,指定你感興趣的 USB 設備:




    

在你的Activity中,你可以獲得 UsbDevice ,它代表從 intent 過濾器連接的設備,像這樣:

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

枚舉設備

在你的應用運行期間,如果你的應用對當前所有連接的 USB 設備感興趣,它可以枚舉設備。使用 getDeviceList() 方法獲得一個所有已連接的 USB 設備的散列集合。這個集合的鍵是 USB 設備的名字,如果你想要從集合中獲取一個設備。

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");

如果有需要,你也可以從哈希集合中獲得一個迭代器然後一個一個列舉設備:

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap deviceList = manager.getDeviceList();
Iterator deviceIterator = deviceList.values().iterator();
while(deviceIterator.hasNext()){
    UsbDevice device = deviceIterator.next();
    //your code
}

獲得與設備通信的權限

在與 USB 設備進行通信之前,你的應用必須要有來自用戶的同意。

注意:如果你的應用使用 intent 過濾器發現設備作為他們的連接,如果用戶允許你的應用去處理 intent 他會自動接受許可。如果沒有,在連接設備前你必須在你的應用裡明確地請求許可。

明確請求許可在某些情況裡是必需的,如當你的應用枚舉已經連接的 USB 設備,然後想要與其中一個進行通信時。你必須在嘗試與其通信前檢查訪問設備設備的許可。如果沒有得到許可,如果用戶拒絕訪問設備的許可你將會接收到一個運行時錯誤。

為了明確地獲取許可,首先創建一個廣播接收器。當你調用 requestPermission() 方式時這個接收器監聽獲得的廣播的意圖。調用 requestPermission() 方法顯示一個對話框去詢問用戶是否同意連接設備。下面的示例代碼展示了如何創建廣播接收器:

private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = 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);
                }
            }
        }
    }
};

注冊廣播接收器,添加如下代碼到你的 activity 的 onCreate() 方法中:

UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
    "com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);

顯示對話框詢問用戶是否同意連接到設備,調用 requestPermisson() 方法:

UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);

當用戶對窗口做出響應後,你的廣播接收器會接收到 intent 且包含 EXTRA_PERMISSION_GRANTED 的附加值,一個表示回復的布爾型的值。連接設備前檢查這個值是否為 true 。

和一個設備通信

和一個 USB 設備通信可以是同步的也可以是異步的。無論那種情況,你都應該創建一個新的線程去進行所有的數據傳輸,這樣就不會阻塞 UI 線程。要正確的和設備建立通信,你需要獲得你想要進行通信的設備的合適的 USBInterface 和 UsbEndpoint ,然後在這個 endpoint 上用一個 UsbDeviceConnection 發送請求。通常,你的編碼應該這樣:

檢查 UsbDevice 對象的屬性,如產品 ID ,供應商 ID ,或者設備類,關於是否想要進行通信的設備。

當你確定想要和一個設備通信時,找到合適的 UsbInterface ,並且在該 interface 中找到合適的 UsbEndpoint 。 interface 可以有一個或多個 endpoint ,通常,在雙向通信中會有一個輸入和出書的 endpoint 。

當你找到正確的 endpoint ,在這個 endpoint 上打開 一個 UsbDeviceConnection 。

提供你想要在 endpoint 上用 bulkTransfer() 或者 controlTransfer() 方法傳輸的數據。你應該在另一個線程中執行這個步驟,以避免阻塞 UI 主線程。

下面的代碼片段是做一個同步數據傳輸的一般方式。你的代碼應該有更多的邏輯去尋找適合通信的 interface 和 endpoint ,而且你做任何數據傳輸應該在其他的線程中執行而不是 UI 主線程:

private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;

...

UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread

發送異步數據使用 UsbRequest 類去初始化和排隊等候一個異步請求,然後在 requestWait() 中等待結果。

結束與設備的通信

當你完成和設備的通信時或者設備已經斷開,應該調用 releaseInterface() 和 close() 方法去關閉 USsbInterface 和 UsbDeviceConnection 。要監聽分離事件,應該創建如下的廣播接收器:

BroadcastReceiver mUsbReceiver = 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
            }
        }
    }
};

在應用裡創建廣播接收器,而不是在 manifest 中,僅僅允許你的應用在運行的時候去處理分離事件。這種方式,分離事件僅在應用運行的時候發送而不是對所有的應用廣播。

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved