Android 使用 CipherInputStream 和 ObjectInputStream 的随机 IOExceptions

Android random IOExceptions with CipherInputStream and ObjectInputStream

我正在使用由 ObjectOutputStream 包装的 CipherOutputStream 将数据(当然还有相应的输入流来读取它)结构写入我的 Android 应用程序中的磁盘。几乎 100% 的时间读取成功,但有时读取失败(根本没有写入错误),但有多种异常:

我尝试用 SealedObject 替换 CipherOuputStream,但异常保持不变。我还使用 ReadWriteLock 同步了对文件的访问。

这是正在连载的 classes 之一。每个文件只包含一个 class 对象的 ArrayList(因此只调用一次 writeObject())。

public class SdTransaction implements Serializable {

    public static final long serialVersionUID =-784295048753453223L;
    private int mAmount;
    private SdCurrency mCurrency;
    private Date mCreatedAt;
    private Source mSource;
    @Nullable
    private String mAccountId;
    private boolean mIsVirtual;

}

我的代码锁基本上是这样的(没有错误处理,没有同步):

OutputStream os = mContext.openFileOutput("test.bin", Context.MODE_PRIVATE);
os = new Base64OutputStream(os, Base64.NO_PADDING);
os = new CipherOutputStream(os, mCipher);
ObjectOutputStream oos = new ObjectOutputStream(os);

oos.writeObject(mSerializable);
oos.flush();
oos.close();


InputStream is = mContext.openFileInput("test.bin", Context.MODE_PRIVATE);
is = new Base64InputStream(is, Base64.NO_PADDING);
is = new CipherInputStream(is, mCipher);
ObjectInputStream ois = new ObjectInputStream(is);

Object o = ois.readObject();
ois.close();

以下是一些堆栈跟踪:

java.io.StreamCorruptedException
    at java.io.ObjectInputStream.readStreamHeader (ObjectInputStream.java:2068)
    at java.io.ObjectInputStream.<init> (ObjectInputStream.java:371)
    at com.xyz.model.pin.SdPinRepositoryPersister.load (SdPinRepositoryPersister.java:72)
    at com.xyz.dagger.SdComponentHolder$Initializer.init (SdComponentHolder.java:293)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:109)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:120)
    at com.xyz.model.smoove.execution.SdSmooveExecutorService.onDataSetChanged0 (SdSmooveExecutorService.java:106)
    at com.xyz.model.smoove.execution.SdSmooveExecutorService.access[=13=]0 (SdSmooveExecutorService.java:35)
    at com.xyz.model.smoove.execution.SdSmooveExecutorService.run (SdSmooveExecutorService.java:98)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:422)
    at java.util.concurrent.FutureTask.run (FutureTask.java:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1112)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:587)
    at java.lang.Thread.run (Thread.java:818)

java.io.StreamCorruptedException: Wrong format: d2
    at java.io.ObjectInputStream.corruptStream (ObjectInputStream.java:675)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:788)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.io.ObjectInputStream.readFieldValues (ObjectInputStream.java:1113)
    at java.io.ObjectInputStream.defaultReadObject (ObjectInputStream.java:454)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1345)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.util.ArrayList.readObject (ArrayList.java:661)
    at java.lang.reflect.Method.invoke (Native Method)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1330)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at com.xyz.model.persistance.SdDiskPersister.load (SdDiskPersister.java:80)
    at com.xyz.model.persistance.SdPersisterComposite.load (SdPersisterComposite.java:67)
    at com.xyz.dagger.SdComponentHolder$Initializer.init (SdComponentHolder.java:390)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:109)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:120)
    at com.xyz.model.data.SdDataService.onDataSetChanged0 (SdSmooveExecutorService.java:106)
    at com.xyz.model.data.SdDataService.access[=13=]0 (SdSmooveExecutorService.java:35)
    at com.xyz.model.data.SdDataService.run (SdSmooveExecutorService.java:98)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:423)
    at java.util.concurrent.FutureTask.run (FutureTask.java:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:588)
    at java.lang.Thread.run (Thread.java:818)

java.io.UTFDataFormatException: bad byte at 2
    at java.nio.charset.ModifiedUtf8.decode (ModifiedUtf8.java:60)
    at java.io.DataInputStream.decodeUTF (DataInputStream.java:179)
    at java.io.DataInputStream.decodeUTF (DataInputStream.java:173)
    at java.io.DataInputStream.readUTF (DataInputStream.java:169)
    at java.io.ObjectInputStream.readClassDescriptor (ObjectInputStream.java:1704)
    at java.io.ObjectInputStream.readNewClassDesc (ObjectInputStream.java:1634)
    at java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:657)
    at java.io.ObjectInputStream.readNewClassDesc (ObjectInputStream.java:1663)
    at java.io.ObjectInputStream.readClassDesc (ObjectInputStream.java:657)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1782)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.util.ArrayList.readObject (ArrayList.java:661)
    at java.lang.reflect.Method.invoke (Native Method)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1330)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.io.ObjectInputStream.readFieldValues (ObjectInputStream.java:1113)
    at java.io.ObjectInputStream.defaultReadObject (ObjectInputStream.java:454)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1345)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.io.ObjectInputStream.readFieldValues (ObjectInputStream.java:1113)
    at java.io.ObjectInputStream.defaultReadObject (ObjectInputStream.java:454)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1345)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.util.ArrayList.readObject (ArrayList.java:661)
    at java.lang.reflect.Method.invoke (Native Method)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1330)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.io.ObjectInputStream.readFieldValues (ObjectInputStream.java:1113)
    at java.io.ObjectInputStream.defaultReadObject (ObjectInputStream.java:454)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1345)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at java.util.ArrayList.readObject (ArrayList.java:661)
    at java.lang.reflect.Method.invoke (Native Method)
    at java.io.ObjectInputStream.readObjectForClass (ObjectInputStream.java:1330)
    at java.io.ObjectInputStream.readHierarchy (ObjectInputStream.java:1242)
    at java.io.ObjectInputStream.readNewObject (ObjectInputStream.java:1835)
    at java.io.ObjectInputStream.readNonPrimitiveContent (ObjectInputStream.java:761)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1983)
    at java.io.ObjectInputStream.readObject (ObjectInputStream.java:1940)
    at com.xyz.model.persistance.SdDiskPersister.load (SdDiskPersister.java:80)
    at com.xyz.model.persistance.SdPersisterComposite.load (SdPersisterComposite.java:67)
    at com.xyz.dagger.SdComponentHolder$Initializer.init (SdComponentHolder.java:385)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:109)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:120)
    at com.xyz.model.smoove.execution.SdService.onDataSetChanged0 (SdSmooveExecutorService.java:106)
    at com.xyz.model.smoove.execution.SdService.access[=13=]0 (SdSmooveExecutorService.java:35)
    at com.xyz.model.smoove.execution.SdService.run (SdSmooveExecutorService.java:98)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:423)
    at java.util.concurrent.FutureTask.run (FutureTask.java:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:588)
    at java.lang.Thread.run (Thread.java:818)

android.util.Base64DataException: bad base-64
    at android.util.Base64InputStream.refill (Base64InputStream.java:148)
    at android.util.Base64InputStream.read (Base64InputStream.java:121)
    at java.io.InputStream.read (InputStream.java:162)
    at javax.crypto.CipherInputStream.fillBuffer (CipherInputStream.java:99)
    at javax.crypto.CipherInputStream.read (CipherInputStream.java:155)
    at com.xyz.model.streams.sync.SdSupervisedInputStream.read (SdSupervisedInputStream.java:80)
    at libcore.io.Streams.readFully (Streams.java:81)
    at java.io.DataInputStream.readShort (DataInputStream.java:152)
    at java.io.ObjectInputStream.readStreamHeader (ObjectInputStream.java:2061)
    at java.io.ObjectInputStream.<init> (ObjectInputStream.java:371)
    at com.xyz.model.persistance.SdDiskPersister.load (SdDiskPersister.java:77)
    at com.xyz.model.persistance.SdPersisterComposite.load (SdPersisterComposite.java:67)
    at com.xyz.dagger.SdComponentHolder$Initializer.init (SdComponentHolder.java:389)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:109)
    at com.xyz.dagger.SdComponentHolder.init (SdComponentHolder.java:120)
    at com.xyz.model.smoove.execution.SdService.onDataSetChanged0 (SdSmooveExecutorService.java:106)
    at com.xyz.model.smoove.execution.SdService.access[=13=]0 (SdSmooveExecutorService.java:35)
    at com.xyz.model.smoove.execution.SdService.run (SdSmooveExecutorService.java:98)
    at java.util.concurrent.Executors$RunnableAdapter.call (Executors.java:423)
    at java.util.concurrent.FutureTask.run (FutureTask.java:237)
    at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:588)
    at java.lang.Thread.run (Thread.java:818)

有没有人经历过类似的事情或者可以给我一些如何定位错误的提示?只有 API 21 及更高版本的设备似乎会出现这些错误...

我想出了一个解决方案,但我仍然对这个问题一无所知。当用对象的 BufferedReader/BufferedWrite 和 JSON 序列化替换 ObjectOutputStream/ObjectInputStream 时,一切正常。在服务中使用 ObjectInputStream 时似乎发生了一些事情...