Error in reading data from InputStream in Bluetooth on Android

23,567

Solution 1

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 example You 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...

And now a possible solution if the 2 previous warnings are of no use is this:

Instead of an infinite loop which calls .read - a blocking call - you can do something like this:

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!

Solution 2

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;
        }
    }
}

Solution 3

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:

  // 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.

Solution 4

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:

  1. 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.
  2. I created a temporary string tmp_msg.
  3. Every time a Stream is received, a second temporary String "readMessage" is created out of that Stream.
  4. "readMessage" is searched for end character (".").
  5. 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.
  6. When end character "." is detected, tmp_msg is concatinated with last readMessage and bytes "buffer" are constructed out of it.
  7. "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;
            }
        }
    }
    
Share:
23,567
theman
Author by

theman

Updated on June 14, 2021

Comments

  • theman
    theman almost 3 years

    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.

    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

    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:

    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.

  • radix07
    radix07 over 11 years
    Was having a heck of a time getting "bytes = mmInStream.read(buffer);" to work in my implementation as well. But your sleep method works great, not quite sure why I can get out of the lock using the Blue Chat as a base.
  • Radu
    Radu over 11 years
    Nice to hear it worked - it's a hack basically, and it still might produce issues sometime. Basically, they should've implemented an read() method with minLen and maxTimetout variables, just like any other socket. However we can do that ourselves, OR do the sleep hack.
  • Piotr Kula
    Piotr Kula almost 10 years
    Problem with defining a buffer is that if more data comes in it get truncated. But for me defining a larger buffer than 4096 crashed the app :( I got this thing were I cant split the buffer as its a formatted response and sometimes the data bit is pretty large and splitting it would mean I loose my place in the parsing :(
  • Louis CAD
    Louis CAD about 7 years
    Do not copy paste this code in your project as it is allocating a new bytearray on each while loop iteration, which may introduce a big memory overhead and getting the GC strike like crazy
  • Louis CAD
    Louis CAD about 7 years
    Beware of autoboxing in your Integer ArrayList
  • user207421
    user207421 almost 7 years
    There is no difference between a blocking read call and a blocking sleep, except that the sleep will block for too long half the time, and the spin loop will waste CPU cycles.
  • user207421
    user207421 almost 7 years
    No you don't. You just have to use new String(buffer, 0, bytes) instead of converting the entire buffer to a String ignoring the read count.
  • user207421
    user207421 almost 7 years
    @ppumkin If more data comes in it remains available to be read next time. It is not truncated.
  • user207421
    user207421 almost 7 years
    And you've forgotten to check for end of stream.
  • user207421
    user207421 almost 7 years
    Do not use this code because it is a hard spin loop and because it logs junk at the end of the buffer and because it is in general a complete waste of time and space.
  • Smitty-Werben-Jager-Manjenson
    Smitty-Werben-Jager-Manjenson about 5 years
    You saved me some headache! Thank you very much for this hack/solution!