Android4.0之后,OTG开始普及,我们可以拓展U盘、鼠标等设备用于安卓设备。那么就需要了解USB的数据交换。
USB设备分为Host主设备和Slave从设备,我们通常理解的OTG就是安卓作为”host”:通过安卓设备作为主设备进行设备间通信。安卓中也封装好了API使我们不必研究USB协议便能进行简单设备通信。
首先 USB Host需要3.1以上的安卓版本支持,即minSdkVersion 12。然后我们需要一台设备,一部支持OTG的安卓手机,当然也需要一条OTG的数据线。
然后我们开始了解API:
UsbManager管理器允许你枚举出USB用来后续使用
UsbInterface设备通信接口
UsbEndpoint接口的接入点,分为单向和双向,多数设备请求和响应分占两个端口,部分设备共用同一端口。以我测试为例,使用的是虹膜识别设备,就是共用端口,发送和接收不能同时使用。
关于设备我们需要知道Vid和Pid,分别为VendorID(生产厂商ID)和ProductID(产品ID),用它可以区分设备的种类,这样就可以在枚举后筛选出需要的设备。
UsbDevice获取到的USB设备
UsbDeviceConnection获取到与设备的连接
通信流程如下:实例管理器枚举设备检查/获取权限获取接口获取端点打开设备连接发送数据接收数据关闭连接释放接口
为了实现设备通信,我需要设计一套使用思路:
通信的前提是:因为处于安卓平台,又是耗时操作,线程是不可少的;一定一定一定要熟悉要通信的设备,它的PID、VID、接收指令、消息格式、单包最大数据量、大小端……
为了进行通信,我们需要先建立连接,大致步骤为:插入设备è枚举设备è获取接口è获取端点è建立连接
拿到连接对象后,就可以进行发送:可以通过bulkTransfer()和controlTransfer()这两个方法,散装传递和控制传递。顾名思义,控制传递就是位于控制端口的发送,一般用来设置设备参数信息,也可以用来传递简单数据。较为复杂的信息,推荐前一个方法,它们的返回值为响应消息的长度,为-1则表示消息发送失败。
接收数据:关于数据接收,有同步和异步的区别,同步很好理解,发送响应发送响应。我们来聊聊异步,关于异步接收,谷歌官方推荐的做法是UsbRequest建立异步请求,不过这很难于控制,关于监听后的业务逻辑处理得也不是很完善;所以我使用了简单粗暴的办法——同步块数据传输+单例中开线程持续监听,并使用HandlerMessage传递。
==============================不===需===要===太===华===丽===的===分===割===线=========================
接下来我们需要单独写一个demo,来实现计划:
使用USB Host中需要声明应用需要使用usb.host功能,并要求Android版本高于12,这是支持OTG传输要求的最低版本;如果需要监听设备的自行注册广播接收者,并在插入逻辑中加入权限询问、在拔出同时释放资源和关闭设备连接,插入拔出意图:
android.hardware.usb.action.USB_DEVICE_ATTACHED和android.hardware.usb.action.USB_DEVICE_GRANTED
需要在清单文件中配置加入:
<uses-feature
android:name="android.hardware.usb.host"
android:required="true" />
<uses-permission
android:name="android.hardware.usb.host"
android:required="true" />
<uses-sdk
android:minSdkVersion="12"
android:targetSdkVersion="17" />
然后我们按照初始化的顺序来建立连接:
获取单例模式USBManager(不建议使用服务,在服务使用会影响UI线程):
public static utils instance = null;
public static utils getInstance(Context context) {
if (instance == null) {
instance = new utils();
}
// 初始化方法
init(context, myUsbManager = (UsbManager) context
.getSystemService(Context.USB_SERVICE));
return instance;
}
枚举设备
public static void enumerateDevice(UsbManager mUsbManager) {
if (mUsbManager == null) {
return;
} else {
HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList();
if (!(deviceList.isEmpty())) {
// 确认设备列表不为空
Iterator<UsbDevice> deviceIterator = deviceList.values()
.iterator();
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
int VendorID = device.getVendorId();
int ProductID = device.getProductId();
if (VendorID == vid && ProductID == pid) {
myUsbDevice = device;
// 筛选匹配设备
if (!mUsbManager.hasPermission(myUsbDevice)) {
PendingIntent pi = PendingIntent.getBroadcast(context, 0,
new Intent(ACTION_USB_PERMISSION), 0);
mUsbManager.requestPermission(myUsbDevice, pi);
// 如无权限询问权限
}
break;
}
}
}
}
}
获取设备接口
public static void getDeviceInterface() {
if (myUsbDevice != null) {
for (int i = 0; i < myUsbDevice.getInterfaceCount(); i++) {
UsbInterface intf = myUsbDevice.getInterface(i);
// 获取接口
if (i == 0) {
interface1 = intf;
}
if (i == 1) {
interface2 = intf;
}
}
if (!myUsbManager.hasPermission(myUsbDevice)) {
PendingIntent pi = PendingIntent.getBroadcast(context, 0,
new Intent(ACTION_USB_PERMISSION), 0);
myUsbManager.requestPermission(
iris_utils.myUsbDevice, pi);
}
} else {
}
}
获取通信端点
public static UsbEndpoint assignEndpoint(UsbInterface mInterface) {
UsbEndpoint epControl = null;
UsbEndpoint epIntEndpointOut = null;
UsbEndpoint epIntEndpointIn = null;
UsbEndpoint ep0 = mInterface.getEndpoint(0);
UsbEndpoint ep1 = mInterface.getEndpoint(1);
// 取到端点ep0 ep1
epBulkOut = ep1;
epBulkIn = ep0;
if (epBulkOut == null && epBulkIn == null && epControl == null
&& epIntEndpointOut == null && epIntEndpointIn == null) {
throw new IllegalArgumentException("not endpoint is founded!");
}
return epIntEndpointIn;
}
打开设备连接
public static void openDevice(UsbInterface mInterface) {
if (mInterface != null) {
UsbDeviceConnection conn = null;
// open前判断连接权限
if (myUsbManager.hasPermission(myUsbDevice)) {
conn = myUsbManager.openDevice(myUsbDevice);
}
if (conn != null && conn.claimInterface(mInterface, true)) {
mDeviceConnection = conn;
Message message = new Message();
message.what = 3;
MainActivity.handler.sendMessage(message);
if (mDeviceConnection != null)
;// android设备已经连接硬件设备
} else {
}
}
}
发送,需要以字节数组形式封装信息:
发送分为两种方式,块传输和控制传输,格式为:
mDeviceConnection.bulkTransfer(epBulkOut,buffer,buffer.length,timeout);
mDeviceConnection.controlTransfer(requestType, request, value, index, buffer, length, timeout);
public static void sendMessage (final byte[] buffer) {
tsend = new Thread() {
public void run() {
tba = 0;
ix = 0;
bf = new byte[0];
int ret = mDeviceConnection.bulkTransfer(epBulkOut, buffer,
buffer.length, 0);
// 参数依次为:下行端点,字节数组消息,消息长度,响应时间
if (ret < 0) {
mDeviceConnection.close();
// 如得不到响应,则关闭设备连接
} else {
mDeviceConnection.releaseInterface(interface1);
// 发送成功,释放资源
}
}
};
tsend.start();
}
接收,以单例形式启动线程接收上行字节数组:
public static void receiveMessage () {
treceive = new Thread() {
public void run() {
flag = true;
while (flag) {
byte[] arrayOfByte = new byte[512];
// 定义缓冲区
try {
if ((mDeviceConnection.bulkTransfer(epBulkIn,
arrayOfByte, arrayOfByte.length, 2000) > -1))
if (arrayOfByte != null) {
}
Thread.sleep(200L);
} catch (Exception localException) {
localException.printStackTrace();
}
}
}
};
treceive.start();
}
初始化方法
public static void init(Context cont, UsbManager mUsbManager) {
myUsbManager = mUsbManager;
context = cont;
enumerateDevice(mUsbManager); // 枚举设备
getDeviceInterface();// 查找设备接口
if (interface1 != null) {
// 获取设备endpoint
assignEndpoint(interface1);
// 打开conn连接通道
openDevice(interface1);
}
}