【USB读写速率 android USB HID通信】成功的实现了每125微妙接收到一包数据,每秒8000包数据(256字节一包) 您所在的位置:网站首页 usb传输速率如何测试的 【USB读写速率 android USB HID通信】成功的实现了每125微妙接收到一包数据,每秒8000包数据(256字节一包)

【USB读写速率 android USB HID通信】成功的实现了每125微妙接收到一包数据,每秒8000包数据(256字节一包)

2024-07-12 09:20| 来源: 网络整理| 查看: 265

由于业务的需要,最近需要使用USB HID进行通信进行读取USB数据;起初用Android平台自有的API,前面也记录过;可是作用不大;然后推测是否是因为CPU占用率高、或者是受到程序中其他线程运行挤占资源的影响;于是,便采用了多进程的方式,把读取数据单独放入了一个进程;可效果也不明显;于是便有了这种方式;

文章目录 前言一、USB HID是什么?二、使用controlTransfer或bulkTransfer读取1.UsbManager2.UsbDevice3.UsbInterface4.UsbEndpoint5.UsbDeviceConnection6.Android配置文件要求7.开发步骤1.检测设备2.获取和设备通讯的权限3.和设备通信4.终止通信 三、usb多进程开发1.把读写单独放在一个进程2.把数据解析放在另外一个进程 四、io流读取五、JNI方式实现通过USB串口读取数据六、修改USB缓冲区1.缓冲区2.增加延时 七、UVCCamera1.借鉴UVCCamera 八、移植libusb1.libusb目录2.Android上可以直接引用的部分

前言

谈到USB HID通信;这一路刨的坑也比较多;最起初,直接利用官方的API进行调用读取;经过测试发现,在USB2.0 运行内存为2G、可运行内存440M的Android设备上,10秒钟能收包4000-7000包;平均每毫秒不到1包;这在一般的设备功能上或许没有问题,但在一些短时间内有巨大数据要求的功能上,是肯定达不到要求的;于是就想着是不是别的线程挤占了资源、以至于影响了USB数据输出;于是,我便把读取单独放在了一个进程;这个时候,平均每10秒能收到9000包左右的数据;这就意味着,平均每毫秒只能接收0.9包数据;这个当然满足不了需求;这是,我推测,会不会是CPU过高影响的?于是我马上查看在接收高并发数据情况下读写进程的CPU情况;一直都保持在1%;于是就在博客上看有相似经历的人的文字,希望能找出破解的办法;很遗憾,没有找到; 于是,便在一些程序员常用的接单软件上开出了高价,只求一个具有读写功能的DEMO;可这段经历让我有些后悔;十万元的高价,只求一个Android读写USB数据的功能,结果依然求而不得;许多团队接了单,但是把定金骗走了,事后就没有回音了;还有的发的程序一看就是是直接在网上随便下载敷衍人的;一点都不靠谱;还有的是,兴致勃勃的接单,一听到我的需求,马上说做不了的; 于是我又怀疑是不是设备不支持高速模式;在多次和华为官方高级工程师确认过后,能确定的是,我所用的Android设备的确是支持USB2.0高速模式的;但为什么高速模式的设备,跑出来的效果只有低速的效果?在转了一大圈本想依靠外来力量而无用后,我决定还是自己慢慢自我研究与摸索。 最后看来,还是仅仅只能自己来了;真可谓皇天不负有心人,经过一个多月的摸索,终于有见成效了;下面,我就记录一下我的刨坑经历;

一、USB HID是什么?

USB-HID是Universal Serial Bus-Human Interface Device的缩写。由于其是免驱的,在一些人机交互设备中很是流行; 目前的USB主要分低速模式、全速模式、高速模式;低速设备每一笔事务最大是8个字节,全速设备每一笔事务最大是64个字节,高速设备每一笔事务最大是1024个字节。 通常情况下,全速模式下,平均每毫秒USB能传输1包数据;高速模式下,125微妙一包,平均每毫秒能传输8包数据; 那么,在Android上常用哪些方式进行读取USB数据;

二、使用controlTransfer或bulkTransfer读取

这个就要涉及对Android提供的一些方法进行了解一下了;

1.UsbManager

负责管理USB设备的类,你可以在相应代码中通过以下方法获得此对象的一个实例:UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 该类提供的主要方法有:

getDeviceList() 获得设备列表,返回的是一个HashMap.;hasPermission(UsbDevice device) 判断你的应用程序是否有接入此USB设备的权限,如果有则返回真,否则返回false.openDevice(UsbDevice device) 打开USB设备,以便向此USB设备发送和接受数据,返回一个关于此USB设备的连接。requestPermission(UsbDevice device, PendingIntent pi) 向USB设备请求临时的接入权限。 2.UsbDevice

代表一个USB设备的类,每个设备都包含了一个或多个接口,每个接口又包含一个或多个节点用来与此设备传输数据。该类的主要方法有: 1) getDeviceClass() 返回此USB设备的类别,用一个整型来表示。 2) getDeviceId() 返回唯一标识此设备的ID号,也用一个整型来表示。 3) getDeviceName() 返回此设备的名称,用一个字符串来表示。 4) getDeviceProtocol() 返回此设备的协议类别,用一个整型来表示。 5) getDeviceSubclass() 返回此设备的子类别,用一个整型来表示。 6) getVendorId() 返回生产商ID 7) getProductId() 返回产品ID 8) getInterfaceCount() 返回此设备的接口数量 9) getInterface(int index) 得到此设备的一个接口,返回一个UsbInterface。

3.UsbInterface

代表USB设备的一个接口,注意:UsbInterface本身是一个类,并不是一个接口。此类的主要方法有以下:

getId() 得到给接口的id号。getInterfaceClass() 得到该接口的类别。getInterfaceSubclass() 得到该接口的子类。getInterfaceProtocol() 得到该接口的协议类别。getEndpointCount() 获得关于此接口的节点数量。getEndpoint(int index) 对于指定的index获得此接口的一个节点,返回一个UsbEndpoint. 4.UsbEndpoint

代表一个接口的某个节点的类。该类主要提供了一下方法供你使用:

getAddress() 获得此节点的地址getAttributes() 获得此节点的属性getDirection() 获得此节点的数据传输方向 5.UsbDeviceConnection 代表USB连接的一个类。用此连接可以想USB设备发送和接收数据,可以通过调用该方法openDevice(UsbDevice) 来得到该类的一个实例。该类提供了以下方法供你使用:

1)bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) 通过给定的endpoint来进行大量的数据传输,传输的方向取决于该节点的方向,buffer是要发送或接收的字节数组,length是该字节数组的长度。传输成功则返回所传输的字节数组的长度,失败则返回负数。 2)controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout) 该方法通过0节点向此设备传输数据,传输的方向取决于请求的类别,如果requestType为USB_DIR_OUT则为写数据,USB_DIR_IN, 则为读数据

6.Android配置文件要求

在你使用以上API进行开发之前,你需要在你的AndroidManifest.xml文件中添加以下内容: 1.因为并不能保证所有的安卓设备都支持以上API,所以你需要声明: 12以下的版本是不支持以上APId的。

2.如果你想有一个USB设备接入你的安卓设备时能够通知你的应用程序,那么你需要在你的Activity标签中声明以下内容 Resource属性指定了要过滤的数据设备信息,包括: 1) vendor-id 设备生产商id 2) product-id 设备id 3) class 设备类别 4) subclass 设备子类 5) protocol(device or interface) 协议类别 device_filter.xml文件放在res\xml目录下,其中文件名不包括扩展名部分需要与 标签中声明的一致。示例如下: --- 7.开发步骤 1.检测设备 你的应用可以通过两种方式来发现USB设备,一种是用一个意图过滤器在用户连接一个设备时对其进行通知, 另一种则是通过枚举您已经连接的所有USB设备。如果你希望你的应用能够自动的探测到你想要的设备, 请使用一个意图过滤器来做。但是,如果你希望得到一个已连接设备的列表或者你不希望过滤意图, 枚举所有的设备会是一个更好的选择。 1)Intent Filter

为了让应用可以发现一个特定的USB设备,你可以为android.hardware.usb.action.USB_DEVICE_ATTACHED这个意图指定一个意图来进行过滤。伴随着这个意图过滤器,您需要指定一个资源文件来特别说明这个USB设备的属性,例如供应商和产品ID。当用户连接到一个符合你配件过滤条件的配件时,这个系统会谈出一个对话框询问他们是否希望开始你的应用。如果用户同意,那么你的应用在失去连接之前会自动获取和设备连接的权限。 下面的例子告诉你该如何声明这个意图过滤器:

下面的例子告诉你怎么样声明指定你希望连接的USB设备的相关资源文件:

2)列举设备 你可以通过UsbMnanger来列举已经连接的USB设备:代码如下 首先,得到UsbMnanger的一个实例 UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); 其次,通过此类的getDeviceList()方法得到包含所有已连接的USB设备的列表 HashMap deviceList = manager.getDeviceList(); 最后,通过设备名称来得到给设备对象 UsbDevice device = deviceList.get("deviceName"); 如果你想一个一个的列举所有的设备,可以实用迭代器,代码实例如下: UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); HashMap deviceList = manager.getDeviceList(); Iterator deviceIterator = deviceList.values().iterator(); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next() //在这里添加处理设备的代码 }

————

2.获取和设备通讯的权限 如果你实用intent filter来发现一个USB设备,即上述方法1)那么应用程序可以自动的获取权限; 如果是使用方法2)来检测USB设备,则你需要显示声明权限。其步骤如下: 首先:创建一个广播接收器,接受请求权限的广播,代码如下: private static final String ACTION_USB_PERMISSION = private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { 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 (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(device != null){ //在这里增加通信的代码 }}}}}}; 然后:在你的主Activity中注册该广播接收器,代码如下: UsbManager mUsbManager= (UsbManager) getSystemService(Context.USB_SERVICE); private static final String ACTION_USB_PERMISSION= "com.android.example.USB_PERMISSION"; mPermissionIntent=PendingIntent.getBroadcast( this,0,new Intent(ACTION_USB_PERMISSION), 0); IntentFilter filter= new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(mUsbReceiver, filter); //最后,调用此函数 requestPermission() 来显示询问对话框, 即在上面红线处添加如下代码: mUsbManager.requestPermission(device, mPermissionIntent); 3.和设备通信

3.和设备通信 Android设备和一个连接它的USB设备通信即可以是同步的也可以使异步的,不管是那种情况,你都应该创建一个新的线程去负责所有的数据传输,以免阻塞UI线程。为了和US B设备建立通信,你首先需要得到合适的接口和节点,然后通过UsbDeviceConnection在此节点上进行数据的传输,由此你的代码应该: 1)检查该USB设备的属性,例如,生产商id,产品id,设备类别,以确定你是否需要和此设备进行通信。 2)当你确定要和此设备进行通信的时候,找到合适的接口和节点。 3)当你找到一个合适的节点后,用UsbDeviceConnection在此节点上打开连接。 4)使用bulkTransfer()或者controlTransfer() 来发送和接收数据。示例代码如下:

private Byte[] bytes private static int TIMEOUT= 0; private boolean forceClaim= true; ... UsbInterface intf= device.getInterface(0); UsbEndpoint endpoint= intf.getEndpoint(0); UsbDeviceConnection connection= mUsbManager.openDevice(device); connection.claimInterface(intf, forceClaim); connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread 4.终止通信 当你完成数据的传输或者你的设备已拔出时,通过调用releaseInterface()和 close()来关闭接口和连接。

这个方法靠不靠谱?我用事件证明,这个方法不行;但对于想接触USB这块的、或想入门的人来说,可以看一下;毕竟,就目前对Android测试的结果来看,这种方式,能不用,还是最好的不用;

三、usb多进程开发

在多进程中,Android对USB数据的读写要优于单进程下的情况;

1.把读写单独放在一个进程 public boolean send(byte[] data) { boolean flag = false; try { int length = this.mUsbDeviceConnection.bulkTransfer(this.mOutUsbEndpoint, data, data.length, this.TIME_OUT_WRITE); if (length if (this.mUsbDeviceConnection.bulkTransfer(this.mOutUsbEndpoint, data, data.length, this.TIME_OUT_WRITE) flag = true; } } else { flag = true; } } else { flag = true; } } catch (Exception var4) { var4.printStackTrace(); flag = false; } return flag; } public byte[] read(int size, byte[] receives) { try { if (this.mUsbDeviceConnection == null) { return null; } else if (this.mInUsbEndpoint == null) { return null; } else { int length = this.mUsbDeviceConnection.bulkTransfer(this.mInUsbEndpoint, receives, receives.length, this.TIME_OUT_READ); return length > 0 ? receives : null; } } catch (Exception var4) { var4.printStackTrace(); return null; } } 2.把数据解析放在另外一个进程

把读取到的数据回调出去

public void startClient() { this.helper.setClientCallback(new ClientCallback() { public void onReceive(byte[] msg) { try { if UsbConnectedManager.this.mUsbManageCallback != null) { UsbConnectedManager.this.mUsbManageCallback.onReceive(msg); } } catch (Exception var3) { } } } }); }

这种方式下,可以避免数据解析过程中CPU过高影响USB读写的速度;也能避免其他的一些线程在消耗资源的过程中挤占Android对USB数据读取;这在一定程度上可以提升USB读取速率,,但是,从读取的效果上来看不是很推荐;

四、io流读取

这个直接贴代码实现;

public class SerialPortManager extends SerialPort implements KTUsbListener { private static final String TAG = SerialPortManager.class.getSimpleName(); public static ILogger logger = new DefaultLogger(Consts.TAG); // 日志工具 private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; private FileDescriptor mFd; private OnOpenSerialPortListener mOnOpenSerialPortListener; private OnSerialPortDataListener mOnSerialPortDataListener; private HandlerThread mSendingHandlerThread; private Handler mSendingHandler; private SerialPortReadThread mSerialPortReadThread; public static final int NORMAL = 0; public static final int SPLICING = 1; public Context mContext; final String ACTION_USB_PERMISSION = "com.spark.teaching.answertool.USB_PERMISSION"; // 请求usb权限的action /** 数据读取方式*/ private int readType; UsbManager mUsbManager = null; @Override public void onConnected(@Nullable UsbDeviceConnection usbDeviceConnection, @Nullable UsbEndpoint inp, @Nullable UsbEndpoint out, @Nullable String serialNum,@Nullable UsbDevice mUsbDevice) { try { int fd=usbDeviceConnection.getFileDescriptor(); mFd = open(mUsbDevice.getDeviceName(), fd); mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); // 开启发送消息的线程 startSendThread(); // 开启接收消息的线程 startReadThread(); } catch (Exception e) { e.printStackTrace(); } } @Override public void onDisConnected() { } /** * 打开串口 * * @param device 串口设备 * @param baudRate 波特率 * @return 打开是否成功 */ /*public SerialPortManager(File device, int baudRate) { this(device, baudRate, NORMAL); }*/ public SerialPortManager(Context context,File device, int baudRate, int readType){ mContext=context; this.readType = readType; // 校验串口权限 if (!device.canRead() || !device.canWrite()) { boolean chmod777 = chmod777(device); if (!chmod777) { Log.e(TAG, "openSerialPort: 没有读写权限"); if (null != mOnOpenSerialPortListener) { mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.NO_READ_WRITE_PERMISSION); } Log.e(TAG, device.getName() + "USB打开失败"); } } if(mContext!=null) KTConnectHelper.INSTANCE.onServiceCreate(mContext, this); } /** * 打开串口的日志 * */ public static synchronized void openLog() { logger.showLog(true); } /** * 关闭串口 */ public void closeSerialPort() { if (null != mFd) { close(); mFd = null; } // 停止发送消息的线程 stopSendThread(); // 停止接收消息的线程 stopReadThread(); if (null != mFileInputStream) { try { mFileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } mFileInputStream = null; } if (null != mFileOutputStream) { try { mFileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } mFileOutputStream = null; } mOnOpenSerialPortListener = null; mOnSerialPortDataListener = null; } /** * 添加打开串口监听 * * @param listener listener * @return SerialPortManager */ public SerialPortManager setOnOpenSerialPortListener(OnOpenSerialPortListener listener) { mOnOpenSerialPortListener = listener; return this; } /** * 添加数据通信监听 * * @param listener listener * @return SerialPortManager */ public SerialPortManager setOnSerialPortDataListener(OnSerialPortDataListener listener) { mOnSerialPortDataListener = listener; return this; } /** * 开启发送消息的线程 */ private void startSendThread() { // 开启发送消息的线程 mSendingHandlerThread = new HandlerThread("mSendingHandlerThread"); mSendingHandlerThread.start(); // Handler mSendingHandler = new Handler(mSendingHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { byte[] sendBytes = (byte[]) msg.obj; if (null != mFileOutputStream && null != sendBytes && 0 mFileOutputStream.write(sendBytes); } catch (IOException e) { e.printStackTrace(); } } } }; } /** * 停止发送消息线程 */ private void stopSendThread() { mSendingHandler = null; if (null != mSendingHandlerThread) { mSendingHandlerThread.interrupt(); mSendingHandlerThread.quit(); mSendingHandlerThread = null; } } /** * 开启接收消息的线程 */ private void startReadThread() { mSerialPortReadThread = new SerialPortReadThread(mFileInputStream, readType) { @Override public void onDataReceived(byte[] bytes) { if (null != mOnSerialPortDataListener) { mOnSerialPortDataListener.onDataReceived(bytes); } } }; mSerialPortReadThread.start(); } /** * 停止接收消息的线程 */ private void stopReadThread() { if (null != mSerialPortReadThread) { mSerialPortReadThread.release(); } } /** * 发送十六进制的数据 * */ public boolean sendHex(String sHex) { byte[] bOutArray = DataUtil.HexToByteArr(sHex); return sendBytes(bOutArray); } /** * 发送数据 * */ public boolean sendTxt(String sTxt) { byte[] bOutArray = sTxt.getBytes(); return sendBytes(bOutArray); } /** * 发送数据 * * @param sendBytes 发送数据 * @return 发送是否成功 */ public boolean sendBytes(byte[] sendBytes) { if (null != mFd && null != mFileInputStream && null != mFileOutputStream) { if (null != mSendingHandler) { Message message = Message.obtain(); message.obj = sendBytes; return mSendingHandler.sendMessage(message); } } return false; } }

这种方式也是在多进程的基础上进行的,速率比前面的要快;但有一个缺点是,需要开辟ROOT权限;串口般的有谷歌提供的实现方式,USB版的,网上众说纷纭,不过大多不靠谱;

五、JNI方式实现通过USB串口读取数据

直接上代码实现:

#include #include #include #include #include #include #include #include #include "SerialPort.h" #include "android/log.h" static const char *TAG="serial_port"; #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) static speed_t getBaudrate(jint baudrate) { switch(baudrate) { case 0: return B0; case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return -1; } } /* * Class: android_serialport_SerialPort * Method: open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_com_giftedcat_serialportlibrary_SerialPort_open (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) { int fd; speed_t speed; jobject mFileDescriptor; Check arguments { speed = getBaudrate(baudrate); if (speed == -1) { LOGE("Invalid baudrate"); return NULL; } } Opening device { jboolean iscopy; const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); // fd = open(path_utf, O_RDWR|flags,777); //fd = open(path_utf,O_RDWR| flags ); fd = open(path_utf, O_TRUNC | O_RDWR | O_NONBLOCK); LOGD("open() fd = %d", fd); (*env)->ReleaseStringUTFChars(env, path, path_utf); if (fd == -1) { LOGE("Cannot open port"); return NULL; } } Configure device { //struct termios cfg; //LOGD("Configuring serial port"); //if (tcgetattr(fd, &cfg)) { LOGE("tcgetattr() failed"); close(fd); return NULL; } cfmakeraw(&cfg); if (tcsetattr(fd, TCSANOW, &cfg)) { LOGE("tcsetattr() failed 2"); close(fd); return NULL; } } /* Create a corresponding file descriptor */ { jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V"); jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); } return mFileDescriptor; } /* * Class: cedric_serial_SerialPort * Method: close * Signature: ()V */ JNIEXPORT void JNICALL Java_com_giftedcat_serialportlibrary_SerialPort_close (JNIEnv *env, jobject thiz) { jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); LOGD("close(fd = %d)", descriptor); close(descriptor); }

SerialPort.JAVA

package com.giftedcat.serialportlibrary; import android.util.Log; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileDescriptor; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStreamWriter; import java.sql.Driver; import java.util.ArrayList; public class SerialPort { static { System.loadLibrary("SerialPort"); } /** * 文件设置最高权限 777 可读 可写 可执行 * * @param file 文件 * @return 权限修改是否成功 */ boolean chmod777(File file) { if (null == file || !file.exists()) { // 文件不存在 return false; } try { String cmd = "chmod 777 " + file.getAbsolutePath() + " \n \n" + "exit \n"; // 获取ROOT权限 Process process = Runtime.getRuntime().exec("su"); BufferedReader reader = new BufferedReader(new InputStreamReader( process.getInputStream())); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter( process.getOutputStream())); BufferedReader error = new BufferedReader(new InputStreamReader( process.getErrorStream())); writer.write(cmd + "\n"); writer.write("exit\n"); writer.flush(); if (0 == process.waitFor() && file.canRead() && file.canWrite() && file.canExecute()) { return true; } return true; } catch (IOException | InterruptedException e) { // 没有ROOT权限 e.printStackTrace(); } return false; } boolean chmod7771(File file) { if (null == file || !file.exists()) { // 文件不存在 return false; } try { // 获取ROOT权限 Process su = Runtime.getRuntime().exec("su"); // 修改文件属性为 [可读 可写 可执行] String cmd = "sudo chmod 777 " + file.getAbsolutePath() + "\n" + "exit\n"; su.getOutputStream().write(cmd.getBytes()); if (0 == su.waitFor() && file.canRead() && file.canWrite() && file.canExecute()) { return true; } } catch (IOException | InterruptedException e) { // 没有ROOT权限 e.printStackTrace(); } return false; } // 打开串口 protected native FileDescriptor open(String path, int baudRate, int flags); // 关闭串口 protected native void close(); } package com.giftedcat.serialportlibrary; import android.app.PendingIntent; import android.content.Context; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.util.Log; import com.giftedcat.serialportlibrary.listener.OnOpenSerialPortListener; import com.giftedcat.serialportlibrary.listener.OnSerialPortDataListener; import com.giftedcat.serialportlibrary.logger.DefaultLogger; import com.giftedcat.serialportlibrary.logger.ILogger; import com.giftedcat.serialportlibrary.thread.SerialPortReadThread; import com.giftedcat.serialportlibrary.utils.Consts; import com.giftedcat.serialportlibrary.utils.DataUtil; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /** * Created by giftedcat on 2020/6/13. * SerialPortManager */ public class SerialPortManager extends SerialPort { private static final String TAG = SerialPortManager.class.getSimpleName(); public static ILogger logger = new DefaultLogger(Consts.TAG); // 日志工具 private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; private FileDescriptor mFd; private OnOpenSerialPortListener mOnOpenSerialPortListener; private OnSerialPortDataListener mOnSerialPortDataListener; private HandlerThread mSendingHandlerThread; private Handler mSendingHandler; private SerialPortReadThread mSerialPortReadThread; public static final int NORMAL = 0; public static final int SPLICING = 1; PendingIntent mPermissionIntent = null; UsbManager mUsbManager = null; UsbDevice mAudioDevice = null; private static final String ACTION_USB_PERMISSION = "com.spark.teaching.answertool.USB_PERMISSION"; //var mUsbManager: UsbManager? = null /** 数据读取方式*/ private int readType; /** * 打开串口 * * @param device 串口设备 * @param baudRate 波特率 * @return 打开是否成功 */ public SerialPortManager(File device, int baudRate) { this(device, baudRate, NORMAL); } public SerialPortManager(File device, int baudRate, int readType){ this.readType = readType; // 校验串口权限 if (!device.canRead() || !device.canWrite()) { boolean chmod777 = chmod777(device); if (!chmod777) { Log.e(TAG, "openSerialPort: 没有读写权限"); if (null != mOnOpenSerialPortListener) { mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.NO_READ_WRITE_PERMISSION); } Log.e(TAG, device.getName() + "USB打开失败"); } } // mUsbManager=(UsbManager)mContext.getSystemService(Context.USB_SERVICE); //mUsbManager.openDevice((UsbDevice)device ); try { mFd = open(device.getAbsolutePath(), baudRate, 0); mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); if (null != mOnOpenSerialPortListener) { mOnOpenSerialPortListener.onSuccess(device); } // 开启发送消息的线程 startSendThread(); // 开启接收消息的线程 startReadThread(); } catch (Exception e) { e.printStackTrace(); if (null != mOnOpenSerialPortListener) { mOnOpenSerialPortListener.onFail(device, OnOpenSerialPortListener.Status.OPEN_FAIL); } } logger.info(TAG, device.getName() + "串口打开失败"); } /** * 打开串口的日志 * */ public static synchronized void openLog() { logger.showLog(true); } /** * 关闭串口 */ public void closeSerialPort() { if (null != mFd) { close(); mFd = null; } // 停止发送消息的线程 stopSendThread(); // 停止接收消息的线程 stopReadThread(); if (null != mFileInputStream) { try { mFileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } mFileInputStream = null; } if (null != mFileOutputStream) { try { mFileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } mFileOutputStream = null; } mOnOpenSerialPortListener = null; mOnSerialPortDataListener = null; } /** * 添加打开串口监听 * * @param listener listener * @return SerialPortManager */ public SerialPortManager setOnOpenSerialPortListener(OnOpenSerialPortListener listener) { mOnOpenSerialPortListener = listener; return this; } /** * 添加数据通信监听 * * @param listener listener * @return SerialPortManager */ public SerialPortManager setOnSerialPortDataListener(OnSerialPortDataListener listener) { mOnSerialPortDataListener = listener; return this; } /** * 开启发送消息的线程 */ private void startSendThread() { // 开启发送消息的线程 mSendingHandlerThread = new HandlerThread("mSendingHandlerThread"); mSendingHandlerThread.start(); // Handler mSendingHandler = new Handler(mSendingHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { byte[] sendBytes = (byte[]) msg.obj; if (null != mFileOutputStream && null != sendBytes && 0 mFileOutputStream.write(sendBytes); } catch (IOException e) { e.printStackTrace(); } } } }; } /** * 停止发送消息线程 */ private void stopSendThread() { mSendingHandler = null; if (null != mSendingHandlerThread) { mSendingHandlerThread.interrupt(); mSendingHandlerThread.quit(); mSendingHandlerThread = null; } } /** * 开启接收消息的线程 */ private void startReadThread() { mSerialPortReadThread = new SerialPortReadThread(mFileInputStream, readType) { @Override public void onDataReceived(byte[] bytes) { if (null != mOnSerialPortDataListener) { if(bytes!=null){ mOnSerialPortDataListener.onDataReceived(bytes); } } } }; mSerialPortReadThread.start(); } /** * 停止接收消息的线程 */ private void stopReadThread() { if (null != mSerialPortReadThread) { mSerialPortReadThread.release(); } } /** * 发送十六进制的数据 * */ public boolean sendHex(String sHex) { byte[] bOutArray = DataUtil.HexToByteArr(sHex); return sendBytes(bOutArray); } /** * 发送数据 * */ public boolean sendTxt(String sTxt) { byte[] bOutArray = sTxt.getBytes(); return sendBytes(bOutArray); } /** * 发送数据 * * @param sendBytes 发送数据 * @return 发送是否成功 */ public boolean sendBytes(byte[] sendBytes) { if (null != mFd && null != mFileInputStream && null != mFileOutputStream) { if (null != mSendingHandler) { Message message = Message.obtain(); message.obj = sendBytes; return mSendingHandler.sendMessage(message); } } return false; } }

这种方式,虽然在一定程度上对速率有改善,但,与我们的实际需求还是有很大的差距;比如,我们实际需要以高速的模式进行USB数据的读取,但是,这里完成后,在可运行内存为2G的设备上的读取速率,也仅仅在全速的标准上;离我们的高速还是有很大的差距;

所以,最终结论,还是不符合我的要求;我也不推荐;于是,又开始慢慢优化摸索。

六、修改USB缓冲区 1.缓冲区

在这里插入图片描述 在博客上看到别人有类似的的经历,他们是通过修改设备的缓冲区大小来达到在相对一段时间内不丢包的问题;但却没有解决另一个根本性的问题——速率慢;所以这种方式直接被我放弃了;看了简直是在浪费生命,而且体验也不好;比如上传语音;客户已经结束录音很久了,但客户端应用还没有收完数据;这种方式,只能在某些不强调时效性的项目中使用;而大多数的项目中都不适用。

在这里插入图片描述 在这里插入图片描述

在这里插入图片描述

看到这些描述,只能说,还是得依靠自己得力量;于是我就又开始了慢慢摸索得漫长之旅。

2.增加延时

这种方式和前面得哪种方式一样,一看也是忽悠人得;根本无法解决本质问题;

七、UVCCamera 1.借鉴UVCCamera

这应该是一个日本人写的东西;在网上随处可见;关键是,你下载下来后不一定看得懂;看的懂,不一定跑得起来;不仅如此,还有一些国内得半瓶水把这个奉为圭臬,到处招摇撞骗,可恶;

在这里插入图片描述

这就是这份代码得目录;功能很强大;如果直接给部分对底层乃至C语言不熟悉得人,很难看明白,这里面是怎么实现数据读写的; 于是我打开目录继续探寻,发现一个似曾相识的的东西, 在这里插入图片描述 在这里插入图片描述

这给我一个灵感;其实这个代码根本不用看,直接进行LibUSB库的移植,说不一定可以改变目前这种速率慢的问题;于是说干就干;

八、移植libusb 1.libusb目录

在这里插入图片描述

2.Android上可以直接引用的部分

在这里插入图片描述

在这里插入图片描述

直接根据示例在程序里面一步步操作就可以了

Android集成libusb资源包路径:https://download.csdn.net/download/zhoupuxian/85190517?spm=1001.2014.3001.5501

也可以直接获取GITHUB上的:第三方库

Libusb接口文档:Libusb库的使用文档

待集成之后,再次运行,速率可以明显的提高; 至于如果集成的,集成的步骤,我将在下次详细说明



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有