Android 自帶的藍芽範例BluetoothChat很好用,
而且想因此學習bluetooth的原理與處理機制可以藉由這隻範例程式來研究,
但是我在測試Android 這隻BluetoothChat與其他裝置連線時(stm32f4 discovery) ,
問題來了:
- 發現在stm32f4 送一段簡單的訊息 --> printf("This is a bug \r\n" );
而BluetoothChat接收端會有分段讀取的現象,例如一段message會拆成
Thi
s is a b
ug
即使BluetoothChat 內部本身已經有處理掉char 與 byte的型別轉換,但是重點不在於這
裡,先來看BluetoothChat核心讀取端的程式碼:
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
//Error handling removed to simplify bug report
}
}
}
首先先注意到這一行:
bytes = mmInStream.read(buffer);
如果你 Log 出 bytes , 你會發現就算你的other device傳送過來的指令都是一模一樣的字數,比如說固定就是傳10個char , 那你每一次在Android 這邊收到的bytes的字數都會不一樣,
這就是會分段的原因所在,所以當然你在Android 抓取一次的結果就是不完整的。
引述 Android Open Source Project - Issue Tracker
裡面提到了問題所在:
The problem is that "buffer" is reused, but obtainMessage(...) isn't guaranteed to be done with buffer by the time more data is available from the stream, and no defensive copy has been made of the buffer.
他的解決方法是將buffer定義在迴圈裡面,讓他每進入此迴圈都重新定義一個新的buffer,但是這不但會浪費多的資源,重點是我試了也沒用........還是會分段,所以再換別的方法。
我認為應該是要在迴圈內部裡面等待一段訊息全部傳完並且乾淨,才能夠當作有效的一段訊息,因此我修改程式如下:
public void run() {
byte[] buffer = new byte[1024];
int bytes;
// Keep listening to the InputStream while connected
while (true) {
try {
String msg="";
while(mmInStream.available()==0){
/*wait the message buffer*/
}
while (true){
// Read from the InputStream
bytes = mmInStream.read(buffer);
String temp_msg = new String(buffer,0,bytes);
msg+=_temp_msg;
if(mmInStream.available()==0)break;
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, msg)
.sendToTarget();
} catch (IOException e) {
//Error handling removed to simplify bug report
}
}
}
或者是這樣也可以,這個版本比較穩定(參照Ref 3),
只是我額外將格式改成了
ISO-8859-1 ,Code如下:
public void run() {
int bytes;
byte[] buffer = new byte[1024];
String end = "\n";
StringBuilder curMsg = new StringBuilder();
while (true){
try {
while (-1 != (bytes = mmInStream.read(buffer))) {
curMsg.append(new String(buffer, 0, bytes, Charset.forName("ISO-8859-1")));
int endIdx = curMsg.indexOf(end);
if (endIdx != -1) {
String fullMessage = curMsg.substring(0, endIdx + end.length());
curMsg.delete(0, endIdx + end.length());
// Now send fullMessage
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(ControlEPW_Fragment.MESSAGE_READ, bytes, -1, fullMessage)
.sendToTarget();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的
end = "\n"; 代表當我接收到一大串字串,只要收到了換行符號,就會送出去String給主Handler。
但是這裡要注意到我跟原始程式不同的是,原始程式傳出去是以byte[] object 傳出去主Handler ,而我是已String方式傳出去主Handler ,所以我在主Handler不用再轉一次String了 ,因此我把主Handler那裡將message 的 byte[] to String那一段拿掉,我想這不用在多說了。
附帶一提: 此修改程式在我的case下成功的,不會分段,若是有其他問題可以再回報與討論,Thanks.