Как загрузить класс динамически из файла 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 (DexClassLoader: загрузчик классов, который загружает классы из файлов.jar и.apk, содержащих запись classes.dex. Это можно использовать выполнить код, не установленный как часть приложения.)
Я создал библиотечный модуль ("testlibrary") в Android-студии, создал Test.java(то, что я хочу загрузить в приложении вызывающего абонента) в библиотечном модуле и создал файл aar через Gradle Project -> Excute Gradle Task
Как я могу динамически загрузить класс через dexclassloader в файл aar, созданный таким общим способом? Я переместил файл через провайдера в CallerApp из CalledApp
Или процесс создания файла 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
1 ответ
Вы не можете загрузить файл aar во время выполнения, поскольку файл aar содержит файл resources и classes.jar и не содержит файл dex.
Но
Вы можете использовать плагин инжектора Gradle, чтобы получить dex из вашего aar и объединить все ваши ресурсы aar в ваш проект, и после этого вы можете использовать libctor injector -android для загрузки этих dex-файлов во время выполнения. Ознакомьтесь с примером проекта
различие в файле jar и dex
приведенное ниже подробное описание статьи о diffrend, и вы можете прочитать его, затем вы можете получить результат
[AAR to DEX] Загрузка и запуск кода во время выполнения в приложении Android
Разница между JAR и AAR
JAR - это просто библиотека Java, содержащая файлы классов java и ничего более. AAR - это формат для библиотек Android, который содержит файл JAR, файл ресурсов Android (например, XML макета, XML атрибутов и т. Д.) И файл манифеста Android. Во время сборки файл R.java создается для каждой библиотеки Android и, конечно же, для основного проекта, и все файлы java компилируются в один или несколько файлов DEX (DEX - это исполняемый формат Dalvik, который может быть загружен средой выполнения Android. (ИЗОБРАЗИТЕЛЬНОЕ ИСКУССТВО)). Таким образом, в APK есть только файлы DEX (не файлы JAR или AAR), а также ресурсы и манифест. Файл Android R.java - это файл, автоматически сгенерированный AAPT (Android Asset Packaging Tool), который содержит идентификаторы ресурсов для всех ресурсов каталога res/.
Почему мне нужно загружать код во время выполнения?
Для этого есть много причин. Возможно, ваши библиотеки зависимостей слишком велики, и вы хотите, чтобы ваш APK имел небольшой размер, или, может быть, библиотеки запрашиваются для какой-то функции, которая не поддерживается для всех устройств, или она не требуется при первом запуске, и у вас есть собственная логика для различения, если устройство поддерживает эту функцию или нет, или если вам нужно показать пользователю эту функцию или нет. Зачем поставлять APK с таким кодом функции? Если вы это читаете, думаю, у вас уже есть свои причины:)
JAR в DEX
Android не поддерживает загрузку файла JAR, поэтому должен быть способ скомпилировать файл JAR в файл DEX. Для этого есть инструмент D8, который находится в android_sdk/build-tools/version/. Чтобы преобразовать JAR в DEX, вы можете запустить эту команду из командной строки
d8 --release --output lib.dex path_to_jar_lib.jar
Создается файл DEX, и нет необходимости создавать проект Android с этой библиотекой JAR, поэтому в разделе зависимостей gradle вместо объявления этой библиотеки как реализации или конфигурации api это должна быть предоставленная конфигурация, что означает сборку этого проекта как если бы эта библиотека существует, но НЕ включайте этот JAR в исходные файлы приложения, из которых скомпилированы файлы DEX
AAR в DEX
Получить файл DEX из библиотеки AAR немного сложно, потому что вы должны иметь дело с файлами ресурсов. AAR содержит файл JAR и ресурсы. Нет необходимости делать эти ресурсы загружаемыми, потому что большинство библиотек содержат только несколько файлов ресурсов, которые невелики и в основном представляют собой XML-файлы макета или некоторые общие числа, логические значения или что-то еще. Поэтому правильным решением будет объединить эти ресурсы с основными ресурсами проекта и изменить эту зависимость на предоставленную зависимость и преобразовать файл JAR в файл DEX. Но есть проблема с этим файлом JAR. это не обычный файл JAR. Во время сборки AAPT не будет генерировать java-файл R для этой библиотеки, потому что библиотека является предоставленной зависимостью, и использование R-файла в этом JAR-файле приведет к сбою во время выполнения. Вместо этого,java-файл приложения R будет содержать идентификаторы ресурсов, включая ресурсы библиотеки. Таким образом, решение этой проблемы состоит в том, чтобы вручную создать файл R.java, который делегирует все идентификаторы ресурсов в файл R с именем пакета приложения и скомпилирует этот файл R и поместит его в файл jar, что можно сделать с помощью параметра jar -ufv. А теперь представьте, что вышло обновление для этой библиотеки.
Решение: инжектор
Как я сказал вначале, я создал решение этой проблемы. Что, если бы я сказал вам, что это можно сделать во время сборки, и вы даже не заметите, что какой-то ресурс перемещается из одного проекта в другой, и вам не нужно запоминать инструменты командной строки с их флагами. Решение - инжектор. Injector - это плагин Gradle, который автоматически выполняет все вышеперечисленное. Прежде всего, вам нужно добавить инжектор в путь к классам Gradle buildscript. Ваш скрипт сборки gradle должен выглядеть так
и так далее......