如何使用Java Android SDK实现良好的实时数据流

问题描述:

我有一个以500Hz为单位测量ECG的自制蓝牙设备:每隔2ms设备发送9个字节的数据(标题,ECG测量,页脚)。所以这大概是一个9 * 500 = 4.5kbytes/s的数据流。如何使用Java Android SDK实现良好的实时数据流

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

我通过QtCreator 3.0.1(Qt 5.2.1)在Android上移植了整个程序。看起来虚拟COM端口不能被boost访问(可能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<size_t>(res) == length) 
    { 
     env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte*>(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; 
} 

这五个第一秒我gettings值排序的实时,然后工作得很好......

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

这里是日志可以查看详细的是什么时,设置为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 

我的心电图设备绝对没有在11毫秒发送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包:

  • 银河S4采用Android 4.4.2:同样的问题。
  • 一个Galaxy S3与自定义CyanogenMod 11 Android 4.4.2:数据流似乎完美,5秒后没有冻结和数据实时到达....所以看起来像整个系统能够实现我想要的,但看起来像Android的默认设置会让事情太慢....不知道是否可以在操作系统级别更改设置来解决此问题。

编辑:由于我没有回答:-(我试图用纯Java程序(没有C++,没有Qt的)做同样的事情有同样的问题:Real-time Bluetooth SPP data streaming on Android only works for 5 seconds

这个问题显然是一个类似报道here

5秒后,我无论是连接丢失,无论是实时流正在急剧放缓。

至于说here的Android> 4.3显然不喜欢单向通信超过5 secondes。因此,我现在每发送一个seconde(类型为“keep-alive”命令)都会向设备发送一个虚拟命令,现在Android很高兴,因为它不再是单向通信了......所以数据流在比以前第五秒!