leader 提的诡异需求,有一批旧项目,需要在一个 android 项目中使用。 使用场景是在 android 设备中通过网络下载这些 jar (已使用 dx --dex 转换 bytecode ),加载并启动。 但是由于环境的变化导致原项目中一些资源请求不存在,需要重定向。
目前的做法是,在 android 项目中使用自定义 ClassLoader 加载这些 jar,并启动。
目前碰到的问题是由于旧项目中大量的使用了
Class.class.getResourceAsSteam
和this.getClass().getResourceAsStream
方法。
后者由于是通过我自定义的 ClassLoader 加载, 因此调用请求会被委托到我自定义 ClassLoader 的 getResourceAsStream 方法上。没有问题。
问题就在于Class.class.getResourceAsSteam
这样的调用,由于双亲委派,是由 Boot ClassLoader 加载。
因此我无法重定向这些资源请求。
尝试过这些方案:
Field.getDeclaredFields()
方法,也确实没有该属性目前需要的效果是,可以通过各种奇技淫巧(不包括修改旧项目的源代码),重定向这些代码中的类似
String/Class/System.clsss.getResourceAsStream
方法的调用。
感谢各位能够提供解决方法或者思路的。
@cppgohan @sutra 再次尝试过这些方法
自定义类加载器逻辑:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class clazz = this.findLoadedClass(name);
if (clazz == null) {
clazz = this.findClass(name);
}
}
return clazz
}
但是依旧失败。在自定义ClassLoader下,确实实现了与宿主环境完全隔离,但是当我加载自己的类的出现了异常 dalvikvm: (LDemoActivity; had used a different Landroid/app/Activity; during pre-verification) 对于这个异常,我不是很理解,因为整个对象是在我自己的ClassLoader下加载,应该不会出现这个问题。
这个异常说明在我的类加载器下,依旧返回了宿主环境的Activity类。
苦于一直找不到原因,因此在自己的类加载器中做了区别对待:
clazz = this.findLoadedClass(name);
if (clazz == null) {
if (name.startsWith("android.")) {
clazz = super.loadClass(name, resolve);
Log.i(TAG, "successfully parent load class " + name);
} else {
clazz = this.findClass(name);
Log.i(TAG, "successfully self load class " + name);
}
}
这样固然可以正常启动,但是由于Activity和其他类的ClassLoader不同,造成大量的类型不匹配,因此也无法跑起来。这就说明,在类加载的验证阶段,期待的是宿主环境下的Activity类,而实际上获得是隔离环境下的Activity类。
那么现在的问题就是当我使用自己的类加载器,调用loadClass方法尝试加载DemoActivity时,为何在验证环境其父类会变成要求是宿主环境的Activity类?根据我的理解,此时自定义ClassLoader并未加载Activity类,上下文中也不存在Activity(由于ClassLoader产生的隔离环境),不应该抛出这样的异常。
求大佬科普
1
cppgohan 2017-10-20 10:27:42 +08:00
关注一下, 总觉得用反射修改 classloader 应该是可以一试的.. 不知道为什么会找不到
|
2
sutra 2017-10-20 15:28:47 +08:00
在 classpath 里给它那个 resource,用符号链接,不知道行不行。
|
3
teemoer 2017-10-23 15:45:32 +08:00
xposed (手动滑稽)
|