Android Unity-在后台线程上加载文件

时间:2019-01-17 11:00:22

标签: c# unity3d

我需要在应用程序中加载文件,由于文件很大(大约250MB),因此需要从主线程执行此加载。而且,由于Android上的资产不是存储在常规目录中,而是存储在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对象。如何创建这样的方法,该方法将读取当前线程上的那些字节?

3 个答案:

答案 0 :(得分:1)

只需将您的while循环放入CoRoutine中,而您的请求没有完成就返回收益。完成后,调用要在其中使用数据的方法:

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
}

答案 1 :(得分:1)

我认为您可以使用类似的

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也可能对您很有趣。

答案 2 :(得分:0)

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

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

    });