Hi all, I need to write a script in which some POST requests are made. At the moment, I'm using Unity coroutines with UnityWebRequest to send everything. This works fine but I have one more constraint which makes using coroutine not quite right. I cannot have a GameObject to run the coroutines on. Ultimately, the output of what I'm doing is gonna to be a Unity Package. That means if I rely on game objects, it is easy for the implementer to mess with my coroutines, change the name of my objects, or stop my requests, even not intentionally! The good thing is that I don't need to go back to the main thread after sending my requests. The best exemple of what I want is Unity's builtin analytics. These are sent at runtime, even on app quit, but still without any GameObject and DontDestroyOnLoad running coroutines. I tried creating a DLL using System.Net.Http but as soon as I include it in my Unity project, it's missing the imports... Manually adding System.Net.Http.dll did not fix the issue. Anyone would be able to help me? Thank you very much
You don't need to use a coroutine. You can directly iterate it yourself using IEnumerator.MoveNext().
To clarify... you don't need to use Unity to create and run a coroutine when you can handle it yourself. Example: Code (csharp): public class Foo { IEnumerator _iterator; public Foo() { } public void Update() { if (_iterator == null) { Debug.Log("Starting DoSomething()"); _iterator = DoSomething(); } if (!_iterator.MoveNext()) { Debug.Log("DoSomething completed!"); _iterator = null; } } IEnumerator DoSomething() { int i = 0; while (i < 10) { ++i; yield return null; Debug.Log("yielding..."); } } } That should give you an idea of how to handle it yourself.
No problem. Good luck! Note: I edited the code above because I forgot to put in "++i"... kind of an important piece of code to leave out
This may also offer a solution https://www.assetstore.unity3d.com/en/#!/content/54975 More effective coroutines don't stop if the gameobject is destroyed, so there may be other options for using it, though I've only touched on it once to test speed and not much else. Just a thought, no guarantee they will fit your use case without testing.
Too bad... The package is also using a GameObject as a singleton to run all its coroutines. Almost working! Problem is that UnityWebRequest prevents iterations with MoveNext when called from a background thread... I should've see that coming.
Oh. Heh. Ugh. I suppose it makes sense since you're required to use the main thread for any access to the Unity API. I actually don't use UnityWebRequest, but use the .NET HttpWebRequest instead. Are you required to use UnityWebRequest?
In future version of Unity we will ship an update to UnityWebRequest where you will be able to connect a handler to completion event. Right now if you want to avoid coroutines/game objects and fire UnityWebRequest with ability to track it's progress, I think the simplest option you have is to attach a custom DonwloadHandler script, it will be invoked for downloaded data. There is an issue with it though: you won't get any invocations for failed requests.
That is nice! Thanks for the info. I'm looking into @BlackPete 's idea to use System.Net.HttpWebRequest. Combining that with a Task.Run call to avoid blocking the main thread. It seems to work perfectly at the moment. Is there any restriction I am not aware of, that could explain why this solution isn't the one you recommend?
HttpWebRequest may not be available on all platforms and it might be less efficient than UnityWebRequest on some platforms (i.e. consume more battery). If that does not concern you, feel free to use it.
When is the update, to be able to connect handler on completion event, going to be done? I mean it is pretty old topic and yet there doesn't seem to be a simple way to use UnityWebRequests without using MonoBehavior and coroutines.
Aurimas sorry I am replying so late as I found my way around it. But can you provide a snippet how to use the request without coroutines?
Have look at this: https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequestAsyncOperation.html This object has "completed" event you can connect to.
Do you have a working example for this completed handler? I am trying to use it but am getting an Exception saying the download handler is already closed. This is my example code snippet: Code (CSharp): using (UnityWebRequest req = UnityWebRequest.Get(String.Format("{0}/players/{1}/offers", endpoint, userId))) { UnityWebRequestAsyncOperation asyncOperation = req.SendWebRequest(); asyncOperation.completed += (asyncOperation) => { byte[] result = req.downloadHandler.data; // throws NullReferenceException: DownloadHandler has already been destroyed }; yield return null; } If I use a co-routine and just yield return the UnityWebRequestAsyncOperation I don't get this error, but I'm also writing a SDK and don't want to use co-routines/game objects.
Your bug is that you put your request inside using block, which means UWR and it's handlers get disposed when you exit this block. With completion event don't put UWR in using block, instead call Dispose() on it when you are done (in completion event handler of course). BTW, you can get UnityWebRequest from async operation.
@Aurimas-Cernius can you please confirm which part of the UWR is threaded and which parts use coroutines or async/await internally?
SendWebRequest() kicks the request on a different thread. From there all runs on different thread until completion with some special case related to particular download handlers: - DownloadHandlerAudioClip only caches data in memory, AudioClip currently is create on main thread when you access it - DownloadHandlerTexture does part of texture creation on other thread, but requires some finalizing work to be done on main thread - DownloadHandlerAssetBundle does decompression and caching on other thread, but final step is done on main thread uppon accessing the bundle - DownloadHandlerScript caches the incomming data, the script itself in invoked once per frame on main thread (if some data arrived during that frame).
so tcp connection, read/write data streams are all done on another thread using sync/async operations?
In the future, could we have a callback that gets executed in a different thread on request completion? For example, say we want to parse some JSON from the request. Right now Unity jumps to the main thread and we queue a task to get the parse to run on a background thread, then queue an action when we are done parsing and need to access something on the main thread. Not a big deal, but saving a jump back and forth would be nice.