Unity 发布 Android 安卓端所有文件可读写

现在有个需求,读写安卓系统的根目录下的文档

路径为

/storage/emulated/0/Documents/

在Unity PlayerSetting里有一个设置访问非持久化路径的设置

但是实际打包后发现还是访问不了。

解决方法

申请与配置方法

先自动生成一下AndroidManifest.xml,然后添加授权

csharp 复制代码
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

MANAGE_EXTERNAL_STORAGE 是 Android 10(API 级别 29)引入的系统权限,允许应用访问和管理设备外部存储上的所有文件,但需用户显式授权。‌

授权方法

csharp 复制代码
    /// <summary>
    /// 检查授权状态
    /// </summary>
    /// <returns></returns>
    private bool HasExternalStoragePermission()
    {
#if (!UNITY_EDITOR && UNITY_ANDROID)
        using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
        {
            if (version.GetStatic<int>("SDK_INT") >= 30)
            {
                using (var environment = new AndroidJavaClass("android.os.Environment"))
                {
                    return environment.CallStatic<bool>("isExternalStorageManager");
                }
            }
        }
#endif
        return true;
    }
    /// <summary>
    /// 唤醒授权
    /// </summary>
    private void RequestExternalStoragePermission()
    {
#if (!UNITY_EDITOR && UNITY_ANDROID)
        using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
        {
            if (version.GetStatic<int>("SDK_INT") >= 30)
            {
                using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
                {
                    using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
                    {
                        using (var intent = new AndroidJavaObject("android.content.Intent"))
                        {
                            intent.Call<AndroidJavaObject>("setAction", "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION");
                            using (var uriClass = new AndroidJavaClass("android.net.Uri"))
                            {
                                using (var uri = uriClass.CallStatic<AndroidJavaObject>("parse", "package:" + currentActivity.Call<string>("getPackageName")))
                                {
                                    intent.Call<AndroidJavaObject>("setData", uri);
                                }
                            }
                            currentActivity.Call("startActivity", intent);
                        }
                    }
                }
            }
        }
#endif
    }

完整的测试代码

功能是要在安卓根目录的文档中,必须存在test.txt,没有就写入,有就读取。

(比如实现写入某个唯一密钥文件,用于激活软件)

csharp 复制代码
public string path = "/storage/emulated/0/Documents/test.txt";
    public Text mt;

    void Start()
    {
        GetPermission();
        CheckFileAndProcess();
    }

    private void OnApplicationFocus(bool focus)
    {
        if (focus)
        {
            GetPermission();
        }
    }

    void GetPermission()
    {
        if (!HasExternalStoragePermission())
        {
            RequestExternalStoragePermission();
        }
        else
        {
            SceneManager.LoadSceneAsync(1);
        }
    }

    private bool HasExternalStoragePermission()
    {
#if (!UNITY_EDITOR && UNITY_ANDROID)
        using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
        {
            if (version.GetStatic<int>("SDK_INT") >= 30)
            {
                using (var environment = new AndroidJavaClass("android.os.Environment"))
                {
                    return environment.CallStatic<bool>("isExternalStorageManager");
                }
            }
        }
#endif
        return true;
    }
    
    private void RequestExternalStoragePermission()
    {
#if (!UNITY_EDITOR && UNITY_ANDROID)
        using (var version = new AndroidJavaClass("android.os.Build$VERSION"))
        {
            if (version.GetStatic<int>("SDK_INT") >= 30)
            {
                using (var unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
                {
                    using (var currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
                    {
                        using (var intent = new AndroidJavaObject("android.content.Intent"))
                        {
                            intent.Call<AndroidJavaObject>("setAction", "android.settings.MANAGE_APP_ALL_FILES_ACCESS_PERMISSION");
                            using (var uriClass = new AndroidJavaClass("android.net.Uri"))
                            {
                                using (var uri = uriClass.CallStatic<AndroidJavaObject>("parse", "package:" + currentActivity.Call<string>("getPackageName")))
                                {
                                    intent.Call<AndroidJavaObject>("setData", uri);
                                }
                            }
                            currentActivity.Call("startActivity", intent);
                        }
                    }
                }
            }
        }
#endif
    }

    private void CheckFileAndProcess()
    {
        if (!HasExternalStoragePermission())
        {
            SetTextSafe("无文件访问权限,无法执行文件操作!");
            RequestExternalStoragePermission();
            return;
        }

        while (!File.Exists(path))
        {
            try
            {
                string directoryPath = Path.GetDirectoryName(path);
                if (!Directory.Exists(directoryPath))
                {
                    Directory.CreateDirectory(directoryPath);
                    Debug.Log("自动创建目录:" + directoryPath);
                }
                string writeContent = "HelloWorld";
                File.WriteAllText(path, writeContent, System.Text.Encoding.UTF8);
                Debug.Log("文件不存在,已自动写入:" + path + " 内容:" + writeContent);
            }
            catch (Exception ex)
            {
                SetTextSafe("文件写入失败,重试中:" + ex.Message);
            }
        }

        try
        {
            string fileContent = File.ReadAllText(path, System.Text.Encoding.UTF8);
            SetTextSafe("程序启动检测到文件,内容:\n" + fileContent);
        }
        catch (Exception ex)
        {
            SetTextSafe("文件存在但读取失败:" + ex.Message);
            Debug.LogError("文件读取异常:" + ex.ToString());
        }
    }

    public void ReadFileContentToText()
    {
        if (!HasExternalStoragePermission())
        {
            SetTextSafe("请先授予文件访问权限!");
            RequestExternalStoragePermission();
            return;
        }

        if (mt == null)
        {
            Debug.LogError("请在Inspector面板为mt赋值Text组件!");
            return;
        }

        try
        {
            if (!File.Exists(path))
            {
                SetTextSafe("文件不存在:" + path);
                return;
            }

            string fileContent = File.ReadAllText(path, System.Text.Encoding.UTF8);
            SetTextSafe("文件内容:\n" + fileContent);
            Debug.Log("文件读取成功:" + path);
        }
        catch (Exception ex)
        {
            SetTextSafe("读取失败:" + ex.Message);
            Debug.LogError("文件读取异常:" + ex.ToString());
        }
    }
    public void WriteHelloWorldToFile()
    {
        if (!HasExternalStoragePermission())
        {
            SetTextSafe("请先授予文件访问权限!");
            RequestExternalStoragePermission();
            return;
        }

        try
        {
            string directoryPath = Path.GetDirectoryName(path);
            if (!Directory.Exists(directoryPath))
            {
                Directory.CreateDirectory(directoryPath);
                Debug.Log("目录已创建:" + directoryPath);
            }

            string writeContent = "HelloWorld";
            File.WriteAllText(path, writeContent, System.Text.Encoding.UTF8);

            if (mt != null)
            {
                SetTextSafe("写入成功!\n路径:" + path + "\n内容:" + writeContent);
            }
            Debug.Log("文件写入成功:" + path);
        }
        catch (Exception ex)
        {
            if (mt != null)
            {
                SetTextSafe("写入失败:" + ex.Message);
            }
            Debug.LogError("文件写入异常:" + ex.ToString());
        }
    }


    private void SetTextSafe(string content)
    {
        if (mt != null && !string.IsNullOrEmpty(content))
        {
            mt.text = content;
        }
    }

验证

相关推荐
aqi0040 分钟前
鸿蒙版本的电子书阅读APP开放源码啦
android·华为·ai编程·harmonyos·鸿蒙
墨天梦1 小时前
B19_SunnyWeather到阅读客户端
android·kotlin
美狐美颜sdk2 小时前
直播APP源码可以直接接入视频美颜sdk吗?技术方案详解
android·人工智能·音视频·美颜sdk·直播美颜sdk
用户69371750013845 小时前
2026,程序员的时代拐点到了
android·前端·后端
浪潮IT馆6 小时前
Android Studio 非最新版本下载安装教程
android·ide·android studio
xiaozongt19897 小时前
MasterGo + Claude Code 生成 Android 布局:完整实践指南
android
Anhty8 小时前
2026最新免费手机音频处理工具 !!
android·功能测试·ios·智能手机·音视频
2501_9159090611 小时前
iOS应用从开发到上架App Store的完整发布流程与步骤指南
android·ios·小程序·https·uni-app·webview
狗凯之家源码网11 小时前
AI 步数修改提交系统源码应用场景与落地指南
android·数据库·人工智能·运动步数
ZHOUPUYU11 小时前
PHP 9.0 前瞻:下一代 PHP 会带来哪些新功能?
android·开发语言·php