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

通过JNI调用APK中打包的控制台程序遇权限问题,求解决方案及替代实现方式

通过JNI调用APK中打包的控制台程序遇权限问题,求解决方案及替代实现方式

看起来你踩中了Android沙箱权限和文件系统限制的坑,我来帮你拆解问题并给出可行的解决方案~

为什么你的chmod会失败?

你把test_run复制到了Documents/Download这类外部存储目录,从Android 10(API 29)开始,Scoped Storage机制严格限制了外部存储的文件权限:外部存储的文件只能进行读/写操作,不允许设置执行权限(chmod +x),这就是你遇到"Operate permission not allowed"的核心原因。

另外你的代码还有个隐藏bug:if (errno = chmod (cmd_str,i) < 0)这里的运算符优先级有问题——<的优先级比=高,所以实际是先判断chmod的返回值是否小于0,再把布尔值(0或1)赋值给errno,导致你无法正确获取chmod的错误码,得改成if ((errno = chmod(cmd_str, i)) < 0)。

正确的解决方案步骤

1. 把可执行文件复制到App私有目录

App的私有目录(比如/data/data/[你的包名]/files/)不受Scoped Storage限制,App拥有完全的权限,包括设置执行权限。在C++Builder里,你可以用TPath.GetHomePath() + "/files/"来获取这个路径(对应Java层的context.getFilesDir()),而不是用外部存储的DocumentsFolder。

2. 修正chmod代码并添加日志

替换你原来的chmod判断逻辑,同时用Android日志替代ShowMessage(JNI线程里调用GUI相关函数容易出问题):

extern "C" JNIEXPORT jstring JNICALL
ExecuteCommand(JNIEnv *env, jstring cmd) {
    const char *cmd_str = env->GetStringUTFChars(cmd, NULL);
    char mode[] = "0755"; // 标准可执行权限
    int i = strtol(mode, 0, 8);
    int err_num;

    // 修正运算符优先级问题
    if ((err_num = chmod(cmd_str, i)) < 0) {
        char err_buf[256];
        // 用线程安全的strerror_r获取错误信息
        strerror_r(err_num, err_buf, sizeof(err_buf));
        char buffer[512];
        sprintf(buffer, "%s: chmod failed with mode %s - %d (%s)", 
                cmd_str, mode, err_num, err_buf);
        // 输出到Android日志,方便调试
        __android_log_print(ANDROID_LOG_ERROR, "JNI_EXEC", "%s", buffer);
    }

3. 安全执行控制台程序

你当前用的execvp会直接替换当前JNI线程的进程空间,导致后面的代码完全不会执行,甚至可能让App崩溃。推荐用popen来捕获程序输出,或者fork子进程执行:

// 用popen执行并捕获输出
    FILE* fp = popen(cmd_str, "r");
    if (!fp) {
        __android_log_print(ANDROID_LOG_ERROR, "JNI_EXEC", "popen failed: %s", strerror(errno));
        env->ReleaseStringUTFChars(cmd, cmd_str);
        return env->NewStringUTF("Execution failed");
    }

    char output[1024];
    std::string result;
    while (fgets(output, sizeof(output), fp) != NULL) {
        result += output;
    }

    pclose(fp);
    env->ReleaseStringUTFChars(cmd, cmd_str);
    return env->NewStringUTF(result.c_str());
}

更推荐的替代实现方式

其实单独打包可执行文件是比较麻烦的方案,更符合Android NDK开发习惯的方式是:把test_run的代码编译成共享库(.so),然后直接通过JNI调用库中的函数,这样完全不需要处理文件复制、权限设置这些步骤,既高效又安全。

比如你可以把test_run的核心逻辑封装成一个函数:

// 在libtestrun.so中定义
extern "C" int run_test(int argc, char** argv) {
    // 原来test_run的核心代码
    return 0;
}

然后在你的JNI代码里直接调用这个函数,省去了所有文件操作的麻烦。

备注:内容来源于stack exchange,提问作者DarkSpy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.22 14:59:37