unity UnityWebRequest下载封装,避免同时开启太多协成

起因:游戏里面玩家好友都是用关系链头像,也就是url头像,玩家进游戏需要动态拉取图片。

之前没有做下载队列缓存,一个url下载就会开启一个协成,协成下载等待时间也设置了太长,导致网络延迟高且玩家好友多时,出现开启协成太多,卡主进程的问题(每个协成都在等待下载回包)。

解决:

1.限制单次的下载等待时间req.timeout = 5;原先是等待30秒。

2.做下载缓存,对下载过的url内容做缓存。

3.限制同时下载数量,比如最多同时下载三个(也就是最多开启三个协成),如果当前下载队列超过3个,把下载任务添加到缓存队列。当前下载任务完成时,从缓存队列取出一个任务执行(如果有的话)。

4.一个url只下载一次,一次下载任务可以对应多个不同回调(可能会出现多个地方依赖同一个url,或者网络延迟太高导致同一个地方同一个url触发多次下载)。

源码:TaskManager是对协成的封装,提供在非mono类开启协成的机制(其实就是绑定了一个DontDestoryOnLoad的mono对象,这个对象永远是Active状态)。

using System;
using System.Collections;
using System.Collections.Generic;
using com.geargames.extensions;
using UnityEngine;
using UnityEngine.Networking;

/// <summary>
/// UnityWebRequest下载接口封装
/// 避免网络卡顿时重复下载,导致协成数量太多
/// </summary>
public class DownLoadUtil
{
private static Dictionary<string, DownCache> m_cacheDownload = new Dictionary<string, DownCache>();//下载缓存
private static Dictionary<string, TaskInfo> m_taskCallBack = new Dictionary<string, TaskInfo>();//下载回调缓存

private static List<string> m_waitDownloadTask = new List<string>();//等待下载的列表
private static List<string> m_curDownloadTask = new List<string>();//当前正在下载的列表

private static int m_maxDownloadNum = 3;//最大可同时下载数量
private static int m_DownloadTimeOut = 5;//下载超时

/// <summary>
/// 一个url对应一个TaskInfo,里面保存了该url的下载类型DownloadHandler,所有监听该url下载的回调
/// </summary>
private class TaskInfo
{
private List<Action<DownCache>> m_callBacks = new List<Action<DownCache>>();

public string Url;
public DownloadHandler Handle;

public TaskInfo(string url, DownloadHandler handle)
{
Url = url;
Handle = handle;
}

public void AddCallBack(Action<DownCache> callBack)
{
if (!m_callBacks.Contains(callBack)) {
m_callBacks.Add(callBack);
}
}

public void RemoveCallBack(Action<DownCache> callBack) {
if (m_callBacks.Contains(callBack)) {
m_callBacks.Remove(callBack);
}
}

public void ClearCallBack() {
m_callBacks.Clear();
}

public int Count() {
return m_callBacks.Count;
}

public void DownloadEnd(DownCache cache) {
for (int i = 0; i < m_callBacks.Count; i++) {
if (m_callBacks[i] != null) {
m_callBacks[i](cache);
}
}

ClearCallBack();
}
}

public class DownCache {
public byte[] data;
public string text;
public Texture tex;
public string url;
}

//下载
public static void Download(string url, Action<DownCache> callBack, DownloadHandler handle = null) {
if (callBack == null) return;

DownCache cache;
if (m_cacheDownload.TryGetValue(url, out cache))
{
callBack(cache);
return;
}

TaskInfo taskInfo = null;
if (!m_taskCallBack.TryGetValue(url, out taskInfo))
{
taskInfo = new TaskInfo(url, handle);
m_taskCallBack.Add(url, taskInfo);
}

taskInfo.AddCallBack(callBack);

//不在当前的下载、等待列表,加入执行队列
if (!m_waitDownloadTask.Contains(url) && !m_curDownloadTask.Contains(url)) {
CastTask(url);
}
}

private static void CastTask(string url)
{
if (string.IsNullOrEmpty(url))
{
if (m_waitDownloadTask.Count == 0) {
return;//没有等待下载的任务
}

url = m_waitDownloadTask[0];
m_waitDownloadTask.RemoveAt(0);
}

//当前并发下载数大于3,缓存
if (m_curDownloadTask.Count > m_maxDownloadNum)
{
m_waitDownloadTask.Add(url);
} else {
int taskId = TaskManager.Instance.Create(RealDownload(url));
m_curDownloadTask.Add(url);
}
}

private static IEnumerator RealDownload(string url)
{
UnityWebRequest req = UnityWebRequest.Get(url);
req.timeout = m_DownloadTimeOut;

TaskInfo taskInfo = null;
if (m_taskCallBack.TryGetValue(url, out taskInfo)) {
req.downloadHandler = taskInfo.Handle;
}

yield return req.SendWebRequest();
if (req.isNetworkError || req.isHttpError)
{
DownloadEnd(url);
yield break;
}

HandleDownload(url, req.downloadHandler);
req.Dispose();

DownloadEnd(url);
}

//下载错误、下载结束都清掉这个url任务
private static void DownloadEnd(string url) {
m_taskCallBack.Remove(url);
m_curDownloadTask.Remove(url);
CastTask(null);
}

private static void HandleDownload(string url, DownloadHandler handle) {
Texture tex = null;
if (handle is DownloadHandlerTexture texHandle) {
tex = texHandle.texture;

if (tex) {
tex.name = url;
}
}

DownCache cacheHandle = new DownCache();//缓存,req.Dispose会销毁handle,所以这边单独缓存
cacheHandle.data = handle.data;
cacheHandle.text = handle.text;
cacheHandle.tex = tex;
cacheHandle.url = url;

if(!m_cacheDownload.ContainsKey(url))
m_cacheDownload.AddValueEx(url,cacheHandle);

TaskInfo taskInfo = null;
if (m_taskCallBack.TryGetValue(url, out taskInfo))
{
taskInfo.DownloadEnd(cacheHandle);
m_taskCallBack.Remove(url);
}

Debug.Log("download end : " + url);
}

//移除某个链接下载
public static void RemoveHandle(string url)
{
m_taskCallBack.Remove(url);
if (m_waitDownloadTask.Contains(url))
m_waitDownloadTask.Remove(url);
}

//移除单个下载任务
public static void RemoveHandle(string url, Action<DownCache> callBack)
{
TaskInfo taskInfo = null;
if (m_taskCallBack.TryGetValue(url, out taskInfo)) {
taskInfo.RemoveCallBack(callBack);

if (taskInfo.Count() == 0) {
m_taskCallBack.Remove(url);
}
}
}

#region 贴图下载封装
private class TextureTaskInfo
{
private List<Action<Texture, string>> m_callBacks = new List<Action<Texture, string>>();

public void AddCallBack(Action<Texture, string> callBack)
{
if (!m_callBacks.Contains(callBack)) {
m_callBacks.Add(callBack);
}
}

public void RemoveCallBack(Action<Texture, string> callBack) {
if (m_callBacks.Contains(callBack)) {
m_callBacks.Remove(callBack);
}
}

public void ClearCallBack() {
m_callBacks.Clear();
}

public int Count() {
return m_callBacks.Count;
}

public void DownloadEnd(DownCache cache) {
bool isGif = cache.text.StartsWith("GIF");
for (int i = 0; i < m_callBacks.Count; i++) {
if (isGif) //gif
{
m_callBacks[i](null, cache.url);
} else {
m_callBacks[i](cache.tex, cache.url);
}
}

ClearCallBack();
}
}

private static Dictionary<string, TextureTaskInfo> m_texCallBack =
new Dictionary<string, TextureTaskInfo>();//下载回调缓存

//下载贴图
public static void DownloadTexture(string url, Action<Texture, string> callBack) {
TextureTaskInfo texCallBack = null;
if (!m_texCallBack.TryGetValue(url, out texCallBack)) {
texCallBack = new TextureTaskInfo();
m_texCallBack.Add(url, texCallBack);
}

texCallBack.AddCallBack(callBack);

Download(url, (cacheHandle) =>
{
TextureTaskInfo finalCallBack = null;
if (!m_texCallBack.TryGetValue(cacheHandle.url, out finalCallBack)) {
return;
}

finalCallBack.DownloadEnd(cacheHandle);
m_texCallBack.Remove(cacheHandle.url);
}, new DownloadHandlerTexture());
}

public static void RemoveTexTask(string url, Action<Texture, string> callBack) {
TextureTaskInfo callBackList = null;
if (m_texCallBack.TryGetValue(url, out callBackList)) {
callBackList.RemoveCallBack(callBack);
if (callBackList.Count() == 0) {
m_texCallBack.Remove(url);
}
}
}

public static void RemoveTexTask(string url) {
m_texCallBack.Remove(url);
}

#endregion
}

  

原文地址:https://www.cnblogs.com/wang-jin-fu/p/13445352.html