简体   繁体   English

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

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

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). 我有一个自制的蓝牙设备,以500Hz的频率测量ECG:每2毫秒该设备发送9字节的数据(标头,ECG测量,页脚)。 So this is roughly a 9*500=4.5kbytes/s data stream. 因此,这大约是9 * 500 = 4.5kbytes / s的数据流。

I have a C++ Windows program able to connect the device and retrieve the data stream (displaying it with Qt/qwt). 我有一个能够连接设备并检索数据流(使用Qt / qwt显示)的C ++ Windows程序。 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. 在这种情况下,我使用Windows控制面板绑定设备,并使用boost serial_port接口通过虚拟COM端口将其连接。 This works perfectly and I'm receiving my data stream in real time: I get a measurment point every 2ms or so. 这非常有效,并且我正在实时接收数据流:每2毫秒左右获得一个测量点。

I ported the whole program on Android via QtCreator 3.0.1 (Qt 5.2.1). 我通过QtCreator 3.0.1(Qt 5.2.1)在Android上移植了整个程序。 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. 似乎虚拟COM端口无法通过boost访问(可能是SDK权限不允许这样做),所以我写了一段Java代码来打开和管理蓝牙连接。 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): 因此,我的应用程序仍然是C ++ / Qt,但是在Java中仅对连接和从设备读取数据的层进行了重新处理(使用createInsecureRfcommSocketToServiceRecord打开连接):

Java code to read the data: 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;
}

Called from C++ like that: 像这样从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;
}

This works pretty well... for the five first seconds I'm gettings values in a sort of real time, then: 这很好用... 在头五个秒中,我实时获取了值,然后:

  • Sometimes it freezes for ever, meaning the mInputStream.available() value remains lower than requested. 有时它永远冻结,这意味着mInputStream.available()值仍然低于要求。
  • Sometimes it freezes only for a second or so and then it continues but data are received by blocks of ~1 second. 有时,它仅冻结一秒钟左右,然后继续,但是大约1秒的块会接收到数据。 Meaning mInputStream.available() can move from 0 to more than 3000 between two calls (elapsed by 10ms). 含义mInputStream.available()可以在两次调用之间从0移到3000以上(经过10ms)。 Actually, I see the same during the 5 firsts seconds, but the buffer availability never exceeds 150 bytes, after 5 seconds, it can go up to 3000 bytes. 实际上,我在5秒钟内看到了相同的结果,但是缓冲区的可用性从未超过150字节,在5秒钟后,它可以达到3000字节。

Here is what the log can look like when verbose is set to true: 将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

My ECG device definitely did not send 1728 bytes in 11ms!! 我的ECG设备绝对没有在11毫秒内发送1728字节!

I know my device sends 9 bytes every 2ms (otherwise, it would not work on my PC application). 我知道我的设备每2ms发送9个字节(否则,它将无法在我的PC应用程序上运行)。 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. 看起来Java进行了一些意外的缓冲,并且每2ms不会提供9个字节。...奇怪的是,开始时仅5秒钟就可以正常工作。

Note that I tried using read() without checking available() (blocking version) but experienced exactly the same behaviour. 请注意,我尝试使用read()而不检查available()(阻止版本),但是体验完全相同。

So I'm wondering what I'm doing wrong... 所以我想知道我在做什么错...

  • Is there a way to force a Java input stream to update itself? 有没有办法强制Java输入流自我更新?
  • Is there a way to ask Java to proceed it's pending events (like we have QApplication::processEvents)? 有没有办法让Java处理未决事件(例如我们有QApplication :: processEvents)?
  • Is there any global settings to specify buffer sizes for streams (I did not find any at BluetoothDevice/BluetoothSocket level) 是否有任何全局设置来指定流的缓冲区大小(我在BluetoothDevice / BluetoothSocket级别未找到任何缓冲区大小)
  • On PC, when opening the virtual COM port, I have to specify baudrate, stop bit, handshaking and stuff like that. 在PC上,打开虚拟COM端口时,我必须指定波特率,停止位,握手和类似内容。 On Android I just open the Rfcomm socket with no option, could this be the problem (then ECG device and smartphone would not be synced...)? 在Android上,我只是打开Rfcomm套接字而没有任何选择,这可能是问题所在(然后ECG设备和智能手机将无法同步...)吗?

Any help or idea would be welcomed! 任何帮助或想法都将受到欢迎!

Edit: I'm experiencing that on a Nexus 5 phone, Android 4.4.2 I just tested the same apk package on different devices: 编辑:我在Nexus 5手机,Android 4.4.2上遇到了这个问题,我刚刚在不同设备上测试了相同的apk软件包:

  • a Galaxy S4 with Android 4.4.2: Same problem. 具有Android 4.4.2的Galaxy S4:同样的问题。
  • a Galaxy S3 with custom CyanogenMod 11 Android 4.4.2: data streaming seems perfect, no freezing after 5sec and data are arriving in real-time....so looks like the whole system is able to achieve what I want, but looks like Android default setup makes things too slow....dunno if there could be a setting to be changed at the OS level to fix this issue. 具有自定义CyanogenMod 11 Android 4.4.2的Galaxy S3:数据流似乎很完美,5秒后不会冻结,并且数据实时到达。...因此,看起来整个系统都能实现我想要的功能,但是看起来Android默认设置会使操作太慢....不知道是否需要在操作系统级别进行更改才能解决此问题。

Edit: 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 编辑:因为我没有答案:-(我试图使用纯Java程序(没有C ++,没有Qt)做同样的事情。有同样的问题: Android上的实时蓝牙SPP数据流只能工作5秒钟

This problem is apparently similar to the one reported here . 这个问题显然类似于这里报告的问题

After 5 seconds, I had either a connection lost, either real-time streaming being dramatically slow down. 5秒后,我要么失去了连接,要么实时流变得非常慢。

As said here Android >4.3 apparently does not like one-way communication exceeding 5 secondes. 如此处所述 Android> 4.3显然不喜欢超过5秒的单向通信。 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! 因此,我现在每隔1秒向设备发送一个虚拟命令(有点“保持活动”命令),现在Android感到很高兴,因为它不再是单向通信了……因此,数据流同样好第五秒比以前!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM