You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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,提问作者Артемий Величко

火山引擎 最新活动