Parcel: Class not found when unmarshalling: 自定义类

问题描述

如标题,这个问题网上有很人遇到,场景是跨进程启动activity或service通传Intent传Parcel数据时遇到,网上总结是类加载器设置不对的原因。我遇到的问题场景是在当前进程中,启动新的activity解析传过来的Parcel子类时出现的异常,异常backtrace如下:

 Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.myapplication.UserBean
        at android.os.Parcel.readParcelableCreator(Parcel.java:2554)
        at android.os.Parcel.readParcelable(Parcel.java:2480)
        at android.os.Parcel.readValue(Parcel.java:2383)
        at android.os.Parcel.readMapInternal(Parcel.java:2733)
        at android.os.Parcel.readHashMap(Parcel.java:1871)
        at com.example.myapplication.TestBean.(TestBean.java:19)
        at com.example.myapplication.TestBean$1.createFromParcel(TestBean.java:36)
        at com.example.myapplication.TestBean$1.createFromParcel(TestBean.java:33)
        at android.os.Parcel.readParcelable(Parcel.java:2489)
        at android.os.Parcel.readValue(Parcel.java:2383)
        at android.os.Parcel.readArrayMapInternal(Parcel.java:2750)
        at android.os.BaseBundle.unparcel(BaseBundle.java:269)
        at android.os.Bundle.getParcelable(Bundle.java:864)
        at android.content.Intent.getParcelableExtra(Intent.java:6464)
        at com.example.myapplication.TestActivity.onStart(TestActivity.java:25)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1249)
        at android.app.Activity.performStart(Activity.java:6830)

问题分析

顺藤摸瓜,使用when unmarshalling关键字去看下Parcel.java的源码
Parcel: Class not found when unmarshalling: 自定义类_第1张图片
然后,设置一下断点,去看下loader是什么,以及什么时候传进入来的(tips:这里时候要用方法断点,源码行号断点是断不下的呀)
Parcel: Class not found when unmarshalling: 自定义类_第2张图片
问题的代码是:

public class TestBean implements Parcelable {
    private String tag;
    private HashMap mHashMap;

    public TestBean(String tag) {
        this.tag = tag;
        mHashMap = new HashMap<>();
    }

    protected TestBean(Parcel in) {
        tag = in.readString();
        mHashMap = in.readHashMap(HashMap.class.getClassLoader());//这里传入了Loader有问题!!!
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(tag);
        dest.writeMap(mHashMap);
    }

    @Override
    public int describeContents() {
        return 0;
    }

解决方案

手动更改类加载器为PathClassLoader

		mHashMap = in.readHashMap(HashMap.class.getClassLoader())
		# 修改如下
        mHashMap = in.readHashMap(TestBean.class.getClassLoader());

我们可以调试看下TestBean.clas.getClassLoader是什么Loader,如下图所示:
Parcel: Class not found when unmarshalling: 自定义类_第3张图片

经验总结

  • 在Parcel对象中使用HashMap时,反序列化的时候,设置的类加载器必须是应用类加载器(dalvik.system.PathClassLoader),不要使用
  • Android Studio的Parcel自动代码生成,对于HashMap的读取/反系统化默认是设置了启动器加载器(HashMap.class.getClassLoader()),需要手机改成应用类加载器(自定义类.class.getClassLoader)
  • Android中BootClassLoader是用于加载framework中的类,PathClassLoader是用于加载apk的dex中的类,即我们自己定义的类
  • 在Parcel中使用HashMap,且HashMap存放类是没有问题的,注意Loader的配置就行了

参考文档

  • 类加载器CLASSLOADER的工作机制
  • 对Android类加载器最全面的分析
  • ClassNotFoundException when unmarshalling 问题总结

你可能感兴趣的:(Android开发之旅,ANDROID,unmarshalling,android)