UnityWebRequest 源码分析
一、概念UnityWebRequest提供了一个用于编写HTTP请求和处理HTTP响应的模块化系统。UnityWebRequest系统的主要目标是允许Unity游戏与Web浏览器后端进行交互。它还支持高需求的功能,如分块的HTTP请求,流式POST / PUT操作以及对HTTP标头和动词的完全控制。
二、常用操作
1、 从HTTP服务器检索文本或二进制数据
要从标准HTTP或HTTPS Web服务器检索简单数据(如文本数据或二进制数据),请使用该UnityWebRequest.GET调用。该函数将单个字符串作为参数,字符串指定从中检索数据的URL。
例子:
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(GetText());
}
IEnumerator GetText() {
UnityWebRequest www = UnityWebRequest.Get("403 Forbidden");
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;
}
}
}
2、 从HTTP服务器检索纹理
要从远程服务器检索纹理文件,可以使用UnityWebRequest.Texture.此功能与UnityWebRequest.GET下载和存储纹理效果非常相似,但经过优化。该函数将一个字符串作为参数。该字符串指定您希望从中下载图像文件以用作纹理的URL。
例子:
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(GetTexture());
}
IEnumerator GetTexture() {
UnityWebRequest www = UnityWebRequestTexture.GetTexture("http://www.xxxx.com/image.png");
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Texture myTexture = ((DownloadHandlerTexture)www.downloadHandler).texture;
}
}
}3、 从HTTP服务器下载AssetBundle
要从远程服务器下载AssetBundle,可以使用UnityWebRequest.GetAssetBundle。此函数将数据流式传输到内部缓冲区,该缓冲区解码并解压缩工作线程上的AssetBundle数据。
函数的参数有几种形式。以最简单的形式,它只需要从中下载AssetBundle的URL。您可以选择提供校验和来验证下载数据的完整性。
例子:
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(GetAssetBundle());
}
IEnumerator GetAssetBundle() {
UnityWebRequest www = UnityWebRequest.GetAssetBundle("http://www.xxxxx.com/xxx.bundle");
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www);
}
}
}
4、 将表单发送到HTTP服务器
例子:
public class Example : public MonoBehaviour {
void Start() {
StartCoroutine(Upload());
}
IEnumerator Upload() {
WWWForm form = new WWWForm();
form.AddField("myField", "myData");
UnityWebRequest www = UnityWebRequest.Post("403 Forbidden", form);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Debug.Log("Form upload complete!");
}
}
}
5、 将原始数据上传到HTTP服务器
例子:
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(Upload());
}
IEnumerator Upload() {
byte[] myData = System.Text.Encoding.UTF8.GetBytes("This is some test data");
UnityWebRequest www = UnityWebRequest.Put("http://www.xxxx.com/upload", myData);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Debug.Log("Upload complete!");
}
}
}
三、高级操作
现在我们应用底层API创建对象,可以提供更大的灵活性。
1、创建UnityWebRequest
UnityWebRequest可以像任何其他对象一样实例化。有两个构造函数可用:
标准的无参数构造函数创建一个新的UnityWebRequest,其中所有设置为空白或默认。目标网址未设置,未设置自定义标题,并且重定向限制设置为32。
第二个构造函数接受一个字符串参数。它将UnityWebRequest的目标URL分配给字符串参数的值,否则与无参数构造函数相同。
多个其他属性可用于设置,跟踪状态和检查结果或UnityWebRequest。
UnityWebRequest wr = new UnityWebRequest(); // Completely blank
UnityWebRequest wr2 = new UnityWebRequest("http://www.xxxx.com"); // Target URL is set
// the following two are required to web requests to work
wr.url = "403 Forbidden";
wr.method = UnityWebRequest.kHttpVerbGET; // can be set to any custom method, common constants privided
wr.useHttpContinue = false;
wr.chunkedTransfer = false;
wr.redirectLimit = 0;// disable redirects
wr.timeout = 60; // don't make this small, web requests do take some time
2、创建UploadHandlers
(1)UploadHandlerRaw:这个类在构造时接受数据缓冲区。该缓冲区在内部复制到本机代码存储器中,然后UnityWebRequest在远程服务器准备好接受正文
例
byte[] payload = new byte;
// ... fill payload with data ...
UnityWebRequest wr = new UnityWebRequest("http://www.xxxxx.com/data-upload");
UploadHandler uploader = new UploadHandlerRaw(payload);
// Sends header: "Content-Type: custom/content-type";
uploader.contentType = "custom/content-type";
wr.uploadHandler = uploader;
(2)UploadHandlerFile: 它从给定的文件中读取数据,并将原始字节作为请求体发送到服务器
例
public class Example : MonoBehaviour
{
void Start()
{
StartCoroutine(UploadFileData());
}
IEnumerator UploadFileData()
{
using (var uwr = new UnityWebRequest("https://xxxx.com/upload", UnityWebRequest.kHttpVerbPUT))
{
uwr.uploadHandler = new UploadHandlerFile("/path/to/file");
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
Debug.LogError(uwr.error);
else
{
// file data successfully sent
}
}
}
}
3、创建DownloadHandlers
(1)DownloadHandlerBuffer 用于简单的数据存储。
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(GetText());
}
IEnumerator GetText() {
UnityWebRequest www = new UnityWebRequest("http://www.xxx.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;
}
}
}(2)DownloadHandlerFile 用于下载文件并将其保存到内存较少的磁盘。
public class Example : 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);
}
}(3)DownloadHandlerTexture 用于下载图像。
public class Example : MonoBehaviour {
UnityEngine.UI.Image _img;
void Start () {
_img = GetComponent<UnityEngine.UI.Image>();
Download(&#34;http://www.xxxx.com/myimage.png&#34;);
}
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;
}
}
}(4)DownloadHandlerAssetBundle 用于提取AssetBundles。
public class Example : MonoBehaviour {
void Start() {
StartCoroutine(GetAssetBundle());
}
IEnumerator GetAssetBundle() {
UnityWebRequest www = new UnityWebRequest(&#34;http://www.xxxx.com&#34;);
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;
}
}
}(5)DownloadHandlerAudioClip 用于下载音频文件。
public class Example : MonoBehaviour {
void Start () {
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip() {
using (var uwr = UnityWebRequestMultimedia.GetAudioClip(&#34;http://xxxxx.com/xxx.ogg&#34;, 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
}
}
}(6)DownloadHandlerMovieTexture 用于下载视频文件。
public class Example : MonoBehaviour {
void Start () {
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip() {
using (var uwr = UnityWebRequestMultimedia.GetMovieTexture(&#34;http://xxxx.com/xxxxx.ogg&#34;)) {
yield return uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError) {
Debug.LogError(uwr.error);
yield break;
}
MovieTexture movie = DownloadHandlerMovieTexture.GetContent(uwr);
// use movie texture
}
}
}(7)DownloadHandlerScript是一个特殊的类。
对于需要完全控制下载数据处理的用户,Unity提供DownloadHandlerScript该类。
默认情况下,这个类的实例什么都不做。但是,如果您从中派生自己的类DownloadHandlerScript,则可能会覆盖某些函数,并在数据从网络到达时使用它们来接收回调。
注意:实际下载发生在工作线程上,但所有DownloadHandlerScrpt回调都在主线程上运行。避免在这些回调期间执行计算量大的操作。
protected void ReceiveContentLength(long contentLength);
这个函数在收到Content-Length头时被调用。请注意,如果您的服务器在处理UnityWebRequest的过程中发送一个或多个重定向响应,则可能会多次发生此回调。
protected void OnContentComplete();
当UnityWebRequest从服务器完全下载所有数据并将所有接收到的数据转发给ReceiveData回调时,将调用此函数。
protected bool ReceiveData(byte[] data, long dataLength);
该数据从远程服务器到达后调用,每帧调用一次。该data参数包含从远程服务器接收到的原始字节,并dataLength指示数据数组中新数据的长度。
当不使用预先分配的数据缓冲区时,系统每次调用此回调时都会创建一个新的字节数组,并且dataLength始终等于data.Length。使用预先分配的数据缓冲区时,数据缓冲区将被重用,并且dataLength必须用于查找更新的字节数。
该函数需要返回值为true或false。如果您返回false,系统将立即中止UnityWebRequest。如果返回true,则处理正常继续。
public class Example : DownloadHandlerScript {
// Standard scripted download handler - allocates memory on each ReceiveData callback
public Example(): base() {
}
// Pre-allocated scripted download handler
// reuses the supplied byte array to deliver data.
// Eliminates memory allocation.
public Example(byte[] buffer): base(buffer) {
}
// Required by DownloadHandler base class. Called when you address the &#39;bytes&#39; 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(&#34;Example :: ReceiveData - received a null/empty buffer&#34;);
return false;
}
Debug.Log(string.Format(&#34;Example :: ReceiveData - received {0} bytes&#34;, dataLength));
return true;
}
// Called when all data has been received from the server and delivered via ReceiveData.
protected override void CompleteContent() {
Debug.Log(&#34;Example :: CompleteContent - DOWNLOAD COMPLETE!&#34;);
}
// Called when a Content-Length header is received from the server.
protected override void ReceiveContentLength(int contentLength) {
Debug.Log(string.Format(&#34;Example :: ReceiveContentLength - length {0}&#34;, contentLength));
}
}
四、以上UnityWebRequest相关的类和功能就差不多了,我画了几张图做个总结:
UnityWebRequest继承关系如下
这里UnityWebRequestBase是一个宏定义,会根据不同平台而不同,比如安卓平台就是 UnityWebRequestAndroid类,IOS平台就是UnityWebRequestiPhone类。这两个类都是通过原生网络接口进行调用。
当然如果定义了UNITYWEBREQUEST_USE_CURL宏就宏统一使用curl进行处理。
UnityWebRequestDefaultBase是一个中间层,没有太多的作用。
UnityWebRequestProto类:大部分的逻辑接口都在这里处理,这个基类持有了Downloadhandler 和 UpLoadHandler类的对象。
DownloadHandler继承关系如下:
UpLoadHandler继承关系如下:
通过上面三幅图我们可以看出,UnityWebRequest通过继承、接口类、组合的方式实现了代码的多功能性。使代码具有了更高的内聚性、可读性和可扩展性。
本篇内容就先写这么多,如果喜欢这篇文章,请点赞+关注,后面还有更多精彩内容。
页:
[1]