
正文
Android系统编程入门系列之硬件交互——通信硬件USB
提示:扫一扫查出行【扫一扫了解最新限行尾号】
复制提示
在硬件交互的首篇对设备硬件的分类中,互联通信系列硬件主要用来与其他设备进行数据交互。从本文开始,将重点介绍该系列相关硬件。
互联通信系列硬件
根据硬件的可通信距离,由近及远分为USB、NFC、蓝牙、WLAN,SIM卡槽,这些硬件之间的功能原理及关系可以查找其他资料详细学习。总之,他们为当前设备与其他设备的交互搭建了桥梁,只要双方设备均遵循该系列硬件的协议,就可以在硬件层互相通信,而设备上的Android操作系统便会将硬件层的数据转换为应用层数据,进而与应用程序交互。这样也就实现了两个不同设备上的应用程序间的交互方案。理论上这个方案是可行的,那实际各硬件的使用方式分别是怎么样的呢?
USB接口
在应用程序中与USB硬件的交互,系统提供了两种方式,包括将该应用程序所在设备的USB接口作为 主机模式 ,和该应用程序所在设备的USB接口作为 配件模式 。在 主机模式 下,该应用程序所在设备通过USB接口为其他接入的USB设备供电,通常连接没有自带电源的设备(比如U盘)时启用此种模式;反之在 配件模式 下,是该应用程序所在设备接收通过USB接口接入的其他USB设备的电源提供,通常在连接有电源的设备(比如笔记本电脑)时启用此模式。理论上这两种模式只是针对USB硬件的供电方不同而区分,均可以在USB接口连接的两个设备之间的数据传输。
主机模式
权限声明
在应用程序的清单文件中,需要声明
<uses-feature />
标签,并设置其属性
android:name
值为
"android.hardware.usb.host"
。该标签设置并不是通过应用程序向系统申请权限,而是声明应用程序需要使用USB主机模式。
使用流程
获取USB主机设备
主机模式下,其中一种常用情况,应用程序可以监听插入USB接口的设备,此时可以在需要监听的界面
Activity
对应的清单文件注册信息中增加指定的接收意图,意图值为
android.hardware.usb.action.USB_DEVICE_ATTACHED
。这样在USB设备接入后,系统会发送上述意图值的广播,从而启动当前界面
Activity
。
在启动后的界面
Activity
中,可以调用
getIntent().getParcelableExtra(UsbManager.EXTRA_DEVICE)
系列方法,获取android.hardware.usb.UsbDevice USB设备类的对象。
主机模式下另外一种情况,是直接获取已连接的USB设备。在能获取上下文环境
Context
对象的地方,调用该对象的
getSystemService(String name)
,并指定参数值
name
为
Context.USB_SERVICE="usb"
,获得android.hardware.usb.UsbManagerUSB管理类的对象。调用该对象的
getDeviceList()
方法,返回
List<UsbDevice>
,同样能获取到当前设备连接的所有USB硬件对象的列表数据。
在直接获取已连接USB设备的情况下,应用程序需要主动向用户申请设备通信权限。通过
USBManager设备管理类对象的requestPermission(UsbAccessory accessory, PendingIntent pi)方法,在用户同意授权后,才能继续后续设备通信操作。
USB设备通信
在拿到
UsbDevice
类型的USB设备类对象,和
UsbManager
管理类对象之后,调用USB管理类对象的
openDevice(UsbDevice device)
方法,得到android.hardware.usb.UsbDeviceConnection USB设备连接对象,其参数
device
便是要通信的USB设备对象。
建立
UsbDeviceConnection
设备连接对象后,通信过程中还需要分别借助android.hardware.usb.UsbInterface USB接口类和android.hardware.usb.UsbEndpoint USB端点类。
在获取
UsbDevice
类型的设备类对象后,调用其
getInterface(int index)
方法获取
UsbInterface
USB接口类对象,其中参数
index
是当前USB设备可获取的所有接口数量中的索引值,而获取
UsbDevice
的所有接口数量,可根据另外一个方法
getInterfaceCount()
查看。
在获取
UsbInterface
类型的接口对象后,调用其
getEndpoint(int index)
方法获取
UsbEndpoint
USB端点类对象,其中参数
index
是当前USB接口中可获取的所有断点数量中的索引值,而获取
UsbInterface
的所有断点数量,可根据另外一个方法
getEndpointCount()
查看。
通过梅开二度获取USB接口对象和USB端点对象之后,首先要占用USB设备资源。继续调用
UsbDeviceConnection
连接对象的
claimInterface(UsbInterface intf, boolean force)
方法,其中参数
intf
便是上文获取
UsbInterface
接口类对象,参数
force
标明是否强制占用。返回
boolean
类型的结果表示占用是否成功。
在占用USB设备资源之后,就可以接收USB设备的通信数据了。调用
UsbDeviceConnection
连接对象的
bulkTransfer (UsbEndpoint endpoint, byte[] buffer, int offset, int length, int timeout)
方法。参数
endpoint
便是上文获取
UsbEndpoint
端点类对象;参数
buffer
用以存储通信中的二进制数组;参数
offset
可选项,默认值为
0
,用以标记存放数组
buffer
的起始位置;参数
length
用以标记存放数组的长度;参数
timeout
作为通信连接的最大时长。返回通信过程中实际传输的数据长度。
通信结束后,只需要关闭连接并释放占用的设备资源。调用
UsbDeviceConnection
连接对象的
close()
方法可以关闭通信连接。而调用该对象的
releaseInterface(UsbInterface intf)
方法可以释放占用的
UsbInterface
设备接口类型的参数
intf
对象。
配件模式
权限声明
在应用程序的清单文件中,需要声明
<uses-feature />
标签,并设置其属性
android:name
值为
"android.hardware.usb.accessory"
。该标签设置并不是通过应用程序向系统申请权限,而是声明应用程序需要使用USB配件模式。
使用流程
获取USB配件
配件模式下,其中一种常用情况,应用程序可以监听插入USB接口的设备,此时可以在需要监听的界面
Activity
对应的清单文件注册信息中增加指定的接收意图,意图值为
android.hardware.usb.action.USB_ACCESSORY_ATTACHED
。这样在USB设备接入后,系统会发送上述意图值的广播,从而启动当前界面
Activity
。
在启动后的界面
Activity
中,可以调用
getIntent().getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
系列方法,获取android.hardware.usb.UsbAccessory USB配件类的对象。
同样在配件模式下另外一种情况,是直接获取已连接的USB配件。在能获取上下文环境
Context
对象的地方,调用该对象的
getSystemService(String name)
,并指定参数值
name
为
Context.USB_SERVICE="usb"
,获得android.hardware.usb.UsbManagerUSB管理类的对象。调用该对象的
getAccessoryList()
方法,返回
List<UsbAccessory>
,同样能获取到当前设备连接的所有USB配件对象的列表数据。
与主机模式类似,在直接获取已连接USB配件的情况下,应用程序需要主动向用户申请设备通信权限。通过
USBManager设备管理类对象的requestPermission(UsbAccessory accessory, PendingIntent pi)方法,在用户同意授权后,才能继续后续设备通信操作。
USB设备通信
在拿到
UsbAccessory
类型的USB配件类对象,和
UsbManager
管理类对象之后,调用USB管理类对象的
openAccessory(UsbAccessory accessory)
方法,得到android.os.ParcelFileDescriptor 数据流化的文件描述符类的对象,其参数
accessory
便是要通信的USB配件对象。
在获取
ParcelFileDescriptor
数据流化的文件描述符类的对象后,调用其
getFileDescriptor()
方法得到java.io.FileDescriptor普通的文件描述符类型的对象,之后通过该对象创建基本的文件输入流java.io.FileInputStream对象以读取USB配件中的数据,或者创建基本的文件输出流java.io.FileOutputStream对象以将数据写入USB配件中。
通信结束后,只需要关闭数据流化的文件描述符的占用即可。通过调用
ParcelFileDescriptor
对象的
close()
方法以实现该操作。
主机模式与配件模式的区别
代码软件层
主机模式下,主要使用
UsbDevice
类,可以获取所连接USB设备的详细信息。
配件模式下,只能使用
UsbAccessory
类,只能获取连接USB设备的基本信息。
硬件层
主机模式下,由应用程序所在的设备向主线供电,并向连接的USB设备供电。
配件模式下,由连接的USB设备作为主机向主线供电,并向应用程序所在的设备供电。







