Android应用中如何通过C代码访问相机媒体文件?
Android应用中通过C代码稳定访问媒体文件的方案
问题描述
我希望在Android应用中通过C代码访问媒体文件,目前使用如下Kotlin方法通过文件Uri获取文件路径:
fun getFileNameThatICanUseInNativeCode(uri: Uri?, applicationContext: Context): String? { val mParcelFileDescriptor = applicationContext.contentResolver.openFileDescriptor(uri!!, "r") if (mParcelFileDescriptor != null) { val fd: Int = mParcelFileDescriptor.fd val file = File("/proc/self/fd/$fd") var path: String? = null try { path = Os.readlink(file.absolutePath).toString() mParcelFileDescriptor.close() } catch (e: ErrnoException) { e.printStackTrace() } return path } else { return null } }
该方法通常返回路径:/mnt/user/0/emulated/0/DCIM/Camera/{fileName},但部分设备找不到user文件夹(推测是权限问题,如adb shell无法访问该文件夹)。另外,文件也存在于/storage/emulated/0/DCIM/Camera/$fileName路径,但使用C代码if((file = fopen(native_file_path,"a"))!=NULL)时,部分设备可访问,部分无法访问。
最优解决方案:直接传递文件描述符(fd)给C代码
不要依赖文件路径访问,直接传递文件描述符是最稳定的方案,原因如下:
- Android存储虚拟化机制导致不同设备的文件路径存在差异,依赖路径易出现兼容性问题
- 媒体文件Uri对应的路径可能受权限限制,直接用路径打开会触发权限校验失败
实现步骤
- Kotlin层:获取文件描述符后直接传递给Native方法,用完再关闭
fun passFdToNative(uri: Uri?, context: Context) { val pfd = context.contentResolver.openFileDescriptor(uri!!, "r") pfd?.let { // 调用Native方法传入文件描述符 nativeAccessFile(it.fd) // 操作完成后关闭文件描述符 it.close() } } // 声明Native方法 external fun nativeAccessFile(fd: Int) - C代码层:使用
fdopen将文件描述符转为FILE指针进行操作#include <stdio.h> #include <unistd.h> void nativeAccessFile(int fd) { // 打开模式需与Kotlin层一致(此处为"r"只读) FILE* file = fdopen(fd, "r"); if (file != NULL) { // 示例:读取文件内容 char buffer[1024]; size_t bytesRead = fread(buffer, 1, sizeof(buffer), file); // 执行你的业务操作... // 注意:不要调用fclose(file),避免重复关闭文件描述符 // 如需提前关闭,直接调用close(fd)即可 close(fd); } }
关键注意事项
- 确保Kotlin层打开文件的模式与C层一致(如"r"只读、"w"写入、"a"追加)
- 不要在C层调用
fclose,否则会关闭底层文件描述符,导致Kotlin层关闭时出错;若需提前关闭,直接调用close(fd) - 该方案利用ContentResolver已获取的权限,无需额外申请存储权限,兼容性更强
可选路径访问方案(稳定性较差)
如果必须通过路径访问,可尝试以下两种方式,但需注意权限和兼容性问题:
- 标准公共目录路径:使用
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)获取DCIM目录的标准路径,拼接文件名后访问。但Android 10及以上需要MANAGE_EXTERNAL_STORAGE权限(需用户手动授予,Google Play审核严格) - MediaStore获取相对路径:针对Android 10+,通过MediaStore API获取文件相对路径,再拼接
/storage/emulated/0/得到完整路径。但仍存在路径兼容性和权限限制问题
内容的提问来源于stack exchange,提问作者Артемий Величко




