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

How to do good real-time data streaming using Java Android SDK

本文关键字:实时 数据流 SDK 何使用 Java Android      更新时间:2023-10-16

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

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

我通过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;
}

这个工作得很好…的前五秒我得到的值在一种实时的,然后:

  • 有时它永远冻结,这意味着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

我的ECG设备肯定没有在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:同样的问题。
  • 一个Galaxy S3与自定义CyanogenMod 11 Android 4.4.2:数据流似乎完美,没有冻结后5秒和数据实时到达....所以看起来整个系统能够实现我想要的,但看起来Android的默认设置使事情太慢....不知道是否可以在操作系统级别更改设置来解决这个问题。

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

这个问题显然与这里报告的问题相似。

5秒后,我要么失去了连接,要么实时流急剧减慢。

正如这里所说的,Android>4.3显然不喜欢超过5秒的单向通信。所以我现在每隔1秒向设备发送一个虚拟命令(类似于"keep-alive"命令),现在Android很高兴,因为它不再是单向通信了……所以数据流在第五秒之后还是和之前一样好!