Android USB 开发详解
先附上 Android USB 官方文档
Android通过两种模式支持各种 USB 外设和 Android USB 附件(实现Android附件协议的硬件):USB附件和USB主机。USB开发需 Android 3.1(API级别12)以上。由于本人工作中只用到了主机模式,所以本文的侧重点在主机模式开发。
调试
在使用 USB 连接设备调试的时候,USB 外设将不能连接至设备,可以使用 WIFI 的方式连接调试,settings -> plugin -> WiFi ADB 插件好几个,选个适合自己的就 OK。
或者…我这里正好有一篇Android 模拟器连接 USB 设备喜欢点个赞哈!
一、AndroidManifest 文件设置
- uses-feature 申明这个软件需要使用 USB 功能,申明这个 Google Play 会把不满足的设备过滤掉,一般用 USB 的都是定制开发,忽略就行
<uses-feature android:name="android.hardware.usb.host"/>
- 1
- 将应用程序的最低SDK设置为API级别12或更高,早期 API 没有。
- 如果你希望你的应用程序连接指定的 USB 设备时被通知,需指定 和 元素对用于 android.hardware.usb.action.USB_DEVICE_ATTACHED。该 元素指向声明识别有关您要检测的设备信息的外部XML资源文件。
<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>
- 在XML资源文件中,声明要过滤的USB设备的元素。以下列表描述了属性 。通常,如果要过滤特定设备并使用类,子类和协议(如果要过滤一组USB设备(如大容量存储设备或数码相机)),请使用供应商(vendor-id)和产品(product-id)ID,在开发中这些过滤ID一般可以在文档中找到,或者自己连上看也行。你可以指定部分或全部这些属性。
将资源文件保存在res/xml/目录中。资源文件名(不带.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>
配置好清单文件后当用户连接与您的设备过滤器匹配的设备时,系统会向他们显示一个对话框,询问他们是否要启动您的应用程序。如果用户接受,则应用程序将自动具有访问设备的权限,直到设备断开连接。如果给了默认,那么这个 USB 设备插入后会自动启动这个 Activity
二、USB 设备的连接和使用
在清单文件中配置好以后我们直接进入 Java 代码环节
1.Android 中的 USB
Android 3.1(API级别12)以上原生提供了 USB 开发的 API,在android.hardware.usb包下提供了开发的相关类。
Class | 说明 |
---|---|
UsbManager | 获得 USB 管理器,与连接的 USB 设备通信。 |
UsbDevice | USB 设备的抽象,每个UsbDevice 都代表一个 USB 设备。 |
UsbInterface | 定义了设备的功能集,一个 UsbDevice 可能包含一个或多个UsbInterface,每个 Interface 都是独立的。 |
UsbEndpoint | UsbEndpoint 是 interface 的通信通道。 |
UsbDeviceConnection | host 与 device 建立的连接,并在 endpoint 传输数据。 |
UsbRequest | USB 请求包。 |
UsbConstants | USB 常量的定义 |
2.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)) {
// 有设备拔出了
}
}
}
3.获取 UsbManager
usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
4.获取 USB 设备列表
public List<UsbDevice> getDeviceList() {
HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
List<UsbDevice> usbDevices = new ArrayList<>();
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
usbDevices.add(device);
Log.e("USBUtil", "getDeviceList: " + device.getDeviceName());
}
return usbDevices;
}
5.获取特定的设备
/**
* mVendorId=1137,mProductId=85 佳博 3150T 标签打印机
*
* @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;
}
6.申请 USB 设备使用权限
安卓系统对 USB 设备的使用需要得到相应的权限,这个权限要用户手动授予,或插入设备时应用到你的应用中。在使用 USB 设备前首先我们要确认一下上一节中的device是否已经获得权限,如果没有就要主动申请权限:
/**
* 判断对应 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();
}
}
}
}
注册广播:
public void registerReceiver(Activity context) {
mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
context.registerReceiver(usbReceiver, filter);
}
7.通信
与 USB 设备的通信可以是同步的也可以是异步的。无论哪种情况,你都应该创建一个新线程来执行所有数据传输,避免阻塞UI线程。
第一步,打开通信端口
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;
} else {
usbEndpointOut = end;
}
}
return true;
}
第二步,发送数据
usbConnection.bulkTransfer(usbEndpointOut, bytes, bytes.length, 500);
其他
剩余的 API 我会在项目不断完善的同时更新上来
附:demo 传送门