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;
        }
    }

验证

相关推荐
王码码20357 小时前
Flutter for OpenHarmony: Flutter 三方库 cryptography 在鸿蒙上实现金融级现代加解密(高性能安全库)
android·安全·flutter·华为·金融·harmonyos
亚历克斯神9 小时前
Flutter for OpenHarmony:Flutter 三方库 yaml_edit 精准修改 YAML 文件内容(保留注释与格式的编辑神器)
android·flutter·华为·harmonyos
左手厨刀右手茼蒿9 小时前
Flutter for OpenHarmony: Flutter 三方库 image_size_getter 零加载极速获取图片尺寸(鸿蒙 UI 布局优化必备)
android·服务器·flutter·ui·华为·harmonyos
风痕天际9 小时前
Godot扫雷游戏制作记录3——随机埋雷
游戏·游戏引擎·godot
亚历克斯神9 小时前
Flutter for OpenHarmony:zxing2 纯 Dart 条码扫描与生成库(不仅是扫码,更是编解码引擎) 深度解析与鸿蒙适配指南
android·flutter·华为·harmonyos
钛态9 小时前
Flutter for OpenHarmony:dio_cookie_manager 让 Dio 发挥会话管理能力,像浏览器一样自动处理 Cookie 深度解析与鸿蒙适配指南
android·linux·运维·flutter·ui·华为·harmonyos
键盘鼓手苏苏9 小时前
Flutter for OpenHarmony: Flutter 三方库 ntp 精准同步鸿蒙设备系统时间(分布式协同授时利器)
android·分布式·算法·flutter·华为·中间件·harmonyos
louisgeek12 小时前
Android ViewBinding
android
城东米粉儿14 小时前
Kotlin 协程的异常处理 笔记
android
锥栗14 小时前
【其他】基于Trae的大模型智能应用开发
android·java·数据库