当您的 Android 设备处于 USB 主机模式时,它会充当 USB 主机,为总线供电, 并枚举连接的 USB 设备。Android 3.1 及更高版本支持 USB 主机模式。
API 概览
在开始前,请务必了解您需要使用的类。通过
下表介绍了 android.hardware.usb
软件包中的 USB 主机 API。
类 | 说明 |
---|---|
UsbManager |
您可以枚举连接的 USB 设备并与之通信。 |
UsbDevice |
表示连接的 USB 设备,并包含用于访问其标识的方法 信息、接口和端点。 |
UsbInterface |
表示 USB 设备的接口,用于定义 设备。设备可以具有一个或多个用于通信的接口。 |
UsbEndpoint |
表示接口端点,是此接口的通信通道。一个 接口可以有一个或多个端点,并且通常具有用于 实现与设备的双向通信 |
UsbDeviceConnection |
表示与设备的连接,可在端点上传输数据。此课程 允许您同步或异步来回发送数据。 |
UsbRequest |
表示通过 UsbDeviceConnection 与设备通信的异步请求。 |
UsbConstants |
定义与 Linux 的 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 主机 API 不支持 存在的一些限制
- 如果您希望自己的应用在连接 USB 设备时收到通知,请指定
<intent-filter>
和<meta-data>
元素对android.hardware.usb.action.USB_DEVICE_ATTACHED
intent。通过<meta-data>
元素指向声明 待检测设备的识别信息。在 XML 资源文件中,为 USB 声明
<usb-device>
元素 您要过滤的设备以下列表介绍了<usb-device>
。一般来说,如果您要过滤广告,请使用供应商 ID 和产品 ID 如果您想过滤出某个组,请使用类、子类和协议 大容量存储设备或数码相机等 USB 设备。您可以指定不指定任何一项 所有这些属性如果不指定任何属性,则会匹配每个 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 系统可以确定 您的应用是否对已连接的设备感兴趣。如果是这样,您可以设置 与设备进行通信。为此,您的应用必须执行以下操作:
- 使用 intent 过滤器发现连接的 USB 设备,以便在用户时收到通知 连接 USB 设备,或枚举已连接的 USB 设备。
- 请求用户授予连接到 USB 设备的权限(如果尚未获得权限)。
- 通过在相应接口上读取和写入数据,与 USB 设备通信 端点。
发现设备
您的应用可以使用 Intent 过滤器发现 USB 设备,方法是: 用户连接设备,或枚举已连接的 USB 设备。使用 如果您希望应用自动检测 所需的设备如果您想获取所有 USB 设备的列表, 连接的设备,或者您的应用未针对某个 intent 进行过滤。
使用 Intent 过滤器
要让您的应用发现特定的 USB 设备,可以指定一个 intent 过滤器,
android.hardware.usb.action.USB_DEVICE_ATTACHED
intent 的过滤器。以及
此 intent 过滤器,您需要指定一个资源文件来指定 USB 接口的属性
例如产品和供应商 ID。当用户连接与您的设备匹配的设备时
过滤器,系统将向其显示一个对话框,询问他们是否要启动您的应用。
如果用户接受,您的应用会自动获得访问设备的权限,直到
设备已断开连接。
以下示例展示了如何声明 Intent 过滤器:
<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>
在您的 activity 中,您可以获取代表UsbDevice
从 intent 中加载连接的设备,如下所示:
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
枚举设备
如果您的应用想要检查当前连接的所有 USB 设备
当应用运行时,它可以枚举总线上的设备。使用 getDeviceList()
方法获取所有
已连接的 USB 设备。哈希映射由 USB 设备的名称进行键控(如果您想
从地图获取设备。
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... val deviceList = manager.getDeviceList() val device = deviceList.get("deviceName")
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); UsbDevice device = deviceList.get("deviceName");
如果需要,您还可以从哈希映射中获取迭代器,并处理每台设备 按 1:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager .. val deviceList: HashMap<String, UsbDevice> = manager.deviceList deviceList.values.forEach { device -> // your code }
Java
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 }
获取与设备通信的权限
您的应用必须先获得 用户。
注意:如果您的应用使用 intent 过滤器来发现已连接的 USB 设备,它会自动接收 权限。否则,您必须请求 权限。
在某些情况下,您可能需要明确请求权限,例如, 应用枚举已连接且想要与之通信的 USB 设备 一个。在尝试与设备通信之前,您必须先检查是否具有访问设备的权限。如果 否则,如果用户拒绝授予访问设备的权限,您会收到运行时错误消息。
要明确获取权限,请先创建一个广播接收器。此接收器监听
当您调用 requestPermission()
时获取广播的 intent。调用 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 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") } } } } }
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) { 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()
方法中添加以下代码
活动:
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), PendingIntent.FLAG_IMMUTABLE) 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), PendingIntent.FLAG_IMMUTABLE); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbReceiver, filter);
要显示对话框以向用户请求连接到设备的权限,请调用 requestPermission()
方法:
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, permissionIntent);
当用户回复该对话框时,您的广播接收器会收到包含
EXTRA_PERMISSION_GRANTED
extra,是一个布尔值
代表答案。在连接到
设备。
与设备通信
与 USB 设备的通信可以是同步的,也可以是异步的。无论是哪种情况
应创建一个新线程来执行所有数据传输,这样您就不会阻塞
界面线程。要正确设置与设备的通信,您需要获取相应的
UsbInterface
和UsbEndpoint
您要通过 UsbDeviceConnection
在此端点进行通信并发送请求的设备。通常,您的代码应该执行以下操作:
- 检查
UsbDevice
对象的属性,例如产品 ID、 供应商 ID 或设备类别来确定您是否要 设备。 - 当您确定要与该设备通信时,
UsbInterface
,以便与 该接口的相应UsbEndpoint
。接口可以有一个 通常具有用于双向访问的 输入和输出端点 通信。 - 找到正确的端点后,打开
UsbDeviceConnection
访问该端点 - 使用
bulkTransfer()
或controlTransfer()
方法提供要在端点上传输的数据。您应该 在另一个线程中执行此步骤以防止阻塞主界面线程。有关 有关在 Android 中使用线程的信息,请参阅进程和 线程。
以下代码段是执行同步数据传输的一种简单方式。您的代码 应具有更多逻辑,以正确找到要进行通信的正确接口和端点 此外,还应在与主界面线程不同的线程中执行数据转移:
Kotlin
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 } } }
Java
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()
。如需监听分离事件,请执行以下操作:
创建如下所示的广播接收器:
Kotlin
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 } } } }
Java
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 } } } };
在应用内(而不是清单中)创建广播接收器,即可 让应用仅在运行时处理断开连接事件。这样,分离事件 只会发送到当前正在运行的应用,而不会广播到所有应用。