Unity获取Android设备全部音乐文件的权限异常问题求助
问题原因分析
- Scoped Storage限制:Android 10(API 29)及以上引入了Scoped Storage机制,即便授予
READ_EXTERNAL_STORAGE权限,也禁止直接遍历/storage根目录或其他应用私有目录。你的文件获取代码用Directory.GetDirectories遍历/storage,这在Android 10+设备上会被系统拦截,抛出权限异常。 - 权限申请逻辑缺陷:原协程固定等待3秒再检查权限,无法精准捕捉用户授权时机,可能导致权限未完全授予就跳转场景;同时权限请求顺序和检查逻辑存在漏洞,比如未等待用户完成
READ权限授权就进入循环,可能导致该权限未被正确获取。 - Manifest配置缺失:你的
targetSdkVersion为24,虽低于29,但Android 10+设备会对低版本targetSdk应用强制启用部分Scoped Storage限制,需添加requestLegacyExternalStorage属性兼容旧存储模型(仅作为临时过渡方案)。
解决方案
一、修正权限申请逻辑
修改ButtonPressing脚本,确保权限请求和检查逻辑更可靠,等待用户完成授权后再跳转场景:
using System.Collections; using UnityEngine; using UnityEngine.Android; using UnityEngine.SceneManagement; public class ButtonPressing : MonoBehaviour { private void Start() { #if PLATFORM_ANDROID StartCoroutine(RequestStoragePermissions()); #endif } private IEnumerator RequestStoragePermissions() { // 先检查是否已拥有所有需要的权限 if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead) && Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) { SceneManager.LoadScene(1); yield break; } // 请求WRITE权限(Android中WRITE权限隐含READ权限,但仍需显式申请) if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) { Permission.RequestUserPermission(Permission.ExternalStorageWrite); // 等待用户授权结果 while (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) { yield return null; } } // 确认READ权限 if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead)) { Permission.RequestUserPermission(Permission.ExternalStorageRead); while (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead)) { yield return null; } } // 所有权限授予完成,跳转场景 SceneManager.LoadScene(1); } }
二、临时兼容旧存储模型(仅针对targetSdk<29)
在AndroidManifest.xml的application标签中添加android:requestLegacyExternalStorage="true",让Android 10+设备允许应用使用旧存储访问方式:
<application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false" android:isGame="true" android:banner="@drawable/app_banner" android:requestLegacyExternalStorage="true"> <!-- 新增该行 --> <!-- 其他内容保持不变 --> </application>
注意:这是临时过渡方案,Android 11(API 30)及以上会逐步限制该属性,长期需适配Scoped Storage。
三、适配Scoped Storage的正确方式(推荐长期方案)
放弃直接遍历目录的方式,使用Android官方推荐的MediaStore API查询设备音乐文件,完全符合Scoped Storage要求:
using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class MusicFileLoader : MonoBehaviour { public Text directoryTexts; public void GetFiles() { #if PLATFORM_ANDROID if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageRead)) { directoryTexts.text = "正在获取音乐文件...\n"; List<string> musicPaths = GetMusicFilesViaMediaStore(); if (musicPaths.Count > 0) { directoryTexts.text += $"找到{musicPaths.Count}个音乐文件:\n"; foreach (string path in musicPaths) { directoryTexts.text += path + "\n"; } } else { directoryTexts.text += "未找到音乐文件\n"; } } else { directoryTexts.text += "缺少读取存储权限\n"; } #endif } private List<string> GetMusicFilesViaMediaStore() { List<string> musicPaths = new List<string>(); using (AndroidJavaClass mediaStoreClass = new AndroidJavaClass("android.provider.MediaStore")) using (AndroidJavaObject contentResolver = new AndroidJavaClass("com.unity3d.player.UnityPlayer") .GetStatic<AndroidJavaObject>("currentActivity") .Call<AndroidJavaObject>("getContentResolver")) { // 音乐文件的URI AndroidJavaObject audioUri = mediaStoreClass.GetStatic<AndroidJavaObject>("Audio.Media.EXTERNAL_CONTENT_URI"); // 需要查询的列:文件路径 string[] projection = new string[] { mediaStoreClass.GetStatic<string>("Audio.Media.DATA") }; // 查询所有音乐文件 AndroidJavaObject cursor = contentResolver.Call<AndroidJavaObject>("query", audioUri, projection, null, null, null); if (cursor.Call<bool>("moveToFirst")) { int dataColumnIndex = cursor.Call<int>("getColumnIndex", mediaStoreClass.GetStatic<string>("Audio.Media.DATA")); do { string filePath = cursor.Call<string>("getString", dataColumnIndex); musicPaths.Add(filePath); } while (cursor.Call<bool>("moveToNext")); } cursor.Call("close"); } return musicPaths; } }
额外注意事项
- 若将
targetSdkVersion升级到29及以上,可移除WRITE_EXTERNAL_STORAGE权限(Scoped Storage下访问公共媒体文件无需该权限,除非要修改其他应用的文件)。 - 测试时使用Android 10+真实设备,模拟器可能存在存储模拟异常问题。
内容的提问来源于stack exchange,提问作者Kamal Azizov
相关产品推荐
相关产品推荐

