Android: 当加载接口时,DexClassLoader类加载失败

3

简介:我正在尝试从一个包含在classes.jar中的classes.dex文件中动态加载代码。我已经进行了大量的研究和花费了很多时间,因此我真的需要帮助。

问题:当我的jar文件包含一个简单的类时,它可以成功加载。然而,在完全相同的情况下,当我的jar文件包含相同的类,只是现在这个类实现了一个接口时,加载此类将失败并显示以下错误信息:

"Didn't find class "com.x.y.z.w.Patch" on path: DexPathList[[zip file "/data/user/0/x.y.z/app_dex/classes_dexed.jar"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64]]"

我还会得到以下信息:

I/dalvikvm: Failed resolving Lcom/x/y/TestClassImpl; interface 4 'Lcom/x/y/TestClassInterface;'

环境: - 使用的函数尝试加载类:

(DexClassLoader实例).loadClass和(DexFile实例).loadClass。

具体来说:

方法1:

DexFile dx = DexFile.loadDex(jarInternalPath, File.createTempFile("opt", "dex", 
    context.getCacheDir()).getPath(), 0);
Class<?> targetClass = dx.loadClass(className, 
    ClassLoader.getSystemClassLoader()); // Error

方法二:

DexClassLoader dexClassLoader = new DexClassLoader(jarInternalPath,
    dexOutPath.getAbsolutePath(), null, ClassLoader.getSystemClassLoader());
Class<?> targetClass = dexClassLoader.loadClass(className); // Error
  • IDE: Android Studio 2.1.2
  • 测试设备: 模拟器 Nexus 4 (APIs 16、19 和 23),还有物理 Nexus 5 设备,运行 Android 6.0.1
  • 最令人惊讶的事情: 当我打印 dex 文件的类时,我看到了正确的类路径。我使用 DexFile 加载 dex 内容并打印其类路径。 这种方法能够识别出正确的内容! 这让我想到可能与 Android 权限或 Linux 权限有关。我不知道……

这是我的打印方法:

try {
    DexFile dx = DexFile.loadDex(jarInternalPath, File.createTempFile("opt", "dex",
        context.getCacheDir()).getPath(), 0);

    // Print all classes in the DexFile
    for (Enumeration<String> classNames = dx.entries(); classNames.hasMoreElements();) {
        String className = classNames.nextElement();
        Log.d(TAG, "Analyzing dex content, fonud class: " + className);
    }
} catch (IOException e) {
    Log.d(TAG, "Error opening " + jarInternalPath, e);
}
  • P.S. 似乎与主题无关,但也许并非如此: 我的主机应用程序、通用接口和新内容的实现均位于同一项目的3个不同模块中。该应用程序的包名称为x.y.z,接口的包名称为x.y.z.interface,实现的包名称为x.y.z.impl。

我尝试了第二个项目,所有3个模块都在同一个项目中,但其中1个模块无法正常工作。

请帮忙!


你在使用Gradle吗?你是如何添加依赖项(compile/provided)的?为什么你有3个包(apk和2个jar)?所有接口(由apk本身使用)都应该编译到apk中(因为你不能在动态加载时使用它们)...你是否有任何循环依赖关系? - Selvin
嘿Selvin,回答你的第一个评论,我还不担心多个加载器实例。至于第二个评论,我正在使用Gradle,有3个模块:应用程序、接口和实现。已验证将接口编译到apk中。'app'模块依赖于接口模块,接口没有依赖项,实现也依赖于接口,但在将'app'模块编译为apk时不会被编译(因为我手动编译它,将其dex并放入'app'模块的资产文件夹中进行本地测试)。 - esWoobi
正如我所写的那样,对于实现模块的依赖项,您必须使用provided而不是compile - Selvin
https://github.com/SelvinPL/android_loaderapp ... http://selvin/loaderappimplX.jar 只是来自implX构建的未签名apk 最重要的是implX和应用程序模块中的build.gradle ... 此外,正如您所看到的,接口是一个Java模块。 - Selvin
"interface is defined twice",这不是真的。使用impl和interface之间的“provided”依赖进行相同的测试也导致了相同的错误。它无法加载类。为什么你的接口被定义为Java? - esWoobi
显示剩余5条评论
1个回答

0

这还不足以加载您的类。您必须更改系统类加载器,以便从dex动态和编程方式使用自己的类。否则,您将无法使用您的接口或类或任何其他内容。

如果您想要动态加载类,请先从dexfile或dexclassloader加载类,然后使用它。

例如:

Class<?> myOwnClass = MyDexClassLoader.loadClass("com.example.my");

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接