Search code examples
javaandroiddeserializationobjectinputstreameofexception

Java ObjectInputStream throws EOFException with bigger object


I have this method to deserialize:

public static Object deserialize(byte[] data) throws IOException, ClassNotFoundException {
    ByteArrayInputStream in = new ByteArrayInputStream(data);
    ObjectInputStream is = new ObjectInputStream(in);
    Object res = is.readObject();
    is.close();
    in.close();
    return res;
}

and this one to serialize:

public static byte[] serialize(Object obj) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(out);
    os.writeObject(obj);
    byte[] res = out.toByteArray();
    out.close();
    os.close();
    return res;
}

I use these methods to serialize and deserialize a class object, that only has a string and an arrayList of another class, exchanged between 2 devices. Both the class of the object and the class of the arrayList implement serializable.

When I send an object with up to 3 elements in the arrayList these methods work perfectly. However, when the arrayList has 4 or more elements, the device receiving the object still detects that some data has "arrived" but the deserialize method generates an "EOFException" in the "Object res = is.readObject();" line.

Any ideas about what the problem could be ?

EDIT

This is the class of the arrayList:

import java.io.Serializable;

public class Info implements Serializable {

    public Info() {
        ...
    }

    ...
}

This is the class of the object:

import java.io.Serializable;
import java.util.ArrayList;

public class BluetoothDataContainer implements Serializable{

    private ArrayList<Info> dataList;
    private String originDevice;

    public BluetoothDataContainer(String originDevice){
        dataList= new ArrayList<Info>();
        this.originDevice = originDevice;
    }

    ...
}

This is the code I use to send the object:

BluetoothDataContainer data = new BluetoothDataContainer(mBluetoothAdapter.getName());

...

// add needed info to variable 'data'

...

s.write(data);

Where 's' is a thread with the method 'write':

private BluetoothSocket mmSocket = bluetoothDevice.createRfcommSocketToServiceRecord(ID_CONNECTION);
private OutputStream mmOutStream = mmSocket.getOutputStream();

...

public void write(BluetoothDataContainer m) {
    try {
        mmOutStream.write(serialize(m));
    } catch (IOException e) {
        this.mContext.showToast("IOException caught in thread ConnectedThread [Bluetooth connection handler] - write() !");
    }
    //cancel();
    this.interrupt();
}

And this is how I handle the object when it is read:

private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                final BluetoothDataContainer data;
                try {
                    data = (BluetoothDataContainer) deserialize(readBuf);

                    ...

                } catch (IOException | ClassNotFoundException e) {
                    e.printStackTrace();
                }
                break;
            default:
                break;
        }
    }
};

And this is how I read the object:

private final Handler mHandler;   // value set in the constructor

...

public void run() {

    mmInStream = bluetoothSocket.getInputStream();

    byte[] buffer = new byte[1024];  // buffer store for the stream
    int bytes;

    try {
        // Read from the InputStream
        bytes = mmInStream.read(buffer);
        this.mContext.showToast("ConnectedThread [Bluetooth connection handler] data received !");

        // Send the obtained bytes to the UI activity
        mHandler.obtainMessage(1, bytes, -1, buffer).sendToTarget();

    } catch (IOException e) {
        this.mContext.showToast("IOException caught in thread ConnectedThread [Bluetooth connection handler] - run() !");
    }

}

Solution

  • Clearly you have not 'exchanged' the entire byte array in the case that fails.

    bytes = mmInStream.read(buffer);
    

    You can't possibly know from this alone whether you've read:

    • an entire message
    • less than one message
    • more than one message.

    As you already have a socket with input and output streams, it beats me why you are creating byte arrays at all. Just wrap the ObjectOutputStream around the socket output stream and use writeObject(). At the receiver, wrap the ObjectInputStream around the socket input stream and use readObject().

    NB you should use the same object streams for the life of the socket, and if you are sending objects both ways you must create the object output stream before the object input stream for the same socket: otherwise you can get a deadlock.