Динамически загружать файл DEX на Android 5.0
До Android 5.0 я мог загружать файлы DEX динамически, используя DexClassLoader и вызывая loadClass()
метод, но с последней версией Android я получаю ClassNotFoundException
,
Вот что я делаю:
Создать файл DEX.
../android-sdk/android-sdk-linux_86/build-tools/21.1.1/dx --dex --output=bin/output.dex bin/output.jar
Создайте DexClassLoader.
DexClassLoader cl = new DexClassLoader( dexFile.getAbsolutePath(), odexFile.getAbsolutePath(), null, mContext.getClassLoader());
Вызов
cl.loadClass("myMethod");
Мне известно, что ART использует dex2oat для генерации ELF-файла, загружаемого ART, но на шаге 2 я генерирую ODEX-файл, поэтому мне не нужно делать то, что нужно сделать в ART для загрузки DEX-файла во время выполнения, может кто-нибудь помочь мне?
2 ответа
Обновить
Это работает как на Dalvik, так и на ART: new DexClassLoader(jarredDex.getAbsolutePath(), context.getDir("outdex", Context.MODE_PRIVATE).getAbsolutePath(), null, context.getClassLoader());
где jarredDex
это JAR-файл с classes.dex
, Банку можно получить, запустив dx --dex --output=filename.jar your/classes/dir
,
Оригинальный ответ
Я взял пример кода из этой статьи. Но АРТ использует PathClassLoader
вместо Далвика DexClassLoader
, Этот код протестирован на эмуляторе с Android 6 и на Xiaomi с Android 5.1 и работает нормально:
// Before the secondary dex file can be processed by the DexClassLoader,
// it has to be first copied from asset resource to a storage location.
File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE), SECONDARY_DEX_NAME);
try (BufferedInputStream bis = new BufferedInputStream(getAssets().open(SECONDARY_DEX_NAME));
OutputStream dexWriter = new BufferedOutputStream(new FileOutputStream(dexInternalStoragePath))) {
byte[] buf = new byte[BUF_SIZE];
int len;
while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
dexWriter.write(buf, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
try {
PathClassLoader loader = new PathClassLoader(dexInternalStoragePath.getAbsolutePath(), getClassLoader());
Class<?> toasterClass = loader.loadClass("my.package.ToasterImpl");
Toaster toaster = (Toaster) toasterClass.newInstance();
toaster.show(this, "Success!");
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
Это проблема с созданием файла dex. Измените команду, как показано ниже, тогда она будет работать во всех версиях Android.
dx.bat --dex --output path/output.jar path/input.jar
Вышеупомянутый метод в Windows создаст файл.jar с файлом classes.dex внутри этого файла jar. DexClassLoader ищет файл classes.dex внутри, поэтому он выбрасывает ClassNotFoundException