Размещение исполняемого файла в приложении Android
Я работаю над приложением для Android, которое зависит от двоичного файла ELF: наш Java-код взаимодействует с этим двоичным файлом для выполнения задач. Эта среда выполнения должна запускаться и завершаться при запуске приложения и выходе из приложения / по требованию.
Вопросы:
Я предполагаю, что мы сможем выполнить этот двоичный файл, используя Runtime.exec() API. Есть ли какие-либо ограничения относительно того, куда мне нужно поместить мою библиотеку в структуре папок? Как системная среда выполнения найдет этот исполняемый файл? Есть ли какая-то настройка пути к классу?
Поскольку приложение зависит от этой среды выполнения, я думал обернуть его вокруг службы, чтобы его можно было запускать или останавливать по мере необходимости. Каков наилучший способ обработки таких исполняемых файлов в Android Project?
Каковы другие альтернативы, при условии, что у меня нет исходного кода для этого исполняемого файла?
Пожалуйста, дайте совет.
Спасибо.
Переведено автоматически
Ответ 1
1) Нет, не должно быть никаких ограничений, кроме тех, которые обеспечивают доступ к системным файлам и, следовательно, требуют root. Лучшим местом было бы сразу перейти в /data/data /[your_package_name], чтобы избежать загрязнения в другом месте.
2) Очень подробное обсуждение компиляции с использованием собственных библиотек можно найти здесь: http://www.aton.com/android-native-libraries-for-java-applications / . Другой вариант - кросс-компилятор для arm (вот тот, который используется для компиляции ядра, он бесплатный: http://www.codesourcery.com/sgpp/lite/arm ). Если вы планируете поддерживать службу, которая выполняет ваш cammand, имейте в виду, что службы могут быть остановлены и перезапущены Android в любой момент.
3) Теперь, если у вас нет исходного кода, я надеюсь, что ваш файл, по крайней мере, скомпилирован как исполняемый файл arm. Если нет, я не понимаю, как вы вообще могли бы его запустить.
Вы запустите файл, выполнив следующие команды в вашем классе 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());
Я ничего не знаю о вашем исполняемом файле, поэтому вам может понадобиться, а может и не понадобиться на самом деле получать InputStream и OutputStream.
Я предполагаю, что о запуске adb для отправки двоичного файла не может быть и речи, поэтому я искал аккуратный способ его упаковки. Я нашел отличный пост о включении исполняемого файла в ваше приложение. Проверьте это здесь: http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App
Важная часть - это (выделено мной):
Из приложения Android Java, используя
assets
папку
- Включите двоичный файл в папку assets.
- Используйте
getAssets().open(FILENAME)
, чтобы получитьInputStream
.- Запишите его в
/data/data/APPNAME
(например,/data/data/net.gimite.nativeexe
), где у вашего приложения есть доступ для записи файлов, и сделайте его исполняемым.- Запустите
/system/bin/chmod 744 /data/data/APPNAME/FILENAME
, используя приведенный выше код.- Запустите свой исполняемый файл, используя приведенный выше код.
В сообщении используется assets
папка вместо raw
папки, которую Android предлагает для статических файлов:
Совет: Если вы хотите сохранить статический файл в своем приложении во время компиляции, сохраните файл в каталоге project res / raw / . Вы можете открыть его с помощью openRawResource(), передав идентификатор R.raw. resource . Этот метод возвращает InputStream, который вы можете использовать для чтения файла (но вы не можете выполнить запись в исходный файл).
Чтобы получить доступ к папке с данными, вы можете следовать инструкциям здесь: http://developer.android.com/guide/topics/data/data-storage.html#filesInternal Также есть File#setExecutable(boolean);
метод, который должен работать вместо команды командной строки.
Итак, собрав все воедино, я бы попробовал:
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 = context.getFileStreamPath (FILENAME);
file.setExecutable(true);
Конечно, все это следует выполнять только один раз после установки. Вы можете выполнить быструю проверку внутри onCreate()
или что-то еще, что проверяет наличие файла и выполняет все эти команды, если файла там нет.
Дайте мне знать, если это сработает. Удачи!
Ответ 2
Вот полное руководство по упаковке и запуску исполняемого файла. Я основал его на том, что нашел здесь, и других ссылках, а также на своих собственных пробах и ошибках.
1.) В вашем проекте SDK поместите исполняемый файл в папку / assets
2.) Программно получаем строку этого каталога файлов (/data /данные /имя__аппликации/файлы) следующим образом
String appFileDirectory = getFilesDir().getPath();
String executableFilePath = appFileDirectory + "/executable_file";
3.) В Java-коде проекта вашего приложения: скопируйте исполняемый файл из папки / assets в подпапку "files" вашего приложения (обычно /data / данные / имя__приложения / файлы) с помощью функции, подобной этой:
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.) Измените права доступа к файлу executable_file, чтобы он действительно стал исполняемым. Сделайте это с помощью вызовов Java:
File execFile = new File(executableFilePath);
execFile.setExecutable(true);
5.) Запустите файл следующим образом:
Process process = Runtime.getRuntime().exec(executableFilePath);
Обратите внимание, что для любых файлов, упомянутых здесь (таких как входные и выходные файлы), должны быть сконструированы полные строки пути. Это связано с тем, что это отдельный созданный процесс, и он не имеет понятия о том, что такое "pwd".
Если вы хотите прочитать стандартный вывод команды, вы можете это сделать, но пока у меня это работает только для системных команд (например, "ls"), а не для исполняемого файла:
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(ТЕГ, "output: " + вывод.toString());
Ответ 3
Выполнение двоичного файла, начиная с Android 10, возможно только из папки, доступной только для чтения. Это означает, что вы должны упаковать двоичный файл в свое приложение. Android doc
- Поместить
android:extractNativeLibs="true"
в AndroidManifest; - Поместите свой двоичный файл в
src/main/resources/lib/*
каталог, где*
– обозначает архитектуру процессора, например,armeabi-v7a
; Используйте подобный код для выполнения:
private fun exec(command: String, params: String): String {
try {
val process = ProcessBuilder()
.directory(File(filesDir.parentFile!!, "lib"))
.command(command, params)
.redirectErrorStream(true)
.start()
val reader = BufferedReader(
InputStreamReader(process.inputStream)
)
val text = reader.readText()
reader.close()
process.waitFor()
return text
} catch (e: Exception) {
return e.message ?: "IOException"
}
}
Вот обсуждение с ответом от команды Android на reddit.
Ответ 4
Я сделал что-то подобное с помощью NDK. Моя стратегия заключалась в том, чтобы перекомпилировать программу с помощью NDK и написать некоторый JNI-код-оболочку, который вызывал функцию main
программы.
Я не уверен, на что похож жизненный цикл кода NDK. Даже службы, которые предназначены для длительной работы, могут запускаться и останавливаться системой, когда это удобно. Вероятно, вам придется завершить работу вашего потока NDK и перезапустить его при необходимости.