android - 如何让协程等待下载(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。所以,我假设 coroutine 开始下载,但是没有等待,在代码中我尝试使用这个文件,但实际上它还没有被下载,所以我崩溃了。
问题是 - 在这种情况下如何让协程等到文件被下载?
编辑
感谢@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
但请注意您必须如何双重实施反应更灵活。
推荐阅读
- reactjs - React Native 如何在应用程序中保存图像?
- c - 构建 FreeRDP 并安装错误:字段“des3_ctx”的类型不完整
- linux - 恢复无限嵌套/递归的 tmux 会话?
- c++ - 读取文件到 istream 替换类变量
- java - LibGDX 浮动与相机位置导致黑线与平铺地图
- java - 检查下一个要保存的输入是否在这些范围内
- class - fortran中的多态性
- android - 在 Android 生命周期中我应该在哪里使用 getIntent()
- python - if 函数的迭代器返回错误
- rust - 有没有办法在 Option 中拆分变量而不必使用 if 语句?