Android 13设备上如何从C/C++原生端写入DCIM文件夹?
解决Android 13下MediaStore结合原生代码写入DCIM的问题
问题根源
Android 13强化了SELinux策略与/proc文件系统的访问限制,你之前通过/proc/self/fd/拼接路径让C++代码访问文件的方式会被拦截——因为该路径对应的文件属于MediaStore的沙箱化资源,直接通过路径访问会触发权限校验失败。
无需SAF的MediaStore解决方案
核心思路是直接传递文件描述符(fd)给C++代码,而非转换为路径。ParcelFileDescriptor返回的fd本身就是有效的、拥有写入权限的文件描述符,原生代码可直接用它进行IO操作,完全绕开路径访问的权限问题。
步骤1:保留原有MediaStore条目插入逻辑
这部分代码在Android 13上依然有效,无需修改:
Uri placeholderUri = null; ContentResolver contentResolver = context.getContentResolver(); ContentValues contentValues = new ContentValues(); contentValues.put(MediaStore.Video.Media.DISPLAY_NAME, "TEST_VIDEO"); contentValues.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4"); contentValues.put(MediaStore.Video.Media.RELATIVE_PATH, "DCIM/" + "APP_NAME"); try { placeholderUri = contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues); } catch (RuntimeException e) { e.printStackTrace(); }
步骤2:获取ParcelFileDescriptor并传递fd到C++
不再拼接/proc路径,直接把fd的数值传递给原生层:
if (placeholderUri != null) { try (ParcelFileDescriptor fd = context.getContentResolver().openFileDescriptor(placeholderUri, "rw")) { if (fd != null) { // 调用原生方法,传入fd nativeWriteVideoData(fd.getFd()); } } catch (IOException e) { e.printStackTrace(); } }
步骤3:C++层直接操作fd
在原生代码中,直接使用传入的fd进行写入,无需再次打开文件:
#include <jni.h> #include <stdio.h> #include <unistd.h> extern "C" JNIEXPORT void JNICALL Java_com_your_package_YourActivity_nativeWriteVideoData(JNIEnv* env, jobject thiz, jint fd) { // 替换为你的图像处理/视频录制写入逻辑 const char* testData = "Sample video data"; ssize_t bytesWritten = write(fd, testData, strlen(testData)); if (bytesWritten == -1) { perror("Failed to write to fd"); } // 若Java层使用try-with-resources,fd会自动关闭,无需手动调用close(fd) }
关键注意事项
- fd生命周期管理:在Java层用
try-with-resources包裹ParcelFileDescriptor,确保操作完成后自动释放资源;若原生代码需要长时间写入,需保持ParcelFileDescriptor的引用,避免fd被提前回收。 - 权限说明:Android 13及以上版本,写入视频到MediaStore无需申请
WRITE_EXTERNAL_STORAGE权限,系统默认允许创建媒体文件。 - 兼容性:该方案兼容Android 10/11/13,无需针对不同版本做分支处理。
内容的提问来源于stack exchange,提问作者yellowsubmarine
相关产品推荐
相关产品推荐

