一 简介
1.1 USB是一种常见的设备端接口,在PC端,移动端等上面应用非常的广泛,这就涉及到USB设备与主机端的通信。下面就讲下Android设备上的USB通信流程。
1.2 在android提供了USBManager接口来管理USB设备,USBManager类是Android系统管理USB设备与操作的核心组件,涉及设备识别、权限请求、数据传输、事件监听等。通过USBManager,开发者可以实现设备模式切换、USB权限管理、配置选择、自定义USB驱动程序开发以及支持MIDI和音频设备的管理。
二 USBManager通信流程
2.1 添加USB权限和特性声明
<uses-permission android:name="android.permission.USB_PERMISSION" />
<uses-feature android:name="android.hardware.usb.host" />
2.2 初始化USB通信
//设备管理器private UsbManager usbManager;//连接的设备private UsbDevice currentDevice = null;//连接状态类private UsbDeviceConnection connection;//USB连接接口private UsbInterface usbInterface;//请求权限private String ACTION_USB_PERMISSION = "com.your.package.USB_PERMISSION";private BroadcastReceiver mUsbBroadcast;/*** USB请求权限*/private class UsbBroadcast extends BroadcastReceiver {public void onReceive(Context paramAnonymousContext, Intent paramAnonymousIntent) {try {String action = paramAnonymousIntent.getAction();// 判断广播类型if (ACTION_USB_PERMISSION.equals(action)) {try {// 如果用户同意,则对读写器进行操作connectDevice();} catch (Exception e) {disconnectDevice();}}} catch (Exception e) {e.printStackTrace();}}}
2.3 注册USB广播接收器
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(usbReceiver, filter);
2.4 搜索USB设备
/*** 获取Usb列表* @return*/public List<UsbDevice> getUsbPrinters() {List<UsbDevice> usbDevices = new ArrayList<>();usbManager = (UsbManager) App.getContext().getSystemService(Context.USB_SERVICE);HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();while (deviceIterator.hasNext()) {UsbDevice device = deviceIterator.next();if (isCscidDevice(device)) {usbDevices.add(device);}}return usbDevices;}/*** 判断UsbDevice 是否为读卡器类型设备* @param device* @return true:读卡器设备*/public boolean isCscidDevice(UsbDevice device) {if (device == null) {return false;}if (device.getInterfaceCount() == 0) {return false;}for (int i = 0; i < device.getInterfaceCount(); i++) {android.hardware.usb.UsbInterface usbInterface = device.getInterface(i);if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {int productId = device.getProductId();int vendorId = device.getVendorId();if (productId == 10203 && vendorId == 5824) {return true;}}}return false;}
2.5 请求权限,并连接设备
/*** 连接设备*/public void connectDevice() {try {//权限请求/** 在使用USB读写器设备前,应用必须获得权限。* 为了确切地获得权限,首先需要创建一个广播接收器。在调用requestPermission()这个方法时从得到的广播中监听这个意图。* 通过调用requestPermission()这个方法为用户跳出一个是否连接该设备的对话框。*/if (mUsbBroadcast == null) {mUsbBroadcast = new UsbBroadcast();IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);App.getContext().registerReceiver(mUsbBroadcast, filter);}//获取全部设备if (currentDevice == null) {List<UsbDevice> usbDeviceList = getUsbPrinters();if (usbDeviceList.size() > 0) {currentDevice = usbDeviceList.get(0);}}if (currentDevice != null) {// 检查设备是否具有所需的权限if (usbManager.hasPermission(currentDevice)) {// 打开设备并进行通信if (connection == null) {connection = usbManager.openDevice(currentDevice);}if (connection != null) {// 获取接口并配置if (usbInterface == null) {usbInterface = currentDevice.getInterface(0);}connection.claimInterface(usbInterface, true);// 你可以在这里与设备进行通信//readDataFromDevice();Log.d(TAG, "连接到设备: " + currentDevice.getDeviceName());} else {Log.e(TAG, "无法打开设备: " + currentDevice.getDeviceName());}} else {// 如果没有则请求权限PendingIntent mPermissionIntent = PendingIntent.getBroadcast(JuAiApp.getContext(), 0,new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);usbManager.requestPermission(currentDevice, mPermissionIntent);}}} catch (Exception r) {r.printStackTrace();}}
2.6 发送数据,先获取UsbEndpoint输出流端点
/*** 发送数据** @param message*/UsbEndpoint endpointOut = null;private void sendDataToDevice(String message) {// 获取输出端点if (endpointOut == null) {for (int i = 0; i < usbInterface.getEndpointCount(); i++) {UsbEndpoint endpoint = usbInterface.getEndpoint(i);if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK &&endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {endpointOut = endpoint; // 获取输出端点break;}}}if (endpointOut == null || connection == null) {Log.e(TAG, "输出端点或连接未初始化");return;}byte[] buffer = message.getBytes(); // 将要发送的字符串转换为字节数组int bytesSent = connection.bulkTransfer(endpointOut, buffer, buffer.length, 1000); // 1秒超时if (bytesSent < 0) {Log.e(TAG, "发送数据失败");} else {Log.d(TAG, "成功发送数据: " + message);}}
2.7 读取数据,先获取输入流端点
/*** 读取数据*/UsbEndpoint endpointIn = null;private static final int BUFFER_SIZE = 1024; // 定义读取的缓冲区大小private void readDataFromDevice() {if (endpointIn == null) {// 获取输入端点for (int i = 0; i < usbInterface.getEndpointCount(); i++) {UsbEndpoint endpoint = usbInterface.getEndpoint(i);Log.e(TAG, "readDataFromDevice: " + endpoint.getType());Log.e(TAG, "getDirection: " + endpoint.getDirection());if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT &&endpoint.getDirection() == UsbConstants.USB_DIR_IN) {endpointIn = endpoint; // 获取输入端点break;}}}if (endpointIn == null || connection == null) {Log.e(TAG, "输入端点或连接未初始化");return;}byte[] buffer = new byte[BUFFER_SIZE]; // 创建缓冲区// 循环读取数据Thread readThread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {int bytesRead = connection.bulkTransfer(endpointIn, buffer, buffer.length, 1000); // 1秒超时if (bytesRead > 0) {// 处理读取到的数据String data = new String(buffer, 0, bytesRead); // 将字节数组转换为字符串Log.d(TAG, "读取数据: " + data);} else if (bytesRead == 0) {Log.d(TAG, "没有更多数据可读");break;} else {Log.e(TAG, "读取数据失败: " + bytesRead);break;}}}});readThread.start(); // 启动读取线程}
2.8 断开USB设备并关闭资源
/*** 断开设备*/public void disconnectDevice() {try {if (usbManager != null && currentDevice != null && usbInterface != null) {// 获取之前连接的接口// 释放接口connection.releaseInterface(usbInterface);// 关闭连接connection.close();// 清空连接connection = null;usbInterface = null;//读取置空endpointIn = null;//写入置空endpointOut = null;//设备置空currentDevice = null;if (mUsbBroadcast != null) {App.getContext().unregisterReceiver(mUsbBroadcast);}Log.d(TAG, "设备已断开: ");}} catch (Exception e) {e.printStackTrace();}}
三 USB协议相关概念
3.1 USB常见类型,用于判断是哪种USB设备
//获取设备类型
for (int i = 0; i < device.getInterfaceCount(); i++) {android.hardware.usb.UsbInterface usbInterface = device.getInterface(i);/*** 设备类 ID 设备类名称 描述* 0x01 Audio 音频设备,如 USB 音频接口。* 0x02 Communications 通信设备,如调制解调器和网络适配器。* 0x03 HID (Human Interface Device) 人机接口设备,通常指键盘、鼠标等。* 0x05 Physical Interface Device 物理接口设备,通常用于各种控制设备。* 0x06 Image 图像设备,如 USB 摄像头和扫描仪。* 0x07 Printer 打印机设备。* 0x08 Mass Storage 大容量存储设备,如 USB 闪存驱动器和外部硬盘。* 0x09 Hub USB 集线器。* 0x0A CDC (Communications Device Class) 通信设备类,常用于调制解调器和串行口。* 0x0B Smart ICCard 智能卡读卡器。* 0x0D Content Security Device 内容保护设备,用于安全性应用。* 0x0E Video 视频设备,如视频采集卡。* 0xEF Miscellaneous 其他不属于上述类别的设备。* 0xFF Vendor Specific 厂商自定义设备,通常特定于某个硬件厂商的设备。* @param deviceClass* @param deviceSubclass* @param deviceProtocol* @return*/if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {int productId = device.getProductId();int vendorId = device.getVendorId();if (productId == 10203 && vendorId == 5824) {return true;}}}
ID | 设备类名称 | 描述 | |
0x01 | Audio | 音频设备,如 USB 音频接口。 | |
0x02 | Communications | 通信设备,如调制解调器和网络适配器。 | |
0x03 | HID(Human Interface Device) | 人机接口设备,通常指键盘、鼠标等。 | |
0x05 | Physical Interface Device | 物理接口设备,通常用于各种控制设备。 | |
0x06 | Image | 图像设备,如 USB 摄像头和扫描仪。 | |
0x07 | Printer | 打印机设备。 | |
0x08 | Mass | 大容量存储设备,如 USB 闪存驱动器和外部硬盘。 | |
0x09 | Hub USB | 集线器。 | |
0x0A | CDC | 通信设备类,常用于调制解调器和串行口。 | |
0x0B | Smart ICCard | 智能卡读卡器。 | |
0x0D | Content Security Device | 内容保护设备,用于安全性应用。 | |
0x0E | Video | 视频设备,如视频采集卡。 | |
0x0F | Miscellaneous | 其他不属于上述类别的设备。 | |
0x0F | Vendor Specific | 厂商自定义设备,通常特定于某个硬件厂商的设 |
3.2 设备信息参数
//获取设备类型
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {//产品IDint productId = device.getProductId();//厂商IDint vendorId = device.getVendorId();if (productId == 10203 && vendorId == 5824) {return true;}
}
* DeviceName=/dev/bus/usb/005/007 USB端口名字 * ProductName=null 产品名字 * DeviceId=5007(0x138F) 设备ID * VendorId=5824(0x16C0) 厂商ID * ProductId=10203(0x27DB) 产品ID * DeviceClass=0 设备分类 * device Class 为0------------- * Interface.describeContents()=0 接口描述 * Interface.getEndpointCount()=2 接口端点数量 * Interface.getId()=0 ID接口 * Interface.getInterfaceClass()=3 接口数量 * anInterface.getInterfaceProtocol()=0 接口协议数量 * anInterface.getInterfaceSubclass()=0 * device Class 为0------end-------
四 示例代码
public class USBManagerUtils {private static final String TAG = "USBManagerUtils";private static USBManagerUtils mInstance;public static USBManagerUtils getInstance() {if (mInstance == null) {synchronized (USBManagerUtils.class) {if (mInstance == null) {mInstance = new USBManagerUtils();}}}return mInstance;}//设备管理器private UsbManager usbManager;//连接的设备private UsbDevice currentDevice = null;//连接状态类private UsbDeviceConnection connection;//USB连接接口private UsbInterface usbInterface;//请求权限private String ACTION_USB_PERMISSION = "com.your.package.USB_PERMISSION";private BroadcastReceiver mUsbBroadcast;/*** USB请求权限*/private class UsbBroadcast extends BroadcastReceiver {public void onReceive(Context paramAnonymousContext, Intent paramAnonymousIntent) {try {String action = paramAnonymousIntent.getAction();// 判断广播类型if (ACTION_USB_PERMISSION.equals(action)) {try {// 如果用户同意,则对读写器进行操作connectDevice();} catch (Exception e) {disconnectDevice();}}} catch (Exception e) {e.printStackTrace();}}}/*** 获取Usb 打印机列表* get USB printer list** @return*/public List<UsbDevice> getUsbPrinters() {List<UsbDevice> usbDevices = new ArrayList<>();usbManager = (UsbManager) App.getContext().getSystemService(Context.USB_SERVICE);HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();while (deviceIterator.hasNext()) {UsbDevice device = deviceIterator.next();if (isCscidDevice(device)) {usbDevices.add(device);}}return usbDevices;}/*** 判断UsbDevice 是否为读卡器类型设备* check a UsbDevice* DeviceName=/dev/bus/usb/005/007* ProductName=null* DeviceId=5007(0x138F)* VendorId=5824(0x16C0)* ProductId=10203(0x27DB)* DeviceClass=0* device Class 为0-------------* Interface.describeContents()=0* Interface.getEndpointCount()=2* Interface.getId()=0* Interface.getInterfaceClass()=3* anInterface.getInterfaceProtocol()=0* anInterface.getInterfaceSubclass()=0* device Class 为0------end-------** @param device* @return true:读卡器设备* 对于同一型号的USB产品,其 vendorId 和 productId 通常是一致的,因为它们来自同一个制造商且属于同一产品系列。例如,如果一个制造商生产了一个特定的USB闪存驱动器型号,那么该型号的所有设备都会共享相同的 vendorId 和 productId。*/public boolean isCscidDevice(UsbDevice device) {if (device == null) {return false;}if (device.getInterfaceCount() == 0) {return false;}for (int i = 0; i < device.getInterfaceCount(); i++) {android.hardware.usb.UsbInterface usbInterface = device.getInterface(i);/*** 设备类 ID 设备类名称 描述* 0x01 Audio 音频设备,如 USB 音频接口。* 0x02 Communications 通信设备,如调制解调器和网络适配器。* 0x03 HID (Human Interface Device) 人机接口设备,通常指键盘、鼠标等。* 0x05 Physical Interface Device 物理接口设备,通常用于各种控制设备。* 0x06 Image 图像设备,如 USB 摄像头和扫描仪。* 0x07 Printer 打印机设备。* 0x08 Mass Storage 大容量存储设备,如 USB 闪存驱动器和外部硬盘。* 0x09 Hub USB 集线器。* 0x0A CDC (Communications Device Class) 通信设备类,常用于调制解调器和串行口。* 0x0B Smart ICCard 智能卡读卡器。* 0x0D Content Security Device 内容保护设备,用于安全性应用。* 0x0E Video 视频设备,如视频采集卡。* 0xEF Miscellaneous 其他不属于上述类别的设备。* 0xFF Vendor Specific 厂商自定义设备,通常特定于某个硬件厂商的设备。* @param deviceClass* @param deviceSubclass* @param deviceProtocol* @return*/if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {int productId = device.getProductId();int vendorId = device.getVendorId();if (productId == 10203 && vendorId == 5824) {return true;}}}return false;}/*** 连接设备*/public void connectDevice() {try {//权限请求/** 在使用USB读写器设备前,应用必须获得权限。* 为了确切地获得权限,首先需要创建一个广播接收器。在调用requestPermission()这个方法时从得到的广播中监听这个意图。* 通过调用requestPermission()这个方法为用户跳出一个是否连接该设备的对话框。*/if (mUsbBroadcast == null) {mUsbBroadcast = new UsbBroadcast();IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);App.getContext().registerReceiver(mUsbBroadcast, filter);}//获取全部设备if (currentDevice == null) {List<UsbDevice> usbDeviceList = getUsbPrinters();if (usbDeviceList.size() > 0) {currentDevice = usbDeviceList.get(0);}}if (currentDevice != null) {// 检查设备是否具有所需的权限if (usbManager.hasPermission(currentDevice)) {// 打开设备并进行通信if (connection == null) {connection = usbManager.openDevice(currentDevice);}if (connection != null) {// 获取接口并配置if (usbInterface == null) {usbInterface = currentDevice.getInterface(0);}connection.claimInterface(usbInterface, true);// 你可以在这里与设备进行通信//readDataFromDevice();Log.d(TAG, "连接到设备: " + currentDevice.getDeviceName());} else {Log.e(TAG, "无法打开设备: " + currentDevice.getDeviceName());}} else {// 如果没有则请求权限PendingIntent mPermissionIntent = PendingIntent.getBroadcast(App.getContext(), 0,new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);usbManager.requestPermission(currentDevice, mPermissionIntent);}}} catch (Exception r) {r.printStackTrace();}}/*** 发送数据** @param message*/UsbEndpoint endpointOut = null;private void sendDataToDevice(String message) {// 获取输出端点if (endpointOut == null) {for (int i = 0; i < usbInterface.getEndpointCount(); i++) {UsbEndpoint endpoint = usbInterface.getEndpoint(i);if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK &&endpoint.getDirection() == UsbConstants.USB_DIR_OUT) {endpointOut = endpoint; // 获取输出端点break;}}}if (endpointOut == null || connection == null) {Log.e(TAG, "输出端点或连接未初始化");return;}byte[] buffer = message.getBytes(); // 将要发送的字符串转换为字节数组int bytesSent = connection.bulkTransfer(endpointOut, buffer, buffer.length, 1000); // 1秒超时if (bytesSent < 0) {Log.e(TAG, "发送数据失败");} else {Log.d(TAG, "成功发送数据: " + message);}}/*** 读取数据*/UsbEndpoint endpointIn = null;private static final int BUFFER_SIZE = 1024; // 定义读取的缓冲区大小private void readDataFromDevice() {if (endpointIn == null) {// 获取输入端点for (int i = 0; i < usbInterface.getEndpointCount(); i++) {UsbEndpoint endpoint = usbInterface.getEndpoint(i);Log.e(TAG, "readDataFromDevice: " + endpoint.getType());Log.e(TAG, "getDirection: " + endpoint.getDirection());if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_INT &&endpoint.getDirection() == UsbConstants.USB_DIR_IN) {endpointIn = endpoint; // 获取输入端点break;}}}if (endpointIn == null || connection == null) {Log.e(TAG, "输入端点或连接未初始化");return;}byte[] buffer = new byte[BUFFER_SIZE]; // 创建缓冲区// 循环读取数据Thread readThread = new Thread(new Runnable() {@Overridepublic void run() {while (true) {int bytesRead = connection.bulkTransfer(endpointIn, buffer, buffer.length, 1000); // 1秒超时if (bytesRead > 0) {// 处理读取到的数据String data = new String(buffer, 0, bytesRead); // 将字节数组转换为字符串Log.d(TAG, "读取数据: " + data);} else if (bytesRead == 0) {Log.d(TAG, "没有更多数据可读");break;} else {Log.e(TAG, "读取数据失败: " + bytesRead);break;}}}});readThread.start(); // 启动读取线程}/*** 断开设备*/public void disconnectDevice() {try {if (usbManager != null && currentDevice != null && usbInterface != null) {// 获取之前连接的接口// 释放接口connection.releaseInterface(usbInterface);// 关闭连接connection.close();// 清空连接connection = null;usbInterface = null;//读取置空endpointIn = null;//写入置空endpointOut = null;//设备置空currentDevice = null;if (mUsbBroadcast != null) {App.getContext().unregisterReceiver(mUsbBroadcast);}Log.d(TAG, "设备已断开: ");}} catch (Exception e) {e.printStackTrace();}}
}
五 注意事项
5.1 对于同一型号的USB产品,其 vendorId 和 productId 通常是一致的,因为它们来自同一个制造商且属于同一产品系列。例如,如果一个制造商生产了一个特定的USB闪存驱动器型号,那么该型号的所有设备都会共享相同的 vendorId 和 productId。
5.2 扫码枪usb插拔时,会造成Activity重新加载,说明对Activity生命周期有影响,需要处理一下。
可在Activity配置
android:configChanges="orientation|keyboard|keyboardHidden"
六 使用场景举例-扫码枪设备
6.1 Android端获取扫码枪数据一般有如下三种获取方式
- USB键盘模式,自感模式(出厂默认):大多数扫码枪是模拟键盘进行输入(不支持包含有汉字的二维码),大多数时候需要使用带焦点的 EditText 接收其扫描的信息。设置一个带焦点的EditText进行接收,此方式容易漏读扩展的ASCII码数据,除非二维码是标准的ASCII码(1-128)。由于扫码枪会将扫描出来的内容转化为键盘事件,对应的就是Android中的KeyEvent事件,所以我们只需要在我们的activity中重写dispatchKeyEvent方法,即可获取相应事件。
- 通过 USB COM连接扫码:发送扫码开关指令进行扫码,即基于Usb相关类控制扫码开关
- USB串口自感模式:即串口连接(当扫码枪使用 USB 通讯接口,但主机应用程序是采用串口通讯方式接收数据),客户端只负责监听读取数据,无须控制扫码枪指令,需要依赖usb串口传输数据的第三方库:https://github.com/mik3y/usb-serial-for-android
6.2 USB键盘模式使用
/*** usb键盘模式扫码示例,无需EditText接收*/
public class ScannerUsbKeyboardActivity extends AppCompatActivity {UsbKeyboardAutoScan usbKeyboardAutoScan;EditText et_barcode;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.scanner_usb_keyboard_activity);et_barcode = findViewById(R.id.et_barcode);usbKeyboardAutoScan = new UsbKeyboardAutoScan();usbKeyboardAutoScan.setOnScanListener(new OnScanListener() {@Overridepublic void onScanSuccess(String barcode) {//扫码接收et_barcode.setText(barcode);}});}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {if (usbKeyboardAutoScan.isIntercept()&& event.getKeyCode() != KeyEvent.KEYCODE_BACK) {//不处理返回键usbKeyboardAutoScan.analysisKeyEvent(event);return true;//防止输入框接收事件}return super.dispatchKeyEvent(event);}@Overrideprotected void onDestroy() {super.onDestroy();usbKeyboardAutoScan.cancel();}
}
6.3 USB串口自感模式使用
/*** 基于usb扫码*/
public class ScannerUsbActivity extends AppCompatActivity {BaseUsbScan usbScan;EditText et_barcode;UsbConfig usbConfig = new UsbConfig();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.scanner_usb_activity);et_barcode = findViewById(R.id.et_barcode);usbConfig.setProductId(1233);usbConfig.setVendorId(1234);
// usbScan = new UsbCmdScan(this); //通过usb连接扫码, 发送命令扫码UsbConfig.SerialPortConfig serialPortConfig = new UsbConfig.SerialPortConfig();usbConfig.setSerialPortConfig(serialPortConfig);usbScan = new UsbComAutoScan(this);//通过usb转串口,自感模式,推荐usbScan.setOnScanListener(new OnScanListener() {@Overridepublic void onScanSuccess(String barcode) {}});}public void listUsbDevice(View view) {//找到设备插入的usb孔对应UsbDeviceList<UsbDevice> usbDevices = usbScan.getUsbDevices(this);for (UsbDevice device : usbDevices) {et_barcode.setText(et_barcode.getText() + "\n" + device.getDeviceName() + ",vendorID:" + device.getVendorId() + ",ProductId:" + device.getProductId());}}public void openUsbScan(View view) {int ret = usbScan.openScan(usbConfig);if (ret != 0) {showMsg("打开usb扫码失败,ret" + ret);} else {showMsg("打开usb扫码成功");}}private void showMsg(String s) {et_barcode.setText(s);}public void closeUsbScan(View view) {usbScan.closeScan();}public void loopScan(View view) {usbScan.startReading();}public void stopLoopScan(View view) {usbScan.stopReading();}@Overrideprotected void onDestroy() {super.onDestroy();usbScan.closeScan();}
}