UnityWebRequest详解

思考并回答以下问题:

  • Uri类是干嘛用的?

之前初步了解了UnityWebRequest是做什么用的以及如何使用,今天来仔细看一下昨天代码中的几个部分。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

public class GetFileByWebRequest : MonoBehaviour
{
IEnumerator Start()
{
var uri = new System.Uri(Path.Combine(Application.streamingAssetsPath, "data.json"));
var request = UnityWebRequest.Get(uri.AbsoluteUri);
yield return request.SendWebRequest();

if (request.isNetworkError || request.isHttpError)
{
Debug.Log(request.error);
}
else
{
Debug.Log(request.downloadHandler.text);
}
}
}

我们从上到下来分析这几行代码。

Uri类

首先用到了System.Uri这个类,这个类可以帮助我们更好的构造uri,特别是在请求本地的file文件时。在请求本地文件时,不同的平台使用的Uri略有不同。

如果不使用Uri类时,各个平台所需要的Uri的字符串示例如下:

Windows平台 file:///D:/DATA/StreamingAssets/data.json

WebGL平台 http://localhost/StreamingAssets/data.json

Android平台 jar:file:///data/app/xxx!/assets/data.json

使用Uri类之后,就可以自动帮我们拼接好这些Uri的字符串,一定要记得:

1
var uri = new System.Uri(Path.Combine(Application.streamingAssetsPath, "data.json"));

UnityWebRequest

有了Uri以后,就可以使用UnityWebRequest来进行请求了。

虽然这个叫做WebRequest,但是要记住还可以请求本地的文件。

UnityWebRequest详解

UnityWebRequest封装了网络请求,支持http、https、file、和匿名ftp协议的请求以及处理。

这个类替代了WWW类,相对WWW类使用起来更复杂一些,但是功能和性能也更强大了。

UnityWebRequest类将一个请求分解为了3部分:

  • 提供请求时的输出,传输给服务器

  • 从服务器接收数据

  • 控制HTTP的请求流程

UnityWebRequest由三个元素组成。

  • UploadHandler 处理数据,将数据发送到服务器的对象

  • DownloadHandler 从服务器接收数据的对象

  • UnityWebRequest 负责HTTP通信流量控制来管理上面两个对象的对象。

这些对象之间的关系,如下图所示。

流程图示

对于一次请求,通常的代码流程是:

  • 创建一个UnityWebRequest对象
  • 配置UnityWebRequest对象
  • 设置请求的header
  • 设置HTTP的方法(比如GET,POST,HEAD等,自定义HTTP方法除了Android平台,其他平台也都支持)
  • (可选)创建一个UploadHandler附加到这个UnityWebRequest对象上面
  • 提供上传的数据
  • 提供上传的HTTP表单
  • (可选)创建一个DownloadHandler附加到这个UnityWebRequest对象上面,如果需要获取返回数据一定要创建
  • 发送Send这个请求
  • 如果是在一个协程里,可以使用yield来返回SendWebRequest(),用于等待请求完成。需要注意的是:如果你之前习惯使用www,一定要注意这个UnityWebRequest在调用SendWebRequest()方法之后才会真正执行请求。
  • (可选)从DownloadHandler里面读取接收到的数据
  • (可选)从UnityWebRequest对象读取error信息、HTTP状态码、相应头等信息。

UnityWebRequest捷径

使用Get获取数据

获取简单的文本或者二进制数据,可以使用UnityWebRequest.Get方法。这个方法的参数非常简单,只需要传入Uri即可,和之前的WWW类很类似,可以替代WWW类使用:

1
2
3
4
// 被抛弃的WWW
WWW myWww = new WWW('http://www.myserver.com/foo.txt');
// 使用起来很类似
UnityWebRequest myWr = UnityWebRequest.Get('http://www.myserver.com/foo.txt');

一般使用的方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;

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

IEnumerator GetText() {
UnityWebRequest www = UnityWebRequest.Get('http://www.my-server.com');
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;
}
}
}

结合协程使用(Coroutine)

之前在69节学习过使用协程,但是这里你会发现使用的方法不太一样了,这里使用的是yield return request.SendWebRequest()。

为什么可以这么写呢?

我可以先告诉你作用是什么:那就是可以等待请求完成后才继续执行。

之前我们学协程的时候使用过

1
yield return new WaitForSeconds(3);

也就是3秒后才继续执行。

在这里呢,UnityWebRequest的请求同样可以通过协程的yield return来等待请求执行完毕,但是不会阻塞主线程。

有BOM的问题

假设我们获取到数据后要使用json解析,如果你的文件中有bom,就会出现问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

[System.Serializable]
public class Npc
{
public int Id;
public string 名称;
public float 血量;
public float 攻击力;
public float 防御力;
}

public class Npcs
{
public Npc[] npcs;
}

public class GetFileByWebRequest : MonoBehaviour
{
IEnumerator Start()
{
var uri = new System.Uri(Path.Combine(Application.streamingAssetsPath, 'data.json'));
var request = UnityWebRequest.Get(uri);
Debug.Log(uri);
var www = request.SendWebRequest();
yield return www;

if (request.isNetworkError || request.isNetworkError)
{
Debug.Log(request.error);
}
else
{
var jsonStr = request.downloadHandler.text;
Debug.Log(jsonStr);
var npcs = JsonUtility.FromJson<Npcs>(jsonStr);
Debug.Log(npcs.npcs.Length);
}

}
}

你可以尝试下如下的json文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
'npcs': [
{
'Id': 1,
'名称': '小猴子',
'血量': 10,
'攻击力': 1,
'防御力': 1
},
{
'Id': 2,
'名称': '大猴子',
'血量': 100,
'攻击力': 2,
'防御力': 2
},
{
'Id': 3,
'名称': '孙猴子',
'血量': 999999,
'攻击力': 9999,
'防御力': 9999
}
]
}

如果使用utf8+bom的编码方式,就会报错:

UTF-8 with BOM

报错

所以一定一定要使用无BOM的UTF8编码的文件。

总结

UnityWebRequest.Get方法是最简单的,可以代替www使用

UnityWebRequest对象需要调用SendWebRequest之后才会执行请求

一定一定要使用无BOM的UTF8编码的文件

0%