Динамически загружать файл DEX на Android 5.0

До Android 5.0 я мог загружать файлы DEX динамически, используя DexClassLoader и вызывая loadClass() метод, но с последней версией Android я получаю ClassNotFoundException,

Вот что я делаю:

  1. Создать файл DEX.

    ../android-sdk/android-sdk-linux_86/build-tools/21.1.1/dx --dex --output=bin/output.dex  bin/output.jar
    
  2. Создайте DexClassLoader.

    DexClassLoader cl = new DexClassLoader(
    dexFile.getAbsolutePath(),
    odexFile.getAbsolutePath(),
    null,
    mContext.getClassLoader());
    
  3. Вызов 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

Другие вопросы по тегам