项目中使用插件框架,当插件在Intent中传递Serializable对象时,在android 5.0以下系统上会出现
E/InstrumentationHacker(25176): Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.qihoo.browser.imageplugin.bean.HomeListBean)
E/InstrumentationHacker(25176): java.lang.RuntimeException: Parcelable encounteredClassNotFoundException reading a Serializable object (name = com.qihoo.browser.imageplugin.bean.HomeListBean)
E/InstrumentationHacker(25176): at android.os.Parcel.readSerializable(Parcel.java:2251)
E/InstrumentationHacker(25176): at android.os.Parcel.readValue(Parcel.java:2096)
E/InstrumentationHacker(25176): at android.os.Parcel.readListInternal(Parcel.java:2375)
E/InstrumentationHacker(25176): at android.os.Parcel.readArrayList(Parcel.java:1735)
E/InstrumentationHacker(25176): at android.os.Parcel.readValue(Parcel.java:2066)
E/InstrumentationHacker(25176): at android.os.Parcel.readArrayMapInternal(Parcel.java:2346)
E/InstrumentationHacker(25176): at android.os.Bundle.unparcel(Bundle.java:249)
E/InstrumentationHacker(25176): at android.os.Bundle.keySet(Bundle.java:345)
E/InstrumentationHacker(25176): at com.qihoo.plugin.core.PluginManager.unwrapIntent(PluginManager.java:1753)
原因是序列化对象的类是存放在插件的dex中的,因此反序列化要使用插件的ClassLoader才行,该错误是Intent在进行反序列化时,使用的宿主默认的ClassLoader导致出现找不到类的异常。
而实际上在使用该Intent前,已经将插件ClassLoader设置到Intent中了,理论上来说,不应该有问题。
activity.getIntent().setExtrasClassLoader(plugin.getCl());
这里使用android 4.4.2_r2 源码分析:
跟踪异常堆栈和系统源码,找到
/frameworks/base/core/java/android/os/Parcel.java
跟踪readValue()方法,定位到读取序列化对象的代码
2059 2060 case VAL_BYTE: 2061 return readByte(); 2062 2063 case VAL_SERIALIZABLE: 2064 return readSerializable(); 2065 2066 case VAL_PARCELABLEARRAY: 2067 return readParcelableArray(loader); 2068 2069 case VAL_SPARSEARRAY: 2070 return readSparseArray(loader);
可以发现,readSerializable()没有将loader传入进去,loader是通过setExtrasClassLoader()设置的插件ClassLoader,而readSerializable()中,直接使用默认的ClassLoader来进行反序列化,代码如下:
2200 public final Serializable readSerializable() { 2201 String name = readString(); 2202 if (name == null) { 2203 // For some reason we were unable to read the name of the Serializable (either there 2204 // is nothing left in the Parcel to read, or the next value wasn't a String), so 2205 // return null, which indicates that the name wasn't found in the parcel. 2206 return null; 2207 } 2208 2209 byte[] serializedData = createByteArray(); 2210 ByteArrayInputStream bais = new ByteArrayInputStream(serializedData); 2211 try { 2212 ObjectInputStream ois = new ObjectInputStream(bais); 2213 return (Serializable) ois.readObject(); 2214 } catch (IOException ioe) { 2215 throw new RuntimeException("Parcelable encountered " + 2216 "IOException reading a Serializable object (name = " + name + 2217 ")", ioe); 2218 } catch (ClassNotFoundException cnfe) { 2219 throw new RuntimeException("Parcelable encountered" + 2220 "ClassNotFoundException reading a Serializable object (name = " 2221 + name + ")", cnfe); 2222 } 2223 }
也就是说,我们通过setExtrasClassLoader()方法设置的ClassLoader对Serializable对象来说,是没有作用的。
同样的,从
2066 case VAL_PARCELABLEARRAY: 2067 return readParcelableArray(loader);
可以看出,
setExtrasClassLoader()对Parcelable对象是有作用的。
从android 5.0.0_r2源码同样的位置:
/frameworks/base/core/java/android/os/Parcel.java
2193 case VAL_BYTE: 2194 return readByte(); 2195 2196 case VAL_SERIALIZABLE: 2197 return readSerializable(loader); 2198 2199 case VAL_PARCELABLEARRAY: 2200 return readParcelableArray(loader); 2201 2202 case VAL_SPARSEARRAY: 2203 return readSparseArray(loader);
可以发现,已经对这个问题进行了处理。
结论:
android 5.0系统以下,Intent存在一个序列化bug,通过setExtrasClassLoader()方法设置的ClassLoader对象,对Serializable对象无效,当Intent进行反序列化时,使用的依旧为默认的ClassLoader对象,如果反序列化的类在默认的ClassLoader中不存在,则会出现找不到类的异常。5.0以上系统已经修复该bug。