当前位置: 首页 > 面试题库 >

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

刘焱
2023-03-14
问题内容

我有一个自制的蓝牙设备,以500Hz的频率测量ECG:每2毫秒该设备发送9字节的数据(标头,ECG测量,页脚)。因此,这大约是9 * 500 =4.5kbytes / s的数据流。

我有一个C ++ Windows程序,能够连接设备并检索数据流(使用Qt/qwt显示)。在这种情况下,我使用Windows控制面板绑定设备,并使用boost
serial_port接口通过虚拟COM端口将其连接。这非常有效,并且我实时接收数据流:每2毫秒左右获得一个测量点。

我通过QtCreator 3.0.1(Qt5.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设备绝对没有在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包:

  • 具有Android 4.4.2的Galaxy S4:同样的问题。
  • 具有自定义CyanogenMod 11 Android 4.4.2的Galaxy S3:数据流似乎很完美,5秒后不会冻结,并且数据实时到达....所以看起来整个系统都能实现我想要的功能,但是看起来Android默认设置会使操作太慢....不知道是否需要在操作系统级别进行更改才能解决此问题。

编辑:因为我没有答案:-(我试图使用纯Java程序(没有C++,没有Qt)做同样的事情。


问题答案:

这个问题显然类似于这里报告的问题。

5秒后,我要么失去了连接,要么实时流变得非常慢。

如此处所述Android>4.3显然不喜欢超过5秒的单向通信。因此,我现在每隔1秒向设备发送一个虚拟命令(有点“ keep-
alive”命令),现在Android很高兴,因为它不再是单向通信了……所以数据流在之后第五秒比以前!



 类似资料:
  • 问题内容: 是否有使用Java直播视频的良好库?理想情况下,管道的两端都应使用Java编写,但我最关心的是视频播放器。您会推荐什么软件? 更新 :似乎VLC引入了1-2秒的延迟。我需要真正的实时视频流。记录到播放的延迟必须小于300ms。 问题答案: 我见过的最好的视频播放/编码库是ffmpeg。它播放您扔给它的所有内容。(它是MPlayer使用的。)它是用C编写的,但是我发现了一些Java包装器

  • 问题内容: 我正在使用具有以下功能的simplehtmldom: 我这样使用它: 有时,URL可能只是无效的,我想对此进行处理。我以为我可以使用try and catch,但是这没有用,因为它不会抛出异常,它只是给出了这样的php警告: 第39行在上面的代码中。 我如何正确处理此错误,我可以只使用普通条件,它看起来不像返回布尔值。 谢谢大家的帮助 更新资料 这是一个好的解决方案吗? 问题答案: 这

  • 问题内容: 关于如何在Hibernate实体和Web服务要返回的数据传输对象之间进行转换,我也有类似的问题和疑虑,如以下问题所述: 此处提到的因素之一是,如果域模型发生更改,则在Web服务的情况下,一组DTO将保护使用者。 即使看起来它将为我的项目添加大量代码,这种推理也似乎是合理的。 是否可以使用一种好的设计模式将一个Hibernate实体(实现一个接口)转换为一个实现相同接口的DTO? 因此,

  • 问题内容: 关于如何在Hibernate实体和Web服务要返回的数据传输对象之间进行转换,我也有类似的问题和疑虑,如以下问题所述: 在ejb3中使用数据传输对象被认为是最佳实践 此处提到的因素之一是,如果域模型发生更改,则在Web服务的情况下,一组DTO将保护使用者。 即使看起来它将为我的项目添加大量代码,这种推理也似乎是合理的。 是否可以使用一种好的设计模式将Hibernate实体(实现一个接口

  • 我正在构建一个有以下要求的应用程序,我刚刚开始使用Flink。 null null 谢谢并感激你的帮助。

  • 问题内容: 当实现具有多个属性的类时(例如下面的玩具示例),处理哈希的最佳方法是什么? 我认为和应该是一致的,但是如何实现能够处理所有属性的适当的哈希函数呢? 问题答案: 对于相等的对象应返回相同的值。它也不应在对象的整个生命周期内发生变化。通常,您只为不可变的对象实现它。 一个简单的实现就是公正。这始终是正确的,但效果很差。 您的解决方案,返回一个属性元组的哈希,是很好的。但是请注意,您无需列出