¿Es posible cargar dinámicamente una biblioteca en tiempo de ejecución desde una aplicación Android?


¿ Hay alguna forma de hacer una aplicación Android para descargar y usar una biblioteca Java en tiempo de ejecución?

Aquí hay un ejemplo:

Imagine que la aplicación necesita hacer algunos cálculos dependiendo de los valores de entrada. La aplicación solicita estos valores de entrada y luego comprueba si los Classes o Methods requeridos están disponibles.

Si no, se conecta a un servidor, descarga la biblioteca necesaria y la carga en tiempo de ejecución para llamar a los métodos necesarios utilizando reflexión técnica. La implementación podría cambiar dependiendo de varios criterios, como el usuario que está descargando la biblioteca.

Author: Shlublu, 2011-07-28

7 answers

Lo siento, llego tarde y la pregunta ya tiene una respuesta aceptada, pero , puede descargar y ejecutar bibliotecas externas. Así es como lo hice:

Me preguntaba si esto era factible, así que escribí la siguiente clase:{[19]]}

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.");
    }
}

Y lo empaqueté en un archivo DEX que guardé en la tarjeta SD de mi dispositivo como /sdcard/shlublu.jar.

Luego escribí el" programa estúpido " a continuación, después de haber eliminado MyClass de mi proyecto Eclipse y limpiarlo:

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();
        }
    }
}

It básicamente carga la clase MyClass de esa manera:

  • Crear un DexClassLoader

  • Utilícelo para extraer la clase MyClass de "/sdcard/shlublu.jar"

  • Y almacena esta clase en el directorio privado "dex" de la aplicación (almacenamiento interno del teléfono).

Luego, crea una instancia de MyClass e invoca doSomething() en la instancia creada.

Y funciona... Veo las trazas definidas en MyClass en mi LogCat:

introduzca la descripción de la imagen aquí

He probado tanto en un emulador 2.1 y en mi teléfono celular HTC física (que está ejecutando Android 2.2 y que NO está arraigado).

Esto significa que puede crear archivos DEX externos para que la aplicación los descargue y ejecute. Aquí se hizo de la manera difícil (ugly Object casts, Method.invoke() ugly calls...), pero debe ser posible jugar con Interfaces para hacer algo más limpio.

Wow. Soy la primera sorprendida. Estaba esperando un SecurityException.

Algunos hechos para ayudar a investigar más:

  • Mi DEX shlublu.jar fue firmado, pero no mi aplicación
  • Mi aplicación se ejecutó desde la conexión Eclipse / USB. Así que este es un APK sin firmar compilado en modo de depuración
 83
Author: Shlublu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-29 06:08:36

Shlublu's anwser es realmente agradable. Sin embargo, algunas cosas pequeñas que ayudarían a un principiante:

  • para el archivo de biblioteca "MyClass" haga un proyecto de aplicación Android separado que tenga el archivo MyClass como único archivo en la carpeta src (otras cosas, como el proyecto.propiedades, manifiesto, res, etc. también debe estar allí)
  • En el manifiesto del proyecto de biblioteca, asegúrese de tener: <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NotExecutable" android:label="@string/app_name"> </activity> </application> (".NotExecutable" no es una palabra reservada. Es sólo que tenía que poner algo aquí)

  • Por hacer el .archivo dex, simplemente ejecute el proyecto de la biblioteca como aplicación Android (para la compilación )y busque.archivo apk de la carpeta bin del proyecto.

  • Copie el .archivo apk a su teléfono y cambiarle el nombre como shlublu.archivo jar (un APK es en realidad una especialización de un jar, aunque)

Otros pasos son los mismos descritos por Shlublu.

  • Muchas gracias a Shlublu por su cooperación.
 13
Author: Pätris Halapuu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-09 15:36:42

No estoy seguro de si puede lograr esto cargando dinámicamente el código java. Puede ser que usted puede intentar incrustar un motor de script su código como rhino que puede ejecutar scripts Java que se pueden descargar y actualizar dinámicamente.

 1
Author: Naresh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-28 11:03:34

Claro, es posible. apk que no está instalado puede ser invocado por la aplicación host Android.generalmente,resolver el ciclo de vida del recurso y la actividad,entonces, puede cargar jar o apk dinámicamente. detalle, por favor refiérase a mi investigación de código abierto en github: https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md

Además, se necesita DexClassLoader y reflexión, ahora mira algún código clave:

/**
 * 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;
}

Sus demandas son solo parcialmente de función en el código abierto proyecto mencionado al principio.

 1
Author: singwhatiwanna,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-06 06:17:06

Si usted está guardando su.Archivos DEX en la memoria externa del teléfono, como la tarjeta SD (¡no se recomienda! Cualquier aplicación con los mismos permisos puede sobrescribir fácilmente su clase y realizar un ataque de inyección de código) asegúrese de que ha dado permiso a la aplicación para leer la memoria externa. La excepción que se lanza si este es el caso es 'ClassNotFound', que es bastante engañosa, ponga algo como lo siguiente en su manifiesto (consulte a Google para la versión más actualizada).

<manifest ...>

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="18" />
    ...
</manifest>
 1
Author: user4356087,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-13 02:13:32

Técnicamente debería funcionar, pero ¿qué pasa con las reglas de Google? De: play.google.com/intl/en-GB/about/developer-content-policy-print

Una aplicación distribuida a través de Google Play no puede modificar, reemplazar o actualizar usando cualquier método que no sea el mecanismo de actualización de Google Play. Del mismo modo, una aplicación no puede descargar código ejecutable (por ejemplo, dex, JAR,. so archivos) de una fuente que no sea Google Play. Esta restricción no aplicar al código que se ejecuta en una máquina virtual y tiene limitaciones acceso a API de Android (como JavaScript en una vista web o navegador).

 1
Author: Marcin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-07 10:40:44

Creo que la respuesta de @Shlublu es correcta, pero solo quiero destacar algunos puntos clave.

  1. Podemos cargar cualquier clase desde el jar externo y el archivo apk.
  2. De cualquier manera, podemos cargar Actividad desde jar externo pero no podemos iniciarla debido al concepto de contexto.
  3. Para cargar la interfaz de usuario desde jar externo podemos usar fragment. Cree la instancia del fragmento e insértela en la Actividad. Pero asegúrese de que fragment crea la interfaz de usuario dinámicamente como se indica debajo.

    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;
     }
    }
    
 0
Author: User10001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-20 10:04:11