如何使用Java Android SDK实现良好的实时数据流
我有一个以500Hz为单位测量ECG的自制蓝牙设备:每隔2ms设备发送9个字节的数据(标题,ECG测量,页脚)。所以这大概是一个9 * 500 = 4.5kbytes/s的数据流。如何使用Java Android SDK实现良好的实时数据流
我有一个C++ Windows程序能够连接设备并检索数据流(用Qt/qwt显示它)。在这种情况下,我使用Windows控制面板绑定设备,并使用boost serial_port接口通过虚拟COM端口连接它。这很好地工作,我正在实时接收我的数据流:我每隔2ms左右就会得到一个测量点。
我通过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;
}
这五个第一秒我gettings值排序的实时,然后工作得很好...... :
- 有时会永久冻结,意味着mInputStream.available()值保持低于请求的值。
- 有时它只会冻结一秒钟左右,然后它会继续,但数据是通过〜1秒的块接收的。含义mInputStream.available()可以在两次调用之间从0移动到3000以上(经过10ms)。实际上,我在5秒钟内看到了相同的情况,但缓冲区可用性从未超过150个字节,5秒后可达3000个字节。
这里是日志可以查看详细的是什么时,设置为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
我的心电图设备绝对没有在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包:
- 银河S4采用Android 4.4.2:同样的问题。
- 一个Galaxy S3与自定义CyanogenMod 11 Android 4.4.2:数据流似乎完美,5秒后没有冻结和数据实时到达....所以看起来像整个系统能够实现我想要的,但看起来像Android的默认设置会让事情太慢....不知道是否可以在操作系统级别更改设置来解决此问题。
编辑:由于我没有回答:-(我试图用纯Java程序(没有C++,没有Qt的)做同样的事情有同样的问题:Real-time Bluetooth SPP data streaming on Android only works for 5 seconds