如何使用DexClassLoader从aar文件动态加载类

时间:2018-02-10 01:21:15

标签: android dex aar dexclassloader

我已成功以下列方式从dex文件动态加载类

enter code here
File file = getDir("dex", 0);
DexClassLoader dexClassLoader = new DexClassLoader("/data/data/com.example.callerapp/files/test.dex", file.getAbsolutePath(), null, getClassLoader());
try {
    Class<Object> _class = (Class<Object>) 
    dexClassLoader.loadClass("com.example.calledapp.test");
    Object object = _class.newInstance();
    Method method = _class.getMethod("function");
    method.invoke(object);
} catch (Exception e) {
    e.printStackTrace();
}

但我想要做的是从aar文件动态加载类,如android dev页面所示(DexClassLoader:从.jar加载类的类加载器和包含classes.dex条目的.apk文件。可用于执行未作为应用程序的一部分安装的代码。)

我在Android工作室中创建了一个库模块(&#34; testlibrary&#34;),在库模块中创建了Test.java(我想在调用者应用程序中加载dynimically),并创建了一个aar文件Gradle项目 - &gt;执行Gradle任务

如何通过dexclassloader在以这种通用方式创建的aar文件中动态加载类?我已经通过提供商将aar文件从CalledApp

移到了CallerApp

或者创建aar文件的过程是错误的? 在运行时,会出现错误消息

02-10 09:43:48.744 16487-16487/com.example.callerapp W/System.err: java.lang.ClassNotFoundException: Didn't find class "com.example.calledlibrary.Test" on path: DexPathList[[zip file "/data/data/com.example.callerapp/files/testlibrary.aar"],nativeLibraryDirectories=[/system/lib64, /vendor/lib64]]
02-10 09:43:48.744 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:93)
02-10 09:43:48.744 16487-16487/com.example.callerapp W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
02-10 09:43:48.744 16487-16487/com.example.callerapp W/System.err:     at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
02-10 09:43:48.745 16487-16487/com.example.callerapp W/System.err:     at com.example.callerapp.CallerActivity.onClick(CallerActivity.java:42)
02-10 09:43:48.745 16487-16487/com.example.callerapp W/System.err:     at android.view.View.performClick(View.java:6877)
02-10 09:43:48.745 16487-16487/com.example.callerapp W/System.err:     at android.widget.TextView.performClick(TextView.java:12651)
02-10 09:43:48.745 16487-16487/com.example.callerapp W/System.err:     at android.view.View$PerformClick.run(View.java:26069)
02-10 09:43:48.745 16487-16487/com.example.callerapp W/System.err:     at android.os.Handler.handleCallback(Handler.java:789)
02-10 09:43:48.746 16487-16487/com.example.callerapp W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:98)
02-10 09:43:48.746 16487-16487/com.example.callerapp W/System.err:     at android.os.Looper.loop(Looper.java:164)
02-10 09:43:48.746 16487-16487/com.example.callerapp W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:6938)
02-10 09:43:48.746 16487-16487/com.example.callerapp W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
02-10 09:43:48.746 16487-16487/com.example.callerapp W/System.err:     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
02-10 09:43:48.747 16487-16487/com.example.callerapp W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
02-10 09:43:48.747 16487-16487/com.example.callerapp W/System.err:  Suppressed: java.io.IOException: No original dex files found for dex location (arm64) /data/data/com.example.caller/files/testlibrary.aar
02-10 09:43:48.747 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexFile.openDexFileNative(Native Method)
02-10 09:43:48.747 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexFile.openDexFile(DexFile.java:353)
02-10 09:43:48.747 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexFile.<init>(DexFile.java:100)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexFile.<init>(DexFile.java:74)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexPathList.loadDexFile(DexPathList.java:374)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexPathList.makeDexElements(DexPathList.java:337)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexPathList.<init>(DexPathList.java:157)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.BaseDexClassLoader.<init>(BaseDexClassLoader.java:65)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at dalvik.system.DexClassLoader.<init>(DexClassLoader.java:57)
02-10 09:43:48.748 16487-16487/com.example.callerapp W/System.err:     at com.example.caller.CallerActivity.onClick(CallerActivity.java:40)
02-10 09:43:48.749 16487-16487/com.example.callerapp W/System.err:      ... 10 more

2 个答案:

答案 0 :(得分:2)

您无法在运行时加载aar文件,因为aar文件包含资源和classes.jar文件,并且不包含dex文件。

您可以使用injector gradle插件从您的aar获取dex并将所有aar资源合并到您的项目中,之后您可以使用injector-android lib在运行时加载该dex文件。查看inject-example项目

答案 1 :(得分:0)

关于jar和dex文件的区别

以下有关diffrend的详细描述 然后您可以阅读它,然后得到结果

[AAR to DEX] Loading and Running Code at Runtime in Android Application

JAR和AAR之间的区别

JAR只是一个Java库,其中包含Java类文件,仅此而已。 AAR是android库的一种格式,其中包含JAR文件,android资源文件(例如布局XML,属性XML等)和android清单文件。 在构建期间,当然会为每个android库和主项目生成一个R.java文件,并且所有java文件都将编译为一个或多个DEX文件(DEX是Dalvik可执行格式,可以由android运行时加载(艺术) )。因此,在APK中,只有DEX文件(没有JAR或AAR文件)以及资源和清单。 Android R.java文件是AAPT(Android资产打包工具)自动生成的文件,其中包含res /目录的所有资源的资源ID。

为什么我需要在运行时加载一些代码?

这样做的原因很多。也许您的依赖库太大,您想让APK变小,或者要求某些功能要求库,但某些功能并非所有设备都支持,或者在首次启动时不需要,如果您有自己的区分逻辑,设备是否支持该功能,或者是否需要向用户显示该功能。为什么附带该功能代码的APK?如果您正在阅读本文,我想您已经有自己的理由了:)

JAR到DEX

Android不支持加载JAR文件,因此必须有一种将JAR文件编译为DEX文件的方法。为此,位于android_sdk / build-tools / version /中的D8工具。要将JAR转换为DEX,可以从命令行运行此命令

d8 --release --output lib.dex path_to_jar_lib.jar

已生成DEX文件,无需使用该JAR库构建android项目,因此在gradle依赖项部分中,无需将其声明为实现或api配置,而是需要提供的配置,这意味着像创建该库一样构建该项目,但不要在从中编译DEX文件的应用程序源文件中包含该JAR

AAR到DEX

要从AAR库获取DEX文件有点困难,因为您必须处理资源文件。 AAR包含一个JAR文件和资源。无需使这些资源可下载,因为大多数库仅包含少数几个不大的资源文件,大部分是布局XML文件或一些通用数字或布尔值或其他内容。因此,正确的做法是将资源与主要项目资源合并,并将该依赖关系更改为提供的依赖关系,然后将JAR文件转换为DEX文件。但是该JAR文件存在问题。它不是普通的JAR文件。在构建期间,AAPT不会为该库生成R java文件,因为该库是提供的依赖项,并且该JAR文件中的R文件使用情况将在运行时崩溃。取而代之的是,应用程序R java文件将包含资源ID,其中包括库资源。因此,解决此问题的方法是手动创建一个R.java文件,该文件将使用应用程序包名称将所有资源ID委派给R文件,然后编译该R文件并将其放在jar文件中,这可以通过jar -ufv选项完成。现在想象一下该库的更新已发布。

解决方案:喷射器

正如我在一开始所说的那样,我已经为这个问题创建了解决方案。如果我告诉您可以在构建时完成该操作,而您甚至没有注意到某些资源正在从一个项目移至另一个项目,并且您不必记住带有标志的命令行工具该怎么办。解决方案是喷油器。 Injector是一个Gradle插件,可以自动为您完成上述所有操作。 首先,您需要将注射器添加到Gradle buildscript类路径中。您的gradle buildscript应该看起来像这样

以此类推...