最近自己做一个计步器,需要从从机(arduino uno + mpu6050 + hc-05)接收数据,每一秒钟接收20组长度为20字节左右的数据,在串口上观察数据输出结果正常,如图1:
但是当利用蓝牙传数据的时候,上位机端(安卓手机客户端)就会出现数据丢失的问题,如图:
可以发现,这个数据丢的特别严重,既然串口上面输出的结果是正确的,那么证明单片机本身没有任何问题。我把目光转移到了蓝牙模块上。蓝牙模块hc-05,支持蓝牙v2.0,传输速率大约在1.8M/s---2.1M/s,所以一秒传我那最多400字节的数据没有任何压力(这个排除法我可是做了整整2天啊。。。还和网友通宵交流。。。没办法遇到难题还能怎么半呢,还是得自己想法子解决,不要想着别人可以帮你解决)。那么问题出在哪里呢?还有最后一个可以怀疑得目标,上位机!
上位机上得蓝牙代码用得是google官方demo,然后demo里面接收数据是这么写的:
<span style="font-size:18px;">public void run() {//接收数据的线程
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(MainActivity.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}</span>
然后handler的处理是这么写的:
case MESSAGE_READ://3读取数据
byte[] readBuf = (byte[]) msg.obj;
recieveStr = new String(readBuf, 0, msg.arg1);//得到数据字符串
Log.e("Length",String.valueOf(recieveStr.length()) + " " + recieveStr);
<span style="white-space:pre"> </span> break;
然后结果就有了上面的那一幕,数据丢了!
我苦思冥想,觉得官方的demo毕竟是google神牛写的啊,99%都没啥问题的呀(1%留给自己来质疑),然后我就一行代码一行代码的排除,然后就发现handler消息传递有两种方法,一种就是上面代码里面给出来的obtainMessage()方法,另外一种就是sendMessage()方法,然后这两种方法有什么区别呢,大家可以先看看这个解释详细的:传送门1,传送门2。
其实就是一个new与不new,导致的效率不同的区别,obtainMessage()因为是直接从message global pools里面取出的一个新的message实例,所以在快传大量数据的过程中,MessageQueue大小有限,如果消息处理不及时,而又有新的数据写入,有可能造成message被覆盖,也就出现了上面的数据丢失现象。虽然android推荐使用obtainMessage(),有其利但是也有其弊。
于是我就尝试用sendMessage()来解决问题:
public void run() {
byte[] buffer = new byte[1024];
int bytes;
String readMessage;
// Keep listening to the InputStream while connected
while (true) {
try {
int availableBytes = mmInStream.available();
if (availableBytes > 0) {
bytes = mmInStream.read(buffer);
Message msg = new Message();
Bundle data = new Bundle();
readMessage = new String(buffer,0,bytes);
data.putString("BTdata",readMessage);
msg.what = MainActivity.MESSAGE_READ;
msg.setData(data);
mHandler.sendMessage(msg);
}
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
handler处理:
case MESSAGE_READ://3读取数据
Bundle data = msg.getData();
recieveStr = data.getString("BTdata");
Log.e("Length",String.valueOf(recieveStr.length()) + " " + recieveStr);
writeFile(recieveStr);//把收到的数据写到文件里面
break;
看看运行结果:
非常好,解决了数据丢失的问题。虽然这样做牺牲了部分效率,但是相比丢数据这种危险的事情,这是非常非常值得的。
上文如果有错误的地方,请各位指正!