Search Unity

Can't Start Coroutine Inside of .GetDownloadUrlAsync() method?

Discussion in 'Scripting' started by jacksonbarnes, Aug 1, 2019.

  1. jacksonbarnes

    jacksonbarnes

    Joined:
    Sep 13, 2017
    Posts:
    16
    So I know I probably can't start a coroutine using a separate thread (if that's even how you say it), but I would like to why this is not possible and if there is a way around this? Below is the code. (The code in a nutshell) I'm basically getting a direct link from firebase using the .GetDownloadUrlAsync() method than trying to download an asset bundle using a coroutine.

    Code (CSharp):
    1. public void GetTargetOnFound(ImageTarget targetInfo)
    2.     {
    3.         string assetBundleName = "assetbundle";
    4.         string assetBundleEndpoint = _storageUrl + "/" + targetInfo.Name + "/" + assetBundleName;
    5.  
    6.         StorageReference _Ref = _storage.GetReferenceFromUrl(assetBundleEndpoint);
    7.  
    8.         _Ref.GetDownloadUrlAsync().ContinueWith((Task<Uri> task) =>
    9.         {
    10.             if (!task.IsFaulted && !task.IsCanceled)
    11.             {
    12.                 StartCoroutine(HandleDownloadAssets(task.Result.AbsoluteUri, assetBundleName));
    13.             }
    14.         });
    15.     }
    16.  
    17.     IEnumerator HandleDownloadAssets(string assetBundleEndpoint, string assetBundleName)
    18.     {
    19.         UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle(assetBundleEndpoint);
    20.         yield return www.SendWebRequest();
    21.  
    22.         if (www.isNetworkError || www.isHttpError)
    23.         {
    24.             Debug.Log(www.error);
    25.         }
    26.         else
    27.         {
    28.             AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www);
    29.             var prefab = bundle.LoadAsset<GameObject>(assetBundleName);
    30.             Instantiate(prefab, _imageTrackable.transform);
    31.         }
    32.     }
    Any ideas? Thanks!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    4,081
    The standard way to do this is to have a list of delegates (such as a List<System.Action>) that you lock() and check in your Update() loop.

    If that list is ever not-empty, it copies all the delegates into a separate list, then clears the list and unlocks it.

    Now with the copied delegates, iterate them and run each delegate, because now you are on the main thread.

    To get the delegates into this list, you would in the async handler above:

    - create the delegate to do what you want (i.e., start the coroutine with a given context and data payload)
    - lock the list
    - add the delegate to the list
    - unlock the list

    Effectively you are preparing a little blob of code to start your coroutine, then putting it in a list for the main thread (running Update()) to do for you at its next opportunity.

    You can actually use something like Thread Ninja from the Unity asset store for a more-general solution to this, or just implement the simple steps above.
     
    jacksonbarnes likes this.