如何使用 Java Android SDK 做好实时数据流传输 [英] How to do good real-time data streaming using Java Android SDK

查看:16
本文介绍了如何使用 Java Android SDK 做好实时数据流传输的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a home-made bluetooth device measuring ECG at 500Hz: every 2 ms the device sends 9 bytes of data (header, ECG measurment, footer). So this is roughly a 9*500=4.5kbytes/s data stream.

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

I have a C++ Windows program able to connect the device and retrieve the data stream (displaying it with Qt/qwt). In this case, I use Windows control panel to bond the device and I connect it via a virtual COM port using boost serial_port interface. This works perfectly and I'm receiving my data stream in real time: I get a measurment point every 2ms or so.

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

I ported the whole program on Android via QtCreator 3.0.1 (Qt 5.2.1). It appears that virtual COM ports cannot be accessed by boost (probably SDK permissions won't allow that) so I wrote a piece of Java code to open and manage the Bluetooth connection. So my app remains C++/Qt but only the layer connecting and reading data from the device was reworked in Java (opening the connexion with createInsecureRfcommSocketToServiceRecord):

读取数据的Java代码:

Java code to read the data:

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++ 调用:

Called from C++ like that:

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;
}

这很有效...在前五秒我正在实时获取值,然后:

This works pretty well...for the five first seconds I'm gettings values in a sort of real time, then:

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

当verbose 设置为true 时,日志会是什么样子:

Here is what the log can look like when verbose is set to 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字节!!

My ECG device definitely did not send 1728 bytes in 11ms!!

我知道我的设备每 2 毫秒发送 9 个字节(否则,它将无法在我的 PC 应用程序上运行).看起来 Java 做了一些意外的缓冲,并且每 2 毫秒没有提供 9 个字节......同样奇怪的是,开始时似乎只运行了 5 秒.

I know my device sends 9 bytes every 2ms (otherwise, it would not work on my PC application). Looks like Java does some unexpected buffering and does not make available 9 bytes every 2ms.... It's also strange things appear to work fine for only 5 seconds at the beginning.

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

Note that I tried using read() without checking available() (blocking version) but experienced exactly the same behaviour.

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

So I'm wondering what I'm doing wrong...

  • 有没有办法强制 Java 输入流自我更新?
  • 有没有办法让 Java 处理它的待处理事件(比如我们有 QApplication::processEvents)?
  • 是否有任何全局设置来指定流的缓冲区大小(我在 BluetoothDevice/BluetoothSocket 级别没有找到任何设置)
  • 在 PC 上,当打开虚拟 COM 端口时,我必须指定波特率、停止位、握手等等.在 Android 上,我只是打开 Rfcomm 插座而没有选择,这可能是问题所在(然后心电图设备和智能手机不会同步...)?

欢迎任何帮助或想法!

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

I'm experiencing that on a Nexus 5 phone, Android 4.4.2 I just tested the same apk package on different devices:

  • 搭载 Android 4.4.2 的 Galaxy S4:同样的问题.
  • 带有自定义 CyanogenMod 11 Android 4.4.2 的 Galaxy S3:数据流看起来很完美,5 秒后没有冻结并且数据实时到达......所以看起来整个系统能够实现我想要的,但看起来 Android 默认设置让事情变得太慢了......不知道是否可以在操作系统级别更改设置以解决此问题.

因为我没有得到答案:-(我尝试使用纯 Java 程序(没有 C++,没有 Qt)做同样的事情.遇到了同样的问题:Android 上的实时蓝牙 SPP 数据流仅工作 5 秒

As I got no answer :-( I tried to do the same thing using a pure Java program (no C++, no Qt). Had the same problem: Real-time Bluetooth SPP data streaming on Android only works for 5 seconds

推荐答案

这个问题显然与here.

5 秒后,我要么失去了连接,要么实时流传输速度显着变慢.

After 5 seconds, I had either a connection lost, either real-time streaming being dramatically slow down.

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

As said here Android >4.3 apparently does not like one-way communication exceeding 5 secondes. So I'm now sending a dummy command to the device every 1 seconde (kind of "keep-alive" command) and now Android is happy because it's not a one-way communication anymore...and so data streaming is as good after the fifth second than before!

这篇关于如何使用 Java Android SDK 做好实时数据流传输的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆