Можно ли динамически загружать библиотеку во время выполнения из приложения Android?

Есть ли способ сделать приложение Android для загрузки и использования библиотеки Java во время выполнения?

Вот пример:

Представьте, что приложение должно выполнять некоторые вычисления в зависимости от входных значений. Приложение запрашивает эти входные значения, а затем проверяет наличие доступных Classe или Method.

Если нет, он подключается к серверу, загружает необходимую библиотеку и загружает ее во время выполнения, чтобы вызвать необходимые методы с использованием методов отражения. Реализация может измениться в зависимости от различных критериев, таких как пользователь, загружающий библиотеку.

Ответ 1

Извините, я опаздываю, и вопрос уже принят, но да, вы можете загружать и выполнять внешние библиотеки. Вот как я это сделал:

Мне было интересно, возможно ли это, поэтому я написал следующий класс:

package org.shlublu.android.sandbox;

import android.util.Log;

public class MyClass {
    public MyClass() {
        Log.d(MyClass.class.getName(), "MyClass: constructor called.");
    }

    public void doSomething() {
        Log.d(MyClass.class.getName(), "MyClass: doSomething() called.");
    }
}

И я упаковал его в файл DEX, который я сохранил на SD-карте устройства, как /sdcard/shlublu.jar.

Затем я написал "тупую программу" ниже, удалив MyClass из моего проекта Eclipse и очистив его:

public class Main extends Activity {

    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        try {
            final String libPath = Environment.getExternalStorageDirectory() + "/shlublu.jar";
            final File tmpDir = getDir("dex", 0);

            final DexClassLoader classloader = new DexClassLoader(libPath, tmpDir.getAbsolutePath(), null, this.getClass().getClassLoader());
            final Class<Object> classToLoad = (Class<Object>) classloader.loadClass("org.shlublu.android.sandbox.MyClass");

            final Object myInstance  = classToLoad.newInstance();
            final Method doSomething = classToLoad.getMethod("doSomething");

            doSomething.invoke(myInstance);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Он в основном загружает класс MyClass следующим образом:

  • создать DexClassLoader

  • используйте его для извлечения класса MyClass из "/sdcard/shlublu.jar"

  • и сохраните этот класс в приватном каталоге приложения "dex" (внутренняя память телефона).

Затем он создает экземпляр MyClass и вызывает doSomething() в созданном экземпляре.

И он работает... Я вижу следы, определенные в MyClass в моем LogCat:

enter image description here

Я пробовал как на эмуляторе 2.1, так и на моем физическом мобильном телефоне HTC (который работает под управлением Android 2.2 и который НЕ внедрен).

Это означает, что вы можете создавать внешние DEX файлы для загрузки и выполнения приложения. Здесь это было сложным (уродливые Object приведения, Method.invoke() уродливые звонки...), но с помощью Interface должно быть возможно сделать что-то более чистое.

Ого. Я первый удивлен. Я ожидал SecurityException.

Некоторые факты, которые помогут расследовать больше:

  • Мой DEX shlublu.jar был подписан, но не мое приложение
  • Мое приложение было выполнено из Eclipse/USB-соединения. Таким образом, это unsigned APK, скомпилированный в режиме DEBUG.

Ответ 2

Shlublu anwser действительно приятный. Некоторые мелочи, хотя это поможет новичкам:

  • для файла библиотеки "MyClass" создайте отдельный проект приложения для Android, в котором файл MyClass есть только файл в папке src (там также должны быть другие файлы, такие как project.properties, manifest, res и т.д.)
  • в манифесте проекта библиотеки убедитесь, что у вас есть: <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NotExecutable" android:label="@string/app_name"> </activity> </application> ( ".NotExecutable" не является зарезервированным словом. Просто я должен что-то здесь положить)

  • Для создания файла .dex просто запустите проект библиотеки как приложение для Android (для компиляции) и найдите файл .apk из папки bin проекта.

  • Скопируйте файл .apk на свой телефон и переименуйте его как файл shlublu.jar(APK на самом деле специализируется на банке)

Другие шаги такие же, как описано Shlublu.

  • Большое спасибо Shlublu за сотрудничество.

Ответ 3

Я не уверен, что вы можете достичь этого, динамически загружая Java-код. Возможно, вы можете попробовать внедрить script движок вашего кода, например, rhino, который может выполнять java-скрипты, которые можно динамически загружать и обновлять.

Ответ 4

Конечно, это возможно. apk, который не установлен, может быть вызван хостом android application.generally, разрешить ресурс и жизненный цикл активности, а затем динамически загружать jar или apk. подробно см. в моем исследовании с открытым исходным кодом о github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md

также требуется DexClassLoader и отражение, теперь посмотрите на некоторые key code:

/**
 * Load a apk. Before start a plugin Activity, we should do this first.<br/>
 * NOTE : will only be called by host apk.
 * @param dexPath
 */
public DLPluginPackage loadApk(String dexPath) {
    // when loadApk is called by host apk, we assume that plugin is invoked by host.
    mFrom = DLConstants.FROM_EXTERNAL;

    PackageInfo packageInfo = mContext.getPackageManager().
            getPackageArchiveInfo(dexPath, PackageManager.GET_ACTIVITIES);
    if (packageInfo == null)
        return null;

    final String packageName = packageInfo.packageName;
    DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
    if (pluginPackage == null) {
        DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
        AssetManager assetManager = createAssetManager(dexPath);
        Resources resources = createResources(assetManager);
        pluginPackage = new DLPluginPackage(packageName, dexPath, dexClassLoader, assetManager,
                resources, packageInfo);
        mPackagesHolder.put(packageName, pluginPackage);
    }
    return pluginPackage;
}

ваши требования частично отчасти зависят от проекта с открытым исходным кодом, упомянутого в начале.

Ответ 5

Если вы сохраняете свои файлы .DEX во внешней памяти телефона, например, SD-карту (не рекомендуется! Любое приложение с одинаковыми разрешениями может легко перезаписать ваш класс и выполнить атаку инъекции кода) вы получили разрешение на чтение внешней памяти. Исключением, которое возникает, если это так, является "ClassNotFound", который довольно вводит в заблуждение, добавьте в манифесте что-то вроде следующего (обратитесь к Google для самой последней версии).

<manifest ...>

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="18" />
    ...
</manifest>

Ответ 6

Технически нужно работать, но как насчет правил Google? От: play.google.com/intl/ru-RU/about/developer-content-policy-pr int

Приложение, распространяемое через Google Play, не может изменять, заменять или обновлять используя любой метод, отличный от механизма обновления Google Play. Аналогично, приложение не может загружать исполняемый код (например, dex, JAR,.so файлы) из источника, отличного от Google Play. Это ограничение не применять к коду, который работает на виртуальной машине, и имеет ограниченный доступ к Android API (например, JavaScript в WebView или браузере).