“这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战”
1 引言
随着安卓手机的市场份额逐步增加,安卓系统的功能也日益强大。使用手机与各类设备通信的场景也越来越多,本篇文章将介绍如何使用安卓手机与USB设备进行通信。
2 USB简介
USB是英文Universal Serial Bus(通用串行总线)的缩写,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。现在智能手机也加入了对于USB设备的支持。
安卓手机支持USB accessory模式和USB host模式。USB host模式是手机充当主机,为总线提供电力支持。USB accessory模式正好相反,将手机当作附件把USB设备当作主机。本文中介绍的为Host模式。
3 相关API
| class | 说明 |
|---|---|
| UsbManager | USB管理器,与连接的USB设备通信。 |
| UsbDevice | USB设备的抽象,每个UsbDevice都代表一个USB设备。 |
| UsbInterface | 定义了设备的功能集,一个UsbDevice可能包含一个或多个UsbInterface,每个Interface都是独立的。 |
| – | – |
| UsbEndpoint | UsbEndpoint是Interface的通信通道。 |
| UsbDeviceConnection | host与device 建立的连接,并在endpoint 传输数据。 |
| – | – |
| UsbRequest | USB 请求包。 |
| UsbConstants | USB常量的定义 |
4 配置AndroidManifest.xml文件
在进行USB开发时,需要在AndroidManifest.xml文件中配置相应的属性。配置代码如下:
<!--添加权限-->
<uses-feature android:name="android.hardware.usb.host"/>
<activity
android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<!-- 如果这里是启动Activity的话,点击USB接入的弹窗会启动该页面 -->
<category android:name="android.intent.category.LAUNCHER" />
<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>
5 过滤设备
在XML资源文件中,声明要过滤的USB设备的元素。通常,如果要过滤特定设备并使用类,子类和协议(如果要过滤一组USB设备(如大容量存储设备或数码相机)),请使用供应商(vendor-id)和产品(product-id)ID,在开发中这些过滤ID一般可以在文档中找到,或者在设备管理器中查看。
将资源文件保存在res/xml/目录中。资源文件名(不带.xml扩展名)必须与您在元素中指定的文件名相同。配置格式如下:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device
class="255"
product-id="5678"
protocol="1 "
subclass="66"
vendor-id="1234" />
</resources>
6 监听USB设备的插拔
Android 系统中,USB 设备的插入和拔出是以系统广播的形式发送的,我们只要注册监听这个广播就好。
注册广播代码如下:
public class USBReceiver extends BroadcastReceiver {
public static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
@Override
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 (device != null) {
//call method to set up device communication
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Log.e("USBReceiver", "获取权限成功:" + device.getDeviceName());
} else {
Log.e("USBReceiver", "获取权限失败:" + device.getDeviceName());
}
}
}
}else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
// 有新的设备插入了,在这里一般会判断这个设备是不是我们想要的,是的话就去请求权限
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
// 有设备拔出了
}
}
}
7 获取UsbManager
UsbManager类是安卓系统提供的用于管理USB设备的类,其中对于USB设备的操作大多数需要调用此类对象中的方法实现。UsbManager类通过获取系统服务的方式获取。
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
8 获取目标UsbDevice
UsbDevice标识着搜索到的USB设备,目标UsbDevice设备需要通过pid和vid进行区别。
/**
* @param vendorId 厂商ID
* @param productId 产品ID
* @return device
*/
public UsbDevice getUsbDevice(int vendorId, int productId) {
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
if (device.getVendorId() == vendorId && device.getProductId() == productId) {
Log.e("USBUtil", "getDeviceList: " + device.getDeviceName());
return device;
}
}
Toast.makeText(context, "没有对应的设备", Toast.LENGTH_SHORT).show();
return null;
}
9 申请USB设备使用权限
/**
* 判断对应 USB 设备是否有权限
*/
public boolean hasPermission(UsbDevice device) {
return usbManager.hasPermission(device);
}
/**
* 请求获取指定 USB 设备的权限
*/
public void requestPermission(UsbDevice device) {
if (device != null) {
if (usbManager.hasPermission(device)) {
Toast.makeText(context, "已经获取到权限", Toast.LENGTH_SHORT).show();
} else {
if (mPermissionIntent != null) {
usbManager.requestPermission(device, mPermissionIntent);
Toast.makeText(context, "请求USB权限", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "请注册USB广播", Toast.LENGTH_LONG).show();
}
}
}
}
10 USB设备收发数据
(1)打开通信端口
public boolean openPort(UsbDevice device) {
//获取设备接口,一般只有一个
usbInterface = device.getInterface(0);
// 判断是否有权限
if (hasPermission(device)) {
// 打开设备,获取 UsbDeviceConnection 对象,连接设备,用于后面的通讯
usbConnection = usbManager.openDevice(device);
if (usbConnection == null) {
return false;
}
if (usbConnection.claimInterface(usbInterface, true)) {
Toast.makeText(Utils.getContext(), "找到 USB 设备接口", Toast.LENGTH_SHORT).show();
} else {
usbConnection.close();
Toast.makeText(Utils.getContext(), "没有找到 USB 设备接口", Toast.LENGTH_SHORT).show();
return false;
}
} else {
Toast.makeText(Utils.getContext(), "没有 USB 权限", Toast.LENGTH_SHORT).show();
return false;
}
//获取接口上的两个端点,分别对应OUT和IN
for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
UsbEndpoint end = usbInterface.getEndpoint(i);
if (end.getDirection() == UsbConstants.USB_DIR_IN) {
usbEndpointIn = end;//获取读入数据的UsbEndpoint
} else {
usbEndpointOut = end;//获取发送的数据的UsbEndpoint
}
}
return true;
}
(2)收发数据
调用UsbDeviceConnection的bulkTransfer方法与USB设备通信,向USB设备发送数据用usbEndpointOut,接受USB设备的数据用usbEndpointIn。 发送数据:
int ret = usbDeviceConnection.bulkTransfer(usbEndpointOut, data, data.length, 100);
接收数据:USB接收数据需开启数据读取线程。
//开线程读取数据
private void startReading() {
new Thread(new Runnable() {
@Override
public void run() {
while (isReading) {
synchronized (this) {
//创建接收数据的数组
byte[] data = new byte[usbEndpointIn.getMaxPacketSize()];
//读取数据
int ret = usbConnection.bulkTransfer(usbEndpointIn, data, data.length, 100);
}
}
}
}).start();
}
11 结语
安卓USB通信具有即插即用,可热插拔,具有自动配置能力,用户只要简单地将外设插人到手机就能自动识别和配置USB设备。目前安卓手机、平板都具备USB接口,连接灵活,易扩展。
USB通信速率相对较快,USB2.0理论速度约每秒480Mbps(约每秒60MB),USB3.0的理论速度能够达到每秒5Gbps(约为每秒625MB)。
公众号:程序员喵大人(专注于Android各类学习笔记、面试题以及IT类资讯的分享。)