gpt4 book ai didi

android - 在 Android 中动态加载类时发生 ClassCastException

转载 作者:塔克拉玛干 更新时间:2023-11-02 09:01:21 26 4
gpt4 key购买 nike

我有一个线程,根据系统的具体实现,为它需要的资源加载不同的类。我的实现是在 Android 上,我有一个类返回我的实现所需的特定类。我似乎能够很好地加载类,但是当我尝试将它分配给主线程中的对象时,它给了我一个 ClassCastException。以下是 fragment :

在我的主线程中,我这样做:

    try {
grammarProcessor = config.loadObject(GrammarProcessor.class);

这给了我这个堆栈跟踪:

    E/AndroidRuntime(6682): FATAL EXCEPTION: JVoiceXmlMain
E/AndroidRuntime(6682): java.lang.ClassCastException: org.jvoicexml.android.JVoiceXmlGrammarProcessor
E/AndroidRuntime(6682): at org.jvoicexml.JVoiceXmlMain.run(JVoiceXmlMain.java:321)

GrammarProcessor 是一个接口(interface),JVoiceXmlGrammarProcessor 是我加载并实现该接口(interface)的类。加载代码如下:

else if(baseClass == GrammarProcessor.class){
String packageName = "org.jvoicexml.android";
String className = "org.jvoicexml.android.JVoiceXmlGrammarProcessor";
String apkName = null;
Class<?> handler = null;
T b = null;

try {
PackageManager manager = callManagerContext.getPackageManager();
ApplicationInfo info= manager.getApplicationInfo(packageName, 0);
apkName= info.sourceDir;
} catch (NameNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return null;
}
PathClassLoader myClassLoader =
new dalvik.system.PathClassLoader(
apkName,
ClassLoader.getSystemClassLoader());
try {
handler = Class.forName(className, true, myClassLoader);
return (T) handler.newInstance();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}

调试时,我会检查加载方法返回的内容,它是一个带有 ID 号的对象。如果我点击它,它会显示 org.jvoicexml.android.JVoiceXmlGrammarProcessor@40565820,下拉列表将显示 JVoiceXmlGrammarProcessor 应该有的两个私有(private)字段,所以看起来装得很好。有什么想法吗?

最佳答案

我想我明白这里发生了什么,但我必须假设 org.jvoicexml.android 不是你的包,也就是说,你从不同的 apk(正如赏金所暗示的那样)。

考虑到这一点,这是不可能的,而且有充分的理由。

让我们从您自己的应用程序开始 - 您可以从自己的 classes.dex 中获得类型 GrammarProcessor 并进入默认的 ClassLoader(PathClassLoader 当 zygote fork 你的进程时你会得到)。我们称此类型为 GP1。您自己的应用程序中实现 GrammarProcessor 的任何类实际上在其接口(interface)列表中都有 GP1

然后,您实例化一个新的类加载器。如果您查看 source ,您会看到 PathClassLoader 只是 BaseDexClassLoader 的一个薄包装器这又委托(delegate)给一个 DexPathList , 又委托(delegate)给 DexFile对象,这些对象又以 native 代码进行加载。呸。

BaseDexClassLoader 中有一个微妙的部分是您遇到麻烦的原因,但如果您以前没有见过它,您可能会错过它:

this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);

再往下一点:

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class c = pathList.findClass(name);
if (c == null) {
...
}
return c;
}

BaseDexClassLoader 不会首先检查其父级!

.. 简而言之,这是您的问题。

更准确地说,它里面的 DexPathListDexFile 从另一个 dex 加载所有类,从不查看已经加载的类虚拟机。

因此,您最终得到了 GrammarProcessor 的两个不同加载版本。然后,您正在实例化的对象引用新的 GP2 类,而您正试图将其转换为 GP1。显然不可能。

有解决办法吗?

有一个以前做过,但你不会喜欢它。 Facebook use it在他们的应用程序中加载一堆 dex 文件,它们之间有很强的关系。 (它就在那里,在 LinearAlloc 乱七八糟之前):

we examined the Android source code and used Java reflection to directly modify some of its internal structures

我 90% 确定他们获得了您提供的 PathClassLoader (getSystemClassLoader()),获得 DexPathList 并覆盖dexElements 私有(private)字段有一个额外的 Element 和另一个 dex 文件(在你的例子中是 apk)。太黑了,我建议不要这样做。

我刚刚想到,如果您不想以框架看到它们的方式使用新加载的类,您可以从 BaseDexClassLoader 扩展并实现适当的查找- parent-before-try-to-load 行为。我还没有做过,所以我不能保证它会奏效。

我的建议?只需使用远程服务。这就是 Binder 的用途。或者,重新考虑您的 apk 分离。

关于android - 在 Android 中动态加载类时发生 ClassCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10792136/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com