Unity中JSON数据PC端正常读取,安卓端失效的原因求助
为什么安卓端无法读写StreamingAssets里的JSON文件?
这个问题我太熟了——Unity的StreamingAssets目录在不同平台的行为差异很大,这就是你遇到的核心问题:
- PC端:
StreamingAssets是普通的文件系统目录,完全支持读写操作,所以你的代码能正常工作。 - 安卓端:
StreamingAssets里的文件是打包进APK压缩包的,属于只读资源,既不能直接写入,甚至连File.ReadAllText这种同步读取方式都无法正常使用。
解决方案:切换到persistentDataPath存储
正确的做法是:把StreamingAssets作为初始默认数据的存放地,第一次运行游戏时将默认的lastlevel.json复制到有读写权限的Application.persistentDataPath目录,之后所有的读写操作都针对这个目录下的文件。
修改后的完整代码
这里我帮你重写了ProgressSavior脚本,处理了跨平台的读写逻辑,包括安卓端的异步读取:
using System.Collections; using System.IO; using System.Text.RegularExpressions; using UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; public class ProgressSavior : MonoBehaviour { // 最终的读写路径:persistentDataPath(全平台可读写) private string _savePath; // 初始默认文件的路径(StreamingAssets) private string _defaultFilePath; private readonly Regex _levelRegex = new Regex(@"^level_[1-9][0-9]*$"); private Level _savedLevel; private void Awake() { // 初始化路径 _defaultFilePath = Path.Combine(Application.streamingAssetsPath, "lastlevel.json"); _savePath = Path.Combine(Application.persistentDataPath, "lastlevel.json"); // 启动协程加载初始数据 StartCoroutine(InitializeSaveData()); } private IEnumerator InitializeSaveData() { // 检查persistentDataPath里是否已有保存文件 if (!File.Exists(_savePath)) { // 如果没有,从StreamingAssets复制默认文件 yield return CopyDefaultFileToPersistentPath(); } // 读取保存的数据 string jsonData = File.ReadAllText(_savePath); _savedLevel = JsonUtility.FromJson<Level>(jsonData); // 检查当前场景是否是关卡,是的话尝试保存 string currentSceneName = SceneManager.GetActiveScene().name; if (_levelRegex.IsMatch(currentSceneName)) { TrySaveCurrentLevel(currentSceneName); } } private IEnumerator CopyDefaultFileToPersistentPath() { // 安卓端需要用UnityWebRequest读取StreamingAssets if (Application.platform == RuntimePlatform.Android) { using UnityWebRequest webRequest = UnityWebRequest.Get(_defaultFilePath); yield return webRequest.SendWebRequest(); if (webRequest.result != UnityWebRequest.Result.Success) { Debug.LogError($"Failed to load default save file: {webRequest.error}"); // 初始化一个默认的Level对象 _savedLevel = new Level { name = "level_1" }; string defaultJson = JsonUtility.ToJson(_savedLevel); File.WriteAllText(_savePath, defaultJson); yield break; } // 将读取到的内容写入persistentDataPath File.WriteAllText(_savePath, webRequest.downloadHandler.text); } else { // PC/其他平台直接复制文件 File.Copy(_defaultFilePath, _savePath); } } private bool ShouldSaveLevel(string currentLevelName) { if (!_levelRegex.IsMatch(currentLevelName) || !_levelRegex.IsMatch(_savedLevel.name)) { return false; } if (int.TryParse(currentLevelName.Split('_')[1], out int currentLevelNum) && int.TryParse(_savedLevel.name.Split('_')[1], out int savedLevelNum)) { return currentLevelNum > savedLevelNum; } return false; } private void TrySaveCurrentLevel(string currentLevelName) { if (ShouldSaveLevel(currentLevelName)) { _savedLevel.name = currentLevelName; string jsonData = JsonUtility.ToJson(_savedLevel); File.WriteAllText(_savePath, jsonData); Debug.Log($"Saved progress: {currentLevelName}"); } else { Debug.LogWarning("No need to save - current level is not newer than saved progress"); } } public string LoadLastLevel() { return _savedLevel?.name ?? "level_1"; } } [System.Serializable] public class Level { public string name; }
关键修改点说明
- 路径切换:用
Application.persistentDataPath替代StreamingAssets作为读写目录,这个路径在安卓、iOS、PC等所有平台都有完整的读写权限。 - 初始数据复制:第一次运行时,将
StreamingAssets里的默认lastlevel.json复制到persistentDataPath,避免每次都读取只读的APK资源。 - 安卓端读取适配:安卓端用
UnityWebRequest异步读取StreamingAssets里的资源,这是Unity官方推荐的方式。 - 错误处理:添加了读取失败时的默认值初始化,避免空引用错误。
- 逻辑优化:将启动时的逻辑移到协程中,处理异步读取的问题,避免主线程阻塞。
额外注意事项
- 确保
lastlevel.json已经放在Assets/StreamingAssets目录下,并且内容是有效的JSON(比如{"name":"level_1"})。 - 安卓打包时,Unity会自动将
StreamingAssets里的文件打包进APK,不需要额外设置。 - 测试安卓端时,可以通过
adb shell查看persistentDataPath的位置(通常是/storage/emulated/0/Android/data/[你的包名]/files/),确认文件是否正确生成。
内容的提问来源于stack exchange,提问作者lolozen
相关产品推荐
相关产品推荐

