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

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

问题:

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

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

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

请指教。

谢谢。

Solutions Collecting From Web of "在Android应用程序中托管可执行文件"

1)不,除了那些访问系统文件因此需要root权限之外,应该没有约束。 最好的地方是直接到/ data / data / [your_package_name]以避免在其他地方造成污染。

2)关于编译本机库的非常详尽的讨论可以在这里find: 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来推送二进制文件是不可能的,所以我一直在寻找一种巧妙的方法来打包它。 我发现了一篇关于在您的应用中包含可执行文件的精彩post 在这里查看: 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
  • 使用上面的代码运行您的可执行文件

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

如果有效,请告诉我。 祝你好运!

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

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

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

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

3.)在你的应用程序的项目中Java代码:使用如下函数将/ assets文件夹中的可执行文件复制到应用程序的“files”子文件夹(通常是/ 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”的概念。

如果你想读取命令的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());

我使用NDK做过类似的事情。 我的策略是使用NDK重新编译程序并编写一些调用程序main函数的包装器JNI代码。

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