【问题标题】:Android Unity - Load a file on background threadAndroid Unity - 在后台线程上加载文件
【发布时间】:2023-03-25 00:40:01
【问题描述】:

我需要在我的应用程序中加载一个文件,因为它很大(大约 250MB),我需要在主线程中执行这个加载。更何况Android上的assets不是存放在一个常规目录下,而是一个jar文件,我需要使用WWW或者UnityWebRequest类。

我最终得到了这样的辅助方法:

public static byte[] ReadAllBytes(string filePath)
{

    if (Application.platform == RuntimePlatform.Android)
    {
        var reader = new WWW(filePath);
        while (!reader.isDone) { }

        return reader.bytes;
    }
    else
    {
        return File.ReadAllBytes(filePath);
    }
}

问题是我不能在后台线程上使用它——Unity 不允许我在那里创建 WWW 对象。如何创建这样的方法,它将读取当前线程上的这些字节?

【问题讨论】:

    标签: c# unity3d


    【解决方案1】:

    只需将您的 while 循环放在 CoRoutine 中,而您的请求未完成到 yield return。完成后调用您要使用数据的方法:

    IEnumerator MyMethod()
    {
        var reader = new WWW(filePath);
        while (!reader.isDone) 
        { 
            yield return; // <- use endofFrame or Wait For ore something else if u want
        }
        LoadingDoneDoData(reader.bytes);
    }
    
    void LoadingDoneDoData(bytes[] data)
    {
        // your Code here
    }
    

    【讨论】:

    • 嘿,我知道如何在协程中实现它。关键是我想要一个简单的方法,现在就加载它。就像上面的一样,除了我的不能在后台线程上工作。
    • 然后您使用与主线程不同的线程来同步它们。或者你说的后台线程是什么意思,现在就加载它?
    • 我放弃了,决定将其部分加载到协程中。我做了一些协程和异步操作的混合。马上发布吧。
    【解决方案2】:

    我认为你可以使用类似的东西

    public static async void ReadAllBytes(string filePath, Action<byte[]> successCallback)
    {
        byte[] result;
        using (FileStream stream = File.Open(filePath, FileMode.Open))
        {
            result = new byte[stream.Length];
            await stream.ReadAsync(result, 0, (int)stream.Length);
        }
    
        // Now pass the byte[] to the callback
        successCallback.Invoke();
    }
    

    (Source)

    我猜你可以像这样使用它

    TheClass.ReadAllBytes(
        "a/file/path/",
    
        // What shall be done as soon as you have the byte[]
        (bytes) =>
        {
            // What you want to use the bytes for
        }
    );
    

    我不是多线程专家,但herehere 你可以找到更多示例以及async - awaitUnity3d 的操作方法。


    另外,新的Unity Jobsystem 也可能对您感兴趣。

    【讨论】:

    • 嗨,我得到了类似的结果,但恐怕你的解决方案行不通。无法使用原始文件操作从 Android 上的流媒体资产中读取文件,因为它存储在 jar 文件中...
    • @MichałPowłoka 哦,我没看到你想从 StreamingAssets 中阅读它。无论如何,您应该能够读取字节...我不知道您到底想用它做什么,但您需要自己解压缩等内容。
    • 我知道... Unity 文档建议在这种情况下使用 WWW 类或 UnityWebRequest。我的问题是我不允许在不是主线程的任何线程上创建这些实例,否则会引发异常。
    • @MichałPowłoka 但是你为什么不能使用FileStream 呢?它会像 WWW 一样返回原始字节......但不同的是 FileStream 可以使用异步/线程
    • 没有尝试 FileStream tbh。我确实尝试过 File.ReadAllBytes。我会检查的。
    【解决方案3】:

    我最终得到了以下解决方案。我没有找到加载原始文件主线程的方法,但我设法在协程中加载它并在后台线程上对该文件执行进一步(也是繁重的)操作。我做了一个这样的静态方法:

    public static void ReadAllBytesInCoroutine(MonoBehaviour context, string filePath, Action<ReadBytesInCoroutineResult> onComplete)
    {
        context.StartCoroutine(ReadFileBytesAndTakeAction(filePath, onComplete));
    }
    
    private static IEnumerator ReadFileBytesAndTakeAction(string filePath, Action<ReadBytesInCoroutineResult> followingAction)
    {
        WWW reader = null;
    
        try
        {
            reader = new WWW(filePath);
        }
        catch(Exception exception)
        {
            followingAction.Invoke(ReadBytesInCoroutineResult.Failure(exception));
        }
    
        while (reader != null && !reader.isDone)
        {
            yield return null;
        }
        followingAction.Invoke(ReadBytesInCoroutineResult.Success(reader.bytes));
    }
    

    ReadBytesInCoroutineResult 是我简单的自定义数据类:

    public class ReadBytesInCoroutineResult
    {
        public readonly bool successful;
        public readonly byte[] data;
        public readonly Exception reason;
    
        private ReadBytesInCoroutineResult(bool successful, byte[] data, Exception reason)
        {
            this.successful = successful;
            this.data = data;
            this.reason = reason;
        }
    
        public static ReadBytesInCoroutineResult Success(byte[] data)
        {
            return new ReadBytesInCoroutineResult(true, data, null);
        }
    
        public static ReadBytesInCoroutineResult Failure(Exception reason)
        {
            return new ReadBytesInCoroutineResult(true, null, reason);
        }
    
    }
    

    这样我就有了一种机制可以在任何地方(只要它在主线程上)在协程中加载文件。同步加载文件,但由于协程,它没有阻塞主线程。后来我调用这个函数并在一个单独的线程上获取获取的字节,在那里我对它们执行繁重的计算。

        ResourcesUtils.ReadAllBytesInCoroutine(monoBehavior, filePath, (bytes) => {
    
            //here I run an async method which takes bytes as parameter
    
        });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-06-22
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多