使用自定义可打包类解组 Android 应用程序中的错误

Posted

技术标签:

【中文标题】使用自定义可打包类解组 Android 应用程序中的错误【英文标题】:Unmarshalling errors in Android app with custom parcelable classes 【发布时间】:2012-12-09 11:18:33 【问题描述】:

对于我的 android 应用程序,我遇到了几个解组错误,尽管我认为我已经完成了通过 Parcelables 正确保存和加载对象所需的一切。你能告诉我我的代码有什么问题吗?

错误 1:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@41279860: Unmarshalling unknown type code 6619241 at offset 1372
at android.os.Parcel.readValue(Parcel.java:1922)
at android.os.Parcel.readMapInternal(Parcel.java:2094)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getParcelable(Bundle.java:1158)
at android.app.Activity.onCreate(Activity.java:860)
at my.app.package.PlayComputer.onCreate(PlayComputer.java:1012)
at android.app.Activity.performCreate(Activity.java:4465)

MyActivity 中的第 1012 行是对ActivityonCreate() 中的super.onCreate(savedInstanceState); 的调用。

protected void onSaveInstanceState(Bundle savedInstanceState) 
    if (myObject == null) 
        savedInstanceState.putParcelable("myObject", null);
    
    else 
        savedInstanceState.putParcelable("myObject", myObject);
    
    savedInstanceState.putInt(...);
    savedInstanceState.putString(...);
    savedInstanceState.putBoolean(...);
    super.onSaveInstanceState(savedInstanceState);

myObject 属于 MyObject 类,具有以下方法:

public void writeToParcel(Parcel out, int flags) 
    out.writeIntArray(...);
    out.writeInt(...);
    out.writeStringArray(...);
    out.writeString(...);
    out.writeParcelableArray(..., flags);


public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() 
    public MyObject createFromParcel(Parcel in) 
        try 
            if (in == null) 
                return null;
            
            else 
                return new MyObject(in);
            
        
        catch (Exception e) 
            return null;
        
    
    public MyObject[] newArray(int size) 
        return new MyObject[size];
    
;

private MyObject(Parcel in) 
    in.readIntArray(...);
    ... = in.readInt();
    in.readStringArray(...);
    ... = in.readString();
    ... = (OtherObject[]) in.readParcelableArray(OtherObject.class.getClassLoader());

错误 2:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling:
at android.os.Parcel.readParcelable(Parcel.java:1971)
at android.os.Parcel.readValue(Parcel.java:1859)
at android.os.Parcel.readMapInternal(Parcel.java:2099)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getParcelable(Bundle.java:1158)
at android.app.Activity.onCreate(Activity.java:905)
at my.app.package.PlayComputer.onCreate(SourceFile:1012)

相同的文件和类。

错误 3:

java.lang.RuntimeException: Unable to start activity ComponentInfo
Caused by: java.lang.RuntimeException: Parcel android.os.Parcel@4051aff8: Unmarshalling unknown type code 7340149 at offset 1276
at android.os.Parcel.readValue(Parcel.java:1913)
at android.os.Parcel.readMapInternal(Parcel.java:2083)
at android.os.Bundle.unparcel(Bundle.java:208)
at android.os.Bundle.getParcelable(Bundle.java:1100)
at my.app.package.PlayComputer.onCreate(SourceFile:1111)

这一次,导致的行(1111)是以下一行:

myObject = (MyObject) savedInstanceState.getParcelable("myObject");

【问题讨论】:

GameState(Parcel in) 构造函数是不是拼写错误,而您要编写 MyObject(Parcel in) 构造函数? 您是否在构造函数中单独创建数组对象? readXXXArray() 方法需要传入一个完全初始化的数组实例。您可以使用 createXXXArray() 方法将该数组的新实例返回给您。 当然,这些数组之前已经声明过,但还没有必要初始化。从文档中,我看不出这是一个先决条件:developer.android.com/reference/android/os/Parcel.html 嗯,ParcelParcelable 的文档很差,所以我看不出 readXXXArray()writeXXXArray 的区别。 那么writeTypedArray(MyObject.CREATOR)也是保存Parcelables的推荐方法吗?正如你在上面看到的,我使用了writeParcelableArray() 【参考方案1】:

在从 bundle 读取之前,设置 ClassLoader:

bundle.setClassLoader(getClass().getClassLoader());

这节省了我的时间!

【讨论】:

【参考方案2】:

Android 有两种不同的类加载器:框架类加载器(知道如何加载 Android 类)和 APK 类加载器(知道如何加载代码)。 APK 类加载器将框架类加载器设置为其父级,这意味着它也可以加载 Android 类。

错误 #2 可能是由使用框架类加载器的 Bundle 引起的,因此它不知道您的类。我认为当 Android 需要保留您的 Bundle 并稍后恢复它时(例如在后台内存不足时),可能会发生这种情况。您可以通过在捆绑包上设置 APK 类加载器来解决此问题:

savedInstanceState.setClassLoader(getClass().getClassLoader());

错误 #1 和 #3 更神秘,您是否在 writeToParcel() 中写入空值?恐怕Android不太喜欢这样。

【讨论】:

谢谢! savedInstanceState.setClassLoader(getClass().getClassLoader()); 看起来不错,但恐怕我只能将其称为一节课。如果我必须将多个类中的对象放入Bundle 怎么办?什么类调用它? 此外,经过一些更改,似乎问题现在已经解决了:***.com/questions/13997550/… 你能想象这种读取对象的方式是导致问题的原因吗? 这将适用于您的所有类,因为 getClass().getClassLoader() 将检索 APK 类加载器(它可以加载您的所有类)。使用 readXXXArray() 读取时,传入了哪些数组?你怎么知道它们的长度是正确的? createTypedArray() 和其他方法不会写入/读取类型信息,这意味着类加载器并没有真正参与。 这里的文档很糟糕,我查看了源代码。是的, readXXXArray() 仅在您有固定大小的数组时才有用。我认为 writeTypedArray() 更干净一些,是的,我不确定他们对这些不同调用的意图是什么。您应该只需要在恢复时设置它,保存时您已经加载了类,因此它们的类加载器是已知的。 我在片段的onCreate 中尝试了savedInstanceState.setClassLoader(getClass().getClassLoader());,但这并没有改变任何东西。你有什么想法为什么这不起作用?【参考方案3】:

看起来,createFromParcelnewArray 应该像这样被覆盖:

public static final Parcelable.Creator<MyObject> CREATOR = new Parcelable.Creator<MyObject>() 
    @Override
    public MyObject createFromParcel(Parcel in) 
        MyObject myObj = new MyObject();
        myObj.intArray = in.readIntArray(...);
        myObj.intValue = in.readInt(...);
        // ....
        // IN THE SAME ORDER THAT IS WRITTEN OUT AS PER writeToParcel!
        //
        return myObj;
    
    @Override
    public MyObject[] newArray(int size) 
        return new MyObject[size];
    
;

编辑:

我忘了说要让上面的方法起作用,应该有一个空的构造函数!

public MyObject()

【讨论】:

谢谢!这不正是我正在做的吗?我只是使用构造函数MyObject(in) 使事情更清楚。哦,好吧,也许你没有看到这个,因为我在构造函数的名字上打错了,现在已经更正了。 我唯一能想到的,也许应该有一个空的构造函数,已经修改了答案... 请验证你是@Override这两个方法describeContents()writeToParcel():) 我已经检查过我是否覆盖了这些方法 - 是的,我这样做了。难道您没有看到您的解决方案和我的非真正有效的解决方案完全相同吗?我刚刚将一些代码放入名为 GameState(Parcel in) 的辅助方法中 - 就像在文档中一样。 这就是我在项目中使用它的方式,并且对 Parcelable 没有任何问题 - 如果我的回答对您没有太大帮助,请见谅。

以上是关于使用自定义可打包类解组 Android 应用程序中的错误的主要内容,如果未能解决你的问题,请参考以下文章

Android studio 自定义打包APK名称

Android studio 自定义打包apk名

在 Android 中将 XML 文件解组为 Java 对象?

Android Gradle 插件将自定义 Gradle 插件上传到自建 Maven 仓库 ① ( Maven 仓库上传源码上传源码设置 | 自定义源码打包任务 | 自定义文档打包任务 )

android打包生成apk时自定义文件名版本号。自定义项目字段等等

pom文件配置打包外部/自定义jar文件在fat spring boot可执行jar文件中