Unity开发Android应用进行文件操作是否需申请权限及配置方法
Unity开发Android应用执行文件/媒体操作必须申请对应权限,PC端Editor运行正常是因为Windows环境没有Android系统级的沙箱权限管控,打包为APK后未配置、申请权限的情况下,所有跨沙箱的文件访问、修改、删除操作都会被系统直接拦截。
一、Manifest静态权限配置
你的猜测是对的,权限声明首先要在AndroidManifest.xml中完成,操作步骤:
- 找到Unity对应编辑器版本的基础Manifest模板,路径一般在Unity安装目录下的
Editor/Data/PlaybackEngines/AndroidPlayer/Apk/AndroidManifest.xml,复制到你项目的Assets/Plugins/Android目录下再修改,不要直接改安装目录内的模板文件。 - 根据你要支持的最低安卓版本,在
<manifest>标签内添加对应权限声明:
- 适配Android 12及以下(API 32及以下),访问外部公共存储需要添加:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
- 适配Android 13及以上(API 33+),WRITE_EXTERNAL_STORAGE权限已被废弃,谷歌按媒体类型拆分了读权限,按需添加:
<!-- 访问图片类文件 --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- 访问视频类文件 --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- 访问音频类文件 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!-- 如果需要操作文档、压缩包等非媒体类任意文件,额外添加全文件访问权限 --> <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
补充说明:如果你的所有文件操作都在应用私有沙箱目录(即Application.persistentDataPath对应路径)内,不需要申请任何存储权限。
3. 适配Android 10(API 29)的分区存储兼容,在Manifest的<application>标签内添加属性:
<application android:requestLegacyExternalStorage="true" ...其他原有属性>
二、运行时动态权限申请
从Android 6.0(API 23)开始,存储类属于危险权限,仅在Manifest中静态声明不会生效,必须在运行时调用系统API弹出授权窗口,让用户手动同意后才能操作文件。Unity内置了Android权限申请接口,不需要额外开发原生插件,参考实现代码:
using UnityEngine; using UnityEngine.Android; public class StoragePermissionHandler : MonoBehaviour { private string[] targetPermissions; void Awake() { if (Application.platform != RuntimePlatform.Android) return; int sdkVersion = GetCurrentAndroidSDK(); // 按系统版本匹配需要申请的权限列表 if (sdkVersion >= 33) { targetPermissions = new string[] { Permission.ReadMediaImages, Permission.ReadMediaVideo, Permission.ReadMediaAudio, "android.permission.MANAGE_EXTERNAL_STORAGE" }; } else { targetPermissions = new string[] { Permission.ExternalStorageRead, Permission.ExternalStorageWrite }; } CheckAndRequestPermissions(); } private void CheckAndRequestPermissions() { bool allPermissionGranted = true; foreach (var perm in targetPermissions) { if (!Permission.HasUserAuthorizedPermission(perm)) { allPermissionGranted = false; break; } } if (allPermissionGranted) return; // 发起权限申请 Permission.RequestUserPermissions(targetPermissions, new PermissionCallbacks { PermissionGranted = permName => Debug.Log($"权限{permName}授权成功"), PermissionDenied = permName => Debug.Log($"权限{permName}被拒绝,对应文件操作会失败"), PermissionDeniedAndDontAskAgain = permName => Debug.Log($"权限{permName}被永久拒绝,需要引导用户到系统设置页手动开启") }); } // 获取当前设备Android SDK版本 private int GetCurrentAndroidSDK() { using (AndroidJavaClass buildVersion = new AndroidJavaClass("android.os.Build$VERSION")) { return buildVersion.GetStatic<int>("SDK_INT"); } } }
注意:
MANAGE_EXTERNAL_STORAGE全文件访问权限不支持普通弹窗申请,需要引导用户跳转到系统专门的「所有文件访问权限」页面手动开启,对应跳转代码:
public void JumpToAllFileAccessSetting() { using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity")) using (AndroidJavaClass uriClass = new AndroidJavaClass("android.net.Uri")) using (AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION")) { string packageName = currentActivity.Call<string>("getPackageName"); using (AndroidJavaObject uri = uriClass.CallStatic<AndroidJavaObject>("parse", $"package:{packageName}")) { intent.Call<AndroidJavaObject>("setData", uri); currentActivity.Call("startActivity", intent); } } }
三、常见问题排查
- 不要硬编码类似
/sdcard/xxx的绝对路径,优先使用Unity官方提供的路径API获取目录,避免不同厂商系统路径映射差异导致的操作失败。 - Editor下运行正常不代表移动端权限配置正确,PC端不会触发Android的沙箱权限校验,必须打真机包通过Logcat查看日志,文件操作失败最常见的报错就是
java.io.IOException: Permission denied。 - 如果应用仅个人使用,也可以在系统设置的应用权限页手动给应用开启存储/全文件访问权限,但正式逻辑里还是要做运行时申请,避免不同ROM默认权限策略差异导致功能失效。
内容的提问来源于stack exchange,提问作者Harsh
相关产品推荐
相关产品推荐

