赞
踩
官网解释:https://developer.android.com/guide/topics/connectivity/usb?hl=zh-cn
Android中的两种USB模式,分别为主机模式、配件模式(也就是我们常说的从模式)。
当您的 Android 设备处于 USB 主机模式时,它会充当 USB 主机,为总线供电并枚举连接的 USB 设备。Android 3.1 及更高版本支持 USB 主机模式。
在开发中用到的类如下:
通信的步骤如下:
Manifest文件需要添加以下内容,才能使用USB主机API:
<uses-feature>
元素来声明应用使用android.hardware.usb.host
功能<intent-filter>
和 <meta-data>
元素对。其中 <meta-data>
元素指向外部的XML资源文件,用于声明有关要检测的设备的标识信息<usb-device>
元素,其中包括供应商ID和产品ID。如果想要过滤一组USB设备(例如大容量存储设备或者数码相机),请使用类、子类、和协议。这些属性可以指定,也可以不指定,如果不指定任何属性则会与每个USB设备进行匹配。清单文件示例:
<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>
您的应用可以通过使用 Intent 过滤器在用户连接 USB 设备时接收通知,或者枚举已连接的 USB 设备来发现设备。如果您希望应用自动检测所需的设备,那么使用 Intent 过滤器就非常有用。如果您想获取所有连接的设备的列表,或者您的应用没有 Intent 过滤器,那么枚举连接的 USB 设备就非常有用。
在Manifest文件中声明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>
device_filter.xml内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" />
</resources>
在Activity中,通过以下方式从Intent获取所连接设备的UsbDevice
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
如果应用有兴趣在运行时检查当前连接的所有USB设备,则可以枚举总线上的设备。使用getDeviceList()方法获取连接的所有USB设备的哈希映射。
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");
如果需要,还可以从HashMap中获取迭代器,并逐个处理设备:
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设备通信。创建一个广播接收器,此接收器会在应用调用requestPermission()
时接收广播的Intent,调用requestPermission()会想用户弹出一个请求连接设备权限的对话框,广播接收器的代码如下:
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); } } } } };
在Activity中注册广播接收器,在onCreate()函数中添加以下代码:
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()方法来显示请求连接到设备权限的对话框
UsbDevice device;
...
usbManager.requestPermission(device, permissionIntent);
与USB设备的通信可以是同步的,也可以是异步的。无论使用哪种方式,在开发过程中都应该创建一个新线程来执行所有数据传输。
示例代码如下:
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
当完成设备的通信或者设备断开之后,需要调用releaseInterface()
和close()
来关闭UsbInterface
和UsbDeviceConnection
。
创建以下广播接收器来监听断开连接事件:
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
}
}
}
};
说明:
在应用内创建广播接收器后,应用可以仅在运行时处理断开连接的事件。这样,断开连接事件只会发送到当前正在运行的应用,而不会广播到所有应用。
在此模式下所连接的Android USB设备会充当主机,为USB总线供电,并枚举所连接的设备。
用到的两个类:
添加<use-feature>
元素来声明应用使用android.hardware.usb.accessory
功能。
如果您希望应用收到 USB 配件连接通知,请为主 Activity 中的 android.hardware.usb.action.USB_ACCESSORY_ATTACHED
Intent 指定 <intent-filter>
和 <meta-data>
元素对。<meta-data>
元素指向一个外部 XML 资源文件,该文件声明关于要检测的配件的识别信息。
在 XML 资源文件中,为要过滤的配件声明 <usb-accessory>
元素。每个 <usb-accessory>
可以包含以下属性:
manufacturer
model
version
将资源文件保存在 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>
您的应用可以通过两种方式发现配件:使用在用户连接配件时会收到通知的 Intent 过滤器,或者枚举已连接的配件。如果您希望应用能够自动检测所需的配件,那么使用 Intent 过滤器会非常有用。如果您希望获得所有已连接配件的列表,或者您的应用没有 Intent 过滤器,那么枚举已连接的配件就非常有用。
如果要让应用发现特定的 USB 配件,您可以指定一个 Intent 过滤器来过滤 android.hardware.usb.action.USB_ACCESSORY_ATTACHED
Intent。除了该 Intent 过滤器之外,您还需要指定一个资源文件来指定 USB 配件的属性,例如制造商、型号和版本。当用户连接与您的配件过滤器匹配的配件时,
以下示例展示了如何声明 Intent 过滤器:
<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>
accessory_filter.xml文件的内容如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
</resources>
在Activity中通过intent来获取所连接配件的UsbAccessory
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
通过UsbManager来枚举已连接的所有USB配件列表
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
UsbAccessory[] accessoryList = manager.getAccessoryList();
应用必须获取用户的许可之后,才能与USB配件进行通信。调用的方式是定义一个广播接收器,用来监听调用requestPermission()时接收广播的Intent,当应用调用requestPermission()时,会显示一个对话框,请求授予连接配件的权限。定义广播接收器的代码如下:
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); } } } } };
定义广播接收器之后,需要在activity的onCreate()函数中进行注册,代码如下:
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()函数:
UsbAccessory accessory;
...
usbManager.requestPermission(accessory, permissionIntent);
当用户对弹出的对话框做出操作时,广播接收器会收到包含EXTRA_PERMISSION_GRANTED
extra的intent,该extra是代表用户操作的布尔值。在连接到配件之前,需要检查此extra的值是否为true。
开发者可以使用UsbManager
来获取文件描述符,然后设置输入和输出流来向描述符读写数据,以此与配件通信。这些流表示配件的输入和输出批量端点。在通信的时候,需要注意:应该另启动一个线程来设置设备和配件的通信,防止主线程阻塞。示例代码如下:
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(); } }
注意:在使用FileInputStream对象从配件读取数据时,请确保您使用的缓冲区足以存储USB数据包数据、Android配件协议支持最大16384字节的数据包缓冲区,因此为了简单期间,可以选择始终将缓冲区声明为此大小。
当完成与配件的通信后或者配件断开连接后,请调用close()
来关闭文件描述符。需要在广播接收器中监听断开的连接事件,代码如下:
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
}
}
}
};
建议点赞、收藏,方便以后查阅。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。