Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/178.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java ObjectInputStream.readObject中的IllegalArgumentException_Java_Android_Serialization_Illegalargumentexception_Objectinputstream - Fatal编程技术网

Java ObjectInputStream.readObject中的IllegalArgumentException

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;

我有一个android应用程序,它表现出奇怪的行为。 我有一个从文件中反序列化对象的方法。下面是我正在使用的代码:

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:

  • 如果指定的对象参数不是声明基础字段的类或接口的实例,则该方法将抛出IllegalArgumentException

  • 如果基础字段为基元类型,则尝试展开转换以将新值转换为基元类型的值。如果此尝试失败,该方法将抛出IllegalArgumentException

  • 如果在可能的展开之后,无法通过标识或加宽转换将新值转换为基础字段的类型,则该方法将抛出IllegalArgumentException

  • 这三件事中有一件正在发生。不可能说出哪一个。。。除非您提供一个完整的工作MCVE(有人可以在Android仿真器上运行!)。。。但这些迹象表明您(不知何故)违反了序列化兼容性规则

    注意,由于行号不匹配,我不能肯定地说您使用的Android与下面的匹配。如果您想确定,您需要在GIT repo中搜索历史记录以找到匹配的版本。。。。或者查看设备的特定于供应商的源代码包/repo

    /**
     * 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)
    }
    //我们可能无法找到磁场,或者它可能是暂时的,但我们仍然能够找到
    //需要读取值并执行以下操作