zoukankan      html  css  js  c++  java
  • 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
    }

      

  • 相关阅读:
    用vuex写了一个购物车H5页面的示例代码
    css如何引入外部字体?
    移动开发中更好的图片自适应
    常见样式问题七、word-break、word-wrap、white-space区别
    你真的了解word-wrap和word-break的区别吗?
    css中word-break、word-wrap和white-space的区别
    另辟蹊径:vue单页面,多路由,前进刷新,后退不刷新
    应该用forEach改变数组的值吗? 原生JS forEach()和map()遍历的异同点
    Vue 全家桶介绍
    Spring MVC配置MyBatis输出SQL
  • 原文地址:https://www.cnblogs.com/wang-jin-fu/p/13445352.html
Copyright © 2011-2022 走看看