Хостинг исполняемого файла в приложении Android
Я работаю над приложением Android, которое зависит от двоичного файла ELF:
наш Java-код взаимодействует с этим двоичным кодом, чтобы все было сделано. Эта
время запуска должно быть запущено и завершено при запуске приложения и
выход приложения/по требованию.
Вопросы:
-
Я предполагаю, что мы сможем выполнить этот двоичный код, используя
Runtime.exec(). Существуют ли какие-либо ограничения относительно того, где я
нужно поместить мою библиотеку в структуру папок? Как бы среда выполнения системы находила этот исполняемый файл? Есть ли какие-то настройки класса?
-
Поскольку приложение имеет зависимости от этого времени выполнения, я был
думая об обертке вокруг службы, чтобы ее можно было запустить или
по необходимости. Каков наилучший способ обработки таких исполняемых файлов
в проекте Android?
-
Каковы другие альтернативы, предполагая, что у меня нет исходного кода для этого исполняемого файла?
Пожалуйста, совет.
Спасибо.
Ответы
Ответ 1
1) Нет, не должно быть ограничений, кроме тех, которые получают доступ к системным файлам и, следовательно, требуют root. Лучшее место было бы прямо в /data/data/ [your_package_name], чтобы избежать загрязнения в другом месте.
2) Очень подробное обсуждение компиляции в отношении родных библиотек можно найти здесь: http://www.aton.com/android-native-libraries-for-java-applications/. Другой вариант - кросс-компилятор для руки (вот тот, который использовался для компиляции ядра, он бесплатный: http://www.codesourcery.com/sgpp/lite/arm). Если вы планируете поддерживать службу, которая выполняет ваш cammand, будьте предупреждены о том, что службы могут быть остановлены и перезагружены андроидом в любой момент.
3) Теперь, если у вас нет исходного кода, я надеюсь, что ваш файл, по крайней мере, скомпилирован как исполняемый файл руки. Если нет, я не понимаю, как вы могли его запустить.
Вы выполните файл, выполнив следующие команды в своем классе 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 для push двоичного файла не может быть и речи, поэтому
Я искал опрятный способ его упаковки. Я нашел отличный пост о включении исполняемого файла в ваше приложение. Проверьте это здесь:
http://gimite.net/en/index.php?Run%20native%20executable%20in%20Android%20App
Важная часть - это одна (внимание мое):
Из приложения Android Java, используя папку assets
- Включите двоичный файл в папке с ресурсами.
- Используйте
getAssets().open(FILENAME)
для получения InputStream
. - Запишите его на
/data/data/APPNAME
(например, /data/data/net.gimite.nativeexe
), где ваше приложение имеет доступ к файлам записи и делает его исполняемым. - Запустите
/system/bin/chmod 744 /data/data/APPNAME/FILENAME
с помощью приведенного выше кода. - Запустите исполняемый файл, используя приведенный выше код.
Сообщение использует папку assets
, установленную в папку raw
, которую предлагает андроид для статических файлов:
Совет. Если вы хотите сохранить статический файл в своем приложении во время компиляции, сохраните файл в каталоге проекта res/raw/. Вы можете открыть его с помощью openRawResource(), передав R.raw. идентификатор ресурса. Этот метод возвращает 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 = getFileStreamPath (FILENAME);
file.setExecutable(true);
Конечно, все это нужно делать только один раз после установки. Вы можете быстро проверить внутри onCreate()
или все, что проверяет наличие файла и выполняет все эти команды, если файл там отсутствует.
Сообщите мне, если это сработает. Удачи!
Ответ 2
Вот полное руководство по пакету и запуску исполняемого файла. Я основывал его на том, что я нашел здесь, и на других ссылках, а также на моих собственных проб и ошибок.
1.) В проекте SDK поместите исполняемый файл в папку /assets
2.) Программно получить строку этого каталога файлов (/data/data/your_app_name/files), подобную этой
String appFileDirectory = getFilesDir().getPath();
String executableFilePath = appFileDirectory + "/executable_file";
3.) В вашем проекте Java-код проекта: скопируйте исполняемый файл из папки /assets в подпапку "файлы" вашего приложения (обычно/data/data/your_app_name/files) с помощью следующей функции:
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.) Измените права на файл для исполняемого файла, чтобы фактически сделать его исполняемым. Сделайте это с помощью Java-вызовов:
File execFile = new File(executableFilePath);
execFile.setExecutable(true);
5.) Выполните файл следующим образом:
Process process = Runtime.getRuntime().exec(executableFilePath);
Обратите внимание, что любые файлы, упомянутые здесь (такие как входные и выходные файлы), должны иметь полный путь Строки, построенные. Это потому, что это отдельный порожденный процесс, и он не имеет понятия о том, что такое "pwd".
Если вы хотите прочитать команду stdout, вы можете это сделать, но пока она работает только для меня для системных команд (например, "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(TAG, "output:" + output.toString());
Ответ 3
Я сделал что-то подобное, используя NDK. Моя стратегия заключалась в том, чтобы перекомпилировать программу с использованием NDK и написать некоторый код JNI-оболочки, который вызывается в функцию main
.
Я не уверен, что такое жизненный цикл кода NDK. Даже услуги, которые предназначены для долговременного использования, могут быть запущены и остановлены системой, когда это удобно. Вам, вероятно, придется отключить поток NDK и при необходимости перезапустить его.