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

Android Studio Native层无法访问/storage/self/primary/Download路径

解决Native层无法访问Android公共Download目录的问题

问题根源

从Android 10(API 29)开始,系统启用了分区存储(Scoped Storage),直接通过文件路径访问公共媒体目录(比如Download)的方式被严格限制。哪怕你拿到了正确的路径,Native层用std::ofstream直接打开也会因为权限不足失败。

解决办法

1. 先配置必要权限

在AndroidManifest.xml里添加对应权限:

<!-- 读取公共目录权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- API 28及以下的写入权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<!-- API 30及以上的全存储管理权限(特殊权限,需引导用户去系统设置开启) -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" android:minSdkVersion="30" />

注意:MANAGE_EXTERNAL_STORAGE是系统级权限,不能通过常规弹窗申请,得引导用户跳转到系统设置开启。如果只是操作自己APP生成的文件,优先用下面的MediaStore方案,尽量别碰这个权限。

2. 合规方案:通过Java层传递文件描述符给Native操作

分区存储下,官方推荐用MediaStore来操作公共目录的文件。可以在Java层创建/获取文件后,把文件的**文件描述符(FD)**传递给Native层,Native通过FD来读写,绕开路径访问限制。

Java层代码示例(创建文件并传递FD):

public void createAndPassDownloadFile(String fileName) throws IOException {
    ContentValues values = new ContentValues();
    values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); // 设置文件名
    values.put(MediaStore.Downloads.MIME_TYPE, "text/plain"); // 设置文件类型
    values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); // 指定存储到Download目录

    // 插入到MediaStore,获取文件Uri
    Uri fileUri = getContentResolver().insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values);
    if (fileUri != null) {
        // 打开文件并获取FD
        try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(fileUri, "w")) {
            if (pfd != null) {
                FileDescriptor fd = pfd.getFileDescriptor();
                // 调用Native方法,传递FD和要写入的内容
                nativeWriteToFile(fd, "hello world");
            }
        }
    }
}

Native层代码示例(通过FD写入文件):

#include <unistd.h>
#include <string.h>
#include <jni.h>

extern "C" JNIEXPORT void JNICALL
Java_com_example_yourpackage_MainActivity_nativeWriteToFile(
        JNIEnv* env,
        jobject /* this */,
        jobject fd_obj,
        jstring content) {

    // 从Java的FileDescriptor对象中拿到原生FD
    jclass fd_cls = env->GetObjectClass(fd_obj);
    jfieldID fd_field = env->GetFieldID(fd_cls, "descriptor", "I");
    int fd = env->GetIntField(fd_obj, fd_field);

    // 写入内容
    const char* c_content = env->GetStringUTFChars(content, nullptr);
    write(fd, c_content, strlen(c_content));

    // 释放资源
    env->ReleaseStringUTFChars(content, c_content);
    close(fd);
}

3. 临时妥协方案:启用旧版存储模式(不推荐)

如果你的APP只需要兼容到API 29,可以在AndroidManifest.xml的application标签里加一行:

android:requestLegacyExternalStorage="true"

但这个属性在API 30及以上会失效,所以只是临时过渡方案,长远来看还是得用MediaStore的合规方案。

你的代码失败原因

你直接用std::ofstream打开/storage/self/primary/Download/sample.txt,在API 29+的分区存储规则下,APP没有直接访问这个路径的权限,所以fp.is_open()会返回false,无法完成写入操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 08:13:34