WX-Game1/Assets/Scripts/Network/WebRequestManager.cs

1095 lines
39 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using DefaultNamespace;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
namespace WXGame.Network
{
/// <summary>
/// WebRequest管理器 - 简化版HTTP请求管理器
/// 支持GET、POST请求自动处理token认证
/// </summary>
public class WebRequestManager : MonoBehaviour
{
#region
private static WebRequestManager _instance;
public static WebRequestManager Instance
{
get
{
if (_instance == null)
{
// 查找场景中是否已存在实例
_instance = FindObjectOfType<WebRequestManager>();
if (_instance == null)
{
// 创建新的GameObject并添加组件
GameObject go = new GameObject("WebRequestManager");
_instance = go.AddComponent<WebRequestManager>();
DontDestroyOnLoad(go); // 保持跨场景存在
Debug.Log("WebRequestManager: 创建新的WebRequestManager实例");
}
}
return _instance;
}
}
#endregion
#region
[Header("网络请求配置")] [SerializeField] private float defaultTimeout = 30f; // 默认超时时间(秒)
[SerializeField] private int maxRetryCount = 3; // 最大重试次数
[SerializeField] private float retryDelay = 2f; // 重试延迟时间(秒)
[SerializeField] private int maxConcurrentRequests = 5; // 最大并发请求数
[Header("调试设置")] [SerializeField] private bool enableLogging = true; // 是否启用日志
[SerializeField] private bool enableVerboseLogging = false; // 是否启用详细日志
[Header("图片下载配置")] [SerializeField] private float imageDownloadTimeout = 10f; // 图片下载超时时间(秒)
#endregion
#region
private Queue<WebRequestTask> requestQueue = new Queue<WebRequestTask>(); // 请求队列
private List<WebRequestTask> activeRequests = new List<WebRequestTask>(); // 活跃请求列表
private Dictionary<string, string> defaultHeaders = new Dictionary<string, string>(); // 默认请求头
// 图片下载相关字段
private Dictionary<string, Texture2D> imageCache = new Dictionary<string, Texture2D>(); // 简单图片缓存
#endregion
#region
/// <summary>
/// 请求完成事件
/// </summary>
public event Action<WebRequestResult> OnRequestCompleted;
/// <summary>
/// 请求失败事件
/// </summary>
public event Action<WebRequestResult> OnRequestFailed;
/// <summary>
/// 网络错误事件
/// </summary>
public event Action<string> OnNetworkError;
/// <summary>
/// 图片下载完成事件
/// </summary>
public event Action<string, Texture2D> OnImageDownloadCompleted;
/// <summary>
/// 图片下载失败事件
/// </summary>
public event Action<string, string> OnImageDownloadFailed;
#endregion
#region Unity生命周期
private void Awake()
{
// 确保单例模式正确实现
if (_instance == null)
{
_instance = this;
DontDestroyOnLoad(gameObject);
InitializeManager();
Debug.Log("WebRequestManager: 初始化完成");
}
else if (_instance != this)
{
Debug.LogWarning("WebRequestManager: 检测到重复实例,销毁当前对象");
Destroy(gameObject);
}
}
private void OnDestroy()
{
if (_instance == this)
{
_instance = null;
Debug.Log("WebRequestManager: 实例已销毁");
}
}
#endregion
#region
/// <summary>
/// 初始化管理器
/// </summary>
private void InitializeManager()
{
// 设置默认请求头不设置Content-Type让每个请求自己决定
defaultHeaders["Accept"] = "application/json";
LogMessage("WebRequestManager初始化完成", LogType.Log);
}
/// <summary>
/// 设置默认请求头
/// </summary>
/// <param name="key">请求头键</param>
/// <param name="value">请求头值</param>
public void SetDefaultHeader(string key, string value)
{
if (string.IsNullOrEmpty(key))
{
LogMessage("设置默认请求头失败:键不能为空", LogType.Warning);
return;
}
defaultHeaders[key] = value;
LogMessage($"设置默认请求头: {key} = {value}", LogType.Log);
}
/// <summary>
/// 清除默认请求头
/// </summary>
public void ClearDefaultHeaders()
{
defaultHeaders.Clear();
LogMessage("已清除所有默认请求头", LogType.Log);
}
#endregion
#region
/// <summary>
/// 记录日志消息
/// </summary>
/// <param name="message">日志消息</param>
/// <param name="logType">日志类型</param>
private void LogMessage(string message, LogType logType = LogType.Log)
{
if (!enableLogging) return;
string prefix = "[WebRequestManager] ";
switch (logType)
{
case LogType.Log:
Debug.Log(prefix + message);
break;
case LogType.Warning:
Debug.LogWarning(prefix + message);
break;
case LogType.Error:
Debug.LogError(prefix + message);
break;
}
}
/// <summary>
/// 记录详细日志(仅在启用详细日志时输出)
/// </summary>
/// <param name="message">详细日志消息</param>
private void LogVerbose(string message)
{
if (enableVerboseLogging)
{
LogMessage($"[详细] {message}", LogType.Log);
}
}
#endregion
#region
/// <summary>
/// 转换参数值为字符串
/// </summary>
/// <param name="value">参数值</param>
/// <returns>字符串值</returns>
private string ConvertParameterValue(object value)
{
if (value == null)
{
return string.Empty;
}
// 处理数组类型
if (value.GetType().IsArray)
{
return JsonUtility.ToJson(value);
}
// 处理列表类型
if (value is System.Collections.IList)
{
return JsonUtility.ToJson(value);
}
// 处理其他对象类型
if (value.GetType().IsClass && value.GetType() != typeof(string))
{
return JsonUtility.ToJson(value);
}
// 处理基本类型
return value.ToString();
}
#endregion
#region
/// <summary>
/// 发送GET请求协程方式
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="onComplete">完成回调</param>
/// <param name="onError">错误回调</param>
/// <param name="enableSign">是否启用sign签名</param>
/// <param name="signParams">签名参数当enableSign为true时使用</param>
public void GetRequest(string url, Action<WebRequestResult> onComplete = null, Action<string> onError = null,
bool enableSign = false, Dictionary<string, string> signParams = null)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("GET请求失败URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary<string, string>();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"GET请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign && signParams != null)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
// 创建完整的参数字典
var fullParams = new Dictionary<string, string>(signParams);
// 添加固定的appkey
fullParams["appkey"] = appKey;
// 添加时间戳
fullParams["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名使用SignUtil与test.cs保持一致
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = sign;
LogMessage($"GET请求生成sign: {sign}", LogType.Log);
LogMessage($"GET请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 将fullParams转换为URL查询参数
var queryParams = new List<string>();
foreach (var kvp in fullParams)
{
queryParams.Add($"{UnityWebRequest.EscapeURL(kvp.Key)}={UnityWebRequest.EscapeURL(kvp.Value)}");
}
// 构建完整的URL
string queryString = string.Join("&", queryParams);
if (url.Contains("?"))
{
url += "&" + queryString;
}
else
{
url += "?" + queryString;
}
LogMessage($"GET请求完整URL: {url}", LogType.Log);
}
else
{
LogMessage("GET请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"GET请求sign生成异常: {ex.Message}", LogType.Error);
}
}
var task = new WebRequestTask
{
Url = url,
Method = "GET",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task);
}
/// <summary>
/// 发送POST请求自动处理token使用JSON格式
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="onComplete">完成回调</param>
/// <param name="onError">错误回调</param>
/// <param name="enableSign">是否启用sign签名</param>
/// <param name="signParams">签名参数当enableSign为true时使用</param>
public void PostRequest(string url, Action<WebRequestResult> onComplete = null, Action<string> onError = null,
bool enableSign = true, Dictionary<string, string> signParams = null)
{
if (string.IsNullOrEmpty(url))
{
LogMessage("POST请求失败URL不能为空", LogType.Error);
onError?.Invoke("URL不能为空");
return;
}
// 获取token并添加到请求头
var requestHeaders = new Dictionary<string, string>();
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
requestHeaders["token"] = token;
LogMessage($"POST请求添加token: {token.Substring(0, Mathf.Min(10, token.Length))}...", LogType.Log);
}
// 处理sign签名
if (enableSign)
{
try
{
// 写死的appkey和appSecret
string appKey = "38kisezhasfgxhh98b";
string appSecret = "2d6wy8hm8rxbi4xt8dghovggdoodqs57";
Dictionary<string, string> fullParams = null;
// 创建完整的参数字典
if (signParams != null)
fullParams = new Dictionary<string, string>(signParams);
else
{
fullParams = new Dictionary<string, string>();
}
// 添加固定的appkey
fullParams["appkey"] = appKey;
// 添加时间戳
fullParams["timestamp"] = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
// 生成sign签名使用SignUtil与test.cs保持一致
string sign = SignUtil.GetSign(fullParams, appSecret);
if (!string.IsNullOrEmpty(sign))
{
fullParams["sign"] = sign;
LogMessage($"POST请求生成sign: {sign}", LogType.Log);
LogMessage($"POST请求签名原文: {SignUtil.LastPlainText}", LogType.Log);
// 序列化为JSON使用Newtonsoft.Json与test.cs保持一致
string data = JsonConvert.SerializeObject(fullParams);
string contentType = "application/json"; // 强制使用JSON格式
LogMessage($"POST请求最终JSON数据: {data}", LogType.Log);
var task1 = new WebRequestTask
{
Url = url,
Method = "POST",
Data = data,
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task1);
}
else
{
LogMessage("POST请求sign生成失败", LogType.Warning);
}
}
catch (Exception ex)
{
LogMessage($"POST请求sign生成异常: {ex.Message}", LogType.Error);
}
}
else
{
Debug.Log(JsonConvert.SerializeObject(signParams));
var task = new WebRequestTask
{
Url = url,
Method = "POST",
Data = enableSign && signParams != null ? JsonConvert.SerializeObject(signParams) : "",
ContentType = "application/json",
Headers = requestHeaders,
Timeout = defaultTimeout,
OnComplete = onComplete,
OnError = onError
};
EnqueueRequest(task);
}
}
/// <summary>
/// 发送POST请求JSON数据
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="jsonData">JSON数据</param>
/// <param name="headers">自定义请求头</param>
/// <param name="timeout">超时时间</param>
/// <param name="onComplete">完成回调</param>
/// <param name="onError">错误回调</param>
public void PostJsonRequest(string url, string jsonData, Dictionary<string, string> headers = null,
float timeout = -1, Action<WebRequestResult> onComplete = null, Action<string> onError = null)
{
// 直接使用UnityWebRequest发送因为这是旧的方法
StartCoroutine(PostJsonCoroutine(url, jsonData, headers, timeout, onComplete, onError));
}
/// <summary>
/// 发送POST请求表单数据
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="formData">表单数据</param>
/// <param name="headers">自定义请求头</param>
/// <param name="timeout">超时时间</param>
/// <param name="onComplete">完成回调</param>
/// <param name="onError">错误回调</param>
public void PostFormRequest(string url, Dictionary<string, string> formData, Dictionary<string, string> headers = null,
float timeout = -1, Action<WebRequestResult> onComplete = null, Action<string> onError = null)
{
string formString = "";
if (formData != null && formData.Count > 0)
{
var formList = new List<string>();
foreach (var kvp in formData)
{
formList.Add($"{UnityWebRequest.EscapeURL(kvp.Key)}={UnityWebRequest.EscapeURL(kvp.Value)}");
}
formString = string.Join("&", formList);
}
// 直接使用UnityWebRequest发送因为这是旧的方法
StartCoroutine(PostFormCoroutine(url, formString, headers, timeout, onComplete, onError));
}
#endregion
#region
/// <summary>
/// POST JSON请求协程
/// </summary>
private IEnumerator PostJsonCoroutine(string url, string jsonData, Dictionary<string, string> headers,
float timeout, Action<WebRequestResult> onComplete, Action<string> onError)
{
var request = new UnityWebRequest(url, "POST");
request.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(jsonData));
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json; charset=utf-8");
// 添加自定义请求头
if (headers != null)
{
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
}
// 添加token
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
request.SetRequestHeader("token", token);
}
// 设置超时
if (timeout > 0)
{
request.timeout = (int)timeout;
}
yield return request.SendWebRequest();
var result = new WebRequestResult
{
Url = url,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler.text,
IsSuccess = request.result == UnityWebRequest.Result.Success
};
if (request.result != UnityWebRequest.Result.Success)
{
LogMessage($"POST JSON请求失败: {request.error}", LogType.Error);
onError?.Invoke(request.error);
}
else
{
LogMessage($"POST JSON请求成功: {request.responseCode}", LogType.Log);
onComplete?.Invoke(result);
}
request.Dispose();
}
/// <summary>
/// POST表单请求协程
/// </summary>
private IEnumerator PostFormCoroutine(string url, string formData, Dictionary<string, string> headers,
float timeout, Action<WebRequestResult> onComplete, Action<string> onError)
{
var request = new UnityWebRequest(url, "POST");
request.uploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(formData));
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
// 添加自定义请求头
if (headers != null)
{
foreach (var header in headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
}
// 添加token
string token = Apis.GetToken();
if (!string.IsNullOrEmpty(token))
{
request.SetRequestHeader("token", token);
}
// 设置超时
if (timeout > 0)
{
request.timeout = (int)timeout;
}
yield return request.SendWebRequest();
var result = new WebRequestResult
{
Url = url,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler.text,
IsSuccess = request.result == UnityWebRequest.Result.Success
};
if (request.result != UnityWebRequest.Result.Success)
{
LogMessage($"POST表单请求失败: {request.error}", LogType.Error);
onError?.Invoke(request.error);
}
else
{
LogMessage($"POST表单请求成功: {request.responseCode}", LogType.Log);
onComplete?.Invoke(result);
}
request.Dispose();
}
#endregion
#region UniTask
/// <summary>
/// 发送GET请求异步方式返回UniTask
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="enableSign">是否启用sign签名</param>
/// <param name="signParams">签名参数当enableSign为true时使用</param>
/// <returns>UniTask<WebRequestResult></returns>
public async UniTask<WebRequestResult> GetRequestAsync(string url, bool enableSign = false, Dictionary<string, string> signParams = null)
{
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
GetRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
enableSign: enableSign,
signParams: signParams
);
return await completionSource.Task;
}
/// <summary>
/// 发送POST请求异步方式返回UniTask
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="enableSign">是否启用sign签名</param>
/// <param name="signParams">签名参数当enableSign为true时使用</param>
/// <returns>UniTask<WebRequestResult></returns>
public async UniTask<WebRequestResult> PostRequestAsync(string url, Dictionary<string, string> signParams = null)
{
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
PostRequest(url,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); },
signParams: signParams
);
return await completionSource.Task;
}
/// <summary>
/// 发送POST请求JSON数据异步方式返回UniTask
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="jsonData">JSON数据</param>
/// <param name="headers">自定义请求头</param>
/// <param name="timeout">超时时间</param>
/// <returns>UniTask<WebRequestResult></returns>
public async UniTask<WebRequestResult> PostJsonRequestAsync(string url, string jsonData, Dictionary<string, string> headers = null, float timeout = -1)
{
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
PostJsonRequest(url, jsonData, headers, timeout,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
);
return await completionSource.Task;
}
/// <summary>
/// 发送POST请求表单数据异步方式返回UniTask
/// </summary>
/// <param name="url">请求URL</param>
/// <param name="formData">表单数据</param>
/// <param name="headers">自定义请求头</param>
/// <param name="timeout">超时时间</param>
/// <returns>UniTask<WebRequestResult></returns>
public async UniTask<WebRequestResult> PostFormRequestAsync(string url, Dictionary<string, string> formData, Dictionary<string, string> headers = null, float timeout = -1)
{
var completionSource = new UniTaskCompletionSource<WebRequestResult>();
PostFormRequest(url, formData, headers, timeout,
onComplete: (result) => { completionSource.TrySetResult(result); },
onError: (error) => { completionSource.TrySetException(new System.Exception(error)); }
);
return await completionSource.Task;
}
#endregion
#region
/// <summary>
/// 将请求加入队列
/// </summary>
/// <param name="task">请求任务</param>
private void EnqueueRequest(WebRequestTask task)
{
requestQueue.Enqueue(task);
LogVerbose($"请求已加入队列: {task.Method} {task.Url}");
// 尝试处理队列中的请求
ProcessQueue();
}
/// <summary>
/// 处理请求队列
/// </summary>
private void ProcessQueue()
{
// 如果当前活跃请求数未达到最大限制,且队列中有待处理请求
while (activeRequests.Count < maxConcurrentRequests && requestQueue.Count > 0)
{
var task = requestQueue.Dequeue();
activeRequests.Add(task);
StartCoroutine(ExecuteRequest(task));
}
}
/// <summary>
/// 执行请求
/// </summary>
/// <param name="task">请求任务</param>
private IEnumerator ExecuteRequest(WebRequestTask task)
{
LogMessage($"开始执行请求: {task.Method} {task.Url}", LogType.Log);
using (UnityWebRequest request = CreateUnityWebRequest(task))
{
// 设置超时
request.timeout = (int)task.Timeout;
// 发送请求
var operation = request.SendWebRequest();
// 等待请求完成或超时
float startTime = Time.time;
while (!operation.isDone)
{
// 检查是否超时
if (Time.time - startTime > task.Timeout)
{
request.Abort();
LogMessage($"请求超时: {task.Method} {task.Url}", LogType.Warning);
HandleRequestError(task, "请求超时");
break;
}
yield return null;
}
// 处理请求结果
if (operation.isDone)
{
HandleRequestResult(task, request);
}
}
// 从活跃请求列表中移除
activeRequests.Remove(task);
// 继续处理队列
ProcessQueue();
}
/// <summary>
/// 创建UnityWebRequest对象
/// </summary>
/// <param name="task">请求任务</param>
/// <returns>UnityWebRequest对象</returns>
private UnityWebRequest CreateUnityWebRequest(WebRequestTask task)
{
UnityWebRequest request;
switch (task.Method.ToUpper())
{
case "GET":
request = UnityWebRequest.Get(task.Url);
break;
case "POST":
// 使用UnityWebRequest.Post发送字符串数据
request = new UnityWebRequest(task.Url, "POST");
// 设置请求数据
if (!string.IsNullOrEmpty(task.Data))
{
byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(task.Data);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
LogMessage($"POST请求数据: {task.Data}", LogType.Log);
}
// 设置Content-Type
if (!string.IsNullOrEmpty(task.ContentType))
{
request.SetRequestHeader("Content-Type", task.ContentType);
LogMessage($"设置Content-Type: {task.ContentType}", LogType.Log);
}
else
{
// 默认使用urlencoded
request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");
LogMessage("使用默认Content-Type: application/x-www-form-urlencoded", LogType.Log);
}
// 设置下载处理器
request.downloadHandler = new DownloadHandlerBuffer();
break;
default:
throw new ArgumentException($"不支持的HTTP方法: {task.Method}");
}
// 设置默认请求头
foreach (var header in defaultHeaders)
{
request.SetRequestHeader(header.Key, header.Value);
}
// 设置自定义请求头
foreach (var header in task.Headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
return request;
}
/// <summary>
/// 处理请求结果
/// </summary>
/// <param name="task">请求任务</param>
/// <param name="request">UnityWebRequest对象</param>
private void HandleRequestResult(WebRequestTask task, UnityWebRequest request)
{
var result = new WebRequestResult
{
Url = task.Url,
Method = task.Method,
ResponseCode = request.responseCode,
ResponseText = request.downloadHandler?.text ?? "",
ResponseHeaders = request.GetResponseHeader(""),
IsSuccess = request.result == UnityWebRequest.Result.Success,
ErrorMessage = request.error
};
if (result.IsSuccess)
{
LogMessage($"请求成功: {task.Method} {task.Url} (状态码: {result.ResponseCode})", LogType.Log);
task.OnComplete?.Invoke(result);
OnRequestCompleted?.Invoke(result);
}
else
{
LogMessage($"请求失败: {task.Method} {task.Url} - {request.error}", LogType.Error);
HandleRequestError(task, request.error);
}
}
/// <summary>
/// 处理请求错误
/// </summary>
/// <param name="task">请求任务</param>
/// <param name="error">错误信息</param>
private void HandleRequestError(WebRequestTask task, string error)
{
// 检查是否需要重试
if (task.RetryCount < maxRetryCount)
{
task.RetryCount++;
LogMessage($"准备重试请求 ({task.RetryCount}/{maxRetryCount}): {task.Method} {task.Url}", LogType.Warning);
// 延迟重试
StartCoroutine(DelayedRetry(task));
}
else
{
LogMessage($"请求最终失败: {task.Method} {task.Url} - {error}", LogType.Error);
task.OnError?.Invoke(error);
OnRequestFailed?.Invoke(new WebRequestResult
{
Url = task.Url,
Method = task.Method,
IsSuccess = false,
ErrorMessage = error
});
OnNetworkError?.Invoke(error);
}
}
/// <summary>
/// 延迟重试
/// </summary>
/// <param name="task">请求任务</param>
private IEnumerator DelayedRetry(WebRequestTask task)
{
yield return new WaitForSeconds(retryDelay);
EnqueueRequest(task);
}
#endregion
#region
/// <summary>
/// 取消所有请求
/// </summary>
public void CancelAllRequests()
{
LogMessage("取消所有请求", LogType.Warning);
// 清空队列
requestQueue.Clear();
// 停止所有活跃请求
foreach (var task in activeRequests)
{
task.OnError?.Invoke("请求被取消");
}
activeRequests.Clear();
}
/// <summary>
/// 获取当前活跃请求数
/// </summary>
/// <returns>活跃请求数量</returns>
public int GetActiveRequestCount()
{
return activeRequests.Count;
}
/// <summary>
/// 获取队列中等待的请求数
/// </summary>
/// <returns>等待请求数量</returns>
public int GetQueuedRequestCount()
{
return requestQueue.Count;
}
#endregion
#region
/// <summary>
/// 异步下载图片UniTask方式
/// </summary>
/// <param name="imageUrl">图片URL</param>
/// <param name="timeout">超时时间(秒),-1使用默认值</param>
/// <returns>下载的纹理失败返回null</returns>
public async UniTask<Texture2D> DownloadImageAsync(string imageUrl, float timeout = -1)
{
try
{
// 检查URL是否有效
if (string.IsNullOrEmpty(imageUrl))
{
LogMessage("图片URL为空", LogType.Warning);
return null;
}
LogMessage($"开始下载图片: {imageUrl}", LogType.Log);
// 检查缓存中是否已存在该图片
if (imageCache.ContainsKey(imageUrl))
{
LogMessage("从缓存中获取图片", LogType.Log);
return imageCache[imageUrl];
}
// 使用UnityWebRequest下载图片
using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(imageUrl))
{
// 设置超时时间
float actualTimeout = timeout > 0 ? timeout : imageDownloadTimeout;
request.timeout = (int)actualTimeout;
LogMessage("发送图片下载请求", LogType.Log);
await request.SendWebRequest();
// 检查请求结果
if (request.result == UnityWebRequest.Result.Success)
{
LogMessage("图片下载成功", LogType.Log);
// 获取下载的纹理
Texture2D texture = DownloadHandlerTexture.GetContent(request);
if (texture != null)
{
// 添加到缓存
imageCache[imageUrl] = texture;
// 触发完成事件
OnImageDownloadCompleted?.Invoke(imageUrl, texture);
LogMessage($"图片设置完成,尺寸: {texture.width}x{texture.height}", LogType.Log);
return texture;
}
else
{
LogMessage("下载的纹理为空", LogType.Warning);
OnImageDownloadFailed?.Invoke(imageUrl, "下载的纹理为空");
return null;
}
}
else
{
LogMessage($"图片下载失败: {request.error}", LogType.Error);
OnImageDownloadFailed?.Invoke(imageUrl, request.error);
return null;
}
}
}
catch (Exception ex)
{
LogMessage($"下载图片时发生异常: {ex.Message}", LogType.Error);
OnImageDownloadFailed?.Invoke(imageUrl, ex.Message);
return null;
}
}
/// <summary>
/// 下载图片(回调方式)
/// </summary>
/// <param name="imageUrl">图片URL</param>
/// <param name="onComplete">完成回调</param>
/// <param name="onError">错误回调</param>
/// <param name="timeout">超时时间(秒),-1使用默认值</param>
public void DownloadImage(string imageUrl, Action<Texture2D> onComplete = null, Action<string> onError = null, float timeout = -1)
{
DownloadImageAsync(imageUrl, timeout).ContinueWith(texture =>
{
if (texture != null)
{
onComplete?.Invoke(texture);
}
else
{
onError?.Invoke("图片下载失败");
}
}).Forget();
}
#endregion
}
/// <summary>
/// Web请求任务类
/// </summary>
[System.Serializable]
public class WebRequestTask
{
public string Url; // 请求URL
public string Method; // HTTP方法
public string Data; // 请求数据
public string ContentType; // 内容类型
public Dictionary<string, string> Headers; // 请求头
public float Timeout; // 超时时间
public Action<WebRequestResult> OnComplete; // 完成回调
public Action<string> OnError; // 错误回调
public int RetryCount; // 重试次数
}
/// <summary>
/// Web请求结果类
/// </summary>
[System.Serializable]
public class WebRequestResult
{
public string Url; // 请求URL
public string Method; // HTTP方法
public long ResponseCode; // 响应状态码
public string ResponseText; // 响应文本
public string ResponseHeaders; // 响应头
public bool IsSuccess; // 是否成功
public string ErrorMessage; // 错误信息
}
}