Ошибка чтения данных из InputStream в Bluetooth на Android
Я работаю над Android-приложением, которое использует соединение Bluetooth для передачи данных между моим Android-телефоном и не-андроидным модулем bluetooth, используя профиль SPP. Я использовал пример Bluetooth Chat с сайта разработчика Android в качестве ссылки.
Я успешно подключил два устройства друг к другу и отправил простые строки с смартфона на модуль bluetooth. Но у меня есть некоторая ошибка при чтении данных, отправленных обратно из модуля. Я использовал следующий код, который точно такой же, как в примере Bluetooth Chat, для чтения данных из 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;
}
}
Когда мой модуль bluetooth отправляет простую строку на телефон, эта строка получена неправильно. Он разбивается на несколько частей случайным образом. Например, если я отправлю "1234567890abcdef1234567890abcdef0123456789" три раза на телефон, Logcat on Eclipse зарегистрирует их:
mmInstream - 12345678910abcdef��������(continuing null)
mmInstream - 1��������(continuing null)
mmInstream - 2345678910abcdef0123456789��������(continuing null)
в первый раз. Во второй и третий раз данные передаются, они принимаются в разностных частях:
mmInstream - 1234567891�������(continuing null)
mmInstream - 0abcdef012�������(continuing null)
mmInstream - 3456789���������(continuing null)
mmInstream - 1234567891����������������(continuing null)
mmInstream - 0abcdef0123456789������������(continuing null)
Я не знаю, почему это происходит и как решить эту проблему. Если данные принимаются произвольным образом, я не могу получить необходимые данные для обработки. Как я могу получить его в одной части?
Любая помощь будет оценена.
Большое спасибо.
Ответы
Ответ 1
Две вещи, которые я заметил с вашим кодом:
-
Прежде всего, отправляя дальше ваше приложение, ссылка на буфер, в котором вы читаете, не всегда является хорошим решением: что, если в то же время буфер будет переопределен? fooobar.com/questions/533169/...
Вы можете обойти это, сделав копию данных (например, use buffer.clone()), которые вы прочитали из Bluetooth, или если вам не нравится использовать слишком много памяти, вы можете сделать свой буфер чтения круглым.
-
Вы должны иметь возможность перекомпилировать ваши данные, даже если они получены в отдельных пакетах (но пакеты принимаются за короткий промежуток времени). Например, вы можете создавать флаги запуска/остановки. Ofc все равно зависит от типа объекта, который вы отправляете по Bluetooth...
И теперь возможно решение, если два предыдущих предупреждения бесполезны:
Вместо бесконечного цикла, который вызывает .read - блокирующий вызов - вы можете сделать что-то вроде этого:
while(true) {
if mmInStream.getAvailable()>0 {
-your read code here-
}
else SystemClock.sleep(100);
}
Это взломать, и иногда он может читать только часть сообщения - но это будет очень редко!
Pls проголосовать вверх/исправить, если полезно!
Ответ 2
У меня есть эта проблема, и я решил проблему этих символов таким образом
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;
}
}
}
Ответ 3
Radu fix работает фантастично!. Я уже давно работаю над этой проблемой, используя пример кода Bluetooth Chat. Ниже я использую, чтобы захватить и отобразить показания температуры с удаленного датчика:
// 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();
}
}
}
Как видите, я изменил (buffer.available() > 0) нa > 2. Это связано с тем, что мой микроконтроллер посылает 2 байта для температуры., До этого исправления входной поток для подсчета байтов будет меняться, иногда только забирая 1 байт, который перепутал показание температуры в приложении Android. Опять же, из всех предложений в Интернете, у Radu есть лучшее обходное решение для ошибки входа в android.
Ответ 4
Я нашел его!
Вы должны reset буфер:
buffer = new byte [1024];
до этого:
bytes = mmInStream.read(buffer);
По крайней мере, это сработало для меня, и никакая команда сна не нужна.
Ответ 5
Я думаю, что есть много хороших ответов. Мой вклад состоит в том, чтобы обрабатывать каждую цифру по одному, когда она поступает в входной поток Bluetooth следующим образом. Значением этого является обеспечение того, чтобы каждый набор данных был одним значением длины (как String), который можно обрабатывать очень легко, помещая каждое новое значение (содержащееся в строке readMessage в основном обработчике операций) в список. Таким образом, вы можете использовать List (String) (редактор не позволит мне использовать объект < > ) для обработки данных обратно в реальные числа путем извлечения через
Объект Integer.valueOf(List (String)).
//Продолжайте прослушивать 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();
}
Ответ 6
Я использовал последние два кода, они работают хорошо, хотя, когда соединение потеряно, пользовательский интерфейс не получает уведомления и поэтому состояние не изменяется на STATE_NONE. В моем случае я хочу, чтобы приложение пыталось подключить последнее устройство, когда соединение потеряно!! после многих попыток я, наконец, решил проблему следующим образом:
- Я создал символ END, этот символ, который я знаю, я никогда не буду использовать, но только в конце строки, которую я отправляю. "" мой конец строкового символа.
- Я создал временную строку tmp_msg.
- Каждый раз, когда поток принимается, из этого потока создается вторая временная строка "readMessage" .
- "readMessage" выполняется поиск символа конца ( "." ).
- Если конечный символ не обнаружен, "readMessage" объединяется с tmp_msg. Поэтому, когда символ конца не принимается первым, вторым и третьим временем tmp_msg = readMessage1 + readMessage2 + readMessage3.
- Когда символ конца ".", tmp_msg объединяется с последним readMessage, а байты "buffer" строятся из него.
-
"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;
}
}
}
Ответ 7
Попробуйте следующее:
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;
}
}
}
У меня тоже была эта проблема. Сейчас работает хорошо. Без символов.
Ответ 8
Мой способ: я создаю textView в MainActivity с его идентификатором 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");
}
});
}
}
}