USB主机当您搭载Android系统的设备处于USB主机模式时,它就像一个USB主机,为总线提供能源,并且列举出所有已经连接上的设备。在Android 3.1或者更高的版本中支持USB主机模式。 API概述在您开始之前,有个很重要的一点就是您必须对将要用到的类有个了解。下面的表格就向您描述了在android.hardware.usb这个包下USB主机APIs的一些特点。 表1.USB主机APIs
在大多数的情况之下,在和一个USB设备进行“交流”时,上面这些类都需要用到(UsbRequest这个类只有在您做异步通信的时候才会用到)。一般来说,您可以通过查询要操作的UsbDevice来获得一个UsbManager。当您有这个设备时,您需要找到正确的UsbInterface以及和这个接口所对应的UsbEndpoint来进行和设备的“交流”。一旦您获得了正确的接入点,打开UsbDeviceConnection来和该USB设备进行“交流”。 Android中manifest文件的需求下面的列表就是描述您应该在用USB主机APIs之前应该在您的应用中的manifest文件中添加些什么:
在这个XML资源文件中,为您希望过滤的USB设备声明<usb-device>元素。下面的列表描述<usb-device>的属性。一般来说,如果您想为一个特定的设备过滤就使用该产品的供应商和产品ID,如果您希望为一组USB设备,例如大量存储设备或者是数码相机来进行过滤那么就应该用类,子类和协议。您可以不指定这些属性,也可以指定所有的属性。不为每个设备指定属性,只有在您的应用需要它时才这么做(这句话翻译的一点问题^_^):
将您的资源文件保存到res/xml/目录下。资源文件名(不包含.xml的扩展名)必须和您在<meta-data>元素中指明的那个名字。在下面的例子中是这个XML资源文件的格式。 Manifest文件和资源文件的例子下面的例子告诉您一个manifest文件以及与它相关资源文件的例子: <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来确保找到那些特定符合您要求属性的USB设备: <?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系统会判断您的应用是否适用于已连接的该配件。如果适用,您就可以根据您的喜好为该设备建立连接。要这么做,您的应用必须做下面这些动作:
发现设备您的应用可以通过两种方式来发现USB设备,一种是用一个意图过滤器在用户连接一个设备时对其进行通知,另一种则是通过枚举您已经连接的所有USB设备。如果您希望您的应用能够自动的探测到你想要的设备,请使用一个意图过滤器来做。但是,如果您希望得到一个已连接设备的列表或者您不希望过滤意图,枚举所有的设备会是一个更好的选择。 使用一个意图过滤器为了让您的应用可以发现一个特定的USB设备,您可以为android.hardware.usb.action.USB_DEVICE_ATTACHED这个意图指定一个意图来进行过滤。伴随着这个意图过滤器,您需要指定一个资源文件来特别说明这个USB设备的属性,例如供应商和产品ID。当用户连接到一个符合您配件过滤条件的配件时,这个系统会谈出一个对话框询问他们是否希望开始您的应用。如果用户同意,那么您的应用在失去连接之前会自动获取和设备连接的权限。 下面的例子告诉您该如何声明这个意图过滤器: <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来代表这个相关的配件: 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'); 如果您喜欢,您也可以一个接一个的从每一个设备的哈希图和过程中获取一个迭代器: 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设备前,您的应用必须从用户那里获得权限。 注意:如果您的应用在连接USB设备时通过一个意图过滤器来发现它们,如果用户允许您的应用来处理这个意图,它将自动接收这个权限。如果用户不允许,那么您就必须在连接设备之前详细在您的应用中写明需要请求的权限。 在某些情况下很有必要明确权限的许可要求,例如当您的应用枚举出所有已经连接的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); 当您需要展示征求用户同意连接这个设备的权限的对话框时,调用requestPermission()这个方法: UsbDevice device;...mUsbManager.requestPermission(device, mPermissionIntent); 当用户回应这个对话框时,你的广播接收器就会收到一个包含用一个boolean值来表示结果的EXTRA_PERMISSION_GRANTED字段的意图。在您连接设备之前检查这个字段的值是否为true。 和设备之间的“交流”我们可以同步或者异步的和USB设备进行“交流”。在任意一种情况之下,您都应该创建一个新的线程来进行数据传输,这样就不会阻塞您的主线程了。要想正确的设置好和一个设备之间的连接,您需要获得该设备正确的UsbInterface和UsbEndpoint来和您进行“交流”以及通过UsbDeviceConnection在这个接入点上发送请求。一般来说,您的代码应该这样:
下面的代码段是做同步数据传输的一个简单方式。您的代码应该有更多的逻辑来准确地找到和设备“交流”的接口和接入点,而且应该能够在不同于主线程的线程中能够传输任何的数据传输。 private Byte[] bytesprivate 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()方法的结果。 想要了解更多地信息,请您参考Adb Test sample,这个参考将会告诉您如何进行异步批量传输,还有MissleLauncher sample将会告诉您如何异步监听一个中断端点。 中止和设备的“交流”当您在完成和设备的“交流”之后,又或者该设备被移除了,通过调用releaseInterface()和close()的方法来关闭UseInterface和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文件中,允许您的应用只能在它运行的时候处理这样的设备分离事件。这样的话,设备分离这个事件就只向正在运行的应用广播,而不是向所有的应用进行广播。 |
|