4

I am trying to make a file transfer Bluetooth app work using these sources:

http://developer.android.com/guide/topics/connectivity/bluetooth.html

https://android.googlesource.com/platform/development/+/25b6aed7b2e01ce7bdc0dfa1a79eaf009ad178fe/samples/BluetoothChat/

When I attempt to get the InputStream bytes using InputStream.read() method this way:

public class ConnectedThread extends Thread {

...(some code here)

public void run(){

        byte[] buffer = new byte[1024];
        int bytes = -1;

        //Keep listening to the InputStream while connected
        while (true){

            try {

                bytes = this.mmInStream.read(buffer);

                //* this part is not reached
                if (bytes==-1){
                    Log.d("NoData:","-1");  
                }

            }
            catch(Exception e){
                Log.d("inStream exception:",e.getMessage());
                break;
            }

        }

    }

...(some code here)

}

The next part of the code ("if" part in this case) is never reached, nor a Log.D debug output or whatever else I put in following. I just get this message from LogCat:

BluetoothSocket read in: android.net.LocalStocketImpl$SocketInputStream@f7e
                b08 len: 1024

To transfer the data from the Client to the Server I am doing this:

public class MainActivity extends Activity {

...(some code here)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    clientConnect();
    //serverConnect();

}

...(some code here)

public void clientConnect(){

        Set<BluetoothDevice> devices;

        devices = bConfig.getPairedDevices(); 

        if (devices == null){                   
            return;
        }

        if (devices.size() > 0) {           

            BluetoothDevice device = devices.iterator().next();

            ConnectThread connectTransmit = new ConnectThread(device,bConfig.getBluetoothAdapter(),BluetoothConfig.mUUID);
            connectTransmit.start();

            Toast.makeText(this, "connected", Toast.LENGTH_SHORT).show();

            socket = connectTransmit.mmSocket;
            ConnectedThread connectedThread = new ConnectedThread(socket);

            //write file bytes to the connected thread, so the thread can receive its own input written bytes later
            File file_to_transfer = new File(Environment.getExternalStorageDirectory() + "/txtTransfer.txt");           

            //get bytes from our File
            int size = (int) file_to_transfer.length();
            byte[] bytes = new byte[size];

            try {

                //14b are read succesfully, the whole text file 
                BufferedInputStream buf = new BufferedInputStream(new FileInputStream(file_to_transfer));
                buf.read(bytes,0,bytes.length);
                buf.close();                

            }catch (FileNotFoundException e){
                Log.d("FileNotFoundException:",e.getMessage());
            }catch (IOException e){ 
                Log.d("IOException:",e.getMessage());
            }

            //send the data to the server
            connectedThread.start();
            connectedThread.write(bytes);
            //connectedThread.cancel();

        }

    }

...(some code here)

}

The AcceptThread (Server part of the implementation) works, because when I run the client part to connect and then transfer the data, while debuging in the device the LogCat on the Server part activates and reaches the run method of the thread, where I call the ConnectedThread implementation but then after it "apparently" reads the bytes but it gets stuck on LogCat with no error.

Please let me know what can I do to finish reading the bytes to move to the next part of the flow.

Thank you

CoderRoller
  • 1,239
  • 3
  • 22
  • 39

4 Answers4

5

You're blocked waiting for more input.

The part labelled ... (some code here) should be inside the read loop, after the test for end of stream. NB If read() returns -1 it doesn't mean 'no data', it means end of stream, and you should close the socket and break out of the read loop. Otherwise you should then go on to process the data you've just read. At present you just read and ignore all input until end of stream, which is pointless. At best you can only process the last partial buffer, and you won't know how long it was.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Hi @EJP, if the application gets blocked on mmInStream.read and the (bytes==-1) part is not reached, How do I keep streaming more input into it, to reach that part and then break out of the loop with the read bytes, so I can finally use them to write the file on the Server?. Would you please write a sample script of the changes in my code?. Thank you very much. – CoderRoller Oct 03 '14 at 06:41
  • I have no idea what you are doing with the data you're reading, as you haven't posted it, but you need to do it *inside the read loop.* – user207421 Oct 03 '14 at 06:54
  • I am not sure if its reading/getting any data actually. The "reading" part of the script is what I posted only: bytes = this.mmInStream.read(buffer); but then the stoppage occurs and don't know what to do next. If the initial connection and writing part is correct and you can extend your explanation about how to correctly read the bytes till the end of the stream on the server side with a sample script if its possible then would be very helpful. Thanks. – CoderRoller Oct 03 '14 at 15:05
  • Hi @EJP, I put a bounty on this question, if you can explain it better with a code implementation sample that works I would thank you. – CoderRoller Oct 06 '14 at 14:22
  • I've answered all this. The 'stoppage' occurs because there is nothing more to read. You need to put your data processing *inside the loop.* The loop won't exit until the peer disconnects. – user207421 Oct 12 '14 at 22:45
  • Hi @EJP. I made the transfer work using this library: https://github.com/simonguest/android-btxfr/tree/master/src/com/simonguest/btxfr , although there were things to change to use this. I will update the answer later to explain better the implementation once my whole App is done for everyone interested. Your concepts were helpful towards doing this. Thanks. – CoderRoller Oct 13 '14 at 16:12
1

In your client code you should probably keep the connectedThread object alive a while longer. Might be that once the if clause closes and it goes out of scope (not quite sure what happens with GC and all) the write just doesn't happen and your connection is not closed but not used either.

Calling flush() on the mmOutStream inside the connectedThread after the write might help also.

Like @EJP suggested, you should put something inside your read loop.

Edit: For the sake of debugging you could add this.wait(1000); immediately after your write in the client code.

Community
  • 1
  • 1
ovikoomikko
  • 577
  • 6
  • 11
  • It *is* 'kept alive a while longer'. It is kept alive until the peer disconnects, and in fact beyond that as he doesn't have a check for end of stream. – user207421 Oct 12 '14 at 23:15
  • @EJP Yes, I see a connection that is not used and not closed. Only a write is attempted, but whether that succeeds is unknown. – ovikoomikko Oct 14 '14 at 07:17
1

In my opinion You should verify if something is in buffer before reading. reading from stream is blocking operation so aplication will hang until somehing data appear. How can I check if an InputStream is empty without reading from it?

Community
  • 1
  • 1
Mateusz B
  • 19
  • 3
1

Try changing your run method to this:

public void run(){
    byte[] buffer = new byte[1024];
    int bytesRead = 0;
    final int shortSleepTime = 1000;
    final int longSleepTime = 5000;
    int emptyReadCounter = 0;
    int sleepCounter = 0;
    int currentSleepTime = shortSleepTime;

    //Keep listening to the InputStream while connected
    while (bytesRead >= 0){
        try {

            // if available() returns 0, there is nothing to read yet
            if (this.mmInStream.available() != 0){
                bytesRead = this.mmInStream.read(buffer);

                // Check if we need to reset the sleep counters
                if (emptyReadCounter != 0){
                    emptyReadCounter = 0;
                    sleepCounter = 0;
                    currentSleepTime = shortSleepTime;

                    // We can also do anything else dependent on just waking up
                    // from a sleep cycle in this block
                }


                // Do something with my now full buffer
                // Remember not to process more than 
                // 'bytesRead' bytes from my buffer because the
                // rest could be filled with crap left over from
                // the last iteration
            } else {                
                // Three consecutive empty reads means sleep
                if (emptyReadCounter++ >= 3){                       
                    if (currentSleepTime != longSleepTime && sleepCounter++ >= 3){
                        currentSleepTime = longSleepTime;
                    }
                    Thread.sleep(currentSleepTime);
                }
            }
        }
        catch(Exception e){
            Log.d("inStream exception:",e.getMessage());
            break;
        }
    }
}
Steve K
  • 4,863
  • 2
  • 32
  • 41
  • This will smoke the cpu while no data is available. – user207421 Oct 12 '14 at 22:46
  • It is an example. You can throttle it however you see fit. For example, if you don't get any data on three consecutive reads, sleep for a second, and if you don't get any data after three consecutive sleep cycles, increase the sleep time to five seconds. That way, when something comes in, you can quickly chunk through it, but when nothing is waiting, it's not using up your CPU. – Steve K Oct 12 '14 at 22:57
  • Updated it to include my example of throttling. There are more efficient ways of doing this, I'm just giving a quick, fairly lazy, example. – Steve K Oct 12 '14 at 23:09
  • It is an example to what purpose? Blocking in the `read()` is just as effective, or more so as it doesn't waste any time, and easier to code. His problem isn't whether or not to block, it is where to put his code. Clearly he thinks `read()` is going to return when the sender has ceased sending for the moment, i.e. a sort of message boundary. The complication you've just added would be better served with a read timeout. You don't need all this. You don't need any of it. – user207421 Oct 12 '14 at 23:13
  • In the scheme of his problem, though, it shouldn't be an issue. He's transferring a file via bluetooth, and the only reason that method should be running is that he is attempting to transfer a file... and the only reason the file shouldn't be reading is that it's still doing pre-work, like setting up the bluetooth connection. It's never working because the first read blocks execution, so the connection doesn't get opened and the read is never successful. – Steve K Oct 12 '14 at 23:24
  • 'In the scheme of his problem' it's still completely pointless. He just needs to understand when and why `read()` blocks, and when to process what it provides. Sleeping just wastes time, on the average half of the sleep interval, to no added benefit whatsoever, and some input streams always return zero for `available()`. NB The comment in the code about 'my now full buffer' is incorrect. – user207421 Oct 13 '14 at 02:20
  • There's nothing to discuss. You're just advocating doing what the operating system already does better. – user207421 Oct 13 '14 at 04:42
  • @EJP, if you can please send me your contact information to the email posted on my profile, I would like to ask you/hire you for a consulting possibility. Thank you. – CoderRoller Dec 07 '14 at 19:58