Java ObjectInputStream.readObject中的IllegalArgumentException
我有一个android应用程序,它表现出奇怪的行为。 我有一个从文件中反序列化对象的方法。下面是我正在使用的代码:Java ObjectInputStream.readObject中的IllegalArgumentException,java,android,serialization,illegalargumentexception,objectinputstream,Java,Android,Serialization,Illegalargumentexception,Objectinputstream,我有一个android应用程序,它表现出奇怪的行为。 我有一个从文件中反序列化对象的方法。下面是我正在使用的代码: public static Object readData(Context context, String fileName) { synchronized (context) { ObjectInputStream input = null; Object object = null;
public static Object readData(Context context, String fileName)
{
synchronized (context) {
ObjectInputStream input = null;
Object object = null;
if (fileExists(context, fileName)) {
try {
input = new ObjectInputStream(context.openFileInput(fileName));
object = input.readObject();
Log.v(Constant.TAG, "Writable object has been loaded from file "+fileName);
} catch (IOException e) {
Log.e(Constant.TAG, e.getMessage(), e);
} catch (ClassNotFoundException e) {
Log.e(Constant.TAG, e.getMessage(), e);
} finally {
try {
if (input != null)
input.close();
} catch (IOException e) {
Log.e(Constant.TAG, e.getMessage(), e);
}
}
}
return object;
}
}
正常情况下,它工作得很好,但当有人最小化我的应用程序,并在某个时候重新打开后,它崩溃了。从崩溃报告中,我发现它在上面代码的下面一行抛出了IllegalArgumentException
object = input.readObject();
我浏览了ObjectInputStream.readObject
的文档,但它没有说明在什么情况下可以抛出IllegalArgumentException
只有当用户从后台带来应用程序时,才会发生这种情况。当应用程序启动时,它工作得非常好(我所说的启动是指应用程序未运行时,甚至在后台也不运行)
PS:有些崩溃报告在同一行上显示了ClassCastException
,这更奇怪,因为我不是在强制转换,只是在读取对象
更新
堆栈跟踪
java.lang.RuntimeException:
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2423)
at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:2483)
at android.app.ActivityThread.access$900 (ActivityThread.java:153)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1349)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:148)
at android.app.ActivityThread.main (ActivityThread.java:5441)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:628)
Caused by: java.lang.IllegalArgumentException:
at java.lang.reflect.Field.set (Native Method)
at java.io.ObjectInputStream.readFieldValues (ObjectInputStream.java:1127)
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 com.pixyfisocial.pixyfi.util.IOUtil.readData (IOUtil.java:245)
at com.pixyfisocial.Login.getLoggedInUserInfoFromCache (Login.java:313)
at com.pixyfisocial.Login.startApp (Login.java:124)
at com.pixyfisocial.Login.onCreate (Login.java:98)
at android.app.Activity.performCreate (Activity.java:6303)
at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1108)
at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:2376)
粗略检查源代码表明,当序列化对象表示形式与读取类所期望的不匹配时,
IllegalArgumentException
通常会在ObjectInputStream
中抛出。例如,您可能有不兼容的自定义readObject
和writeObject
方法。或者,您可能对对象表示进行了二进制不兼容的更改,而没有更改硬编码的serialVersionId
数字或实现自定义方法来处理此问题2
在你的线索里会有更多线索。。。在ObjectInputStream
的源代码中
ClassCastException
s可能是这种情况的另一种表现形式
1-导致语义不兼容的变化是另一回事。它们不会导致
非法argumentexception
,但无论如何你都应该做些什么
2-如果您想处理不兼容问题,那么您可能不想更改SerialVerionId
。如果您这样做了,那么您将需要在类加载和类的多个版本方面做“聪明的事情”。但另一方面,如果您的代码需要处理具有相同serialVersionUID
的多个表示,则表示版本必须可以从表示本身推断出来。这需要计划
后续行动 我获取了你的stacktrace,并试图将其与Android的源代码进行匹配 行号不完全匹配,但我认为问题发生在下面的方法中。特别是我标记为“此处”的行。根据Field.set的javadoc:
/**
* Reads a collection of field values for the class descriptor
* {@code classDesc} (an {@code ObjectStreamClass}). The
* values will be used to set instance fields in object {@code obj}.
* This is the default mechanism, when emulated fields (an
* {@code GetField}) are not used. Actual values to load are stored
* directly into the object {@code obj}.
*
* @param obj
* Instance in which the fields will be set.
* @param classDesc
* A class descriptor (an {@code ObjectStreamClass})
* defining which fields should be loaded.
*
* @throws IOException
* If an IO exception happened when reading the field values.
* @throws InvalidClassException
* If an incompatible type is being assigned to an emulated
* field.
* @throws OptionalDataException
* If optional data could not be found when reading the
* exception graph
* @throws ClassNotFoundException
* If a class of an object being de-serialized can not be found
*
* @see #readFields
* @see #readObject()
*/
private void readFieldValues(Object obj, ObjectStreamClass classDesc) throws OptionalDataException, ClassNotFoundException, IOException {
// Now we must read all fields and assign them to the receiver
ObjectStreamField[] fields = classDesc.getLoadFields();
fields = (fields == null) ? ObjectStreamClass.NO_FIELDS : fields;
Class<?> declaringClass = classDesc.forClass();
if (declaringClass == null && mustResolve) {
throw new ClassNotFoundException(classDesc.getName());
}
for (ObjectStreamField fieldDesc : fields) {
Field field = classDesc.getReflectionField(fieldDesc);
if (field != null && Modifier.isTransient(field.getModifiers())) {
field = null; // No setting transient fields! (http://b/4471249)
}
// We may not have been able to find the field, or it may be transient, but we still
// need to read the value and do the other checking...
try {
Class<?> type = fieldDesc.getTypeInternal();
if (type == byte.class) {
byte b = input.readByte();
if (field != null) {
field.setByte(obj, b);
}
} else if (type == char.class) {
char c = input.readChar();
if (field != null) {
field.setChar(obj, c);
}
} else if (type == double.class) {
double d = input.readDouble();
if (field != null) {
field.setDouble(obj, d);
}
} else if (type == float.class) {
float f = input.readFloat();
if (field != null) {
field.setFloat(obj, f);
}
} else if (type == int.class) {
int i = input.readInt();
if (field != null) {
field.setInt(obj, i);
}
} else if (type == long.class) {
long j = input.readLong();
if (field != null) {
field.setLong(obj, j);
}
} else if (type == short.class) {
short s = input.readShort();
if (field != null) {
field.setShort(obj, s);
}
} else if (type == boolean.class) {
boolean z = input.readBoolean();
if (field != null) {
field.setBoolean(obj, z);
}
} else {
Object toSet = fieldDesc.isUnshared() ? readUnshared() : readObject();
if (toSet != null) {
// Get the field type from the local field rather than
// from the stream's supplied data. That's the field
// we'll be setting, so that's the one that needs to be
// validated.
String fieldName = fieldDesc.getName();
ObjectStreamField localFieldDesc = classDesc.getField(fieldName);
Class<?> fieldType = localFieldDesc.getTypeInternal();
Class<?> valueType = toSet.getClass();
if (!fieldType.isAssignableFrom(valueType)) {
throw new ClassCastException(classDesc.getName() + "." + fieldName + " - " + fieldType + " not compatible with " + valueType);
}
if (field != null) {
field.set(obj, toSet); // <<< --- HERE
}
}
}
} catch (IllegalAccessException iae) {
// ObjectStreamField should have called setAccessible(true).
throw new AssertionError(iae);
} catch (NoSuchFieldError ignored) {
}
}
}
/**
*读取类描述符的字段值集合
*{@code classDesc}(一个{@code ObjectStreamClass})。这个
*值将用于设置对象{@code obj}中的实例字段。
*当模拟字段时,这是默认机制
*{@code GetField})未使用。将存储要加载的实际值
*直接进入对象{@code obj}。
*
*@param obj
*将在其中设置字段的实例。
*@param classDesc
*类描述符(一个{@code ObjectStreamClass})
*定义应加载哪些字段。
*
*@抛出异常
*如果读取字段值时发生IO异常。
*@InvalidClassException
*如果将不兼容的类型分配给模拟
*场。
*@抛出OptionalDataException
*如果在读取时找不到可选数据
*异常图
*@ClassNotFoundException
*如果找不到正在反序列化的对象的类
*
*@see#readFields
*@see#readObject()
*/
私有void readFieldValues(objectobj,ObjectStreamClass classDesc)抛出OptionalDataException,ClassNotFoundException,IOException{
//现在我们必须读取所有字段并将它们分配给接收器
ObjectStreamField[]fields=classDesc.getLoadFields();
字段=(字段==null)?ObjectStreamClass.NO_字段:字段;
Class declaringClass=classDesc.forClass();
if(declaringClass==null&&mustResolve){
抛出新的ClassNotFoundException(classDesc.getName());
}
用于(ObjectStreamField fieldDesc:fields){
Field Field=classDesc.getReflectionField(fieldDesc);
if(field!=null&&Modifier.isTransient(field.getModifiers())){
field=null;//没有设置临时字段(http://b/4471249)
}
//我们可能无法找到磁场,或者它可能是暂时的,但我们仍然能够找到
//需要读取值并执行以下操作