USB 配件總覽

USB 配件模式可讓使用者 Android 裝置專用的 USB 主機硬體。配件必須遵守 。Android 配件開發套件說明文件中所述的 Android 配件通訊協定。 這可讓無法做為 USB 主機的 Android 裝置繼續與 USB 互動 硬體當 Android 裝置進入 USB 配件模式時,連接的 Android USB 裝置 配件充當主機,可為 USB 匯流排供電,並列舉連接的裝置。 Android 3.1 (API 級別 12) 支援 USB 配件模式,這項功能也可向後移植至 Android 2.3.4 (API 級別 10) 即可支援更多種裝置。

選擇合適的 USB 配件 API

雖然 Android 3.1 平台已導入 USB 配件 API,但 適用於 Android 2.3.4,並使用 Google API 外掛程式程式庫。這些 API 使用外部程式庫向後移植,你可以匯入兩種套件,以支援 USB 配件模式。視您想支援哪些 Android 裝置而定,您可能需要 請優先使用:

  • com.android.future.usb:如要在 Android 2.3.4 中支援 USB 配件模式, Google API 外掛程式 程式庫包含向後移植的 USB 配件 API, 命名空間Android 3.1 也支援匯入及呼叫此命名空間中的類別, 支援以外掛程式程式庫編寫的應用程式。這個外掛程式程式庫是精簡包裝函式 android.hardware.usb 配件 API 周圍,且不支援 USB 主機模式。如果 想支援市面上更多支援 USB 配件模式的裝置 並匯入這個套件請特別注意,並非所有 Android 2.3.4 裝置 而必須支援 USB 配件功能。個別裝置製造商會自行決定 此功能是否支援,因此您必須在資訊清單中宣告這項功能 檔案。
  • android.hardware.usb:這個命名空間包含支援 USB 的類別 Android 3.1 內建的配件模式。這個套件包含在架構 API 中 Android 3.1 支援 USB 配件模式,不必使用外掛程式程式庫。使用這個套件 只看在支援 USB 支援 USB 的 Android 3.1 以上版本裝置 配件模式,可在資訊清單檔案中宣告。

安裝 Google API 外掛程式程式庫

如要安裝外掛程式,請安裝 Google API Android API 10 附加在 SDK Manager 中的套件請參閱安裝 Google API 外掛程式進一步瞭解如何安裝外掛程式程式庫。

API 總覽

由於外掛程式程式庫是架構 API 的包裝函式,因此支援 USB 配件功能也類似。即使您使用的是外掛程式程式庫,也可以參考 android.hardware.usb 的參考說明文件。

注意:不過,牽涉到一些小用法 外掛程式程式庫與架構 API 之間的差異

下表說明支援 USB 配件 API 的類別:

類別 說明
UsbManager 列舉一些已連接的 USB 配件並與其通訊。
UsbAccessory 代表 USB 配件,包含存取其識別方法的方法 可能不準確或不適當

外掛程式程式庫和平台 API 之間的差異

使用 Google API 外掛程式程式庫與平台之間有兩種用途差異 相互整合

如果您使用外掛程式程式庫,則必須透過以下方式取得 UsbManager 物件:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

如果您使用的不是外掛程式程式庫,則必須透過以下方式取得 UsbManager 物件:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);

使用意圖篩選器篩選已連結的配件時,UsbAccessory 物件會包含在傳遞至您應用程式的意圖中 應用程式。如果您使用外掛程式程式庫,則必須透過以下方式取得 UsbAccessory 物件:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

如果您使用的不是外掛程式程式庫,則必須透過以下方式取得 UsbAccessory 物件:

Kotlin

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

Java

UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

Android 資訊清單規定

下列清單說明您必須在應用程式的資訊清單檔案中,加入哪些內容 使用 USB 配件 API資訊清單檔案和資源檔案 範例,示範如何宣告這些項目:

  • 由於不是所有 Android 裝置都保證支援 USB 配件 API, 加入 <uses-feature> 元素,以宣告應用程式使用 android.hardware.usb.accessory 功能。
  • 如果您使用的是 外掛程式庫 加入 <uses-library> 元素指定 程式庫的 com.android.future.usb.accessory
  • 如果您使用外掛程式程式庫,請將應用程式的最低 SDK 設為 API 級別 10 如果使用 android.hardware.usb 套件,則為 12。
  • 如果您希望應用程式收到已連接 USB 配件的通知,請指定 <intent-filter><meta-data> 元素配對 主要活動中的 android.hardware.usb.action.USB_ACCESSORY_ATTACHED 意圖。 <meta-data> 元素指向的外部 XML 資源檔案 宣告要偵測的配件識別資訊。

    在 XML 資源檔案中,宣告以下項目的 <usb-accessory> 元素: 。每個 <usb-accessory> 都可以 屬性如下:

    • manufacturer
    • model
    • version

    我們不建議您篩選 version。配件 或是裝置不一定能指定版本字串 (不小心或無意間)。 應用程式宣告要做為篩選依據的版本屬性,以及配件/裝置 並未指定版本字串,這會導致 NullPointerException在 也就是較舊版本的 Android 系統這個問題已在 Android 12 中修正。

    將資源檔案儲存在 res/xml/ 目錄中。資源檔案名稱 (不含 .xml 副檔名) 必須與您在 <meta-data> 元素。XML 資源檔案的格式也會顯示 請參考下方範例

資訊清單和資源檔案範例

以下範例顯示資訊清單和對應的資源檔案:

<manifest ...>
    <uses-feature android:name="android.hardware.usb.accessory" />
    
    <uses-sdk android:minSdkVersion="<version>" />
    ...
    <application>
      <uses-library android:name="com.android.future.usb.accessory" />
        <activity ...>
            ...
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
            </intent-filter>

            <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                android:resource="@xml/accessory_filter" />
        </activity>
    </application>
</manifest>

在這種情況下,下列資源檔案應儲存在 res/xml/accessory_filter.xml,並指定含有 請篩選對應的型號、製造商和版本配件會將 屬性為 Android 裝置:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
</resources>

使用配件

當使用者將 USB 配件連接到 Android 裝置時,Android 系統可以 判斷您的應用程式是否對連線的配件感興趣。例如,您可以將 以及與配件通訊為此,應用程式必須:

  1. 使用意圖篩選器篩選配件,找出已連結的配件 或列舉已連線的配件,並找出適當配件。
  2. 如果尚未取得與配件通訊的權限,請要求使用者授予權限 獲得。
  3. 在適當的介面上讀取及寫入資料,與配件進行通訊 端點。

探索配件

應用程式可以使用意圖篩選器尋找配件, 使用者連接配件或列舉已連接的配件。使用 如果您想讓應用程式自動偵測輸出內容, 所需的配件如果要取得所有已連線的配件清單 連線的配件,或您的應用程式未篩選意圖。

使用意圖篩選器

如要讓應用程式探索特定 USB 配件,您可以指定意圖篩選器 篩選出 android.hardware.usb.action.USB_ACCESSORY_ATTACHED 意圖。同時 就需要指定用於指定 USB 屬性的資源檔案 例如製造商、型號和版本

以下範例說明如何宣告意圖篩選器:

<activity ...>
    ...
    <intent-filter>
        <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
    </intent-filter>

    <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
        android:resource="@xml/accessory_filter" />
</activity>

以下範例說明如何宣告對應的資源檔案,指定 您感興趣的 USB 配件:

<?xml version="1.0" encoding="utf-8"?>

<resources>
    <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>

在活動中,您可以取得代表 UsbAccessory 從意圖附加的配件 (透過外掛程式程式庫):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

或如下所示 (使用平台 API):

Kotlin

val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory

Java

UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

列舉配件

您可以讓應用程式列舉在 應用程式正在執行中。

使用 getAccessoryList() 方法 來取得所有連接的 USB 配件:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
val accessoryList: Array<out UsbAccessory> = manager.accessoryList

Java

UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAccessoryList();

注意: 以下連線僅支援一個已連線的配件: 時間。

取得與配件通訊的權限

與 USB 配件通訊之前,您的應用程式必須取得 使用者。

注意:如果您的應用程式使用 意圖篩選器 來尋找相互連結的配件 是否允許應用程式處理意圖。如果沒有,您就必須 請先在應用程式中明確授予權限,然後才連線至配件。

在某些情況下 (例如 應用程式會列舉已經連線,並且想與其通訊的配件 第一項。您必須先確認配件的存取權限,才能嘗試與配件通訊。 否則,如果使用者拒絕存取 。

如要明確取得權限,請先建立廣播接收器。這個接收器監聽了以下事件: 您呼叫 requestPermission() 時接收廣播的意圖。呼叫 requestPermission() 會顯示對話方塊 使用者要求連線至配件的權限。以下程式碼範例說明如何 建立廣播接收器:

Kotlin

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 accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    accessory?.apply {
                        // call method to set up accessory communication
                    }
                } else {
                    Log.d(TAG, "permission denied for accessory $accessory")
                }
            }
        }
    }
}

Java

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) {
                UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if(accessory != null){
                        // call method to set up accessory communication
                    }
                }
                else {
                    Log.d(TAG, "permission denied for accessory " + accessory);
                }
            }
        }
    }
};

如要註冊廣播接收器,請將此程式碼放入您的 onCreate() 方法 活動:

Kotlin

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), 0)
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)

Java

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), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(usbReceiver, filter);

如要顯示要求使用者授權連線至配件的對話方塊,請呼叫 requestPermission() 方法:

Kotlin

lateinit var accessory: UsbAccessory
...
usbManager.requestPermission(accessory, permissionIntent)

Java

UsbAccessory accessory;
...
usbManager.requestPermission(accessory, permissionIntent);

當使用者回覆對話方塊時,您的廣播接收器會接收包含 額外 EXTRA_PERMISSION_GRANTED,為布林值 能代表答案請先檢查這個額外項目的值是否為 True,然後再連線至 。

與配件進行通訊

您可以使用 UsbManager 與配件通訊, 取得檔案描述元,您可以設定輸入和輸出串流,以便讀取和寫入資料 描述元串流代表配件的輸入和輸出大量端點。建議設定 透過其他執行緒進行裝置和配件之間的通訊,以免鎖定 主 UI 執行緒。以下範例顯示如何開啟配件以便與:

Kotlin

private lateinit var accessory: UsbAccessory
private var fileDescriptor: ParcelFileDescriptor? = null
private var inputStream: FileInputStream? = null
private var outputStream: FileOutputStream? = null
...

private fun openAccessory() {
    Log.d(TAG, "openAccessory: $mAccessory")
    fileDescriptor = usbManager.openAccessory(accessory)
    fileDescriptor?.fileDescriptor?.also { fd ->
        inputStream = FileInputStream(fd)
        outputStream = FileOutputStream(fd)
        val thread = Thread(null, this, "AccessoryThread")
        thread.start()
    }
}

Java

UsbAccessory accessory;
ParcelFileDescriptor fileDescriptor;
FileInputStream inputStream;
FileOutputStream outputStream;
...

private void openAccessory() {
    Log.d(TAG, "openAccessory: " + accessory);
    fileDescriptor = usbManager.openAccessory(accessory);
    if (fileDescriptor != null) {
        FileDescriptor fd = fileDescriptor.getFileDescriptor();
        inputStream = new FileInputStream(fd);
        outputStream = new FileOutputStream(fd);
        Thread thread = new Thread(null, this, "AccessoryThread");
        thread.start();
    }
}

在執行緒的 run() 方法中,您可以使用 FileInputStreamFileOutputStream 物件。朗讀時 來自具有 FileInputStream 物件的配件資料,請確保緩衝區 電腦大小足以儲存 USB 封包資料Android 配件通訊協定支援 封包緩衝區最多為 16384 個位元組,因此您可以選擇一律宣告緩衝區, 為方便起見

注意:在較低層級的 USB 封包中,封包為 64 個位元組 全速配件;USB 高速配件則為 512 位元組。Android 配件 為求簡單,通訊協定會將這兩種速度的封包組合成一個邏輯封包。

如要進一步瞭解如何在 Android 中使用執行緒,請參閱「處理程序和 執行緒

終止與配件的通訊

當您完成與配件通訊或配件卸離時,請關閉 以呼叫 close() 來開啟的檔案描述元。 如要監聽卸離的事件,請建立廣播接收器,如下所示:

Kotlin

var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {

        if (UsbManager.ACTION_USB_ACCESSORY_DETACHED == intent.action) {
            val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
            accessory?.apply {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
}

Java

BroadcastReceiver usbReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
            UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
            if (accessory != null) {
                // call your method that cleans up and closes communication with the accessory
            }
        }
    }
};

在應用程式中 (而非資訊清單) 中建立廣播接收器,即可 應用程式,專門處理卸離的事件。如此一來 只會傳送至目前執行中的應用程式,不向所有應用程式廣播。