繁体   English   中英

如何让 corutine 等待下载(Unity android)?

[英]How to make corutine wait for download (Unity android)?

当应用程序第一次启动时,我需要下载一个 100mb 的文件。 为了做到这一点,我使用了这样的方法:

    protected void Start()
    {
        if (!File.Exists(toCopy))
        {
            StartCoroutine(CopyFile(fromCopy, toCopy));
        }
...
    }

    IEnumerator CopyFile(string from, string to)
    {
        WWW www = new WWW(from);

        while (!www.isDone)
        {
            yield return null;
        }

        byte[] yourBytes = www.bytes;

        File.WriteAllBytes(to, yourBytes);
    }

但是我的应用程序崩溃了,下载的文件实际上有大小 - 0kb。 所以,我假设 corutine 开始下载,但是我尝试使用这个文件的代码没有等待和进一步,但它实际上还没有被下载所以我崩溃了。

问题是 - 在这种情况下如何使 corutine 等待文件下载?

编辑

感谢@derHugo,我更改了代码

    IEnumerator CopyFile(string from, string to)
    {
        using (var uwr = UnityWebRequest.Get(from))
        {
            yield return uwr.SendWebRequest();

            if (uwr.isHttpError || uwr.isNetworkError)
            {
                Debug.LogError($"Unity Download error :: {uwr.responseCode} - {uwr.error}!", this);
                yield break;
            }

            byte[] yourBytes = uwr.downloadHandler.data;

            File.WriteAllBytes(to, yourBytes);
        }
    }

protected void Start()
{
...
        if (!File.Exists(toCopy))
        {
            StartCoroutine(CopyFile(fromCopy, toCopy));
        }

        useCoiedFileByPath(toCopy); <-------------- //Can I to be sure that on this line file has alredy copied and available to use?

...
}

编辑2

    protected IEnumerator Start()
    {
        Debug.LogError("Unity Begin");

#if !UNITY_ANDROID
        if (!clipFile.Contains("\\"))
        {
            string resFolder = Path.GetFullPath(Application.streamingAssetsPath);
            clipFile = Path.GetFullPath(Path.Combine(resFolder, clipFile));
        }
#endif

        Debug.LogError("Unity Before copy");
#if UNITY_ANDROID
        string fromCopy = Path.Combine(Application.streamingAssetsPath, clipFile);
        string toCopy = Application.persistentDataPath + "/copiedfile.tet";
        Debug.LogError($"Unity HERE path from copy :: {fromCopy}, to copy :: {toCopy}");

        if (!File.Exists(toCopy)) 
        {
            yield return CopyFile(fromCopy, toCopy);
            //StartCoroutine(CopyFile(fromCopy, toCopy));
        }
#endif

        Debug.LogError("Unity After copy");

        register_debug_callback_default();
        stream_set_progiling_debug_messages(true);

#if UNITY_ANDROID
        clipFile = Application.persistentDataPath + "/" + clipFile;
#endif

        if (File.Exists(clipFile))
        {
            Debug.LogError($"Path to file :: {clipFile}");
            CreateStreamDecoder();
            stream_init_model(stream, clipFile);
        }
        else
        {
            stream = IntPtr.Zero;
        }

        meshRenderer = gameObject.AddComponent<MeshRenderer>();
        meshRenderer.material = new Material(GetShader());
        meshFilter = gameObject.AddComponent<MeshFilter>();
        FramePlaying = 0;

        Debug.LogError("Unity End");

        yield return null;
    }

    IEnumerator CopyFile(string from, string to)
    {
        using (var uwr = UnityWebRequest.Get(from))
        {
            yield return uwr.SendWebRequest();

            if (uwr.isHttpError || uwr.isNetworkError)
            {
                Debug.LogError($"Unity Download error :: {uwr.responseCode} - {uwr.error}!", this);
                yield break;
            }

            byte[] yourBytes = uwr.downloadHandler.data;

            File.WriteAllBytes(to, yourBytes);
        }
    }

Afaik 这应该有效

IEnumerator CopyFile(string from, string to)
{
    using(var www = new WWW(from))
    {
        yield return www;
        // though it should behave the same as
        //while(!www.isDone) yield return null;
        // or also
        //yield return new WaitUntil(() => www.isDone);

        byte[] yourBytes = www.bytes;

        File.WriteAllBytes(to, yourBytes);
    }
}

但是,一般而言, WWW已经过时,并且没有真正检查过程中的任何错误。 因此,由于缺少文件位置导致空byte[]您可能只是有一个隐藏的错误。

您可能更应该使用UnityWebRequest.Get

IEnumerator CopyFile(string from, string to)
{
    using(var uwr = UnityWebRequest.Get(from))
    {
        yield return uwr.SendRequest();

        if(uwr.isHttpError || uwr.isNetworkError)
        {
             Debug.LogError($"Download error {uwr.responseCode} - {uwr.error}!", this);
            yield break;
        }

        byte[] yourBytes = uwr.downloadHandler.data;

        File.WriteAllBytes(to, yourBytes);
    }
}

问题是:在您的用例中完全不清楚您的应用程序在哪一点崩溃以及导致崩溃的原因。


更新

现在我们知道您的实际问题:不! 您无法确定,因为启动协程不会延迟后面的代码行。 useCoiedFilePath将立即执行。

如果您想确定至少有三种可能的方法:

A) 简单地使Start成为协程本身。 是的,这是可能的,并且可能是您用例的最爱

protected IEnumerator Start()
{
    ...

    if (!File.Exists(toCopy))
    {
        yield return CopyFile(fromCopy, toCopy));
    }

    useCoiedFileByPath(toCopy); 

    ...
}

这只会延迟Start结束时的所有内容,直到下载完成。

B) 将要延迟的代码移到例程中

protected void Start()
{
    ...

    StartCoroutine(CopyFile(fromCopy, toCopy));
}

IEnumerator CopyFile(string from, string to)
{
    if(!File.Exists(to))
    {
        using (var uwr = UnityWebRequest.Get(from))
        {
            yield return uwr.SendWebRequest();

            if (uwr.isHttpError || uwr.isNetworkError)
            {
                Debug.LogError($"Unity Download error : {uwr.responseCode} - {uwr.error}!", this);
                yield break;
            }

            byte[] yourBytes = uwr.downloadHandler.data;

            File.WriteAllBytes(to, yourBytes);
        }
    }

    useCoiedFileByPath(toCopy);
    ...
}

请注意,这个复制例程现在变得非常不灵活,因为检查存在和反应已经包括在内,所以以后不能重复使用它来简单地复制另一个文件。

C)使用回调事件,如

IEnumerator CopyFile(string from, string to, Action whenDone)
{
    using (var uwr = UnityWebRequest.Get(from))
    {
        yield return uwr.SendWebRequest();

        if (uwr.isHttpError || uwr.isNetworkError)
        {
            Debug.LogError($"Unity Download error :: {uwr.responseCode} - {uwr.error}!", this);
            yield break;
        }

        byte[] yourBytes = uwr.downloadHandler.data;

        File.WriteAllBytes(to, yourBytes);
    }

    whenDone?.Invoke();
}

protected void Start()
{
    ...

    if (!File.Exists(toCopy))
    {
        StartCoroutine(CopyFile(fromCopy, toCopy, () => 
        {
            useCoiedFileByPath(toCopy)));

            ...
        }
    }
    else
    {
        useCoiedFileByPath(toCopy);

        ...
    }
}

这比B更灵活,但请注意您必须如何双重实施反应。

你需要在实现一个类之后 yield return 你的 www 类,然后尝试 yield return new waitUntil(()=>www.isDone)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM