Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 翻譯Android USB HOST API

翻譯Android USB HOST API

編輯:關於Android編程

翻譯Android USB HOST API

源碼地址:http://developer.android.com/guide/topics/connectivity/usb/host.html

譯者注:翻譯的好不好不是太重要,重點是在翻譯的過程中會把每句話都看認真看一遍,或者說是抱著翻譯的思想來完成一個讀懂的目的。

USB Host通信

當你的可供電Android設備處理USB host模式時,它擔任著為USB總線供電,枚舉連接的USB從設備等等一個主設備應用的工作。Android 3.1及以後版本開始支持USB host模式。

API概述

開始之前,有必要弄明白以後要用到的類。下表中描述了包含在android.hardware.usb包中的USB host APIs。

表1. USB host APIs.

Class

Description

UsbManger

允許你枚舉USB從設備以及和其通信

UsbDevice

代表一個USB從設備,獲取其信息。如接口,端點等等。

UsbInterface

代表一個USB從設備的接口,一個設備可以擁有多個接口用於通信。

UsbEndpoint

代表接口的一個端點,它是該端點一個通信通道。一個接口可以擁有一個或者多個端點,通常輸入輸出端點用於和設備進行雙向通信。

UsbDeviceConnection

代表一個設備的連接,通過端點傳輸數據,該類允許你發送接收數據,通信方式可以是同步或者異步。

UsbRequest

代表通過UsbDeviceConnection通信的異步請求。

UsbConstants

定義USB常量對應Linux內核中linux/usb/ch9.h中的宏。

多數情況下,在和USB設備進行通信的時候,你需要使用這裡所有的類(UsbRequest只用在異步通信一個請求)。一般是這樣的,使用UsbManager來獲取UsbDevice。確定了設備,需要查找用於通信接口的UsbInterface和UsbEndpoint。一旦獲取了恰當的端點,打開UsbDeviceConnection與USB設備進行通信。

下列表中描述了使用USB host APIs之前需要添加到應用程序的manifest文件中的內容:

由於並不是所有的Android帶供電設備確保支持USB host APIs,添加 對象來表明你的應用使用了android.hardware.usb.host特性。

SDK最低版本設置為12或者更高。USB host APIs不支持更老版本的API。

如果你想讓你的應用檢測一個USB設備的話,在主Activity指定 對象去匹配android.hardware.usb.action.USB_DEVICE_ATTACHED。對象指向一個外部的XML資源文件,文件中描述你想要探測的USB設備的過濾信息。

在這個XML資源文件中,為你想要過濾的USB設備聲明對象。下列表中描述了的屬性。通常,使用vendor 和 product ID過濾特殊設備,使用類,子類和協議來過濾一組設備,比如大容量存儲設備(優盤)或者數碼相機。你可以完全指定這個屬性值,也可以一個也不指定。沒有屬性值說明會過濾每個一個USB設備。所以說,你的應用程序有這個需求的情況下可以指定。


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

    保存資源文件到res/xml/目錄下。資源文件名(不包含.xml後綴)必須要和中指定的相同。XML資源文件格式如果以下示例:

    
        
        
        ...
        
            
                ...
                
                    
                
    
                
            
        
    

    在本例中,以下資源文件應該保存在res/xml/device_filter.xml中,指定需要過濾USB設備的屬性:

    
    
    
        
    

    設備工作

    當USB設備連接到Android上時,無論你的應用程序是否對此USB設備感興趣Android系統都會檢測到。你可以和設備建立通信。你的應用程序需要做如下事情:

    1.探測USB設備通過意圖過濾器檢測用戶連接的設備,或者枚舉已經連接的USB設備。

    2.如果之前沒有請求過,向用戶請求權限以連接USB設備。

    3.和USB設備進行通信,通過接口端點讀寫數據。

    探測設備

    應用程序可以通過兩種方式進行USB設備的探測,使用意圖過濾器監聽用戶連接設備或者枚舉已經連接的USB設備。前者可以方便的實現自動檢測設備。如果想列出所有的已連接設備或者沒有使用意圖過濾器過濾的設備,可以使用枚舉設備來完成。

    使用意圖過濾器

    探測設備可以指定意圖過濾器來過濾android.hardware.usb.action.USB_DEVICE_ATTACHED意圖。此外需要指定一個資源文件描述USB設備,比如product and vendor ID. 當用戶連接上和你探測的設備匹配時,系統會彈出一個對話框詢問用戶是否允許啟動的你的應用程序。如果允許,你的應用程序將會自動被賦予權限和該設備通信,直到設備被移除。

    以下示例顯示如何描述意圖過濾器:

    ...
        
            
        
    
        
    

    以下示例顯示如何描述資源文件,指定你感興趣的USB設備:

    
    
    
        
    

    在你的Activity中,你要獲得UsbDevice,它代表使用意圖探測到的設備。如:

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

    枚舉設備

    在你的應用程序運行時,如果對所以當前正在連接的USB設備感興趣,可以枚舉設備。枚舉設備使用的是getDeviceList()方法獲取所有連接設備的哈希表。如果你想獲取一個設備可以根據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設備,如果用戶允許處理意圖,那麼你的應用程序自動會獲取權限。反之,通信之前你必須向用戶請求權限。

    在一些情況下請求權限是必須的,例如你的應用程序枚舉已經連接的USB設備以及與設備通信。通信之前你需要檢查權限,否則如果用戶權限拒絕你將會收到一個運行錯誤。

    申請權限,首先創建創建一個廣播接收器。調用requestPermission()時,接收器監聽廣播意圖。調用requestRermission()向給用戶顯示一個對話框請求連接該設備的權限。

    下列代碼顯示如何創建這樣一個廣播接收器:

    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);

    顯示對話框向用戶請求權限連接設備,調用requestPermission()方法:

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

    當用戶回復對話框,你的廣播接收器會收到的內容為EXTRA_PERMISSION_GRANTED意圖,它是一個布爾代表答案。通信之前檢查這個額外值是否為真。

    與設備通信

    通信方式可以是同步或者異步。在這兩種情況下,你應該創建一個線程執行所有數據的傳輸,不要阻塞界面線程。建立一個通信,你需要獲取需要通信設備的UsbInterface和UsbEndpoint,以及使用UsbDeviceConnection申請端點。一般地,你的代碼應該是:

    檢查UsbDevice對象的屬性,比如 product ID, vendor ID或者設備類別。以識別是不是需要通信的設備。如果確定了是要通信的設備,檢索出你需要通信設備的接口,以及該接口的端點。接口可以有一個或者多個端點,一般會有輸入輸出各一的雙向通信的端點。如果確定了端點,在該端點上打開 UsbDeviceConnection。 使用bulkTransfer()或者controlTransfer()方法將需要傳輸的數據提供到端點上。你需要將上述操作實現在其它線程以免阻塞當前主界面線程。更多關於Android中線程的使用,見Processes and Threads.

    下面的代碼片段簡單的實現了同步數據傳輸。你的代碼應該使用更多邏輯上的來查找出正確的通信接口和端點,同樣需要在主界面以外的線程中進行數據的傳輸:

    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類初始化(initialize)和排隊(queue)等候異步請求,然後使用requestWait()等待結果。

    更多信息見AdbTest例子,它顯示了如何進行塊傳輸的方法異步通信,以及MissleLauncher sample它顯示了如何監聽中斷端點異步。

    終止通信

    當完成了通信或者設備被移除了,調用releaseInterface()和close()來關閉UsbInterface和UsbDeviceConection。監聽設備移除事件,創建一個廣播接收器如下:

    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中。僅在應用程序程序運行時處理設備移除事件。如此,移除事件只會發送給正在運行的應用而不是所有的應用。

    感悟:

    與原文無關純屬個人感想,USB通信越向下越復雜。Android封裝好一些,其次是libusb,再然後就是Linux內核中進行的USB通信。依次需要了解的知識多少都不一樣,比如這個完整的通信過程中並沒有用到urb等等。目前測試的Android4.2.2上通信的最大塊為:16KiB。所以數據要分割為16KiB大小進行傳輸。文件中提到的那個例子AdbTest和MissleLauncher的源碼位於:development/samples/USB/中。前者功能是讀取usb設備(同樣也是Android系統)的LOG,後者功能是控制一個火箭發射器玩具。Android這套API並不能檢測所有的USB設備,有些設備檢測不到。比如無線數據的適配器。目前測試成功的有打印機。關於對其了解的程度,我一直有個毛病就是了解一個東西沒有上限。類似寫文章沒有主線,想到哪裡就深入到哪裡。我要逐漸改點這個習慣。我對這個的練習是將數據發送打印機,完成打印。當我剛剛實現的時候,我就又開始幻想我是不是可以做一個很牛叉的USB打印APP了。這個是可以實現的,但並不是目前的主要任務,如果我堅持做,很多東西都需要去實現,這也違背了我當前僅僅是了解Android USB host API的初衷。還好,這次我剎住了閘,畢竟我這次是真實的了解了,之前只知道在Linux內核中的通信以及基於Libusb通信,直接使用java通信的好處是解析xml等等在C語言中相對復雜的東西到java中都不顯示復雜了。l 測試APK和上文提到的那兩個例子打包上傳到Here,方便那些沒有Android系統源碼的同志。

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