Android SDK 29+如何运行assets目录下的可执行文件?
Android SDK 29 运行Stockfish二进制文件权限拒绝问题
问题场景
我在Android Studio中编写了startStockfish()函数,用于从Assets中复制Stockfish二进制文件到缓存目录并启动进程:
public void startStockfish() { AssetManager assetManager = context.getAssets(); try { // Open Stockfish binary from assets folder InputStream inputStream = assetManager.open("x86/stockfish_nosimd"); System.out.println("binary opened from assets folder"); // Create a temporary file to copy Stockfish binary File stockfishFile = new File(context.getCacheDir(), "stockfish"); FileOutputStream outputStream = new FileOutputStream(stockfishFile); System.out.println("temporary file created"); // Copy Stockfish binary to a temporary file byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } System.out.println("stockfish binary copied"); // Close streams outputStream.close(); inputStream.close(); System.out.println("streams closed"); // Set executable permission for the file stockfishFile.setExecutable(true); System.out.println("permissions set"); // Start Stockfish process using the copied file stockfishProcess = new ProcessBuilder(stockfishFile.getAbsolutePath()).start(); System.out.println("process started"); // Initialize input and output streams stockfishInput = new BufferedReader(new InputStreamReader(stockfishProcess.getInputStream())); stockfishOutput = new OutputStreamWriter(stockfishProcess.getOutputStream()); System.out.println("I/O initialized"); // Send 'uci' command to Stockfish sendCommandToStockfish("uci"); System.out.println("stockfish was successful"); } catch (IOException e) { System.out.println("stockfish is broken"); e.printStackTrace(); // Handle exceptions (file not found, etc.) accordingly } }
该函数在SDK 28环境下运行正常,但切换到SDK 29后,执行stockfishProcess = new ProcessBuilder(stockfishFile.getAbsolutePath()).start();时抛出权限拒绝错误:
Cannot run program "/data/user/0/com.example.chessrepertoiremanager/cache/stockfish": error=13, Permission denied
已尝试的无效方案
- 在Manifest中添加外部存储权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- 在Manifest中配置FileProvider:
<provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.chessrepertoiremanager.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> </provider>
- 在
res/xml目录下创建provider_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <paths> <cache-path name="cache_files" path="/" /> </paths>
解决方法
Android 10(SDK 29)引入Scoped Storage的同时,限制了应用私有缓存目录的二进制执行权限。正确的做法是将二进制文件复制到应用私有files目录下的子目录,而非缓存目录,因为files目录仍保留执行权限。
修改步骤:
- 替换缓存目录为应用私有files目录的子目录:
// 替换原缓存目录相关代码 File stockfishDir = new File(context.getFilesDir(), "stockfish_bin"); if (!stockfishDir.exists()) { stockfishDir.mkdirs(); // 创建子目录 } File stockfishFile = new File(stockfishDir, "stockfish");
- 明确设置文件的可执行权限(添加
ownerOnly参数确保权限生效):
// 设置可执行权限,第二个参数为false表示允许其他用户执行 stockfishFile.setExecutable(true, false); // 补充设置可读权限 stockfishFile.setReadable(true, false);
- 移除不必要的配置:删除之前添加的外部存储权限和FileProvider相关配置,因为无需访问外部存储或共享文件。
原理:
Android 10对应用缓存目录(getCacheDir())增加了执行权限限制,而应用私有files目录(getFilesDir())属于应用完全可控的私有空间,不受该限制,因此可以正常执行二进制文件。
内容的提问来源于stack exchange,提问作者Brent Underwood
相关产品推荐
相关产品推荐

