73 votes

Est-il possible de charger dynamiquement une bibliothèque au moment de l'exécution à partir d'une application Android ?

Existe-t-il un moyen de faire en sorte qu'une application Android télécharge et utilise une bibliothèque Java au moment de l'exécution ?

Voici un exemple :

Imaginons que l'application doive effectuer certains calculs en fonction des valeurs d'entrée. L'application demande ces valeurs d'entrée et vérifie ensuite si les valeurs requises sont correctes. Classe ou Method sont disponibles.

Sinon, il se connecte à un serveur, télécharge la bibliothèque nécessaire et la charge au moment de l'exécution pour appeler les méthodes requises en utilisant des techniques de réflexion. L'implémentation peut changer en fonction de divers critères tels que l'utilisateur qui télécharge la bibliothèque.

96voto

Shlublu Points 6199

Désolé, je suis en retard et la question a déjà une réponse acceptée, mais oui vous pouvez télécharger et exécuter des bibliothèques externes. Voici comment j'ai procédé :

Je me demandais si cela était possible et j'ai donc écrit la classe suivante :

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

Et je l'ai emballé dans un fichier DEX que j'ai enregistré sur la carte SD de mon appareil en tant que /sdcard/shlublu.jar .

Ensuite, j'ai écrit le "programme stupide" ci-dessous, après avoir supprimé MyClass de mon projet Eclipse et l'a nettoyé :

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

Il charge essentiellement la classe MyClass de cette façon :

  • créer un DexClassLoader

  • l'utiliser pour extraire la classe MyClass de "/sdcard/shlublu.jar"

  • et stocker cette classe dans le répertoire de l'application "dex" répertoire privé (mémoire interne du téléphone).

Ensuite, il crée une instance de MyClass et invoque doSomething() sur l'instance créée.

Et ça marche... Je vois les traces définies dans MyClass dans mon LogCat :

enter image description here

J'ai essayé à la fois sur un émulateur 2.1 et sur mon téléphone portable HTC physique (qui fonctionne sous Android 2.2 et qui n'est PAS enraciné).

Cela signifie que vous pouvez créer des fichiers DEX externes pour que l'application les télécharge et les exécute. Ici, cela a été fait de la manière forte (laid Object des plâtres, Method.invoke() vilains appels...), mais il doit être possible de jouer avec des Interface pour rendre quelque chose plus propre.

Wow. Je suis le premier surpris. Je m'attendais à un SecurityException .

Quelques faits pour aider à enquêter davantage :

  • Mon DEX shlublu.jar a été signé, mais pas mon application.
  • Mon application a été exécutée à partir d'Eclipse / connexion USB. Il s'agit donc d'un APK non signé compilé en mode DEBUG.

4 votes

Super ! C'est vraiment ce que je cherchais ! Comme vous pouvez le voir ici ( groups.google.com/group/Android-security-discuss/browse_thread/ ) il n'y a pas vraiment de problème de sécurité car le code s'exécutera avec la permission de votre application.

0 votes

C'est tout à fait logique, même si c'est surprenant à première vue. Merci de m'avoir prévenu !

3 votes

Jetez aussi un coup d'œil à ceci : Android-developers.blogspot.com/2011/07/

16voto

Pätris Halapuu Points 25

La réponse de Shlublu est vraiment bien. Quelques petites choses cependant qui pourraient aider un débutant :

  • pour le fichier de bibliothèque "MyClass", créez un projet d'application Android distinct dont le fichier MyClass est le seul fichier du dossier src (les autres éléments, tels que project.properties, manifest, res, etc. doivent également s'y trouver).

  • dans le manifeste du projet de bibliothèque, assurez-vous que vous avez : <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".NotExecutable" android:label="@string/app_name"> </activity> </application> (".NotExecutable" n'est pas un mot réservé. C'est juste que je devais mettre quelque chose ici)

  • Pour créer le fichier .dex, il suffit de lancer le projet de bibliothèque en tant qu'application Android (pour la compilation) et de localiser le fichier .apk dans le dossier bin du projet.

  • Copiez le fichier .apk sur votre téléphone et renommez-le en fichier shlublu.jar (un APK est en fait une spécialisation d'un jar).

Les autres étapes sont les mêmes que celles décrites par Shlublu.

  • Un grand merci à Shlublu pour sa coopération.

0 votes

Vous êtes le bienvenu Pätris, et ce fut un plaisir de discuter avec vous. (J'ai initialement écrit ce commentaire lorsque j'ai +1é votre réponse mais j'ai visiblement oublié d'appuyer sur le bouton "Ajouter un commentaire"...)

0 votes

J'apprécie l'astuce, mais reproduire l'exemple de @Shlublu ci-dessus en utilisant votre méthode donne un apk de ~380kB alors que si je crée simplement un projet de bibliothèque, j'obtiens un jar de 3kB. Cependant, l'approche par bibliothèque nécessite une commande supplémentaire, dx --dex --keep-classes --output=newjar.jar libproject.jar . Il y a peut-être un moyen de rendre cette étape dex automatique lorsque Eclipse construit la bibliothèque.

0 votes

Je ne comprends pas pourquoi nous utilisons le fichier apk ! Je suis nouveau dans xamarin Android. Je veux créer un projet de bibliothèque dll (Class Library) et ensuite l'utiliser dans mon application principale au moment de l'exécution. Comment est-il possible de faire cela ?

3voto

Marcin Points 240

Techniquement, cela devrait fonctionner mais qu'en est-il des règles de Google ? De : play.google.com/intl/fr-GB/about/developer-content-policy-print

Une application distribuée via Google Play ne peut pas être modifiée, remplacée ou mise à jour par une méthode autre que le mécanisme de mise à jour de Google Play. en utilisant une méthode autre que le mécanisme de mise à jour de Google Play. De même, une application ne doit pas télécharger de code exécutable (par exemple, des fichiers dex, JAR, .so ) à partir d'une source autre que Google Play. Cette restriction ne s'applique pas s'applique pas au code qui s'exécute dans une machine virtuelle et qui a un accès limité aux API Android (comme JavaScript dans une WebView ou un navigateur).

2 votes

C'est tout à fait exact. Cependant, le PO a demandé si c'était techniquement faisable et peut vouloir faire une telle chose pour son propre usage sans utiliser Google Play du tout. Par ailleurs, il semble qu'une application Google Play puisse intégrer des jarres qui seraient écrites sur le stockage local au moment du déploiement et qui seraient chargées ultérieurement par l'application sans enfreindre cette règle (même si ce n'est pas une bonne idée). Quoi qu'il en soit, vous avez raison de rappeler aux développeurs qu'une application téléchargée depuis Google Play ne doit pas télécharger de code depuis un autre site. Il devrait s'agir d'un commentaire.

1voto

Naresh Points 1143

Je ne suis pas sûr que vous puissiez y parvenir en chargeant dynamiquement du code java. Peut-être pouvez-vous essayer d'intégrer un moteur script dans votre code comme rhino qui peut exécuter des scripts java qui peuvent être téléchargés et mis à jour dynamiquement.

0 votes

Merci. Je vais étudier cette approche (je ne connaissais pas les scripts sur Android avant aujourd'hui). Plus d'informations (si quelqu'un en a besoin) peuvent être trouvées ici : google-opensource.blogspot.com/2009/06/

1 votes

@Naresh Oui, vous pouvez également télécharger et exécuter du code compilé Java. Voir ma réponse dans cette page. Je ne m'attendais pas à cela en fait.

1 votes

Merci @shlublu, cela semble intéressant :). Le seul problème auquel je pense est qu'il repose fortement sur les réflexions ou que vous devez commencer à écrire des wrappers. Je vois beaucoup de problèmes de gestion, si nous devons écrire tout le code de cette façon. C'est pourquoi je pense que les scripts sont un moyen simple et efficace de gérer la logique métier.

1voto

singwhatiwanna Points 21

Bien sûr, c'est possible. apk qui n'est pas installé peut être invoqué par l'application Android hôte. généralement, résoudre la ressource et le cercle de vie de l'activité, puis, peut charger jar ou apk dynamiquement. Pour plus de détails, veuillez vous référer à ma recherche open source sur Github : https://github.com/singwhatiwanna/dynamic-load-apk/blob/master/README-en.md

Aussi, DexClassLoader et la réflexion sont nécessaires, maintenant regardez quelques codes clés :

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

vos demandes ne sont que partiellement fonctionnelles dans le projet open source mentionné au début.

0 votes

J'ai eu quelques erreurs en utilisant votre projet, il y a toujours le même problème avec mon projet précédent. ClassNotFound when startActivity

0 votes

Je suis nouveau dans le développement Android. Je ne sais pas pourquoi nous devrions utiliser l'apk pour charger dynamiquement des méthodes ! Je veux charger ma dll créée par le projet Class Library dynamiquement à l'exécution.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X