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 传送门