从 Android 上的蓝牙中的 InputStream 读取数据时出错
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12294705/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Error in reading data from InputStream in Bluetooth on Android
提问by theman
I'm working on an Android app, which uses bluetooth connection to transfer data between my android smartphone and a non-android bluetooth module, using SPP profile. I've used Bluetooth Chat Example from Android Developer site as reference.
我正在开发一个 Android 应用程序,它使用蓝牙连接在我的 android 智能手机和非 android 蓝牙模块之间传输数据,使用 SPP 配置文件。我使用了 Android 开发人员网站上的蓝牙聊天示例作为参考。
I've successfully made two devices connect to each other and sent simple strings from the smart phone to the bluetooth module. But I've got some error in reading data sent back from the module. I've used the following code, which is exactly the same as in Bluetooth Chat Example, to read data from InputStream
我已经成功地使两个设备相互连接,并将简单的字符串从智能手机发送到蓝牙模块。但是我在读取从模块发回的数据时遇到了一些错误。我使用了以下代码,与蓝牙聊天示例中的完全相同,从 InputStream 读取数据
while (true) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
String str = new String(buffer);
Log.i(TAG, "mmInStream - " + str);
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
When my bluetooth module send a simple string to the phone, that string is not received correctly. It is splited into several pieces in random ways. For example, if I send "1234567890abcdef1234567890abcdef0123456789" for three times to the phone, the Logcat on Eclipse will log these:
当我的蓝牙模块向手机发送一个简单的字符串时,该字符串未正确接收。它以随机方式分成几部分。例如,如果我向手机发送 3 次“1234567890abcdef1234567890abcdef0123456789”,Eclipse 上的 Logcat 将记录这些:
mmInstream - 12345678910abcdef????????(continuing null)
mmInstream - 1????????(continuing null)
mmInstream - 2345678910abcdef0123456789????????(continuing null)
for the first time. In the second and the third time data is transmitted, it is received in a difference pieces:
首次。在第二次和第三次发送数据时,它是在不同的块中接收的:
mmInstream - 1234567891???????(continuing null)
mmInstream - 0abcdef012???????(continuing null)
mmInstream - 3456789?????????(continuing null)
mmInstream - 1234567891????????????????(continuing null)
mmInstream - 0abcdef0123456789????????????(continuing null)
I don't know why this happen and how to solve this problem. If data is received in a arbitrary way like this, I can't get necessary data to process. How can I get it in one pieces?
我不知道为什么会发生这种情况以及如何解决这个问题。如果以这样的任意方式接收数据,我将无法获得必要的数据进行处理。我怎样才能把它一分为二?
Any help would be appreciated.
任何帮助,将不胜感激。
Many Thanks.
非常感谢。
采纳答案by Radu
Two things I noticed with your code:
我在您的代码中注意到两件事:
First of all sending further down to your app a reference to the buffer in which you read is not always a good solution: What if in the meantime the buffer gets overridden? See this bug on stackoverflow for exampleYou can bypass this by making a copy of the data (for example use buffer.clone()) which you have read from Bluetooth, or if you don't like using too much memory you can make your read buffer a circular one.
You should be able to recompile your data even if it is received in separate packets (but packets are received in a short time span). You can make start/stop flags for instance. Ofc it still depends on the type of object you send over Bluetooth...
首先,向您的应用程序进一步发送对您读取的缓冲区的引用并不总是一个好的解决方案:如果同时缓冲区被覆盖怎么办?例如,在 stackoverflow 上查看此错误您可以通过复制从蓝牙读取的数据(例如使用 buffer.clone())来绕过此问题,或者如果您不喜欢使用太多内存,您可以让您的读缓冲区是一个循环的。
即使数据是在单独的数据包中接收的,您也应该能够重新编译数据(但数据包是在很短的时间内收到的)。例如,您可以制作开始/停止标志。Ofc它仍然取决于您通过蓝牙发送的对象类型......
And now a possible solution if the 2 previous warnings are of no use is this:
如果前面的 2 个警告没有用,现在一个可能的解决方案是:
Instead of an infinite loop which calls .read - a blocking call - you can do something like this:
而不是调用 .read 的无限循环 - 阻塞调用 - 您可以执行以下操作:
while(true) {
if mmInStream.getAvailable()>0 {
-your read code here-
}
else SystemClock.sleep(100);
}
It's a hack and it might still sometimes read only some part of the message - but it will be very rare!
这是一个黑客攻击,它有时仍可能只读取消息的一部分 - 但这种情况非常罕见!
Pls vote up/correct if useful!
如果有用,请投票/纠正!
回答by papucho
I have a this problem and I've solved the problem of this characters ? in this way
我有这个问题,我已经解决了这个字符的问题?通过这种方式
public void run() {
int bytes; // bytes returned from read()
int availableBytes = 0;
// Keep listening to the InputStream until an exception occurs
while (needRun) {
try {
availableBytes = mmInStream.available();
if(availableBytes > 0){
byte[] buffer = new byte[availableBytes]; // buffer store for the stream
// Read from the InputStream
bytes = mmInStream.read(buffer);
Log.d("mmInStream.read(buffer);", new String(buffer));
if( bytes > 0 ){
// Send the obtained bytes to the UI activity
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer).sendToTarget();
}
}
} catch (IOException e) {
Log.d("Error reading", e.getMessage());
e.printStackTrace();
break;
}
}
}
回答by mea.samir
I have used last two codes, they work well, though when connection is lost user interface does not get notified and so state does not change to STATE_NONE. In my case I want app to try to reconnect last device whenever connection is lost!! after trying many ways I finally solved the problem this way:
我使用了最后两个代码,它们运行良好,但当连接丢失时,用户界面不会收到通知,因此状态不会更改为 STATE_NONE。在我的情况下,我希望应用程序在连接丢失时尝试重新连接最后一个设备!!在尝试了多种方法后,我终于以这种方式解决了问题:
- I created an END character, this character I know I will never use but just in the end of string I am sending. "." is my end of string character.
- I created a temporary string tmp_msg.
- Every time a Stream is received, a second temporary String "readMessage" is created out of that Stream.
- "readMessage" is searched for end character (".").
- If end character is not detected, "readMessage" is concatinated to tmp_msg. So when end character is not received first and second and third time tmp_msg=readMessage1+readMessage2+readMessage3.
- When end character "." is detected, tmp_msg is concatinated with last readMessage and bytes "buffer" are constructed out of it.
"buffer" then is sent to User Interface, and tmp_msg is reinitialized to ""(empty string). here is the entire code: (no modification is to be done with BluetoothChat activity).
public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected String tmp_msg =""; while (true) { try { // Read from the InputStream bytes = mmInStream.read(buffer); String readMessage = new String(buffer, 0,bytes); if (readMessage.contains(".")){ tmp_msg+=readMessage; byte[] buffer1 = tmp_msg.getBytes(); int bytes1=buffer1.length; tmp_msg=""; // Send the obtained bytes to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_READ,bytes1,-1,buffer1).sendToTarget(); }else{ tmp_msg+=readMessage; } } catch (IOException e) { // Log.e(TAG, "disconnected", e); connectionLost(); // Start the service over to restart listening mode BluetoothChatService.this.start(); break; } } }
- 我创建了一个 END 字符,这个字符我知道我永远不会使用,但只是在我发送的字符串的末尾。“。” 是我的字符串字符的结尾。
- 我创建了一个临时字符串 tmp_msg。
- 每次收到流时,都会从该流中创建第二个临时字符串“readMessage”。
- 搜索“readMessage”以查找结束字符(“.”)。
- 如果未检测到结束字符,则将“readMessage”连接到 tmp_msg。所以当第一次、第二次和第三次没有收到结束字符时,tmp_msg=readMessage1+readMessage2+readMessage3。
- 当结束字符“.”时 被检测到,tmp_msg 与最后一个 readMessage 连接,并且字节“缓冲区”被构造出来。
“buffer”然后被发送到用户界面,并且 tmp_msg 被重新初始化为“”(空字符串)。这是整个代码:(无需对 BluetoothChat 活动进行修改)。
public void run() { Log.i(TAG, "BEGIN mConnectedThread"); byte[] buffer = new byte[1024]; int bytes; // Keep listening to the InputStream while connected String tmp_msg =""; while (true) { try { // Read from the InputStream bytes = mmInStream.read(buffer); String readMessage = new String(buffer, 0,bytes); if (readMessage.contains(".")){ tmp_msg+=readMessage; byte[] buffer1 = tmp_msg.getBytes(); int bytes1=buffer1.length; tmp_msg=""; // Send the obtained bytes to the UI Activity mHandler.obtainMessage(BluetoothChat.MESSAGE_READ,bytes1,-1,buffer1).sendToTarget(); }else{ tmp_msg+=readMessage; } } catch (IOException e) { // Log.e(TAG, "disconnected", e); connectionLost(); // Start the service over to restart listening mode BluetoothChatService.this.start(); break; } } }
回答by Lukeskymuh
I found it!
我找到了!
You have to reset the buffer:
您必须重置缓冲区:
buffer = new byte [1024];
before this:
在这之前:
bytes = mmInStream.read(buffer);
At least it worked for me, and no sleep command is needed.
至少它对我有用,不需要 sleep 命令。
回答by add_em_
I think there are many good answers. My contribution is to process each digit one at a time as it comes into the Bluetooth input stream as follows. The value of this is to ensure that every set of data is one value in length (as a String), which can be processed very easily by putting each new value (contained in the readMessage String in the main Activity Handler) into a List. That way, you can then use the List(String) (the editor won't let me use <>'s) object to process the data back into real numbers by extracting via
我认为有很多很好的答案。我的贡献是在进入蓝牙输入流时一次处理一个数字,如下所示。这样做的价值是确保每组数据的长度都是一个值(作为一个字符串),通过将每个新值(包含在主 Activity Handler 的 readMessage 字符串中)放入一个 List 中,可以非常轻松地处理该值。这样,您就可以使用 List(String) (编辑器不会让我使用 <> 的)对象通过提取通过
Integer.valueOf(List(String) object passed in here)
Integer.valueOf(此处传入的List(String)对象)
// Keep listening to the InputStream while connected while (true) {
// 连接时继续监听 InputStream while (true) {
try {
byte[] buffer = new byte[1]; // make this one byte to pass one digit at a time through Bluetooth Handler
String readMessage;
int bytes;
if (mmInStream.available()>2) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
readMessage = new String(buffer, 0, bytes);
}catch (IOException e) {
Log.e(TAG, "disconnected", e);
break;
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
.sendToTarget();
}
回答by Ryan Harris
Radu's fix works fantastic!. I myself have been working on this issue for quite some time now using the Bluetooth Chat sample code. Below is what i'm using to capture and display temperature reading from a remote sensor:
Radu 的修复效果非常好!我自己已经使用蓝牙聊天示例代码解决这个问题有一段时间了。下面是我用来从远程传感器捕获和显示温度读数的内容:
// Keep listening to the InputStream while connected
while (true) {
try {
byte[] buffer = new byte[128];
String readMessage;
int bytes;
if (mmInStream.available()>2) {
try {
// Read from the InputStream
bytes = mmInStream.read(buffer);
readMessage = new String(buffer, 0, bytes);
}catch (IOException e) {
Log.e(TAG, "disconnected", e);
break;
}
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(HomeBlueRemote.MESSAGE_READ, bytes, -1, readMessage)
.sendToTarget();
}
else {
SystemClock.sleep(100);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
As u see, I modified the (buffer.available() > 0) to > 2. This is because the my micro-controller is sending 2 bytes for the temperature. . Prior to this fix, inputstream for the byte count would vary, sometimes only capturing 1 byte which messed up the temperature display reading in the Android app. Again, out all the suggestions on the web, Radu has the best workaround for the android inputstream bug.
如您所见,我将 (buffer.available() > 0) 修改为 > 2。这是因为我的微控制器正在发送 2 个字节的温度。. 在此修复之前,字节计数的输入流会有所不同,有时仅捕获 1 个字节,这会扰乱 Android 应用程序中的温度显示读数。同样,在网络上的所有建议中,Radu 对 android inputstream 错误有最好的解决方法。
回答by Felipe Gualberto
Try this:
尝试这个:
public void run() {
byte[] buffer;
ArrayList<Integer> arr_byte = new ArrayList<Integer>();
while (true) {
try {
int data = mmInStream.read();
if(mmInStream.available()>0) {
arr_byte.add(data);
} else {
arr_byte.add(data);
buffer = new byte[arr_byte.size()];
for(int i = 0 ; i < arr_byte.size() ; i++) {
buffer[i] = arr_byte.get(i).byteValue();
}
Log.e("INPUT",new String(buffer));
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
arr_byte = new ArrayList<Integer>();
}
} catch (IOException e) {
break;
}
}
}
I had this problem too. Now is working good. Without ? Characters.
我也有这个问题。现在运作良好。没有 ?人物。
回答by Nguy?n Anh Giàu
My way: I build a textView in MainActivity with its id being txtReceive.
我的方式:我在 MainActivity 中构建了一个 textView,其 ID 为 txtReceive。
@Override
public void run() {
byte[] buffer=new byte[1024];
int bytes;
while(true) {
try {
bytes = inputStream.read(buffer);
final String strReceived = new String(buffer, 0, bytes);
runOnUiThread(new Runnable() {
@Override
public void run() {
txtReceive.append(strReceived);
}
});
} catch (Exception e) {
Log.d(TAG, "loi ");
runOnUiThread(new Runnable() {
@Override
public void run() {
txtReceive.setText("Error");
}
});
}
}
}