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

