如何使用 Java Android SDK 做好实时数据流传输 [英] How to do good real-time data streaming using 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屋!