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

Android项目集成Dalvik VM编译工具遇路径错误问题求助

Android项目Dalvik集成编译的路径错误解决

我正在开发一个游戏核心,除编译模块外所有部分均已完成。最初尝试编写如下代码进行编译:

package com.ccs.gencorelite.compiler;

import java.io.IOException;

public class ProjectCompiler {

    public static void compileProject(String projectPath, String outputPath) {
        try {
            ProcessBuilder builder = new ProcessBuilder(
                    "aapt", "package", "-f",
                    "-M", projectPath + "/AndroidManifest.xml",
                    "-S", projectPath + "/res", 
                    "-I", "file:///resources/android.jar",
                    "-F", outputPath + "/app.apk" 
            );

            builder.redirectErrorStream(true);

            Process process = builder.start();

            // 等待进程完成
            int exitCode = process.waitFor();

            // 检查退出状态
            if (exitCode == 0) {
                System.out.println("项目编译成功。");
            } else {
                System.out.println("项目编译出错。");
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

之后出现了编译权限错误,于是我重写了该类,并将build-tools34、platforms、platform-tools、sdk34文件夹添加到assets目录中,重写后的代码如下:

package com.ccs.gencorelite.compiler;


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class ProjectCompiler {


    public static void compileJavaFiles(String projectPath) {

        String javaCompilerPath = "file:///android_asset/compiler/bt34/javac";
        String[] javacCmd = { javaCompilerPath, "-d", projectPath + "/bin", projectPath + "/src/**/*.java" };
        executeCommand(javacCmd);
    }

    public static void compileResources(String projectPath) {
        String aaptPath = "file:///android_asset/compiler/bt34/aapt";
        String[] aaptCmd = { aaptPath, "package", "-f", "-m", "-J", projectPath + "/gen", "-S", projectPath + "/res", "-I", "file:///android_resources/android.jar" };
        executeCommand(aaptCmd);
    }

    public static void convertToDalvikBytecode(String projectPath) {
        String dxPath = "file:///android_asset/compiler/bt34/dx";
        String[] dxCmd = { dxPath, "--dex", "--output=" + projectPath + "/bin/classes.dex", projectPath + "/bin" };
        executeCommand(dxCmd);
    }

    public static void executeCommand(String[] command) {
        try {
            Process process = new ProcessBuilder(command).start();
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
            reader.close();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

现在出现了以下错误:

java.io.IOException: Cannot run program "file:///android_assets/compiler/bt34/javac": error=2, No such file or directory

2024-05-09 22:14:26.305 28386-28386 System.err              com.ccs.gencorelite                  W  java.io.IOException: Cannot run program "file:///android_assets/compiler/bt34/aapt": error=2, No such file or directory

2024-05-09 22:14:26.312 28386-28386 System.err              com.ccs.gencorelite                  W  java.io.IOException: Cannot run program "file:///android_assets/compiler/bt34/dx": error=2, No such file or directory

核心解决方案

1. 问题根源

Android的file:///android_asset/是Content URI,并非文件系统的绝对路径,ProcessBuilder无法直接通过该URI执行文件。同时,assets目录中的文件是压缩在APK内的,默认没有执行权限,必须先复制到应用的私有可写目录才能使用。

2. 复制assets工具到私有目录并添加权限

添加工具方法负责复制文件并设置执行权限:

import android.content.Context;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

private void copyAssetToPrivateDir(Context context, String assetRelativePath, String destRelativePath) throws IOException {
    InputStream inputStream = context.getAssets().open(assetRelativePath);
    File destFile = new File(context.getFilesDir(), destRelativePath);
    
    // 创建目标目录
    if (!destFile.getParentFile().exists()) {
        destFile.getParentFile().mkdirs();
    }

    OutputStream outputStream = new FileOutputStream(destFile);
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, bytesRead);
    }

    inputStream.close();
    outputStream.close();
    
    // 赋予执行权限
    destFile.setExecutable(true);
}

在编译流程启动前,先复制所有需要的工具:

// 在编译前调用,需传入Context
try {
    copyAssetToPrivateDir(context, "compiler/bt34/javac", "compiler/bt34/javac");
    copyAssetToPrivateDir(context, "compiler/bt34/aapt", "compiler/bt34/aapt");
    copyAssetToPrivateDir(context, "compiler/bt34/dx", "compiler/bt34/dx");
    copyAssetToPrivateDir(context, "resources/android.jar", "resources/android.jar");
} catch (IOException e) {
    e.printStackTrace();
}

3. 修改编译方法的路径

使用应用私有目录的绝对路径调用工具:

public static void compileJavaFiles(Context context, String projectPath) {
    String javacPath = new File(context.getFilesDir(), "compiler/bt34/javac").getAbsolutePath();
    String[] javacCmd = { javacPath, "-d", projectPath + "/bin", projectPath + "/src/**/*.java" };
    executeCommand(javacCmd);
}

public static void compileResources(Context context, String projectPath) {
    String aaptPath = new File(context.getFilesDir(), "compiler/bt34/aapt").getAbsolutePath();
    String androidJarPath = new File(context.getFilesDir(), "resources/android.jar").getAbsolutePath();
    String[] aaptCmd = { aaptPath, "package", "-f", "-m", "-J", projectPath + "/gen", "-S", projectPath + "/res", "-I", androidJarPath };
    executeCommand(aaptCmd);
}

public static void convertToDalvikBytecode(Context context, String projectPath) {
    String dxPath = new File(context.getFilesDir(), "compiler/bt34/dx").getAbsolutePath();
    String[] dxCmd = { dxPath, "--dex", "--output=" + projectPath + "/bin/classes.dex", projectPath + "/bin" };
    executeCommand(dxCmd);
}

4. 额外注意事项

  • 确保assets中的工具是对应目标设备架构的版本(如arm64-v8a、armeabi-v7a),否则会出现“无法执行二进制文件”错误。
  • Android 10及以上版本无需申请外部存储权限,应用私有目录getFilesDir()是安全可写的。
  • 可以只在第一次启动时复制工具,避免重复复制浪费资源。

内容的提问来源于stack exchange,提问作者Dmytro Parkhomenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:19:55