Existe-t-il un moyen de créer une application Android pour télécharger et utiliser une bibliothèque Java à l'exécution)?
Voici un exemple:
Imaginez que l'application ait besoin de faire des calculs en fonction des valeurs d'entrée. L'application demande ces valeurs d'entrée, puis vérifie si les Classe
s ou Method
s requis sont disponibles.
Sinon, il se connecte à un serveur, télécharge la bibliothèque requise et la charge lors de l'exécution pour appeler les méthodes requises à l'aide de 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.
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 je l'ai fait:
Je me demandais si c'était faisable alors j'ai écrit le cours suivant:
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
.
J'ai ensuite écrit le "programme stupide" ci-dessous, après avoir supprimé MyClass
de mon projet Eclipse et l'avoir 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
utilisez-le pour extraire la classe MyClass
de "/sdcard/shlublu.jar"
et stockez cette classe dans le répertoire privé de l'application "dex"
(stockage interne du téléphone).
Ensuite, il crée une instance de MyClass
et appelle doSomething()
sur l'instance créée.
Et ça marche ... Je vois les traces définies dans MyClass
dans mon LogCat:
J'ai essayé à la fois un émulateur 2.1 et mon téléphone portable HTC physique (qui fonctionne 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 à la dure (laid Object
casts, Method.invoke()
ugly calls ...), mais il doit être possible de jouer avec Interface
s pour rendre quelque chose de plus propre.
Sensationnel. Je suis le premier surpris. Je m'attendais à un SecurityException
.
Quelques faits pour aider à enquêter davantage:
La réponse de Shlublu est vraiment sympa. Quelques petites choses qui pourraient aider un débutant:
dans le manifeste du projet de bibliothèque, assurez-vous d'avoir: <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 j'ai dû mettre quelque chose ici)
Pour créer le fichier .dex, exécutez simplement le projet de bibliothèque en tant que Android (pour la compilation) et recherchez le fichier .apk dans le dossier bin du projet.
Les autres étapes sont les mêmes que celles décrites par Shlublu.
Si vous conservez vos fichiers .DEX dans la mémoire externe du téléphone, comme la carte SD (non recommandé! Toute application avec les mêmes autorisations peut facilement écraser votre classe et effectuer une attaque par injection de code) assurez-vous d'avoir donné le autorisation de l'application de lire la mémoire externe. L'exception qui est levée si c'est le cas est 'ClassNotFound' qui est assez trompeur, mettez quelque chose comme ceci dans votre manifeste (consultez Google pour la version la plus récente).
<manifest ...>
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE"
Android:maxSdkVersion="18" />
...
</manifest>
Je ne sais pas si vous pouvez y parvenir en chargeant dynamiquement Java code. Peut-être pouvez-vous essayer d'incorporer un moteur de script votre code comme rhino qui peut exécuter Java des scripts qui peuvent être téléchargés et mis à jour dynamiquement.
bien sûr, c'est possible. apk qui n'est pas installé peut être invoqué par l'hôte Android application.généralement, résoudre le cycle de vie de la ressource et de l'activité, puis, peut charger jar ou apk dynamiquement. détail, veuillez vous référer à mes recherches 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, regardez maintenant un code clé:
/**
* 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.
Techniquement, cela devrait fonctionner, mais qu'en est-il des règles de Google? De: play.google.com/intl/en-GB/about/developer-content-policy-pr int
Une application distribuée via Google Play ne peut pas se modifier, se remplacer ou se mettre à jour par une méthode autre que le mécanisme de mise à jour de Google Play. De même, une application ne peut 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 au code qui s'exécute sur une machine virtuelle et a un accès limité aux API Android (comme JavaScript dans une WebView ou un navigateur).
Je pense que la réponse @Shlublu est correcte mais je veux juste souligner certains points clés.
Pour charger l'interface utilisateur à partir d'un pot externe, nous pouvons utiliser un fragment. Créez l'instance du fragment et incorporez-la à l'activité. Mais assurez-vous que fragment crée l'interface utilisateur dynamiquement comme indiqué ci-dessous.
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;
}
}