如何使用DexClassLoader从AAR文件动态加载类 [英] how to load class dynamically from aar file with DexClassLoader

查看:315
本文介绍了如何使用DexClassLoader从AAR文件动态加载类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have succeeded in dynamically loading classes from a dex file in the following way

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:类加载器,它从包含classes.dex条目的.jar和.apk文件中加载类.可用于执行未作为应用程序一部分安装的代码.)

But what I want to do is load the class dynamically from the aar file, as shown in the android dev page(DexClassLoader : A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.)

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

I created a library module("testlibrary") in the Android studio, created Test.java(what I want to load dynimically at caller app) in the library module, and created an aar file through the Gradle Project -> Excute Gradle Task

如何通过dexclassloader在以这种一般方式创建的aar文件中动态加载类?我已通过提供程序将aar文件从CalledApp移至CallerApp

How can I dynamically load a class via the dexclassloader in an aar file created in this general way? I have moved aar file via provider to CallerApp from CalledApp

还是创建aar文件的过程错误?在运行期间,出现错误消息

Or is the process of creating an aar file wrong? During runtime, an error message appears

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

推荐答案

关于jar和dex文件的区别

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

the below article detail descript about the diffrend and you can read it ,then you can get the result

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.

JAR is just a Java library containing java class files and nothing more. AAR is a format for android libraries which contains a JAR file, android resource file (like layout XML, attribute XML, etc..) and an android manifest file. During the build, an R.java file is generated for each android library and for the main project of course, and all java files are compiled to a one or multiple DEX files (DEX is a Dalvik executable format which can be loaded by android runtime (ART) ). So in an APK, there are only DEX files(not JAR or AAR file), and resources and manifest. Android R.java file is an auto-generated file by AAPT (Android Asset Packaging Tool) that contains resource IDs for all the resources of res/ directory.

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

There are many reasons to do that. Maybe your dependency libraries are too big and you want you APK to have a small size or maybe that libraries are requested for some feature which is not supported for all devices or it is not required at first launch and you have your own logic for differentiation if device supports that feature or not, or if you need to show user the feature or not. Why ship the APK with that feature code? If you are reading this I think you already have your own reasons :)

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

Android does not support loading JAR file, so there must be a way to compile the JAR file to DEX file. For that, there is the D8 tool which is located in android_sdk/build-tools/version/. To convert JAR to DEX you can run this command from command line

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

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

The DEX file is generated and there is no need to build the android project with that JAR library, so in gradle dependancies section instead of declaring that library as implementation or an api configuration, it needs to be a provided configuration which means that build this project as if this library exists but DO NOT include that JAR in application source files from which the DEX files are compiled

要从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选项完成.现在想象一下该库的更新已发布.

To get DEX file from AAR library is a little bit difficult because you must deal with resource files. AAR contains a JAR file and resources. There isn’t a need to make that resources downloadable because most libraries only contain a few resource files which aren’t large and are mostly layout XML files or some general numbers or booleans or something else. So the right thing to do is to merge that resources with the main project resources and change that dependency to be a provided dependency and convert the JAR file to DEX file. But there is a problem with that JAR file. it is not an ordinary JAR file. During the build time, AAPT will not generate an R java file for that library because the library is a provided dependency and the R file usages in that JAR file will crash at runtime. Instead of that, the application R java file will contain the resources ids including the library resources. So the solution to this problem is to manulay create a R.java file which will delegate all resources ids to the R file with the app package name and compile that R file and put it in jar file which can be done with jar -ufv option. And now imagine that an update for this library is released.

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

As I said at the beginning I have created a solution to this problem. What if I told you that this could be done at build time and you even can’t notice that some resource is being moved from one project to another and you don’t have to remember the command line tools with their flags. The solution is Injector. Injector is a Gradle plugin that does all the above explained for you automatically. First of all, you need to add injector to your Gradle buildscript classpath. Your gradle buildscript should look like this

以此类推...

这篇关于如何使用DexClassLoader从AAR文件动态加载类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆