Question Hébergement d'un exécutable dans une application Android


Je travaille sur une application Android qui dépend d'un binaire ELF: notre code Java interagit avec ce binaire pour faire avancer les choses. Ce le runtime doit être démarré et arrêté au démarrage de l'application et sortie d'application / à la demande.

Des questions: 

  1. Je suppose que nous serons en mesure d'exécuter ce binaire en utilisant le Runtime.exec () API. Y at-il des contraintes quant à l'endroit où je besoin de mettre ma bibliothèque dans la structure des dossiers? Comment le runtime du système localiserait cet exécutable? Y a-t-il une sorte de chemin de classe?

  2. Étant donné que l'application a des dépendances sur ce runtime, j'étais penser à envelopper un service afin qu'il puisse être lancé ou arrêté au besoin. Quel est le meilleur moyen de gérer de tels exécutables? dans un projet Android?

  3. Quelles sont les autres alternatives, en supposant que je n'ai pas de code source pour cet exécutable?

S'il vous plaît conseil.

Merci.


28
2018-04-07 15:20


origine


Réponses:


1) Non, il ne devrait y avoir aucune contrainte, en dehors de celles qui accèdent aux fichiers système et nécessitent donc une racine. Le meilleur endroit serait directement à / data / data / [votre_nom_package] pour éviter de polluer ailleurs.

2) Une discussion très approfondie sur la compilation avec des bibliothèques natives peut être trouvée ici: http://www.aton.com/android-native-libraries-for-java-applications/ . Une autre option est un compilateur croisé pour arm (voici celui utilisé pour compiler le noyau, il est gratuit: http://www.codesourcery.com/sgpp/lite/arm ). Si vous prévoyez de maintenir un service qui exécute votre cammand, soyez averti que les services peuvent être arrêtés et redémarrés par Android à tout moment.

3) Maintenant, si vous n'avez pas le code source, j'espère que votre fichier est au moins compilé en tant qu'exécutable de bras. Sinon, je ne vois pas comment vous pourriez même le faire fonctionner.


Vous allez exécuter le fichier en exécutant les commandes suivantes dans votre classe Java:

String myExec = "/data/data/APPNAME/FILENAME";
Process process = Runtime.getRuntime().exec(myExec);
DataOutputStream os = new DataOutputStream(process.getOutputStream());
DataInputStream osRes = new DataInputStream(process.getInputStream());

Je ne sais rien de votre exécutable, donc vous pouvez ou non avoir besoin de recevoir le inputStream et le outputStream.


Je suppose que courir adb pour pousser le fichier binaire est hors de question, donc Je cherchais un moyen judicieux de l’emballer. J'ai trouvé un excellent article sur l'inclusion d'un exécutable dans votre application. Vérifiez le ici: http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App

La partie importante est celle-ci (emphase la mienne):

De l'application Java Android, en utilisant assets dossier

  • Incluez le binaire dans le dossier de ressources.
  • Utilisation getAssets().open(FILENAME) pour obtenir un InputStream.
  • Écrivez-le à /data/data/APPNAME (par exemple. /data/data/net.gimite.nativeexe), où votre application a accès pour écrire des fichiers et les rendre exécutables.
  • Courir /system/bin/chmod 744 /data/data/APPNAME/FILENAME en utilisant le code ci-dessus.
  • Exécutez votre exécutable en utilisant le code ci-dessus.

La poste utilise le assets dossier, insted du raw dossier que Android suggère pour les fichiers statiques:

Conseil: Si vous souhaitez enregistrer un fichier statique dans votre application au moment de la compilation, enregistrez le fichier dans le répertoire res / raw / de votre projet. Vous pouvez l'ouvrir avec openRawResource (), en passant le R.raw. ID de ressource. Cette méthode retourne un InputStream que vous pouvez utiliser pour lire le fichier (mais vous ne pouvez pas écrire dans le fichier d'origine).

Pour accéder au dossier de données, vous pouvez suivre les instructions ici: http://developer.android.com/guide/topics/data/data-storage.html#filesInternal En outre, il y a la File#setExecutable(boolean); méthode qui devrait fonctionner à la place de la commande shell.

Donc, en mettant tout ensemble, j'essaierais:

InputStream ins = context.getResources().openRawResource (R.raw.FILENAME)
byte[] buffer = new byte[ins.available()];
ins.read(buffer);
ins.close();
FileOutputStream fos = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
fos.write(buffer);
fos.close();


File file = getFileStreamPath (FILENAME);
file.setExecutable(true);

Bien sûr, tout cela ne doit être fait qu’une fois après l’installation. Vous pouvez faire une vérification rapide à l'intérieur onCreate() ou quoi que ce soit qui vérifie la présence du fichier et exécute toutes ces commandes si le fichier n'est pas là.

Laissez-moi savoir si cela fonctionne. Bonne chance!


38
2018-04-12 22:57



Voici un guide complet sur la façon de regrouper et d'exécuter l'exécutable. Je me suis basé sur ce que j'ai trouvé ici et sur d'autres liens, ainsi que sur mes propres essais et erreurs.

1.) Dans votre projet SDK, placez le fichier exécutable dans votre dossier / assets

2.) Récupérer par programme la chaîne de ce répertoire de fichiers (/ data / data / your_app_name / files) comme ceci

String appFileDirectory = getFilesDir().getPath();
String executableFilePath = appFileDirectory + "/executable_file";

3.) Dans le code Java de votre application: copiez le fichier exécutable à partir du dossier / assets dans le sous-dossier "files" de votre application (généralement / data / data / your_app_name / files) avec une fonction comme celle-ci:

private void copyAssets(String filename) {

AssetManager assetManager = getAssets();

InputStream in = null;
OutputStream out = null;
Log.d(TAG, "Attempting to copy this file: " + filename); // + " to: " +       assetCopyDestination);

try {
    in = assetManager.open(filename);
    Log.d(TAG, "outDir: " + appFileDirectory);
    File outFile = new File(appFileDirectory, filename);
    out = new FileOutputStream(outFile);
    copyFile(in, out);
    in.close();
    in = null;
    out.flush();
    out.close();
    out = null;
} catch(IOException e) {
Log.e(TAG, "Failed to copy asset file: " + filename, e);
} 

Log.d(TAG, "Copy success: " + filename);
}

4.) Modifiez les permissions du fichier sur fichier_exécutable pour le rendre réellement exécutable. Faites-le avec des appels Java:

File execFile = new File(executableFilePath);
execFile.setExecutable(true);

5.) Exécutez le fichier comme ceci:

Process process = Runtime.getRuntime().exec(executableFilePath);

Notez que tous les fichiers référencés ici (tels que les fichiers d'entrée et de sortie) doivent avoir leur chemin complet construit. C'est parce que c'est un processus séparé généré et qu'il n'a aucun concept de ce qu'est le "pwd".

Si vous voulez lire la sortie de la commande, vous pouvez le faire, mais jusqu'à présent, cela ne fonctionne que pour les commandes système (comme "ls"), pas le fichier exécutable:

BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
int read;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((read = reader.read(buffer)) > 0) {
    output.append(buffer, 0, read);
}
reader.close();
process.waitFor();

Log.d (TAG, "output:" + output.toString ());


10
2017-09-12 00:34



J'ai fait quelque chose comme ça en utilisant le NDK. Ma stratégie consistait à recompiler le programme à l’aide du NDK et à écrire un code JNI de wrapper appelé dans le programme. main fonction.

Je ne suis pas sûr du cycle de vie du code NDK. Même les services conçus pour durer longtemps peuvent être démarrés et arrêtés par le système lorsque cela est opportun. Vous devrez probablement éteindre votre thread NDK et le redémarrer si nécessaire.


1
2018-04-07 15:35