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设备作为主机向主线供电,并向应用程序所在的设备供电。