You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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相关的文件过滤规则

验证说明

构建APK后直接解压包体找不到data_train是正常现象——该目录初始存储在APK的assets压缩区块内,应用首次启动时才会被拷贝到沙箱目录。可以通过adb命令连接设备,进入/sdcard/Android/data/[你的应用包名]/files目录查看是否存在拷贝完成的data_train文件夹,确认存在后即可正常调用识别桥接功能。

注意:不要将data_train放在项目根目录、Plugins/Android非assets路径下,上述路径Unity构建时不会自动将资源打入APK,会持续出现资源缺失问题。

内容的提问来源于stack exchange,提问作者chandra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:12:35