ObjectOutputStream.reset()导致StreamCorruptedException

时间:2016-02-14 14:44:05

标签: java android stream

我有一个包含对文件uri的引用的类。这个文件应该通过网络传输,可以很大(例如视频),我想在实例(反)序列化过程中透明地执行:

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

另一方面,实例由等效代码读取:

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

reset()调用就在那里,因为没有它,缓存数据的数量对于糟糕的Android设备来说太多了,整个应用程序在OOM异常时崩溃。 SO上有几篇帖子推荐这样的修复。

问题在于,如果我将几个NetworkAttachment实例放入一个数组并尝试通过网络进行序列化,那么它并不适用于所有情况。

如果两个Android设备正在将序列化数据相互流式传输,没有任何问题,我可以传输10个MB,一切都很好。

然而,当一个Android设备将文件上传到Java服务器应用程序(JDK 7)时,我得到了:

  

java.io.StreamCorruptedException:意外重置;递归深度:2     at java.io.ObjectInputStream.handleReset(ObjectInputStream.java:2028)     at java.io.ObjectInputStream.access $ 600(ObjectInputStream.java:206)     在   java.io.ObjectInputStream中的$ BlockDataInputStream.readBlockHeader(ObjectInputStream.java:2510)     在   java.io.ObjectInputStream中的$ BlockDataInputStream.refill(ObjectInputStream.java:2550)     在   java.io.ObjectInputStream中的$ BlockDataInputStream.read(ObjectInputStream.java:2709)     在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)     在   sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     在java.lang.reflect.Method.invoke(Method.java:606)at   java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)     在   java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1897)     在   java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)     at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)     在java.util.ArrayList.readObject(ArrayList.java:771)at   sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at   sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)     在   sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     在java.lang.reflect.Method.invoke(Method.java:606)at   java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)     在   java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1897)     在   java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)     at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)     在com.croconaut.CptServer $ 2 $ 1.run(CptServer.java:148)

如果我删除了reset()调用,它会自然地工作。但是,我迫切需要reset()调用以降低内存消耗,那么我的选择是什么?

2 个答案:

答案 0 :(得分:0)

您不应在reset()方法中调用writeObject()。在调用代码中将其调用到外部。无论如何它都不会产生太大影响,在写循环中调用它是没有意义的。

答案 1 :(得分:-1)

最后,我以不同的方式实现了它(不使用ObjectOutputStream),但问题在于JDK不兼容 - 运行JDK 6(由Google运行)的Android端和运行JDK 7(由Oracle运行)的服务器。也许流代码中存在一些代码更改,并且没有保留reset()兼容性。