创建DownloadHandlers

有几种类型的DownloadHandlers

    • DownloadHandlerBuffer 用于简单的数据存储。
    • DownloadHandlerFile 用于下载文件并将其保存到内存较少的磁盘。
    • DownloadHandlerTexture 用于下载图像。
    • DownloadHandlerAssetBundle 用于提取AssetBundles。
    • DownloadHandlerAudioClip 用于下载音频文件。
    • DownloadHandlerMovieTexture 用于下载视频文件。
    •DownloadHandlerScript是一个特殊的班级。就其本身而言,它什么都不做。但是,该类可以由用户定义的类继承。该类接收来自UnityWebRequest系统的回调,然后可以用它来完成从网络到达的数据的完全自定义处理。

这些API与DownloadHandlerTexture接口类似。

UnityWebRequest有一个属性disposeDownloadHandlerOnDispose,默认为true。如果此属性为true,则在处置UnityWebRequest对象时,Dispose()也将在附加的下载处理程序上调用,从而使其无效。如果您保留对下载处理程序的引用超过对UnityWebRequest的引用,则应将disposeDownloadHandlerOnDispose设置为false

DownloadHandlerBuffer

这个下载处理程序是最简单的,并处理大部分用例。它将接收到的数据存储在本机代码缓冲区中。下载完成后,您可以以字节数组或字符串的形式访问缓冲的数据。

    using UnityEngine;
    using UnityEngine.Networking; 
    using System.Collections;


    public class MyBehaviour : MonoBehaviour {
        void Start() {
            StartCoroutine(GetText());
        }

        IEnumerator GetText() {
            UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
            www.downloadHandler = new DownloadHandlerBuffer();
            yield return www.SendWebRequest();

            if(www.isNetworkError || www.isHttpError) {
                Debug.Log(www.error);
            }
            else {
                // Show results as text
                Debug.Log(www.downloadHandler.text);

                // Or retrieve results as binary data
                byte[] results = www.downloadHandler.data;
            }
        }
    }

DownloadHandlerFile

这是一个特殊的大文件下载处理程序。它将下载的字节直接写入文件,因此无论正在下载的文件的大小如何,内存使用率都很低。与其他下载处理程序的区别是,您无法从中获取数据,所有数据都保存到文件中。

    using System.Collections;
    using System.IO;
    using UnityEngine;
    using UnityEngine.Networking;

    public class FileDownloader : MonoBehaviour {

        void Start () {
            StartCoroutine(DownloadFile());
        }

        IEnumerator DownloadFile() {
            var uwr = new UnityWebRequest("https://unity3d.com/", UnityWebRequest.kHttpVerbGET);
            string path = Path.Combine(Application.persistentDataPath, "unity3d.html");
            uwr.downloadHandler = new DownloadHandlerFile(path);
            yield return uwr.SendWebRequest();
            if (uwr.isNetworkError || uwr.isHttpError)
                Debug.LogError(uwr.error);
            else
                Debug.Log("File successfully downloaded and saved to " + path);
        }
    }

DownloadHandlerTexture

不使用DownloadHandlerBuffer来下载图像文件,然后使用原始字节创建纹理Texture.LoadImage,而是使用效率更高DownloadHandlerTexture

该下载处理程序将收到的数据存储在UnityEngine.Texture。完成下载后,它会将JPEG和PNG解码为有效UnityEngine.Texture objectsUnityEngine.Texture每个DownloadHandlerTexture对象只创建一个副本。这减少了垃圾收集的性能命中。该处理程序在本地代码中执行缓冲,解压缩和纹理创建。此外,解压缩和纹理创建是在工作线程而不是主线程上执行的,这可以在加载大纹理时提高帧时间。

最后,DownloadHandlerTexture在最终创建Texture本身时只分配托管内存,这消除了与在脚本中执行字节到纹理转换相关的垃圾回收开销。

以下示例从互联网下载PNG文件,将其转换为Sprite并将其分配给图像:

    using UnityEngine;
    using UnityEngine.UI;
    using UnityEngine.Networking; 
    using System.Collections;

    [RequireComponent(typeof(UnityEngine.UI.Image))]
    public class ImageDownloader : MonoBehaviour {
        UnityEngine.UI.Image _img;

        void Start () {
            _img = GetComponent<UnityEngine.UI.Image>();
            Download("http://www.mysite.com/myimage.png");
        }

        public void Download(string url) {
            StartCoroutine(LoadFromWeb(url));
        }

        IEnumerator LoadFromWeb(string url)
        {
            UnityWebRequest wr = new UnityWebRequest(url);
            DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
            wr.downloadHandler = texDl;
            yield return wr.SendWebRequest();
            if(!(wr.isNetworkError || wr.isHttpError)) {
                Texture2D t = texDl.texture;
                Sprite s = Sprite.Create(t, new Rect(0, 0, t.width, t.height),
                                         Vector2.zero, 1f);
                _img.sprite = s;
            }
        }
    }

DownloadHandlerAssetBundle

这个专门的下载处理程序的好处是它能够将数据流式传输到Unity的AssetBundle系统。一旦AssetBundle系统收到足够的数据,AssetBundle就可以作为一个UnityEngine.AssetBundle对象使用。只UnityEngine.AssetBundle创建一个对象的副本。这大大减少了运行时内存分配以及加载AssetBundle时的内存影响。它还允许AssetBundles在未完全下载的情况下部分使用,因此您可以对资产进行流式处理。

所有下载和解压缩都发生在工作线程上。

AssetBundles通过一个DownloadHandlerAssetBundle对象下载,该对象有一个特殊的assetBundle属性来检索AssetBundle

由于AssetBundle系统的工作方式,所有AssetBundle必须有一个与它们相关的地址。通常,这是它们所在的名义URL(意味着任何重定向之前的URL)。几乎在所有情况下,您应该传递给您传递给UnityWebRequest的相同URL。使用高级API(HLAPI)时,这是为您完成的。

    using UnityEngine;
    using UnityEngine.Networking; 
    using System.Collections;

    public class MyBehaviour : MonoBehaviour {
        void Start() {
            StartCoroutine(GetAssetBundle());
        }

        IEnumerator GetAssetBundle() {
            UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
            DownloadHandlerAssetBundle handler = new DownloadHandlerAssetBundle(www.url, uint.MaxValue);
            www.downloadHandler = handler;
            yield return www.SendWebRequest();

            if(www.isNetworkError || www.isHttpError) {
                Debug.Log(www.error);
            }
            else {
                // Extracts AssetBundle
                AssetBundle bundle = handler.assetBundle;
            }
        }
    }

DownloadHandlerAudioClip

该下载处理程序经过优化,可用于下载音频文件。您可以使用此下载处理程序以更方便的方式执行,而不是使用下载原始字节DownloadHandlerBuffer然后创建AudioClip它们。

    using System.Collections;
    using UnityEngine;
    using UnityEngine.Networking;

    public class AudioDownloader : MonoBehaviour {

        void Start () {
            StartCoroutine(GetAudioClip());
        }

        IEnumerator GetAudioClip() {
            using (var uwr = UnityWebRequestMultimedia.GetAudioClip("http://myserver.com/mysound.ogg", AudioType.OGGVORBIS)) {
                yield return uwr.SendWebRequest();
                if (uwr.isNetworkError || uwr.isHttpError) {
                    Debug.LogError(uwr.error);
                    yield break;
                }

                AudioClip clip = DownloadHandlerAudioClip.GetContent(uwr);
                // use audio clip
            }
        }   
    }

DownloadHandlerMovieTexture

该下载处理程序经过优化以下载视频文件。您可以使用此下载处理程序以更方便的方式执行,而不是使用下载原始字节DownloadHandlerBuffer然后创建MovieTexture它们。

    using System.Collections;
    using UnityEngine;
    using UnityEngine.Networking;

    public class MovieDownloader : MonoBehaviour {

        void Start () {
            StartCoroutine(GetAudioClip());
        }

        IEnumerator GetAudioClip() {
            using (var uwr = UnityWebRequestMultimedia.GetMovieTexture("http://myserver.com/mysound.ogg")) {
                yield return uwr.SendWebRequest();
                if (uwr.isNetworkError || uwr.isHttpError) {
                    Debug.LogError(uwr.error);
                    yield break;
                }

                MovieTexture movie = DownloadHandlerMovieTexture.GetContent(uwr);
                // use movie texture
            }
        }   
    }

DownloadHandlerScript

对于需要完全控制下载数据处理的用户,Unity提供DownloadHandlerScript该类。

默认情况下,这个类的实例什么都不做。但是,如果您从中派生自己的类DownloadHandlerScript,则可能会覆盖某些函数,并在数据从网络到达时使用它们来接收回调。

注意:实际下载发生在工作线程上,但所有DownloadHandlerScript回调都在主线程上运行。避免在这些回调期间执行计算量大的操作。

覆盖的功能 ReceiveContentLength()

    protected void ReceiveContentLength(long contentLength);

这个函数在收到Content-Length头时被调用。请注意,如果您的服务器在处理UnityWebRequest的过程中发送一个或多个重定向响应,则可能会多次发生此回调。

OnContentComplete()

    protected void OnContentComplete();

当UnityWebRequest从服务器完全下载所有数据并将所有接收到的数据转发给ReceiveData回调时,将调用此函数。

receiveData()

    protected bool ReceiveData(byte[] data, long dataLength);

该数据从远程服务器到达后调用,每帧调用一次。该data参数包含从远程服务器接收到的原始字节,并dataLength指示数据数组中新数据的长度。

当不使用预先分配的数据缓冲区时,系统每次调用此回调时都会创建一个新的字节数组,并且dataLength始终等于data.Length。使用预先分配的数据缓冲区时,数据缓冲区将被重用,并且dataLength必须用于查找更新的字节数。

该函数需要返回值为truefalse。如果您返回false,系统将立即中止UnityWebRequest。如果返回true,则处理正常继续。

避免垃圾收集开销

Unity的许多更高级用户都关心减少垃圾回收造成的CPU峰值。对于这些用户,UnityWebRequest系统允许预先分配托管代码字节数组,该数组用于将下载的数据传递给DownloadHandlerScriptReceiveData回调函数。

在使用DownloadHandlerScript派生类捕获下载的数据时,使用此函数可以完全消除托管代码内存分配。

DownloadHandlerScript使用预先分配的托管缓冲区进行操作,请向其构造函数提供一个字节数组DownloadHandlerScript

注意:字节数组的大小限制了每帧传送给ReceiveData回调的数据量。如果数据缓慢到达,在很多帧中,您可能提供了太小的字节数组。

    using UnityEngine;
    using UnityEngine.Networking; 
    using System.Collections;

    public class LoggingDownloadHandler : DownloadHandlerScript {

        // Standard scripted download handler - allocates memory on each ReceiveData callback

        public LoggingDownloadHandler(): base() {
        }

        // Pre-allocated scripted download handler
        // reuses the supplied byte array to deliver data.
        // Eliminates memory allocation.

        public LoggingDownloadHandler(byte[] buffer): base(buffer) {
        }

        // Required by DownloadHandler base class. Called when you address the 'bytes' property.

        protected override byte[] GetData() { return null; }

        // Called once per frame when data has been received from the network.

        protected override bool ReceiveData(byte[] data, int dataLength) {
            if(data == null || data.Length < 1) {
                Debug.Log("LoggingDownloadHandler :: ReceiveData - received a null/empty buffer");
                return false;
            }

            Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveData - received {0} bytes", dataLength));
            return true;
        }

        // Called when all data has been received from the server and delivered via ReceiveData.

        protected override void CompleteContent() {
            Debug.Log("LoggingDownloadHandler :: CompleteContent - DOWNLOAD COMPLETE!");
        }

        // Called when a Content-Length header is received from the server.

        protected override void ReceiveContentLength(int contentLength) {
            Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveContentLength - length {0}", contentLength));
        }
    }

🔚