最近在使用Altas插件化框架,遇到了一些问题,记录下
bundle传值报class not found
我们给bundle传了一个继承Serializable的对象,但是在4.x的系统里,报了class not found的异常
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:53)
at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:217)
at java.io.ObjectInputStream.resolveClass(ObjectInputStream.java:2304)
at java.io.ObjectInputStream.readNewClassDesc(ObjectInputStream.java:1663)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:683)
at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:1806)
at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:787)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2006)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:1963)
at android.os.Parcel.readSerializable(Parcel.java:2142)
at android.os.Parcel.readValue(Parcel.java:2016)
at android.os.Parcel.readMapInternal(Parcel.java:2226)
at android.os.Bundle.unparcel(Bundle.java:223)
at android.os.Bundle.getString(Bundle.java:1055)
at android.content.Intent.getStringExtra(Intent.java:4488)
显然不是bundle的classloader,但是altas怎么控制解析时classloader的查找呢。根据研究altas源码发现,在查找bundle前,已经给intent设置了classloader
//InstrumentationHook.java
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle) {
if(activity.getIntent()!=null){
activity.getIntent().setExtrasClassLoader(RuntimeVariables.delegateClassLoader);
}
}
但是这个代码居然没用,于是我们进一步分析,发现了关键问题。
因为ObjectInputStream的查找类的时候,是通过vmstack来查找最近的classloader的,这是不靠谱的。
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
String name = desc.getName();
try {
return Class.forName(name, false, latestUserDefinedLoader());
} catch (ClassNotFoundException ex) {
Class<?> cl = primClasses.get(name);
if (cl != null) {
return cl;
} else {
throw ex;
}
}
}
就是这里导致了bug,但是为啥5.0以上没问题呢。通过研究源码发现,5.0的代码,重写了resolveClass这个方法
// 19
2196 public final Serializable readSerializable() {
2197 String name = readString();
2198 if (name == null) {
2199 // For some reason we were unable to read the name of the Serializable (either there
2200 // is nothing left in the Parcel to read, or the next value wasn't a String), so
2201 // return null, which indicates that the name wasn't found in the parcel.
2202 return null;
2203 }
2204
2205 byte[] serializedData = createByteArray();
2206 ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
2207 try {
2208 ObjectInputStream ois = new ObjectInputStream(bais);
2209 return (Serializable) ois.readObject();
2210 } catch (IOException ioe) {
2211 throw new RuntimeException("Parcelable encountered " +
2212 "IOException reading a Serializable object (name = " + name +
2213 ")", ioe);
2214 } catch (ClassNotFoundException cnfe) {
2215 throw new RuntimeException("Parcelable encountered" +
2216 "ClassNotFoundException reading a Serializable object (name = "
2217 + name + ")", cnfe);
2218 }
2219 }
// 25
private final Serializable readSerializable(final ClassLoader loader) {
String name = readString();
if (name == null) {
// For some reason we were unable to read the name of the Serializable (either there
// is nothing left in the Parcel to read, or the next value wasn't a String), so
// return null, which indicates that the name wasn't found in the parcel.
return null;
}
byte[] serializedData = createByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(serializedData);
try {
ObjectInputStream ois = new ObjectInputStream(bais) {
@Override
protected Class<?> resolveClass(ObjectStreamClass osClass)
throws IOException, ClassNotFoundException {
// try the custom classloader if provided
if (loader != null) {
Class<?> c = Class.forName(osClass.getName(), false, loader);
if (c != null) {
return c;
}
}1
return super.resolveClass(osClass);
}
};
return (Serializable) ois.readObject();
} catch (IOException ioe) {
throw new RuntimeException("Parcelable encountered " +
"IOException reading a Serializable object (name = " + name +
")", ioe);
} catch (ClassNotFoundException cnfe) {
throw new RuntimeException("Parcelable encountered " +
"ClassNotFoundException reading a Serializable object (name = "
+ name + ")", cnfe);
}
}
网友评论