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
相关产品推荐
相关产品推荐

