Unity3D Android端找不到StreamingAssets内预存的SQLite数据库如何解决?
核心问题定位
- 原Android端数据库拷贝逻辑的几处实现缺陷,直接导致预填充数据库无法正常加载:
- 旧的
WWW同步死循环加载写法在高版本Unity、Target API≥30的环境下,容易出现主线程阻塞、字节读取截断的问题,最终写出的数据库文件要么是空的、要么是损坏的 - 手动拼接
jar:file://路径的写法没有加错误兜底,哪怕加载过程报错,代码也会硬把空字节写到应用私有目录,后续打开数据库时就会报路径不存在、表结构缺失的错 - 没有校验StreamingAssets下数据库文件的导入设置,如果文件被Unity默认导入流程做了格式转换、压缩,打出来的包里db文件本身就是损坏的,根本读不出来
- 旧的
- 报错日志里出现的包名大小写拼写差异,和Android系统的路径大小写敏感规则叠加,进一步放大了路径识别错误的影响
可直接落地的修正方案
- 替换原有加载逻辑,去掉手动拼接jar协议、死循环等待的写法
- 直接使用Unity提供的
Application.streamingAssetsPath获取平台适配的StreamingAssets根路径,由Unity自动处理不同平台的路径格式,不用手动拼接Android专用的jar协议 - 增加加载超时、加载错误、字节有效性校验逻辑,避免写出损坏的空数据库文件
- 替换已经被Unity标记为过时的
WWW类,使用官方推荐的UnityWebRequest完成StreamingAssets资源读取
- 直接使用Unity提供的
- 调整StreamingAssets下数据库文件的导入设置
- 选中项目里的
testAndroid.db,在Inspector面板确认导入格式为二进制,不要设置成TextAsset、不要开启任何预处理选项,避免打包过程中文件被篡改、压缩
- 选中项目里的
- 移除不需要的存储权限申请
- 应用私有目录
Application.persistentDataPath天然具备读写权限,不需要额外申请外部存储权限,尤其是Target API≥29的版本,申请WRITE_EXTERNAL_STORAGE反而会被系统拦截
- 应用私有目录
- 校准项目包名
- 严格按照Player Settings里配置的包名大小写、拼写核对,避免Android系统路径映射时出现识别偏差
修正后的核心代码片段
public string InitDBLocation() { string _dbPath = ""; fileName = "testAndroid.db"; #if UNITY_EDITOR _dbPath = Path.Combine(Application.dataPath, "StreamingAssets", fileName); #elif UNITY_ANDROID _dbPath = Path.Combine(Application.persistentDataPath, fileName); // 仅当私有目录下不存在有效数据库文件时,从StreamingAssets拷贝 if (!File.Exists(_dbPath)) { string sourcePath = Path.Combine(Application.streamingAssetsPath, fileName); byte[] dbBytes = null; // 使用UnityWebRequest读取StreamingAssets中的数据库文件 var webRequest = UnityEngine.Networking.UnityWebRequest.Get(sourcePath); webRequest.SendWebRequest(); // 带超时等待加载完成,避免主线程无限阻塞 float elapsedTime = 0; float maxWaitTime = 10f; while (!webRequest.isDone) { elapsedTime += Time.deltaTime; if (elapsedTime > maxWaitTime) break; } // 校验加载结果 #if UNITY_2020_1_OR_NEWER bool isLoadSuccess = webRequest.result == UnityEngine.Networking.UnityWebRequest.Result.Success; #else bool isLoadSuccess = !webRequest.isHttpError && !webRequest.isNetworkError; #endif if (isLoadSuccess) { dbBytes = webRequest.downloadHandler.data; } webRequest.Dispose(); // 校验读取到的文件内容有效性 if (dbBytes == null || dbBytes.Length == 0) { Debug.LogError("数据库文件读取失败,未获取到有效内容"); return null; } // 写入应用私有目录 File.WriteAllBytes(_dbPath, dbBytes); } #endif return _dbPath; }
易踩的后续坑点
- 不要每次启动应用都强制覆盖persistentDataPath下的数据库文件,否则用户后续在应用内对数据库做的修改会被完全覆盖
- 打开数据库连接时增加空路径判断,避免拷贝失败返回空值时,直接触发SQLite连接报错
- 如果使用Mono.Data.Sqlite组件,确认Android平台下的
libsqlite3.so已经正确包含在构建目标的ABI列表里,不然哪怕数据库文件路径正确,也会出现驱动找不到的报错
内容的提问来源于stack exchange,提问作者PeterK
相关产品推荐
相关产品推荐

