Динамическая загрузка ресурсов с других APK - PullRequest
2 голосов
/ 20 сентября 2011

Я думаю о способе загрузки ресурсов из других APK.Например, у меня есть SDK (это просто APK, он не установлен в памяти телефона) на SDCard, который содержит ресурсы, которые я хочу использовать в своем установленном приложении.Можно ли загрузить ресурсы из res / x из APK, хранящиеся на SD-карте, в мое установленное приложение (например, макет, изображения, строки, ...).

Спасибо

Ответы [ 2 ]

3 голосов
/ 23 мая 2017

После прочтения и отладки некоторого кода AOSP мне наконец-то удалось динамически загружать ресурсы.Пошаговое руководство:

  1. Создайте новый проект Android, я назвал его «Динамическая загрузка APK», указал whatever.dynamicapkloading идентификатор приложения и оставил имя модуля «приложение».

  2. Создайте еще один модуль приложения Android без каких-либо действий.Я сделал это в том же проекте, но это зависит от вас.Я назвал модуль «динамическим» и установил идентификатор приложения на whatever.dynamic.

  3. Удалите все ненужные вещи из «динамического» проекта.Я удалил элементы рисования в лаунчере вместе с любыми другими ресурсами, а также удалил зависимость AppCompat из build.gradle.Мое манифестное содержание выглядело минималистично, например: <application />.

  4. Добавить код в «динамический» проект.Например:

    package whatever.dynamic;
    
    import android.content.Context;
    import android.widget.Toast;
    
    public final class Code implements Runnable {
    
        private final Context context;
    
        public Code(Context context) {
            this.context = context;
        }
    
        @Override
        public void run() {
            Toast.makeText(context, "Running dynamic code!",
                    Toast.LENGTH_SHORT).show();
        }
    }
    
  5. Добавьте некоторые ресурсы в «динамический» проект.Я создал strings.xml:

    <resources>
        <string name="someString">This is a dynamically loaded string.</string>
        <string name="anotherString">Lol! That\'s it.</string>
    </resources>
    
  6. Добавьте его для запуска конфигураций.Установите параметры запуска / запуска ничего.Build -> Build APK!На этом этапе у меня есть файл размером 4,9 КБ, который называется dynamic-debug.apk.

  7. Переместите этот файл, dynamic/build/outputs/apk/dynamic-debug.apk, в активы нашего основного проекта, то есть в app/src/main/assets/.

    .
  8. Создать / открыть класс, который будет загружать код и ресурсы.Скажем, это будет DynamicActivity.

  9. Напишите код, который будет копировать APK из ресурсов в личный каталог приложения.Это скучно и тривиально, вы знаете:

    File targetApk = new File(getDir("dex", Context.MODE_PRIVATE), "app.apk");
    // copy APK from assets to private directory
    // remove this condition in order to keep dynamic APK fresh
    if (!targetApk.exists() || !targetApk.isFile()) {
        try (BufferedInputStream bis = new BufferedInputStream(
                getAssets().open("dynamic-debug.apk"));
             OutputStream dexWriter = new BufferedOutputStream(
                    new FileOutputStream(targetApk))) {
    
            byte[] buf = new byte[4096];
            int len;
            while((len = bis.read(buf, 0, 4096)) > 0) {
                dexWriter.write(buf, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
  10. Напишите код, который загрузит необходимый класс и создаст его экземпляр.

    PathClassLoader loader = new PathClassLoader(
            targetApk.getAbsolutePath(), getClassLoader());
    Class<?> dynamicClass = loader.loadClass("whatever.dynamic.Code");
    Constructor<?> ctor = dynamicClass.getConstructor(Context.class);
    dynamicInstance = (Runnable) ctor.newInstance(this);
    
  11. Writeкод, который будет загружать ресурсы из указанного APK.Это был тяжелый!Все конструкторы и методы общедоступны, но они скрыты.Я написал это рефлексивно, но во избежание этого вы можете либо скомпилировать свой код с использованием полного фреймворка jar, либо написать часть кода на Smali.

    AssetManager assets = AssetManager.class.newInstance();
    Method addAssetPath = AssetManager.class
            .getMethod("addAssetPath", String.class);
    if (addAssetPath.invoke(assets, targetApk.getAbsolutePath()) ==
            Integer.valueOf(0)) {
        throw new RuntimeException();
    }
    
    Class<?> resourcesImpl = Class.forName("android.content.res.ResourcesImpl");
    Class<?> daj = Class.forName("android.view.DisplayAdjustments");
    Object impl = resourcesImpl
            .getConstructor(AssetManager.class, DisplayMetrics.class,
                    Configuration.class, daj)
            .newInstance(assets, getResources().getDisplayMetrics(),
                    getResources().getConfiguration(), daj.newInstance());
    
    dynamicResources = Resources.class.getConstructor(ClassLoader.class)
            .newInstance(loader);
    Method setImpl = Resources.class.getMethod("setImpl",
            Class.forName("android.content.res.ResourcesImpl"));
    setImpl.invoke(dynamicResources, impl);
    
  12. Используйте эти ресурсы!Есть два способа получения идентификаторов ресурсов.

    int someStringId = dynamicResources.getIdentifier(
            "someString", "string", "whatever.dynamic");
    String someString = dynamicResources.getString(someStringId);
    
    Class<?> rString = Class.forName("whatever.dynamic.R$string", true, loader);
    anotherStringId = rString.getField("anotherString").getInt(null);
    String anotherString = dynamicResources.getString(anotherStringId);
    

Вот и все!Это сработало для меня. Полный код DynamicActivity.java

В реальных проектах вы должны подписывать APK при сборке и проверять его подпись при загрузке.И, конечно же, никогда не сохраняйте его на SD-карте, иначе ваш APK может быть подделан!

Вы также должны статически кэшировать идентификаторы ресурсов, но заново создавать Resources и повторно запрашивать значения ресурсов при изменении конфигурации.

Полезные ссылки:

2 голосов
/ 11 октября 2011

Вы можете использовать PackageManager.getResourcesForApplication для доступа к ресурсам, найденным в другом apk.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...