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

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版本已确保应用设置中出现读取音频文件的权限选项。


问题解决思路

一、权限请求弹窗不弹出的修复

  1. 主动发起权限请求: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()即可触发权限请求。

  2. 清理冗余权限: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 17:44:51