I have a class which contains a reference to file uri. This file is supposed to be transferred over the network, can be big (a video, for instance) and I'd like to do it transparently during instance (de)serialization:
public class NetworkAttachment implements Serializable {
private static final long serialVersionUID = 1L;
private final String name; // file name
private final long length; // file length
private final long lastModified; // last modification time
private final String type; // mime type
private transient final Context context;
private transient final Uri uri;
[...]
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // write all non-transient fields
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r");
if (parcelFileDescriptor != null) {
FileInputStream fis = new FileInputStream(parcelFileDescriptor.getFileDescriptor());
byte[] buffer = new byte[1024 * 1024]; // 1 MB temp buffer
int len;
while ((len = fis.read(buffer)) != -1) {
oos.write(buffer, 0, len);
// reset the cache else OOM exception
oos.reset();
}
}
}
}
On the other side the instance is read by equivalent code:
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject(); // read all non-transient fields
File file = new File(name);
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[1024 * 1024]; // 1 MB temp buffer
int len;
while ((len = ois.read(buffer)) != -1) { // <--- StreamCorruptedException
fos.write(buffer, 0, len);
}
}
The reset() call is there because without it the amount of cached data is way too much for the poor Android device and the whole app crashes on an OOM exception. There are several posts here on SO which recommend a fix like this.
The problem is that if I put several instances of NetworkAttachment into an array and try to serialize it over the network, it doesn't work in all cases.
If two Android devices are streaming the serialized data to each other, no problem whatsoever, I can transfer 10s of MB and everything is fine.
However when one Android device is uploading the file to a Java server app (JDK 7), I'm getting:
java.io.StreamCorruptedException: unexpected reset; recursion depth: 2 at java.io.ObjectInputStream.handleReset(ObjectInputStream.java:2028) at java.io.ObjectInputStream.access$600(ObjectInputStream.java:206) at java.io.ObjectInputStream$BlockDataInputStream.readBlockHeader(ObjectInputStream.java:2510) at java.io.ObjectInputStream$BlockDataInputStream.refill(ObjectInputStream.java:2550) at java.io.ObjectInputStream$BlockDataInputStream.read(ObjectInputStream.java:2709) at java.io.ObjectInputStream.read(ObjectInputStream.java:865) at java.io.InputStream.read(InputStream.java:101) at com.croconaut.network.NetworkAttachment.readObject(NetworkAttachment.java:186) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1897) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) at java.util.ArrayList.readObject(ArrayList.java:771) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058) at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1897) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370) at com.croconaut.CptServer$2$1.run(CptServer.java:148)
If I remove the reset() call, it works (naturally). However, I desperately need the reset() call to keep the memory consumption down, so what are my options here?