Unity异步网络请求封装实现
在Unity开发过程中,网络请求是常见的需求。为了简化开发流程并提高代码可读性,本文介绍了一种基于async/await模式的UnityWebRequest封装方案,支持请求轮询和自动重试功能。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Networking;
public static class NetworkManager
{
public class ResponseData
{
public int statusCode;
public string status;
public byte[] payload;
public string GetContent()
{
if (payload != null && payload.Length > 0)
{
return Encoding.UTF8.GetString(payload, 0, payload.Length);
}
return string.Empty;
}
}
public class PollConfig
{
/// <summary>
/// 轮询次数,0表示无限轮询
/// </summary>
public ushort Attempts { get; private set; }
/// <summary>
/// 轮询间隔时间(毫秒)
/// </summary>
public ushort Interval { get; private set; }
/// <summary>
/// 自定义终止条件函数
/// </summary>
public Func<ResponseData, bool> ShouldStop { get; private set; }
public PollConfig(ushort attempts, ushort interval, Func<ResponseData, bool> shouldStop)
{
Attempts = attempts;
Interval = interval;
ShouldStop = shouldStop;
}
}
public static async Task<ResponseData> SendRequest(string endpoint, WWWForm formData, Dictionary<string, string> customHeaders)
{
using (UnityWebRequest webRequest = UnityWebRequest.Post(endpoint, formData))
{
if (customHeaders != null)
{
foreach (var header in customHeaders)
{
webRequest.SetRequestHeader(header.Key, header.Value);
}
}
await webRequest.SendWebRequest();
ResponseData response = new ResponseData
{
statusCode = webRequest.responseCode,
status = webRequest.result.ToString(),
payload = webRequest.downloadHandler.data
};
return response;
}
}
public static async Task<ResponseData> SendRequestWithPolling(string endpoint, WWWForm formData,
Dictionary<string, string> customHeaders, PollConfig pollConfig = null)
{
return await ExecuteWithPolling(() => SendRequest(endpoint, formData, customHeaders), pollConfig);
}
private static async Task<ResponseData> ExecuteWithPolling(Func<Task<ResponseData>> requestMethod, PollConfig config)
{
ResponseData response;
if (config == null)
{
response = await requestMethod();
}
else
{
ushort attemptCount = 0;
while (true)
{
response = await requestMethod();
if (config.ShouldStop != null && config.ShouldStop(response))
{
break;
}
if (config.Attempts != 0 && attemptCount >= config.Attempts)
{
break;
}
await Task.Delay(config.Interval);
attemptCount++;
}
}
return response;
}
/// <summary>
/// 为UnityWebRequestAsyncOperation添加异步支持
/// </summary>
public static TaskAwaiter GetAwaiter(this UnityWebRequestAsyncOperation operation)
{
var taskCompletion = new TaskCompletionSource<object>();
operation.completed += _ => taskCompletion.SetResult(null);
return taskCompletion.Task.GetAwaiter();
}
}
使用示例代码:
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEngine;
public class NetworkExample : MonoBehaviour
{
async void Start()
{
Debug.Log("开始网络请求测试");
await TestNetworkRequest();
Debug.Log("测试完成");
}
async void TestNetworkRequest()
{
Debug.Log("发起请求");
var config = new NetworkManager.PollConfig(
attempts: 3,
interval: 1000,
shouldStop: (response) =>
{
Debug.Log($"请求状态: {response.status}");
return false; // 不提前终止
});
NetworkManager.ResponseData result = await NetworkManager.SendRequestWithPolling(
"https://www.example.com",
null,
null,
config);
string content = result.GetContent();
Debug.Log($"响应内容: {content}");
}
}
这个封装方案提供了以下优势:
- 基于async/await的异步编程模式,代码更简洁直观
- 支持自定义请求头
- 内置轮询机制,可根据需要设置轮询次数和间隔
- 提供灵活的终止条件判断
- 统一的响应数据结构,方便处理返回结果
对于需要在协程中使用UnityWebRequest并实现重试功能的场景,可以参考相关Unity异步处理最佳实践。