Kotlin Android:如何申请读取存储权限及扫描全设备音频文件?
音频文件读取权限与扫描问题排查
问题概述
作为Android Kotlin开发新手,遇到两个核心问题:
- 应用首次启动时未弹出读取存储(音频文件)的权限请求弹窗,手动在系统设置授予权限后,仍无法在列表中显示音频文件(已知Android API 13可能无需传统存储权限,但问题依旧存在)。
- 不清楚如何扫描设备所有文件夹中的音频文件。
我的Manifest配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:requestLegacyExternalStorage="true" android:theme="@style/Theme.MPMP" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:theme="@style/Theme.AppCompat"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>
额外说明
Theme.AppCompat对当前应用运行至关重要,需保留;当前Manifest版本已确保应用设置中出现读取音频文件的权限选项。
问题解决思路
一、权限请求弹窗不弹出的修复
主动发起权限请求:Manifest声明权限后,系统不会自动弹出请求弹窗,必须在代码中主动触发。针对不同API版本做适配:
private val REQUEST_PERMISSION_CODE = 1001 private fun requestAudioPermissions() { val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf(Manifest.permission.READ_MEDIA_AUDIO) } else { arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) } if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE) } else { // 权限已授予,开始扫描音频文件 scanAudioFiles() } } // 处理权限请求回调 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_PERMISSION_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { scanAudioFiles() } else { Toast.makeText(this, "需要音频读取权限才能显示文件", Toast.LENGTH_SHORT).show() } } }在
MainActivity的onCreate方法中调用requestAudioPermissions()即可触发权限请求。清理冗余权限:
MANAGE_EXTERNAL_STORAGE是仅针对特殊场景(如全功能文件管理器)的权限,音频读取无需该权限,建议移除以简化权限逻辑;WRITE_EXTERNAL_STORAGE在API 30+已废弃,若无需写入操作也可移除。
二、扫描设备所有音频文件的实现
推荐使用Android官方的MediaStore API,适配所有API版本,可自动索引设备上所有可识别的音频文件:
private fun scanAudioFiles() { val audioList = mutableListOf<Pair<String, String>>() // 存储文件名与路径 val projection = arrayOf( MediaStore.Audio.Media.DATA, MediaStore.Audio.Media.DISPLAY_NAME ) contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, null, null, null )?.use { cursor -> val dataColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA) val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME) while (cursor.moveToNext()) { val filePath = cursor.getString(dataColumn) val fileName = cursor.getString(nameColumn) audioList.add(fileName to filePath) } } // 处理扫描结果,比如更新UI列表 Log.d("AudioScan", "扫描到${audioList.size}个音频文件") }
- MediaStore会自动覆盖设备所有存储分区的音频文件,包括各个文件夹下的内容。
- 若需要扫描系统未自动索引的音频文件,可结合
MediaScannerConnection触发手动扫描,但常规场景下MediaStore已足够。
内容的提问来源于stack exchange,提问作者KaiserxPyro
相关产品推荐
相关产品推荐

