Atlas插件化踩坑

最近在使用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);
    }
}

推荐阅读更多精彩内容