Unity lightweight UnityWebRequest loading

The project needs to load texture from the web service, and organize a simple texture management, including loading, unloading, and controlling the number of simultaneous loading 1. Encapsulate a LoadingTexture, including start
download, interrupt, callback, release, whether it is downloading, whether to start downloading (If you do not need to control the number of downloads, this attribute is not required)

    class LoadingTexture
    {
    
    
        private UnityWebRequest www;
        private string mUrl;
        private List<Action<string, Texture>> mCallback = new List<Action<string, Texture>>();
        bool mIsStarted = false;
        public LoadingTexture(string url, Action<string, Texture> callback)
        {
    
    
            mUrl = url;           
            mCallback.Add(callback);
        }

        public bool IsStarted()
        {
    
    
            return mIsStarted;
        }

        public void StartLoad()
        {
    
    
            mIsStarted = true;

            System.Uri uri = new System.Uri(mUrl);
            //如果不需要读写,第二个参数改为true,节省内存
			www = UnityWebRequestTexture.GetTexture(uri,true);
			www.SendWebRequest();
		}

        public string GetUrl()
        {
    
    
            return mUrl;
        }

        public void AddCallback(Action<string, Texture> callback)
        {
    
    
            mCallback.Add(callback);
        }

        public void RemoveCallback(Action<string, Texture> callback)
        {
    
    
            mCallback.Remove(callback);
            //如果没人需要了,就中断吧
            if (mCallback.Count == 0)
            {
    
    
                if (www != null)
                {
    
    
                    www.Abort();
                    www.Dispose();
                    www = null;
                }
            }
        }

        public bool IsDone()
        {
    
    
            if (www != null)
            {
    
    
                return www.isDone;
            }

            return true;
        }

        public Texture GetTexture()
        {
    
    
            if (www == null)
            {
    
    
                return null;
            }
            if (!www.isDone)
            {
    
    
                return null;
            }

            if (www.isNetworkError || www.isHttpError)
            {
    
    
                return null;
            }

            if(www.downloadHandler == null)
			{
    
    
                return null;
			}
			//如果调用压缩,该texture需要改为可读写,并不能节省内存
            //Texture2D tex = ((DownloadHandlerTexture)www.downloadHandler).texture;
            //tex.Compress(false);
            //return tex;
            return ((DownloadHandlerTexture)www.downloadHandler).texture;
        }

        public void DoCallback()
        {
    
    
            Texture texture = GetTexture();
            foreach ( var callback in mCallback)
            {
    
    
                callback?.Invoke(mUrl, texture);
            }
        }

        public void Dispose()
        {
    
    
            if (www != null)
            {
    
    
                www.Dispose();
                www = null;
            }
        }       
    }

2. Add a Manager that manages the loading process, including TexturePool, LoadTextureList that needs to be downloaded, ReleaseList that needs to be released, and the number of simultaneous downloads MaxLoadingTextureCount

     	int MaxLoadingTextureCount = 3;
        //不能用Dictionary,不能保证执行按照加载顺序
        //private Dictionary<string, LoadingTexture> mDicLoadingTexture = new Dictionary<string, LoadingTexture>();
        List<LoadingTexture> mLisLoadingTexture = new List<LoadingTexture>();
        Dictionary<string, Texture> mTexturePool = new Dictionary<string, Texture>();
        //有可能一个Texture被多个引用,所以当一个Texture不需要时,要遍历是否还有引用
        List<Texture> mTryReleaseTexture = new List<Texture>();
        
        //下载函数
        //被调用时先去池子里找,如果池子里有直接返回
        //再去下载池子里找,如果有把回调加到该loadingtexture里
        //都没有,new 一个 loadingtexture放进下载池子
        public void LoadTexture(string url, Action<string, Texture> callback)
        {
    
    
            //当前容器
            if (mTexturePool .ContainsKey(url))
            {
    
    
                Texture workTexture = mWorkTexture[url];
                callback?.Invoke(url, workTexture);
                return;
            }
            //正在下载,有可能多个地方引用
            LoadingTexture loadingTexture = GetLoadingTexture(url);
            if (loadingTexture != null)
            {
    
    
                if (callback != null)
                {
    
    
                    loadingTexture.AddCallback(callback);
                }
                return;
            }
            //创建www去下载
            mLisLoadingTexture.Add(new LoadingTexture(url, callback));
			
            //如果没有正在下载的,启动当前的下载任务,下载一个一个顺利执行
//             if (mLisLoadingTexture.Count == 1)
//             {
    
    
//                 mLisLoadingTexture[0].StartLoad();
//             }

        }
		//把下载成功的texture放进池子里,每次加载成功后调用
        public void AddTexture(string url, Texture texture)
        {
    
    
            if(!mTexturePool.ContainsKey(url))
			{
    
    
                mTexturePool.Add(url, texture);
            }
        }

        //通过url获取loadtexture
        LoadingTexture GetLoadingTexture(string url)
        {
    
    
            foreach (var loadingTexture in mLisLoadingTexture)
            {
    
    
                if (loadingTexture.GetUrl() == url)
                {
    
    
                    return loadingTexture;
                }
            }
            return null;
        }

        //这种方式不确定能正确的移除
        public void CancelLoadTexture(Action<string, Texture> callback)
        {
    
    
            foreach (var loadingTexture in mLisLoadingTexture)
            {
    
    
                loadingTexture.RemoveCallback(callback);
            }
        }

        //尝试Texture是否被需要
        public void TryReleaseTexture(Texture texture)
        {
    
    
			for (int i = 0; i < mLisLoadingTexture.Count; ++i)
			{
    
    
				if (mLisLoadingTexture[i].GetTexture() == texture)
				{
    
    
					return;
				}
			}
			mTryReleaseTexture.Add(texture);
		}
		//通过URL从池子里取出texture
        public Texture GetTextureByUrl(string url)
        {
    
    
            if (mTexturePool.ContainsKey(url))
            {
    
    
                return mTexturePool[url];
            }
            return null;
        }
		//通过texture取出url
        public string GetUrlByTexture(Texture texture)
        {
    
    
            var eTexture = mTexturePool.GetEnumerator();
            while (eTexture.MoveNext())
            {
    
    
                if (eTexture.Current.Value == texture)
                {
    
    
                    return eTexture.Current.Key;
                }
            }
            return null;
        }

        private float nextTimer = 0;
        private float gap = 0.5f;
        //每帧调用下载,每隔0.5f,调用释放
        void Update()
        {
    
    
            UpdateLoading();
            if(Time.time > nextTimer)
			{
    
    
                UpdateTryReleaseTexture();
                nextTimer = Time.time + gap;
            }
        }
		//下载函数,如果下载列表为空,return
		//遍历下载列表,控制同时load数量为3
		//loadingtexture下载完成后remove该任务
		//将下载成功的对象AddToPool
		//Invoke callback
		//释放www
        void UpdateLoading()
        {
    
    
            if (mLisLoadingTexture.Count == 0)
            {
    
    
                return;
            }
            for( int i = 0; i < MaxLoadingTextureCount && i < mLisLoadingTexture.Count; /*++i*/)
            {
    
    
                var loadingTexture = mLisLoadingTexture[i];
                if (!loadingTexture.IsStarted())
                {
    
    
                    loadingTexture.StartLoad();
                }
                if (!loadingTexture.IsDone())
                {
    
    
                    ++i;
                    continue;
                }
                //下载完删除任务
                mLisLoadingTexture.RemoveAt(i);

                var texture = loadingTexture.GetTexture();
                if (texture != null)
                {
    
    
                    //加载成功先保存Texture,再执行回调
                    mTexturePool.Add(loadingTexture.GetUrl(), texture);
                    loadingTexture.DoCallback();
                }
                else
                {
    
    
                    //失败就不回调了,这里打个log吧
                }
                //释放www资源
                loadingTexture.Dispose();
            }         
        }

        int releaseCount = 0;
        int MAX_RELEASE_COUNT = 8;
        //释放函数
        //本来想实现,判断texture无引用,调用UnloadAsset
        //发现这个函数只能卸载从磁盘下载的texture,不能卸载从web服下载的texture
        //后来改成无引用的texture count > 8,直接调用UnloadUnusedAssets
        //经测试不调用destory 也能被清理掉,考虑版本已验收就不改了
        void UpdateTryReleaseTexture()
        {
    
    
			if (releaseCount > MAX_RELEASE_COUNT)
			{
    
    
				Resources.UnloadUnusedAssets();
				releaseCount = 0;
			}
			if (mTryReleaseTexture.Count == 0)
            {
    
    
                return;
            }
            //获取第一个Texture,每次只处理一个
            var texture = mTryReleaseTexture[0];
            //无论是否成功,都先移除
            mTryReleaseTexture.RemoveAt(0);

            //取出所有的引用
            var workPoints = GetAllTexturePoint();
            foreach (var workPoint in workPoints)
            {
    
    
                if (workPoint != null)
                {
    
    
                    //发现有被引用,直接返回
                    if (workPoint .Texture == texture)
                    {
    
    
                        return;
                    }
                }
            }

            //执行到这说明texture没有被引用了
            //先从TexturePool里移除
            string url = GetUrlByTexture(texture);
            if (url == null)
            {
    
    
                return;
            }
            mTexturePool.Remove(url);
            Destroy(texture);
            releaseCount++;
            //直接卸载
            //Resources.UnloadAsset(texture);
        }

		//清理函数,离开运行环境时调用
		//clear Pool 之后调用UnloadUnusedAssets
		public void Clear()
        {
    
    
            mTryReleaseTexture.Clear();
            foreach (var t in mTexturePool.Values)
            {
    
    
                Destroy(t);
                //Resources.UnloadAsset(t);
            }
            mTexturePool.Clear();
            Resources.UnloadUnusedAssets();      
        }
    }

Guess you like

Origin blog.csdn.net/u014481027/article/details/125413289