如何使用MediaStore读取或打开已下载的.apk文件?
MediaStore实现下载目录APK读取及MD5计算的适配方案
Android 10强制启用分区存储后,直接通过文件路径操作公共下载目录的方式会被系统拦截,也就是你遇到的权限拒绝异常。核心适配思路是完全替换原来的File路径访问逻辑,通过MediaStore的下载集合接口查询目标APK,直接通过Uri拿到文件输入流完成MD5计算,全程不需要接触真实文件路径,就能完全对齐你原来的业务逻辑。
版本分支适配
按照系统版本做兼容拆分,避免低版本走冗余的MediaStore查询逻辑:
- Android 10(API 29)及以上:走MediaStore.Downloads集合查询流程,绕过分区存储限制
- Android 9(API 28)及以下:保留原有File读取逻辑即可,这类版本没有强制分区存储,原代码不会触发权限问题
对齐原有逻辑的Kotlin实现
替换原来直接构造File读取的代码块,整体流程和你原有的try-catch容错、按固定路径找指定安装包、计算MD5的逻辑完全一致:
// 保留你项目中原有的安装包文件名常量 private const val INSTALLER_NAME = "目标安装包文件名.apk" fun getDownloadApkMd5(context: Context): String? { // 低版本直接走原有逻辑 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { return try { File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), INSTALLER_NAME ).md5() } catch (e: Exception) { Timber.e(e) null } } // Android 10及以上走MediaStore查询 val downloadUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI // 查询条件:匹配完全一致的显示文件名 val selection = "${MediaStore.Downloads.DISPLAY_NAME} = ?" val selectionArgs = arrayOf(INSTALLER_NAME) var targetMd5: String? = null try { context.contentResolver.query( downloadUri, arrayOf(MediaStore.Downloads._ID), selection, selectionArgs, null )?.use { cursor -> if (cursor.moveToFirst()) { val fileId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID)) val targetFileUri = ContentUris.withAppendedId(downloadUri, fileId) // 直接打开输入流计算MD5,不需要转成File对象 context.contentResolver.openInputStream(targetFileUri)?.use { input -> targetMd5 = input.md5() } } } } catch (e: Exception) { Timber.e(e) } return targetMd5 }
关键注意点
- 不要尝试把MediaStore返回的Uri转成本地File绝对路径:这类转换在分区存储下基本都会触发权限问题,通过Uri打开的输入流已经完全支持文件读取、计算MD5的操作,没有额外的功能损失
- 如果查询不到匹配的文件:检查文件名匹配条件,部分应用下载文件时会在临时文件阶段加
.tmp后缀,完成写入后才会改成正式APK名,可以适当调整查询条件做模糊匹配 - 权限适配:Android 13及以上读取公共下载目录内容时,只要应用声明了对应版本的基础存储读取权限即可,不需要申请高危的全文件访问权限,就能完成对Downloads集合的常规读取操作
内容的提问来源于stack exchange,提问作者Andreas1234
相关产品推荐
相关产品推荐

