Android通过两种模式来支持各种USB外设和Android USB配件(指那些符合Android附件协议的硬件):USB配件和USB主机。在USB配件的模式之下,外部的USB配件就像USB主机那样。这种配件可以包括机器人控制器,基站连接器,医疗和音乐设备,电话亭以及读卡器这样很多的设备。这样就使得那些搭载Android系统的设备不需要具备主机的特性就可以和USB硬件进行交互。Android USB配件是指那些专门用来为搭载Android系统的设备工作以及符合Android附件通信协议的设备。在USB主机的模式之下,搭载Android的设备就像主机那样工作。这些设备包括数码相机,键盘,鼠标以及游戏控制器。Android USB设备被设计成具有广泛的应用领域,可以很好的完成人机互动应用的通信设备。
图1就显示了这两种模式的区别。当搭载Android系统的设备处于主机的模式下,它就充当USB主机并且为总线提供能源。而当搭载Android系统的设备处于USB配件的模式下时,连接的USB硬件(这种情况下,指的是一个Android USB配件)作为主机一样并且为总线提供能源。
在Android3.1(API12级)或较新的平台直接支持USB配件和主机模式。USB配件模式以一个附加的类库的方式支持范围更广的设备被移植到Android 2.3.4(API10级)。设备生产商可以决定是否在系统镜像上附加这个类库。
注意:支持USB主机和配件模式主要取决于设备的硬件,而不是平台的等级。你可以通过一个[[
调试注意事项
当用USB主机或者配件调试应用程序时,你最好有连接到搭载Android程序的设备的USB硬件。这样可以避免你要通过USB来为搭载Android的设备建立一个adb的连接。你可以在一个网络连接中一直连着adb。确保adb在一个网络连接的方式:
1.通过USB连接搭载Android系统的设备和你的电脑
2.在命令提示符中找到你的SDK platform_tools/ 目录,输入adbtcpip 5555
3.输入adb connect
4.在USB上为你的设备设置一个监听,输入adbusb
USB配件
USB配件模式允许用户连接那些专门搭载Android设备的USB主机硬件。这些配件必须遵守Android配件开发工具包文档中所列出的Android附件协议。这使得搭载Android系统的设备在不充当USB主机的情况之下,仍然可以和USB硬件进行交互。当一台搭载Android系统的设备处于USB配件模式时,所依附的Android USB配件作为主机为USB总线提供能源以及列举出相连的设备。Android3.1(API12级)提供了USB配件模式并且这一特点也继承了Android2.3.4(API10级)以此来支持更多设备。
选择正确的USB附件APIs
尽管USB附件API在Android3.1平台才开始介绍,但是也可以在Android2.3.4API中通过附加类库使用。因为这些APIs都是通过额外的类库来使用的,你可以导入两个包来支持USB配件模式。取决于你想支持什么样的搭载Android系统的设备,你也许不得不在一个的基础上使用另外一个:
com.android.feature.usb:为了支持Android2.3.4的USB配件模式,Google APIs附加类库包括了USB外设APIs并且它们就是包含在这个命名空间的后面。Android3.1还支持导入和调用这个命名空间的类来支持附加类库编写的应用程序。这个附加的类库只是关于android.hardware.usb外设APIs的一个简单的封装并且它不支持USB主机模式。如果你希望更大范围支持USB配件模式的设备,使用附加类库并且导入改包就行。需要注意的是,并不是所有搭载Android2.3.4的设备都需要拥有USB外设这一特色。每个设备生产商在决定是否具有这个特色,这也就是为什么你必须要在manifest文件中声明的原因了。
android.hardware.usb:这个命名空间包含在Android3.1版本中支持USB附件模式的类。因为这个包是框架APIs中的一部分,所以Android3.1版本可以在不用附加类库的前提之下支持USB附件模式。使用这个包时,如果你只关心Android3.1或者更新的支持USB附件模式的硬件的设备,你可以在mainfest文件中进行声明。
安装谷歌APIs的附加类库
如果你想安装这个附加类库,你可以通过在SDK管理器上面安装谷歌APIs中的Android API10包的方式来做。更多关于安装附加类库的信息请参见安装谷歌APIs附加元件。
API 概述
因为附加类库是一个框架APIs的封装,和那些支持USB附件功能的类是相似的。即使你在用附加类库的时候,你也可以用android.hardware.usb参考文档作为参考。
注意:然而,你要注意在附加类库和框架APIs之间还是有一些细微的使用差别的。
下面的表格为您描述了那些支持USB外设APIs的类:
关于平台APIs和附加类库之间的用法差异
在分别使用谷歌APIs附加类库和平台APIs的时候,通常会有两种差异。
如果您正在使用附加类库,则肯定会通过下列方式来创建UsbManager对象:
UsbManager manager = UsbManager.getInstance(this);
如果您不是用的附加类,则必须通过下列方式来创建UsbManager对象:
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
当您通过一个意图过滤器来过滤一个已经连接的配件,那么这个UsbAccessory对象就必须包含在传给您应用的这个意图中。如果您正在使用附加类库,您就必须通过下列方式来声明UsbAccessory对象:
UsbAccessory accessory = UsbManager.getAccessory(intent);
如果您不是用的附加类,则必须通过下列方式来声明UsbAccessory对象:
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Android ManiFest需求
下面的列表向您描述了在用USB配件APIs工作前需要在您应用中的manifest文件里面添加什么。下面的清单和资源文件例子将教您如何声明这些项:
▪ 因为并不是所有搭载Android系统的设备都保证支持USB配件APIs,包括一个
android.hardware.usb.accessory这个特色。
▪ 如果你正在使用附加类库,添加
▪ 如果您使用附加类库,那么您所设置的最低SDK版本是10级;如果您使用android.harware.usb这个类的话,您所设置最低版本的SDK就应该为12级。
▪ 如果您希望您的应用带有一个附加USB配件的通知,在您的主activity中为android.hardware.usb.action.USB_ACCESSORY_ATTACHED这个意图指定一对
在这个XML资源文件中,为您希望过滤的配件声明
▪ 制造商
▪ 模式
▪ 版本
在res/xml/这个目录下保存资源文件。这个资源文件的名字(没有 .xml的拓展名)必须和你在
清单和资源文件例子
下面的例子就为您展示了一个简单的manifest以及与之相关的资源文件:
...
...
android:resource="@xml/accessory_filter" />
在这种情况之下,下面的资源文件保存在res/xml/accessory_filter.xml文件中,并且指定那些只有与其相关的模式,制造商和版本的配件能够被选择。这个配件把这些属性传递给搭载Android系统的设备:
version="1.0"encoding="utf-8"?>
用配件工作
当用户将USB配件连接到搭载Android系统的设备上面时,Android系统会判断您的应用是否适用于已连接的该配件。如果适用,您就可以根据您的喜好为该设备建立连接。要这么做,您的应用必须做下面这些动作:
1. 您需要通过一个可以过滤配件附加事件的意图过滤器或者枚举已连接的配件来发现连接的配件来找到合适的接口。
2. 尚未获得许可的用户在适用配件操作时需要验证权限。
3. 通过在接入的端点进行读写数据的操作达到和配件交互的目的。
发现配件
您的应用可以通过两种方式来发现配件,一种是用一个意图过滤器在用户连接一个配件时对其进行通知,另一种则是通过枚举您已经连接的所有配件。如果您希望您的应用能够自动的探测到你想要的配件,请使用一个意图过滤器来做。但是,如果您希望得到一个已连接配件的列表或者您不希望过滤意图,枚举所有的配件会是一个更好的选择。
使用一个意图过滤器
为了让您的应用可以发现一个特定的USB配件,您可以为android.hardware.usb.action.USB_ACCESSORY_ATTACHED这个意图指定一个意图来进行过滤。伴随着这个意图过滤器,您需要指定一个资源文件来特别说明这个USB配件的属性,例如制造商,模式和版本。当用户连接到一个符合您配件过滤条件的配件时,
下面的例子告诉您该如何声明这个意图过滤器:
...
android:resource="@xml/accessory_filter"/>
下面的例子告诉您怎么样声明指定您希望连接的USB配件的相关资源文件:
version="1.0"encoding="utf-8"?>
在您的activity文件中,您可以从像这样的意图(有附加类的)中获取UsbAccessory来代表这个相关的配件:
UsbAccessory accessory = UsbManager.getAccessory(intent);
或者像这样(用平台APIs的):
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
枚举所有配件
您可以使您的应用在运行时列举出所有能够被识别的配件。
通过getAccessoryList()方法来获得一个包含所有已连接USB配件的数组:
UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
UsbAccessory[]accessoryList = manager.getAcccessoryList();
注意:现在,只能一次连接一个USB配件操作,但是在以后的API中会支持多配件的操作的。
获得使用一个配件的权限
在您使用一个配件前,您的应用必须从用户那里获得权限。
注意:如果您的应用在连接配件时通过一个意图过滤器来发现它们,如果用户允许您的应用来处理这个意图,它将自动接收这个权限。如果用户不允许,那么您就必须在连接配件之前详细在您的应用中写明需要请求的权限。
在某些情况下很有必要明确权限的许可要求,例如当您的应用枚举出所有已经连接的配件并且您希望和其中的一个进行“交流”。您必须在和该配件“交流”前检查是否有连接该配件的权限。如果不是这样,您的应用将在用户拒绝您连接该配件的权限之后收到个运行错误。
为了确切地获得权限,首先需要创建个广播接收器。这个接收器在您调用requestPermission()这个方法时从您得到的广播中舰艇这个意图。通过调用requestPermission()这个方法为用户跳出一个是否连接配件的对话框。下面的例子告诉您如何创建一个广播接收器:
privatestaticfinalString ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
privatefinalBroadcastReceivermUsbReceiver = newBroadcastReceiver(){
publicvoidonReceive(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()方法中去:
UsbManagermUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
privatestaticfinalString ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = newIntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
当您需要展示征求用户同意连接这个配件的权限的对话框时,调用requestPermission()这个方法:
UsbAccessory accessory;
...
mUsbManager.requestPermission(accessory, mPermissionIntent);
当用户回应这个对话框时,你的广播接收器就会收到一个包含用一个boolean值来表示结果的EXTRA_PERMISSION_GRANTED字段的意图。在您连接配件之前检查这个字段的值是否为true。
和配件之间的“交流”
您可以通过使用UsbManager这个类和配件进行“交流”,通过这个类可以获得一个文件描述符,然后您可以利用这个描述符来设置输入和输出流来读取和写入数据。这些流用来代表输入和输出的批量端点。您最好另起一个线程来让您的设备和配件进行“交流”,因为这样您就可以不需要将主线程锁起来了。下面的例子告诉您该如何和一个配件进行“交流”:
UsbAccessorymAccessory;
ParcelFileDescriptormFileDescriptor;
FileInputStreammInputStream;
FileOutputStreammOutputStream;
...
privatevoidopenAccessory(){
Log.d(TAG, "openAccessory: " + accessory);
mFileDescriptor = mUsbManager.openAccessory(mAccessory);
if(mFileDescriptor != null){
FileDescriptorfd = mFileDescriptor.getFileDescriptor();
mInputStream = newFileInputStream(fd);
mOutputStream = newFileOutputStream(fd);
Threadthread = newThread(null, this, "AccessoryThread");
thread.start();
}
}
在这个线程的run()方法中,您可以通过FileInputStream或者FileOutputStream对象来对配件进行读取和写出数据操作。当您通过FileInputStream对象读取配件中的数据时,请确保您所使用的缓存能够存储下USB数据包数据。Android配件协议支持数据包支持高达16384字节的数据包缓存区,所以您可以选择一直让您的缓存区是这个简单的大小。
注意:在一个比较低的水平下,64字节的数据包是全速配件以及512字节的数据包是高速配件。Android配件协议将这两种速度捆绑成一个简单的逻辑数据包。
想要知道更多如何使用Android中多线程的信息,请参见进程和线程。
中止和配件的“交流”
当您在完成和配件的“交流”之后,又或者该配件被移除了,通过调用close()方法来关闭你已经打开的文件描述符。为了监听分离这样的事件,您需要创建一个如下的广播接收器:
BroadcastReceivermUsbReceiver = newBroadcastReceiver(){
publicvoidonReceive(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
}
}
}
};
USB主机
文档内容
▪ API概述
▪ Android中manifest文件需求
▪ 工作的设备
▪ 发现设备
▪ 获得和设备进行“交流”的权限
▪ 和设备进行“交流”
▪ 中止和设备的“交流”
相关例子
▪ Adb测试用例
▪ 相关链接
当您搭载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文件中添加些什么:
▪ 因为不是所有的搭载Android系统的设备都能保证支持USB主机的APIs,不能包含那个声明您的应用使用android.hardware.usb.host这一特点的android.hardware.usb.host的这一元素。
▪ 设置您的应用的最低的SDK版本在12级或者更高。这个USB主机APIs不在更前面的版本之中。
▪ 如果您希望您的应用能够被连接的USB设备所提示,只要在您的主activity中在
在这个XML资源文件中,为您希望过滤的USB设备声明
▪ 供应商ID
▪ 产品ID
▪ 类
▪ 子类
▪ 协议(设备或者借口)
将您的资源文件保存到res/xml/目录下。资源文件名(不包含.xml的扩展名)必须和您在
Manifest文件和资源文件的例子
下面的例子告诉您一个manifest文件以及与它相关资源文件的例子:
...
...
android:resource="@xml/device_filter"/>
在这种情况下,下面的资源文件应该被保存在res/xml/device_filter.xml来确保找到那些特定符合您要求属性的USB设备:
version="1.0"encoding="utf-8"?>
用配件工作
当用户将USB配件连接到搭载Android系统的设备上面时,Android系统会判断您的应用是否适用于已连接的该配件。如果适用,您就可以根据您的喜好为该设备建立连接。要这么做,您的应用必须做下面这些动作:
1. 您需要通过一个可以过滤USB设备附加事件的意图过滤器或者枚举已连接的USB设备来发现连接的配件来找到合适的接口。
2. 尚未获得许可的用户在适用USB设备操作时需要验证权限。
3. 通过在接入的端点进行读写数据的操作达到和USB设备交互的目的。
发现设备
您的应用可以通过两种方式来发现USB设备,一种是用一个意图过滤器在用户连接一个设备时对其进行通知,另一种则是通过枚举您已经连接的所有USB设备。如果您希望您的应用能够自动的探测到你想要的设备,请使用一个意图过滤器来做。但是,如果您希望得到一个已连接设备的列表或者您不希望过滤意图,枚举所有的设备会是一个更好的选择。
使用一个意图过滤器
为了让您的应用可以发现一个特定的USB设备,您可以为android.hardware.usb.action.USB_DEVICE_ATTACHED这个意图指定一个意图来进行过滤。伴随着这个意图过滤器,您需要指定一个资源文件来特别说明这个USB设备的属性,例如供应商和产品ID。当用户连接到一个符合您配件过滤条件的配件时,这个系统会谈出一个对话框询问他们是否希望开始您的应用。如果用户同意,那么您的应用在失去连接之前会自动获取和设备连接的权限。
下面的例子告诉您该如何声明这个意图过滤器:
...
android:resource="@xml/device_filter"/>
下面的例子告诉您怎么样声明指定您希望连接的USB设备的相关资源文件:
version="1.0"encoding="utf-8"?>
在您的activity文件中,您可以从像这样的意图(有附加类的)中获取UsbDevice来代表这个相关的配件:
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
枚举所有配件
您可以使您的应用在运行时列举出所有能够被识别的USB设备。通过getDeviceList()方法来获得一个包含所有已连接USB配件的数组:
UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
...
HashMap
UsbDevice device = deviceList.get("deviceName");
如果您喜欢,您也可以一个接一个的从每一个设备的哈希图和过程中获取一个迭代器:
UsbManager manager = (UsbManager)getSystemService(Context.USB_SERVICE);
...
HashMap
Iterator
while(deviceIterator.hasNext()){
UsbDevice device = deviceIterator.next()
//your code
}
获得使用一个配件的权限
在您使用一个USB设备前,您的应用必须从用户那里获得权限。
注意:如果您的应用在连接USB设备时通过一个意图过滤器来发现它们,如果用户允许您的应用来处理这个意图,它将自动接收这个权限。如果用户不允许,那么您就必须在连接设备之前详细在您的应用中写明需要请求的权限。
在某些情况下很有必要明确权限的许可要求,例如当您的应用枚举出所有已经连接的USB设备并且您希望和其中的一个进行“交流”。您必须在和该设备“交流”前检查是否有连接该设备的权限。如果不是这样,您的应用将在用户拒绝您连接该设备的权限之后收到个运行错误。
为了确切地获得权限,首先需要创建个广播接收器。这个接收器在您调用requestPermission()这个方法时从您得到的广播中监听这个意图。通过调用requestPermission()这个方法为用户跳出一个是否连接该设备的对话框。下面的例子告诉您如何创建一个广播接收器:
privatestaticfinalString ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
privatefinalBroadcastReceivermUsbReceiver = newBroadcastReceiver(){
publicvoidonReceive(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()方法中去:
UsbManagermUsbManager = (UsbManager)getSystemService(Context.USB_SERVICE);
privatestaticfinalString ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = newIntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
当您需要展示征求用户同意连接这个设备的权限的对话框时,调用requestPermission()这个方法:
UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);
当用户回应这个对话框时,你的广播接收器就会收到一个包含用一个boolean值来表示结果的EXTRA_PERMISSION_GRANTED字段的意图。在您连接设备之前检查这个字段的值是否为true。
和设备之间的“交流”
我们可以同步或者异步的和USB设备进行“交流”。在任意一种情况之下,您都应该创建一个新的线程来进行数据传输,这样就不会阻塞您的主线程了。要想正确的设置好和一个设备之间的连接,您需要获得该设备正确的UsbInterface和UsbEndpoint来和您进行“交流”以及通过UsbDeviceConnection在这个接入点上发送请求。一般来说,您的代码应该这样:
▪ 检查一个UsbDevice对象的属性,例如产品ID,供应商ID,或者是关于设备的类,以此来确认您是否希望和该设备进行“交流”。
▪ 当您确信您希望和该设备进行“交流”时,找到关于该设备正确的UsbInterface以及和该接口所对应的UsbEndpoint。接口可以有一个或者多个接入点,而且一般都会有一个双向通信的输入和输出接入点。
▪ 当您找到正确的接入点时,在该接入点时打开一个UsbDeviceConnection。
▪ 您可以通过bulkTransfer()和controlTransfer()这两个方法在接入点上传输您所需要传递的数据。您最好在另起一个新的线程来进行这个步骤以避免阻塞主线程。想要详细地了解关于Android中使用线程的信息,详见线程和进程。
下面的代码段是做同步数据传输的一个简单方式。您的代码应该有更多的逻辑来准确地找到和设备“交流”的接口和接入点,而且应该能够在不同于主线程的线程中能够传输任何的数据传输。
privateByte[] bytes
privatestaticint TIMEOUT = 0;
privatebooleanforceClaim = true;
...
UsbInterfaceintf = 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。为了监听分离这样的事件,您需要创建一个如下的广播接收器:
BroadcastReceivermUsbReceiver = newBroadcastReceiver(){
publicvoidonReceive(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
}
}
}
};
¥29.8
¥9.9
¥59.8