在Android应用程序中托管可执行文件

时间:2011-04-07 15:20:44

标签: java android

我正在开发依赖于ELF二进制文件的Android应用程序: 我们的Java代码与这个二进制文件交互以完成任务。这个 运行时需要在应用程序启动时启动和终止 申请退出/按需。

问题:

  1. 我假设我们将能够使用。执行此二进制文件 Runtime.exec()API。关于我在哪里有任何限制 需要将我的库放在文件夹结构中?系统运行时如何找到此可执行文件?是否有某种类路径设置?

  2. 由于应用程序依赖于此运行时,我就是 想把它包装在一个服务周围,以便它可以启动或 根据需要停止。处理此类可执行文件的最佳方法是什么 在Android项目中?

  3. 假设我没有此可执行文件的源代码,还有什么其他选择?

  4. 请建议。

    感谢。

4 个答案:

答案 0 :(得分:41)

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的服务,请注意可以随时停止并重新启动服务。

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文件夹

     
      
  • 在资源文件夹中包含二进制文件。
  •   
  • 使用getAssets().open(FILENAME)获取InputStream
  •   
  • 将其写入/data/data/APPNAME(例如/data/data/net.gimite.nativeexe),您的应用程序可以在其中写入文件并使其可执行。
  •   
  • 使用上面的代码运行/system/bin/chmod 744 /data/data/APPNAME/FILENAME
  •   
  • 使用上面的代码运行您的可执行文件。
  •   

该帖子使用了assets文件夹,其中包含了android建议用于静态文件的raw文件夹:

  

提示:如果要在编译时在应用程序中保存静态文件,请将该文件保存在项目res / raw /目录中。你可以用openRawResource()打开它,传递R.raw。资源ID。此方法返回一个可用于读取文件的InputStream(但您无法写入原始文件)。

要访问数据文件夹,您可以按照此处的说明操作: http://developer.android.com/guide/topics/data/data-storage.html#filesInternal 此外,还有File#setExecutable(boolean);方法,而不是shell命令。

所以,把所有东西放在一起,我会尝试:

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()内部或检查文件是否存在的任何内容,如果文件不存在,则执行所有这些命令。

让我知道它是否有效。祝你好运!

答案 1 :(得分:10)

以下是有关如何打包和运行可执行文件的完整指南。我的基础是我在这里找到的和其他链接,以及我自己的试验和错误。

1。)在SDK项目中,将可执行文件放在/ assets文件夹中

2.。)以编程方式获取该文件目录的字符串(/ data / data / your_app_name / files),如下所示

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

3。)在你的应用程序项目中的Java代码:将/ assets文件夹中的可执行文件复制到你的app"文件"子文件夹(通常是/ 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.。)更改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(TAG,"输出:" + output.toString());

答案 2 :(得分:1)

我使用NDK完成了类似的操作。我的策略是使用NDK重新编译程序并编写一些调用程序的main函数的包装器JNI代码。

我不确定NDK代码的生命周期是什么样的。即使是长时间运行的服务,也可以在方便时由系统启动和停止。您可能必须关闭NDK线程并在必要时重新启动它。

答案 3 :(得分:0)

要从Android 10开始执行二进制文件,只能从只读文件夹中执行。这意味着您应该在应用程序中打包二进制文件。 Android doc

  1. android:extractNativeLibs="true"放入AndroidManifest;
  2. 将二进制文件放入src/main/resources/lib/*目录,其中* –代表CPU的体系结构,例如armeabi-v7a;
  3. 使用如下代码执行:

    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"
    }
    }
    

这里是reddit上来自Android团队的答案的讨论。