You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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目录仍保留执行权限。

修改步骤:

  1. 替换缓存目录为应用私有files目录的子目录:
// 替换原缓存目录相关代码
File stockfishDir = new File(context.getFilesDir(), "stockfish_bin");
if (!stockfishDir.exists()) {
    stockfishDir.mkdirs(); // 创建子目录
}
File stockfishFile = new File(stockfishDir, "stockfish");
  1. 明确设置文件的可执行权限(添加ownerOnly参数确保权限生效):
// 设置可执行权限,第二个参数为false表示允许其他用户执行
stockfishFile.setExecutable(true, false);
// 补充设置可读权限
stockfishFile.setReadable(true, false);
  1. 移除不必要的配置:删除之前添加的外部存储权限和FileProvider相关配置,因为无需访问外部存储或共享文件。

原理:

Android 10对应用缓存目录(getCacheDir())增加了执行权限限制,而应用私有files目录(getFilesDir())属于应用完全可控的私有空间,不受该限制,因此可以正常执行二进制文件。

内容的提问来源于stack exchange,提问作者Brent Underwood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 15:02:07