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

woo*_*suk 5 android dex dexclassloader aar

我已成功通过以下方式从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();
}
Run Code Online (Sandbox Code Playgroud)

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

我在Android Studio中创建了一个库模块(“ testlibrary”),在该库模块中创建了Test.java(我想在调用方应用中动态加载的内容),并通过Gradle Project-> Excute Gradle Task创建了一个aar文件

如何通过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
Run Code Online (Sandbox Code Playgroud)

小智 6

您无法在运行时加载 aar 文件,因为 aar 文件包含资源和 classes.jar 文件,并且不包含 dex 文件。
但是
您可以使用注入器gradle 插件从您的 aar 获取 dex 并将所有 aar 资源合并到您的项目中,之后您可以使用injector-android lib 在运行时加载该 dex 文件。查看注入示例项目


小智 5

jar和dex文件的区别

\n\n

\n\n

下面的文章详细描述了 diffrend \n你可以阅读它,然后你就可以得到结果

\n\n

[AAR 到 DEX] 在 Android 应用程序中在运行时加载并运行代码

\n\n

JAR和AAR的区别

\n\n

JAR 只是一个包含 java 类文件的 Java 库,仅此而已。\nAAR 是 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。

\n\n

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

\n\n

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

\n\n

JAR 到 DEX

\n\n

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

\n\n
d8 --release --output lib.dex path_to_jar_lib.jar\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

AAR 至 DEX

\n\n

从AAR库获取DEX文件有点困难,因为你必须处理资源文件。AAR 包含 JAR 文件和资源。\xe2\x80\x99t 不需要使该资源可下载,因为大多数库只包含一些 \xe2\x80\x99t 大的资源文件,并且主要是布局 XML 文件或一些常规数字或布尔值或其他内容。因此,正确的做法是将这些资源与主项目资源合并,并将该依赖项更改为提供的依赖项,并将 JAR 文件转换为 DEX 文件。但该 JAR 文件有问题。它不是一个普通的 JAR 文件。在构建期间,AAPT 不会为该库生成 R java 文件,因为该库是提供的依赖项,并且该 JAR 文件中的 R 文件使用将在运行时崩溃。相反,应用程序 R java 文件将包含资源 id,包括库资源。因此,这个问题的解决方案是手动创建一个 R.java 文件,它将所有资源 id 委托给具有应用程序包名称的 R 文件,并编译该 R 文件并将其放入 jar 文件中,这可以使用 jar -ufv 选项来完成。现在想象一下该库的更新已发布。

\n\n

解决方案:注射器

\n\n

正如我在一开始所说的,我已经为这个问题创建了一个解决方案。如果我告诉您这可以在构建时完成,您甚至不会注意到某些资源正在从一个项目移动到另一个项目,并且您不必记住命令行带有旗帜的工具。解决方案是注射器。Injector 是一个 Gradle 插件,它会自动为您执行上述所有操作。\n首先,您需要将注入器添加到 Gradle 构建脚本类路径中。你的 gradle 构建脚本应该是这样的

\n\n

等等 ......

\n