问题描述
如标题,这个问题网上有很人遇到,场景是跨进程启动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.<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有问题!!!
}
@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对象中使用HashMap时,反序列化的时候,设置的类加载器必须是应用类加载器(dalvik.system.PathClassLoader),不要使用
- Android Studio的Parcel自动代码生成,对于HashMap的读取/反系统化默认是设置了启动器加载器(HashMap.class.getClassLoader()),需要手机改成应用类加载器(自定义类.class.getClassLoader)
- Android中BootClassLoader是用于加载framework中的类,PathClassLoader是用于加载apk的dex中的类,即我们自己定义的类
- 在Parcel中使用HashMap,且HashMap存放类是没有问题的,注意Loader的配置就行了