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

Android 12调用openAssetFileDescriptor测算U盘空间耗时过长如何优化

解决方案

问题根因

Android 12对外部存储的MediaProvider逻辑进行了调整,当调用openAssetFileDescriptor传入U盘根目录的tree Uri时,系统会隐式遍历根目录下所有文件的元数据,所以耗时会随目录占用空间线性增长,属于系统已知的异常行为,低版本Android无该遍历逻辑所以调用速度快。

替代方案

  • 方案1:通过DocumentsContract直接查询存储卷容量(推荐,全版本兼容,毫秒级返回)
    不需要打开文件描述符,直接通过ContentResolver查询U盘根对应的存储卷统计值,代码示例:

    val projection = arrayOf(
        DocumentsContract.Root.COLUMN_AVAILABLE_BYTES,
        DocumentsContract.Root.COLUMN_CAPACITY_BYTES
    )
    // 从tree Uri中提取根Uri
    val rootId = DocumentsContract.getRootId(rootUri)
    val rootsUri = DocumentsContract.buildRootsUri(rootUri.authority)
    contentResolver.query(rootsUri, projection, "${DocumentsContract.Root.COLUMN_ROOT_ID}=?", arrayOf(rootId), null)?.use { cursor ->
        if (cursor.moveToFirst()) {
            val availableSpace = cursor.getLong(cursor.getColumnIndexOrThrow(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES))
            // 直接拿到可用空间,单位字节
        }
    }
    

    该方法直接读取系统预存的存储卷统计值,不会触发文件遍历,API 19以上即可使用,不需要额外权限。如果部分定制ROM的文档提供器未实现上述字段查询,可选用方案2。

  • 方案2:使用U盘内单个文件的文件描述符查询
    同一个存储卷下任意文件的文件描述符调用fstatvfs返回的都是整个卷的空间统计值,你可以选择U盘内一个已存在的小文件,或者创建一个临时空文件,打开它的AssetFileDescriptor再查询空间,完全规避根目录遍历的问题:

    // 示例:替换为U盘内任意一个已有文件的Uri,或自行创建临时文件的Uri
    val singleFileUri = /* U盘内任意文件的Uri */
    val descriptor = contentResolver.openAssetFileDescriptor(singleFileUri, "r")
    val stats = Os.fstatvfs(descriptor!!.fileDescriptor)
    val availableSpace = stats.f_bavail * stats.f_bsize
    descriptor.close()
    

    该方法查询结果和原方案完全一致,耗时稳定在毫秒级。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 15:54:07