问题描述
如标题,这个问题网上有很人遇到,场景是跨进程启动activity或service通传Intent传Parcel数据时遇到,网上总结是类加载器设置不对的原因。我遇到的问题场景是在当前进程中,启动新的activity解析传过来的Parcel子类时出现的异常,异常backtrace如下:
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.myapplication.UserBeanat 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.<init>(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的源码
然后,设置一下断点,去看下loader是什么,以及什么时候传进入来的(tips:这里时候要用方法断点,源码行号断点是断不下的呀)
问题的代码是:
public class TestBean implements Parcelable {private String tag;private HashMap<String, Object> 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有问题!!!}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(tag);dest.writeMap(mHashMap);}@Overridepublic int describeContents() {return 0;}
解决方案
手动更改类加载器为PathClassLoader
mHashMap = in.readHashMap(HashMap.class.getClassLoader())# 修改如下mHashMap = in.readHashMap(TestBean.class.getClassLoader());
我们可以调试看下TestBean.clas.getClassLoader是什么Loader,如下图所示:
经验总结
- 在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 问题总结