Ошибка чтения данных из 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");
                }
            });
        }
    }
}