如何使用Java Android SDK进行良好的实时数据流传输

我有一个自制的蓝牙设备,测量500Hz的心电图:设备每2 ms发送9个字节的数据(标头,ECG测量,页脚)。 所以这大约是9 * 500 = 4.5kbytes / s的数据流。

我有一个C ++ Windows程序能够连接设备并检索数据流(用Qt / qwt显示)。 在这种情况下,我使用Windows控制面板来绑定设备,并使用boost serial_port接口通过虚拟COM端口连接它。 这完美地工作,我实时接收我的数据流:我每2ms左右得到一个测量点。

我通过QtCreator 3.0.1(Qt 5.2.1)在Android上移植了整个程序。 似乎无法通过boost访问虚拟COM端口(可能SDK权限不允许),因此我编写了一段Java代码来打开和管理蓝牙连接。 所以我的应用程序仍然是C ++ / Qt,但只有连接和读取设备数据的层在Java中重新编写(用createInsecureRfcommSocketToServiceRecord打开连接):

用于读取数据的Java代码:

public int readData( byte[] buffer ) { if( mInputStream == null ) { traceErrorString("No connection, can't receive data"); } else { try { final boolean verbose = false; int available = mInputStream.available(); if ( verbose ) { Calendar c = Calendar.getInstance(); Date date = new Date(); c.setTime(date); c.get(Calendar.MILLISECOND); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String currentTime = sdf.format(date); traceDebugString( currentTime + ":" + c.get(Calendar.MILLISECOND) + " - " + available + " bytes available, requested " + buffer.length ); } if ( available >= buffer.length ) return mInputStream.read( buffer ); // only call read if we know it's not blocking else return 0; } catch (IOException e) { traceDebugString( "Failed to read data...disconnected?" ); } } return -1; } 

像这样从C ++调用:

 bool ReceiveData( JNIEnv* env, char* data, size_t length, bool& haserror ) { bool result = false; jbyteArray array = env->NewByteArray(length); jint res = env->CallIntMethod(j_object, s_patchIfReceiveDataID, array ); if ( static_cast(res) == length ) { env->GetByteArrayRegion(array, 0, length, reinterpret_cast(data)); result = true; } else if ( res == -1 ) { haserror = true; } else { // not enough data in the stream buffer haserror = false; } return result; } bool readThread( size_t blockSize ) { BTGETANDCHECKENV // retrieving environment char* buf = new char[blockSize]; bool haserror = false; while ( !haserror ) { if ( !ReceiveData( env, buf, blockSize, haserror ) ) { // could not read data if ( haserror ) { // will stop this thread soon } else { boost::this_thread::sleep( boost::posix_time::milliseconds( 10 ) ); } } } delete [] buf; return true; } 

这非常有效…… 对于我在某种实时获得价值的五秒钟 ,然后:

  • 有时它永远冻结,这意味着mInputStream.available()值仍然低于请求值。
  • 有时它只会冻结一秒左右然后继续,但数据会被~1秒的块接收。 含义mInputStream.available()可以在两次调用之间从0移动到超过3000(经过10ms)。 实际上,我在5个第一秒看到相同的情况,但缓冲区可用性从不超过150个字节,5秒后,它可以达到3000个字节。

以下是verbose设置为true时日志的外观:

 14:59:30:756 - 0 bytes available, requested 3 14:59:30:767 - 0 bytes available, requested 3 14:59:30:778 - 0 bytes available, requested 3 14:59:30:789 - 1728 bytes available, requested 3 14:59:30:790 - 1725 bytes available, requested 6 14:59:30:792 - 1719 bytes available, requested 3 

我的心电图设备肯定没有在11ms发送1728字节!!

我知道我的设备每2ms发送9个字节(否则,它将无法在我的PC应用程序上运行)。 看起来Java做了一些意想不到的缓冲,并且每2ms没有提供9个字节….这也是奇怪的事情似乎在开始时仅工作5秒。

请注意,我尝试使用read()而不检查available()(阻塞版本),但经历了完全相同的行为。

所以我想知道我做错了什么……

  • 有没有办法强制Java输入流自行更新?
  • 有没有办法让Java继续它的挂起事件(比如我们有QApplication :: processEvents)?
  • 是否有任何全局设置来指定流的缓冲区大小(我在BluetoothDevice / BluetoothSocket级别找不到任何设置)
  • 在PC上,当打开虚拟COM端口时,我必须指定波特率,停止位,握手和类似的东西。 在Android上我只是打开没有选项的Rfcomm套接字,这可能是问题(然后ECG设备和智能手机将无法同步…)?

任何帮助或想法都会受到欢迎!

编辑:我在Nexus 5手机上遇到过这种情况,Android 4.4.2我刚刚测试了不同设备上的相同apk包:

  • Android 4.4.2的Galaxy S4:同样的问题。
  • 带有自定义CyanogenMod 11 Android 4.4.2的Galaxy S3:数据流看起来很完美,5秒后没有冻结,数据实时到达….所以看起来整个系统能够实现我想要的,但看起来像Android默认设置使得速度太慢….如果可以在操作系统级别更改设置以解决此问题,请不要使用。

编辑:因为我没有回答:-(我尝试使用纯Java程序(没有C ++,没有Qt)做同样的事情。有同样的问题: Android上的实时蓝牙SPP数据流只能工作5秒

这个问题显然与这里报道的问题类似。

5秒后,我连接丢失,实时流媒体播放速度急剧减慢。

正如这里所说的Android> 4.3显然不喜欢超过5秒的单向通信。 所以我现在每1次seconde(这种“保持活动”命令)向设备发送一个虚拟命令,现在Android很高兴,因为它不再是单向通信……所以数据流也是如此之好第五秒比以前!