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

标签 android dynamic classloader classcastexception

我有一个线程,根据系统的具体实现,为它需要的资源加载不同的类。我的实现是在 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/

相关文章:

Tomcat 自定义类加载器

android - OnePlus Espresso 测试运行问题 (Android Studio 3.6.2)

android - keytool 错误 : java. security.UnrecoverableKeyException: 无法恢复 key android

c# - 可以在没有基类型的情况下使用 C# 约束吗?

c++ - 超过 8 个元素的动态数组崩溃

java - 为什么 java.sql.DriverManager.getCallerClassLoader() 是 native 的?

Android:适用于 HTC 智能手机的 ADB 驱动程序

Java/Android - 获取套接字输出流会中断执行,并且不会启动任何异常

excel - 变量范围内最后一个填充单元格的返回值

java - 无法实例化 TrueZip 类