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
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM