Unity2019.4.39调用AndroidJavaObject时APK缺失data_train的实现问题
Unity 2019.4.39 Android识别桥接data_train资源缺失问题修复
问题根因
代码中通过GetExternalPath获取的是安卓设备公共外部存储路径,Unity构建APK时不会自动将data_train文件夹写入该路径,也不会自动把项目中任意位置的data_train打入APK包体,直接调用Init传该路径必然会出现资源找不到、桥接功能失效的问题。
修复步骤
- 配置随包资源存放路径
在项目Assets目录下新建命名完全匹配的StreamingAssets文件夹,将完整的data_train目录整体拷贝到该文件夹下。Unity构建安卓包时会自动将该目录下所有内容打包进APK的assets分区,不会出现资源遗漏。 - 修正初始化路径逻辑
安卓APK内的assets分区内容是压缩存储的,无法直接通过文件路径读取,需要在应用首次启动时将data_train拷贝到应用私有可读写的持久化目录,再将拷贝后的路径传入桥接Init方法。替换原有桥接构造函数代码如下:
public AndroidRecognitionBridge() { Debug.Log("Initializing bridge..."); _recognizeBridge = new AndroidJavaObject("com.mirai.recogbridge.BridgeUnity"); // 替换原有外部存储路径,使用应用私有可读写目录 string targetDataDir = Path.Combine(Application.persistentDataPath, "data_train"); // 首次启动时拷贝随包资源到可读写目录 if (!Directory.Exists(targetDataDir)) { CopyTrainDataFromAssets("data_train", targetDataDir); } _recognizeBridge.Call("Init", targetDataDir + Path.DirectorySeparatorChar); _recognizeBridge.Call("SetKanji"); _recognizeBridge.Call("SetOneLinePunctuation", true); _pointList = new List<Point>(); } /// <summary> /// 安卓平台拷贝StreamingAssets内目录到可读写路径 /// </summary> private void CopyTrainDataFromAssets(string sourceAssetPath, string targetLocalPath) { Directory.CreateDirectory(targetLocalPath); AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"); AndroidJavaObject assetManager = activity.Call<AndroidJavaObject>("getAssets"); string[] childList = assetManager.Call<string[]>("list", sourceAssetPath); foreach (string child in childList) { string childSourcePath = Path.Combine(sourceAssetPath, child); string childTargetPath = Path.Combine(targetLocalPath, child); string[] subChildList = assetManager.Call<string[]>("list", childSourcePath); if (subChildList.Length == 0) { // 拷贝单个文件 using (AndroidJavaObject inputStream = assetManager.Call<AndroidJavaObject>("open", childSourcePath)) { using (FileStream outputStream = new FileStream(childTargetPath, FileMode.Create)) { byte[] buffer = new byte[2048]; int readLen; IntPtr readMethodId = AndroidJNI.GetMethodID(inputStream.GetRawClass(), "read", "([B)I"); while (true) { IntPtr javaBuffer = AndroidJNI.NewByteArray(buffer.Length); readLen = AndroidJNI.CallIntMethod(inputStream.GetRawObject(), readMethodId, new jvalue[] { new jvalue { l = javaBuffer } }); if (readLen <= 0) break; AndroidJNI.CopyFromByteArray(javaBuffer, buffer, 0, readLen); outputStream.Write(buffer, 0, readLen); AndroidJNI.DeleteLocalRef(javaBuffer); } } } } else { // 递归拷贝子目录 CopyTrainDataFromAssets(childSourcePath, childTargetPath); } } }
- 构建配置检查
- 打开Player Settings -> Other Settings,将Write Permission设置为
External(SDCard),适配低版本安卓机型的目录读写权限 - 不需要额外申请外部存储读写权限:
Application.persistentDataPath在安卓平台对应应用沙箱私有目录,安卓10及以上版本不需要存储权限即可正常读写,不会出现权限拒绝问题 - 检查Player Settings -> Other Settings下的
Exclude package entries列表,确认没有添加data_train相关的文件过滤规则
- 打开Player Settings -> Other Settings,将Write Permission设置为
验证说明
构建APK后直接解压包体找不到data_train是正常现象——该目录初始存储在APK的assets压缩区块内,应用首次启动时才会被拷贝到沙箱目录。可以通过adb命令连接设备,进入/sdcard/Android/data/[你的应用包名]/files目录查看是否存在拷贝完成的data_train文件夹,确认存在后即可正常调用识别桥接功能。
注意:不要将data_train放在项目根目录、Plugins/Android非assets路径下,上述路径Unity构建时不会自动将资源打入APK,会持续出现资源缺失问题。
内容的提问来源于stack exchange,提问作者chandra
相关产品推荐
相关产品推荐

