Можно ли динамически загружать библиотеку во время выполнения из приложения Android?
есть ли способ сделать приложение для Android для загрузки и использования библиотеки Java во время выполнения?
вот пример:
представьте, что приложение должно сделать некоторые вычисления в зависимости от входных значений. Приложение запрашивает эти входные значения, а затем проверяет, требуется ли Classeили Methods доступны.
если нет, он подключается к серверу, загружает необходимую библиотеку и загружает ее во время выполнения для вызова требуемой методы, использующие методы отражения. Реализация может меняться в зависимости от различных критериев, таких как пользователь, который загружает библиотеки.
7 ответов:
Извините, я опоздал и на вопрос уже есть принятый ответ, но да, вы можете загружать и выполнять внешние библиотеки. Вот как я это сделал:
мне было интересно, возможно ли это, поэтому я написал следующий класс:
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"private directory (внутренняя память телефона).затем он создает экземпляр
MyClassи вызываетdoSomething()на созданный экземпляр.и это работает... Я вижу следы, определенные в
MyClassв моем LogCat:
я пробовал как на эмуляторе 2.1, так и на моем физическом мобильном телефоне HTC (который работает под управлением Android 2.2 и который не укоренен).
это означает, что вы можете создавать внешние DEX-файлов приложения, чтобы загрузить и выполнить их. Здесь это было сделано нелегко (некрасиво
Objectслепки,Method.invoke()уродливые призывы...), но это должно быть возможным играйте сInterfaces, чтобы сделать что-то чище.Вау. Я первый удивился. Я ожидал
SecurityException.некоторые факты, чтобы помочь в расследовании больше:
- мой Декс шлублу.банку был подписан, но не мое приложение
- мое приложение было выполнено из подключения Eclipse / USB. Итак, это неподписанный APK, скомпилированный в режиме отладки
anwser Shlublu действительно хорош. Некоторые мелочи, хотя это помогло бы новичку:
- для файла библиотеки "MyClass" сделайте отдельный проект приложения для Android, который имеет файл MyClass как только файл в папке src (другие вещи, такие как project.свойства, манифест, 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 на самом деле является специализацией jar, хотя)
другие шаги, такие же, как описано Shlublu.
- большое спасибо Шлублу за сотрудничество.
Я не уверен, что вы можете достичь этого путем динамической загрузки кода java. Может быть, вы можете попробовать встраивание скрипта код, как носорог, который может выполнять Java-скрипты, которые могут быть динамически загружены и обновлены.
конечно, это возможно. apk, который не установлен может быть вызван приложения для Андроид.как правило, жизненный цикл resolve resource и activity может динамически загружать jar или apk. подробно, пожалуйста, обратитесь к моему исследованию с открытым исходным кодом на github:https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md
кроме того, DexClassLoader и отражение необходимо, теперь посмотрите на некоторый код ключа:
/** * 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; }ваши требования только частично функция в проекте с открытым исходным кодом, упомянутом в начале.
Если вы держите ваш .Файлы DEX во внешней памяти телефона, такие как SD-карта (не рекомендуется! Любое приложение с такими же разрешениями может легко перезаписать ваш класс и выполнить атаку на внедрение кода) убедитесь, что вы дали приложению разрешение на чтение внешней памяти. Исключение, которое выбрасывается, если это так, является "ClassNotFound", что довольно вводит в заблуждение, поместите что-то вроде следующего в свой манифест (обратитесь к Google для наиболее актуальной версии).
<manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" /> ... </manifest>
технически должно работать, но как насчет правил Google? От кого: play.google.com/intl/en-GB/about/developer-content-policy-print
приложение, распространяемое через Google Play, не может изменять, заменять или обновлять сам с помощью любого метода, кроме механизма обновления Google Play. Кроме того, приложение не может загружать исполняемый код (например, dex, JAR,. so файлы) из источника, отличного от Google Play. Это ограничение не применить к коду, который выполняется на виртуальной машине и имеет ограниченный доступ к API Android (например, JavaScript в веб-представлении или браузере).
Я думаю, что @Shlublu ответ правильный, но я просто хочу выделить некоторые ключевые моменты.
- мы можем загрузить любые классы из внешнего файла jar и apk.
- в любом случае, мы можем загрузить активность из внешнего jar, но мы не можем запустить его из-за концепции контекста.
для загрузки пользовательского интерфейса из внешнего jar мы можем использовать фрагмент. Создайте экземпляр фрагмента и внедрите его в действие. Но убедитесь, что фрагмент создает пользовательский интерфейс динамически как показано ниже.
public class MyFragment extends Fragment { @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); LinearLayout layout = new LinearLayout(getActivity()); layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); Button button = new Button(getActivity()); button.setText("Invoke host method"); layout.addView(button, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); return layout; } }

Comments