文件下载器是应用程序的基础模块,为应用程序与外部网络交互提供了必要的桥梁。该模块设计初衷是为了热更新过程中,下载CDN站点上的文件资源,所以下载器会验证 要下载的文件是否存在于CDN中。如果存在允许下载器继续工作;如果不存在会跳过本地下载。做这层检测是为了安全性考虑,不允许随意下载网络资源。如果有需求可以跳过这层检测。
下载文件时,不必重头开始下载,而是从上次中断的位置继续下载,这样的功能就叫做断点续传。
在下载文件的过程中,打断文件下载的原因有很多,比如网络不稳定,导致下载,中断如果没有断点续传功能的话,中断之后需要重新开始下载。例如一个文件有100M大小,我下载了99M,马上就要下载完成了,这是突然网络中断导致下载失败了,我重新开始下载的时候发现又需要重新开始下载,这时候是不是会感觉心态崩了,如果有了断点续传功能的话,我下载了99M,即使网络中断,重连之后我的下载依旧是从99M的位置开始下载,这样给用户的体验就很棒了。
1. 在下载文件的时候我们会先创建一个与下载文件对应的以.temp为后缀的临时文件, 下载的文件数据会写入这个临时文件中。
2. 每次开始下载的时候会检查是否存在需下载文件的临时文件,如果存在,便从该文件数据长度的地方开始下载写入。
3. 下载完成后便将临时文件移动到目标下载目录。
下载器在物理结构上切分成了4个文件模块,每个模块各司其职。
文章在讲解的时候会挑选模块内的主要的函数来讲解。
文章格式按照
模块中文名 类名
函数:函数中文名 函数名
具体作用解释。
具体的函数实现可以去改模块底部完整代码部分,根据函数名搜索该函数即可。函数里也对每一句话添加了注释。如果还是不懂的可以私信我。
OnReceiveDataAction重写了DownloadHandlerScript内的函数。其主要作用就是我们程序内部需要拿到下载的进度数据。
- public class DownloadHandler : DownloadHandlerScript
- {
- //下载速度限制1024KB
- const int DownLoadKB = 1024;
-
- //初始化下载句柄,定义每次下载的数据上线为 DownLoadKB KB
- //单位:字节 ,字节 = 1024字节 * DownLoadKB (转换成字节单位)
- public DownloadHandler() : base(new byte[1024 * DownLoadKB])
- {
-
- }
-
- //接收到数据的委托
- public BaseAction<byte[], int> OnReceiveDataAction;
-
- protected override bool ReceiveData(byte[] data, int dataLength)
- {
- if (null == data || dataLength == 0)
- return false;
-
- OnReceiveDataAction?.Invoke(data, dataLength);
- return true;
- }
-
- }
下载文件中会调用,会检查当前内存缓存是否达到数据落地要求,如果达到,往硬盘里写入一次文件。
断点下载函数,从指定字节处下载这个文件。
检查下载目录是否存在,不存在则创建;保存下载文件MD5;拼接真实链接;
有相同文件比较MD5是否相同,若不同删除重新下载;定位断点续传文件下载位置;
从头下载文件
外部调用函数,会把相对url、资源信息、更新、完成回调都传入给该对象
下载中回调;下载失败重试;下载完成操作;
关闭WebRequest下载器;接触文件占用;变量重置;
对象或程序生命周期结束时调用,内部调用了 Reset还原类内类内成员的状态。
- //文件下载器
- public class DownloadRoutine:IDisposable
- {
- //Web请求(存了web的连接)
- private UnityWebRequest m_UnityWebRequest = null;
-
- //文件流(写入文件使用)
- private FileStream m_FileStream;
-
- //当前等待写入磁盘的大小(超过阈值才会把这一部分写入文件的尾部)
- private int m_CurrWaitFlushSize = 0;
-
- //上次写入的大小(上次写入文件流的全量大小)
- private int m_PrevWriteSize = 0;
-
- //文件总大小
- private ulong m_TotalSize;
-
- //当前下载的大小(下载了多少了)
- private ulong m_CurrDownloadSize = 0;
-
- //起始位置
- private uint m_BeginPos = 0;
-
- //当前下载文件的链接(url)
- private string m_CurrFileUrl;
-
- //下载到的本地路径
- private string m_DownloadLocalFilePath;
-
- //下载中的委托(string:url,ulong:下载的大小,float:下载百分比)
- private BaseAction<string, ulong, float> m_OnUpdate;
-
- //下载完毕回调
- private BaseAction<string, DownloadRoutine> m_OnComplete;
-
- //当前的资源包信息(*:是信息,不是文件实体;如果不是资源包,是其他的文件怎么办?比如mp4就不能下载了是吗?必须要把资源压到ab包里)
- private AssetBundleInfoEntity m_CurrAssetBundleInfo;
-
- //当前重试次数
- private int m_CurrRetry = 0;
-
- //上次重试时间
- private float m_PrevRetryTime = 0;
-
- //下载句柄(这个也是继承unity的然后自己封装的一层)
- private DownloadHandler m_DownloadHandler;
-
- /*
- * 功能:保存字节
- * buffer:文件流
- * downloadComplete:是否下载完成
- * bufferCount:文件流的总长度(单位:字节)
- */
- private void Save(byte[] buffer, bool downloadComplete = false, int bufferCount = 0)
- {
- if (null == buffer)
- return;
-
- //len是文件流的总长度
- int len = buffer.Length;
-
- //文件流的总长度-上一次写入的大小 = 这次要写入多少?
- int count = len - m_PrevWriteSize;
-
- //m_FileStream?.Write(buffer,m_PrevWriteSize,count);
- //把偏移写入的方法,换成全量写入了?
- m_FileStream?.Write(buffer, 0, bufferCount);
-
- m_PrevWriteSize = len;
-
- m_CurrWaitFlushSize += count;
-
- //内存中下载的文件大小超过了 FlushSize(2048k)*1024 =多少字节。 到达指定的字节数 || 下载完成 直接把流数据Append到文件尾部
- if (m_CurrWaitFlushSize >= GameEntry.Download.FlushSize * 1024 || downloadComplete)
- {
- m_CurrWaitFlushSize = 0;
-
- //内存缓冲区中的数据流,立即写入磁盘
- m_FileStream.Flush();
- }
- }
-
- //接受到数据后(接受到网络流中的数据后,会回调应用层,然后应用层会毁掉我们自己封装的这个函数)
- //没下载完成
- private void DownloadHandlerReceiveDataCallBack(byte[] buffer, int length)
- {
- Save(buffer, false, length);
- }
-
-
- /*
- * 函数功能:下载
- * url:文件链接
- * beginPos:该文件的起始下载位置 单位:字节(断点续传功能使用)
- */
- public void Download(string url, uint beginPos)
- {
- //开启web Request
- m_UnityWebRequest = UnityWebRequest.Get(url);
-
- //实例化下载器
- m_DownloadHandler = new DownloadHandler();
-
- //注册下载器回调
- m_DownloadHandler.OnReceiveDataAction += DownloadHandlerReceiveDataCallBack;
-
- //m_UnityWebRequest内的下载器使用自定义的下载器
- m_UnityWebRequest.downloadHandler = m_DownloadHandler;
-
- //下载器释放的时候 webRequest 也 跟着释放
- m_UnityWebRequest.disposeDownloadHandlerOnDispose = true;
-
- //定位下载的位置,从文件的哪部分开始下载(单位:字节)
- string headerValue = string.Format("bytes={0}-", beginPos.ToString());
- m_UnityWebRequest.SetRequestHeader("Range", headerValue);
-
- //发起请求
- m_UnityWebRequest.SendWebRequest();
- }
-
- public void Download(string url)
- {
- //开启web Request
- m_UnityWebRequest = UnityWebRequest.Get(url);
-
- //实例化下载器
- m_DownloadHandler = new DownloadHandler();
-
- //注册下载器回调
- m_DownloadHandler.OnReceiveDataAction += DownloadHandlerReceiveDataCallBack;
-
- //m_UnityWebRequest内的下载器使用自定义的下载器
- m_UnityWebRequest.downloadHandler = m_DownloadHandler;
-
- //下载器释放的时候 webRequest 也 跟着释放
- m_UnityWebRequest.disposeDownloadHandlerOnDispose = true;
-
- //发起请求
- m_UnityWebRequest.SendWebRequest();
- }
-
- //进行下载
- private void BeginDownload()
- {
- //目录
- string directory = Path.GetDirectoryName(m_DownloadLocalFilePath);
-
- //文件不存在,就创建一个
- if (!Directory.Exists(directory))
- {
- Directory.CreateDirectory(directory);
- }
-
- m_FileStream = new FileStream(m_DownloadLocalFilePath,FileMode.Create,FileAccess.Write);
-
- PlayerPrefs.SetString(m_CurrFileUrl,m_CurrAssetBundleInfo.MD5);
-
- //开始下载
- string url = string.Format("{0}{1}", GameEntry.Data.SysDataManager.CurrChannelConfig.RealSourceUrl, m_CurrFileUrl);
- Download(url);
- }
-
- //内部下载
- private void DownloadInner()
- {
- //本地是否有该文件
- if (File.Exists(m_DownloadLocalFilePath))
- {
- //验证md5,如果本地文件的md5和cdn的md5不一致,删除本地文件,重新下载
- if (PlayerPrefs.HasKey(m_CurrFileUrl))
- {
- //验证
- if (!PlayerPrefs.GetString(m_CurrFileUrl).
- Equals(m_CurrAssetBundleInfo.MD5, StringComparison.CurrentCultureIgnoreCase))
- {
- //本地文件和cdn md5不一致 删除本地文件
- File.Delete(m_DownloadLocalFilePath);
- BeginDownload();
- }
- else
- {
- //文件一致,打开文件
- m_FileStream = File.OpenWrite(m_DownloadLocalFilePath);
-
- //光标定位到文件的最后
- m_FileStream.Seek(0,SeekOrigin.End);
-
- //开始位置设置成文件的长度
- m_BeginPos = (uint)m_FileStream.Length;
-
- //开始下载,直接成功(只是走了一边下载的流程,其实根本没下载)
- string url = string.Format("{0}{1}",GameEntry.Data.SysDataManager.CurrChannelConfig.RealSourceUrl,m_CurrFileUrl);
- Download(url,m_BeginPos);
- }
- }
- }
- else
- {
- BeginDownload();
- }
- }
-
- //开始下载
- public void BeginDownload(string url, AssetBundleInfoEntity assetBundleInfoEntity,
- BaseAction<string, ulong, float> onUpdate = null,
- BaseAction<string, DownloadRoutine> onComplete = null)
- {
- m_CurrFileUrl = url;
-
- m_CurrAssetBundleInfo = assetBundleInfoEntity;
-
- m_OnUpdate = onUpdate;
-
- m_OnComplete = onComplete;
-
- m_DownloadLocalFilePath = string.Format("{0}/{1}",GameEntry.Resource.LocalFilePath,m_CurrFileUrl);
-
- //如果本地有这个文件,先删除
- if (File.Exists(m_DownloadLocalFilePath))
- {
- File.Delete(m_DownloadLocalFilePath);
- }
-
- m_DownloadLocalFilePath = m_DownloadLocalFilePath + ".temp";
-
- //如果通过这个函数调DownloadInnder,本地一定不会有这个文件了啊
- DownloadInner();
- }
-
- public void OnUpdate()
- {
- if (null == m_UnityWebRequest)
- return;
-
- //如果进行重试了,判断重试间隔
- if (m_CurrRetry > 0 && Time.time < m_PrevRetryTime + GameEntry.Download.RetryInterval)
- return;
-
- //大小=0,获取web中的内容大小数据
- if (m_TotalSize == 0)
- ulong.TryParse(m_UnityWebRequest.GetResponseHeader("Content-Length"),out m_TotalSize);
-
- //下载没完成
- if (!m_UnityWebRequest.isDone)
- {
- //使用 unityWebRequest里面的下载字节大小
- if (m_CurrDownloadSize < m_UnityWebRequest.downloadedBytes)
- {
- m_CurrDownloadSize = m_UnityWebRequest.downloadedBytes;
-
- //通知更新
- m_OnUpdate?.Invoke(m_CurrFileUrl,m_CurrDownloadSize,m_CurrDownloadSize/(float)m_TotalSize);
- }
- return;
- }
-
- //网络错误 || http 请求错误
- if (m_UnityWebRequest.isNetworkError || m_UnityWebRequest.isHttpError)
- {
- ++m_CurrRetry;
- m_PrevRetryTime = Time.time;
-
- //大于了重试次数
- if (m_CurrRetry > GameEntry.Download.Retry)
- {
- Reset();
- GameEntry.Log(LogCategory.Resource, "下载完毕url=>{0} 失败 当前重试次数{1}", m_UnityWebRequest.url, m_CurrRetry);
-
- //尝试重新下载
- DownloadInner();
- return;
- }
- GameEntry.Log(LogCategory.Resource, "下载完毕url=>{0} error=>{1}", m_UnityWebRequest.url, m_UnityWebRequest.error);
- Reset();
- }
- else
- {
- m_CurrDownloadSize = m_UnityWebRequest.downloadedBytes;
-
- //最后再更新一次
- m_OnUpdate?.Invoke(m_CurrFileUrl,m_CurrDownloadSize, m_CurrDownloadSize/(float)m_TotalSize);
-
- GameEntry.Log(LogCategory.Resource,"下载完毕url=>{0}",m_UnityWebRequest.url);
-
- Reset();
-
- //好像File里面没有Rename操作,把a/b/c/xxx.ab.temp 移动到a/b/c/xxx.ab
- //这好像是个改名的骚操作啊!
- File.Move(m_DownloadLocalFilePath, m_DownloadLocalFilePath.Replace(".temp",""));
-
- m_DownloadLocalFilePath = null;
-
- if (PlayerPrefs.HasKey(m_CurrFileUrl))
- {
- PlayerPrefs.DeleteKey(m_CurrFileUrl);
- }
-
- //这个文件 写入本地版本文件信息
- GameEntry.Resource.ResManager.SaveVersion(m_CurrAssetBundleInfo);
-
- //回调OnComplete函数
- m_OnComplete?.Invoke(m_CurrFileUrl,this);
- }
- }
-
- //重置对象信息(我认为每个可以被回收的类,都应该有Reset)
- public void Reset()
- {
- if (null != m_UnityWebRequest)
- {
- //中断连接|下载
- m_UnityWebRequest.Abort();
-
- //释放 m_UnityWebRequest 内部资源
- m_UnityWebRequest.Dispose();
-
- m_UnityWebRequest = null;
- }
-
- if (null != m_DownloadHandler)
- {
- //反注册回调
- m_DownloadHandler.OnReceiveDataAction -= DownloadHandlerReceiveDataCallBack;
- m_DownloadHandler = null;
- }
-
- if (null != m_FileStream)
- {
- //关闭文件句柄(解除对该文件的占用,在操作系统里更改这个文件的状态)
- m_FileStream.Close();
-
- //清理 m_FileStream 内部的资源
- m_FileStream.Dispose();
- m_FileStream = null;
- }
-
- m_PrevWriteSize = 0;
- m_TotalSize = 0;
- m_CurrDownloadSize = 0;
- m_CurrWaitFlushSize = 0;
- }
-
- public void Dispose()
- {
- Reset();
- }
- }
多文件下载器内部也是调用了单文件下载器,本质上是对单文件下载器的封装实现。解决单文件下载器在下载依赖文件或资源包时不好管理问题,所以分出来了一个多文件下载器。
统计下载数据,执行下载中回调
检测继续下载;执行下载完成回调
迭代执行DownloadRoutine内的OnUpdate
下载数据记录;分配DownloadRoutine下载;
释放对象内部状态
- //多文件下载器
- public class DownloadMultiRoutine : IDisposable
- {
- //下载器链接
- private LinkedList
m_ListDownloadRoutine; -
- //需要下载的文件链表
- private LinkedList<string> m_ListNeedDownload;
-
- //多个文件下载中的回调函数
- private BaseAction<int, int, ulong, ulong> m_OnDownloadMultiUpdate;
-
- //多个文件下载完成的回调
- private BaseAction
m_OnDownloadMultiComplete; -
- //多文件下载,需要下载的文件数量
- private int m_DownloadMultiNeedCount = 0;
-
- //多文件 当前下载的数量
- private int m_DownloadMultiCurrCount = 0;
-
- //多文件 下载总共大小(单位:字节)
- private ulong m_DownloadMultiTotalSize = 0;
-
- //多文件 当前下载大小(单位:字节)
- private ulong m_DownloadMultiCurrSize = 0;
-
- //多文件 每个文件当前下载的大小(单位:字节)
- private Dictionary<string, ulong> m_dicDownloadMultiCurrSize;
-
- public DownloadMultiRoutine()
- {
- m_ListDownloadRoutine = new LinkedList
(); - m_ListNeedDownload = new LinkedList<string>();
- m_dicDownloadMultiCurrSize = new Dictionary<string, ulong>();
- }
-
- public void OnUpdate()
- {
- LinkedListNode
iter = m_ListDownloadRoutine.First; - for (; iter != null;)
- {
- iter.Value.OnUpdate();
- iter = iter.Next;
- }
- }
-
-
- #region 下载多个文件
- //多文件 下载中回调
- private void OnDownloadMultiUpdate(string url, ulong currDownloadedSize, float progress)
- {
- //缓存当前文件下载的大小
- m_dicDownloadMultiCurrSize[url] = currDownloadedSize;
-
- ulong currSize = 0;
- IEnumerator
string, ulong>> iter = m_dicDownloadMultiCurrSize.GetEnumerator(); - for (; iter.MoveNext();)
- {
- currSize += iter.Current.Value;
- }
-
- //算出当前下载的大小,保存
- m_DownloadMultiCurrSize = currSize;
-
- //安全保护
- if (m_DownloadMultiCurrSize > m_DownloadMultiTotalSize)
- {
- m_DownloadMultiCurrSize = m_DownloadMultiTotalSize;
- }
-
- //回调,通知当前的下载进度
- m_OnDownloadMultiUpdate?.Invoke(m_DownloadMultiCurrCount, m_DownloadMultiNeedCount,
- m_DownloadMultiCurrSize, m_DownloadMultiTotalSize);
- }
-
- //单个文件下载完毕回调
- private void OnDownloadMultiComplete(string fileUrl, DownloadRoutine routine)
- {
- //检查需要下载链表中 是否还有数据,如果有继续下载
- if (m_ListNeedDownload.Count > 0)
- {
- //让下载器继续工作,拿到头部数据
- string url = m_ListNeedDownload.First.Value;
- m_ListNeedDownload.RemoveFirst();
-
- AssetBundleInfoEntity entity = GameEntry.Resource.ResManager.GetCDNAssetBundleInfo(url);
- routine.BeginDownload(url,entity,OnDownloadMultiUpdate, OnDownloadMultiComplete);
- }
- else
- {
- m_ListDownloadRoutine.Remove(routine);
- GameEntry.Pool.EnqueueClassObject(routine);
- }
-
- //当前已下载数量+1
- m_DownloadMultiCurrCount++;
-
- m_OnDownloadMultiUpdate?.Invoke(m_DownloadMultiCurrCount, m_DownloadMultiNeedCount,
- m_DownloadMultiCurrSize,m_DownloadMultiTotalSize);
-
-
- //全部下载完成
- if (m_DownloadMultiCurrCount == m_DownloadMultiNeedCount)
- {
- //结束的时候 直接把当前下载的大小设置为总大小
- m_DownloadMultiCurrSize = m_DownloadMultiTotalSize;
-
- m_OnDownloadMultiUpdate?.Invoke(m_DownloadMultiCurrCount, m_DownloadMultiNeedCount,
- m_DownloadMultiCurrSize, m_DownloadMultiTotalSize);
-
- m_OnDownloadMultiComplete?.Invoke(this);
- }
- }
-
-
- public void BeginDownloadMulti(LinkedList<string> lstUrl,
- BaseAction<int, int, ulong, ulong> onDownloadMultiUpdate = null,
- BaseAction
onDownloadComplete = null ) - {
- m_OnDownloadMultiUpdate = onDownloadMultiUpdate;
- m_OnDownloadMultiComplete = onDownloadComplete;
-
- //需要下载的文件列表&字典 清空
- m_ListNeedDownload.Clear();
- m_dicDownloadMultiCurrSize.Clear();
-
- //下载器记录的下载数量和当前下载数量重置
- m_DownloadMultiNeedCount = 0;
- m_DownloadMultiCurrCount = 0;
-
- //下载器记录的下载大小数据重置
- m_DownloadMultiTotalSize = 0;
- m_DownloadMultiCurrSize = 0;
-
- //1.把需要下载的加入下载队列
- for (LinkedListNode<string> iter = lstUrl.First; iter != null; iter = iter.Next)
- {
- string url = iter.Value;
-
- //多文件下载器加了限制,只有CDN上的资源才能下载,否则不允许下载
- AssetBundleInfoEntity entity = GameEntry.Resource.ResManager.GetCDNAssetBundleInfo(url);
- if (entity != null)
- {
- //?这里等于不就行了 +=个毛啊?
- m_DownloadMultiTotalSize += entity.Size;
- m_DownloadMultiNeedCount++;
- m_ListNeedDownload.AddLast(url);
- m_dicDownloadMultiCurrSize[url] = 0;
- }
- else
- {
- GameEntry.LogError("CDN站点无此资源=>" + url);
- }
- }
-
- //下载器数量,最大同时下载数(平衡下载速度和下载数量,之间做权衡)
- int routineCount = Math.Min(GameEntry.Download.DownloadRoutineCount, m_ListNeedDownload.Count) ;
- for (int i=0;i
- {
- //类对象池取一个对象
- DownloadRoutine routine = GameEntry.Pool.DequeueClassObject
(); -
- //取头部的url,开始下载
- string url = m_ListNeedDownload.First.Value;
- m_ListNeedDownload.RemoveFirst();
-
- AssetBundleInfoEntity entity = GameEntry.Resource.ResManager.GetCDNAssetBundleInfo(url);
- routine.BeginDownload(url,entity, OnDownloadMultiUpdate, OnDownloadMultiComplete);
- m_ListDownloadRoutine.AddLast(routine);
- }
- }
- #endregion
-
- public void Dispose()
- {
- LinkedListNode
iter=m_ListDownloadRoutine.First; - for (; iter != null;)
- {
- iter.Value.Dispose();
- iter = iter.Next;
- }
-
- m_ListDownloadRoutine.Clear();
- m_ListNeedDownload.Clear();
- m_dicDownloadMultiCurrSize.Clear();
- }
- }
下载管理器 DownloadManager
下载管理器中对,配置了下载数据的落地大小、重试次数、间隔等。管理器中存放了单下载器与多下载器的链表。
+ 函数:管理器初始化 Init
初始化配置
+ 函数:下载单个文件 BeginDownloadSingle
分配下载器,下载单个文件;下载完成后移除下载器;执行完成回调;
+ 函数:下载文件列表 BeginDownloadMulti
分配下载器,下载多个文件;下载完成后移除下载器;执行完成回调;
+ 函数:更新 OnUpdate
执行单文件下载器和多文件下载器的OnUpdate
+ 函数:清理对象状态 Dispose
调用单文件下载器和多文件下载器的Dispose
DownloadManager.cs 完整代码
- //下载管理器
- public class DownloadManager : ManagerBase, IDisposable
- {
- //写入磁盘的缓存大小(单位:K 数据到达多少才写入磁盘)
- public int FlushSize
- {
- get;
- private set;
- }
-
- //每个多文件下载器中的下载器最大数量
- public int DownloadRoutineCount
- {
- get;
- private set;
- }
-
- //连接失败后 重试次数
- public int Retry
- {
- get;
- private set;
- }
-
- //重试间隔
- public int RetryInterval
- {
- get;
- private set;
- }
-
- //单文件下载器链表
- private LinkedList
m_lstDownloadSingleRoutine; -
- //多文件下载器连边
- private LinkedList
m_lstDownloadMultiRoutine; -
- public DownloadManager()
- {
- m_lstDownloadSingleRoutine = new LinkedList
(); - m_lstDownloadMultiRoutine = new LinkedList
(); - }
-
- public override void Init()
- {
- //TODO:这里应该读取配置的
- Retry = 5;
- RetryInterval = 60;
- DownloadRoutineCount = 5;
- FlushSize = 2048;//2Mb
- }
-
- #region BeginDownloadSingle 下载单一文件
- ///
- // 下载单个文件
- ///
- /// 文件链接
- /// 下载中的更新回调
- /// 下载完成的回调
- public void BeginDownloadSingle(string url, BaseAction<string, ulong, float> onUpdate = null, BaseAction<string> onComplete = null)
- {
- AssetBundleInfoEntity entity = GameEntry.Resource.ResManager.GetCDNAssetBundleInfo(url);
- if (null == entity)
- {
- GameEntry.LogError("资源包无效=>" + url);
- return;
- }
-
- DownloadRoutine routine = GameEntry.Pool.DequeueClassObject
(); - routine.BeginDownload(url, entity, onUpdate, onComplete: (string fileUrl, DownloadRoutine r) =>
- {
- //移除,归还到对象池
- m_lstDownloadSingleRoutine.Remove(r);
- GameEntry.Pool.EnqueueClassObject(r);
-
- onComplete?.Invoke(fileUrl);
- });
-
- m_lstDownloadSingleRoutine.AddLast(routine);
- }
- #endregion
-
- #region BeginDownloadMulti 下载多个文件
- ///
- /// 下载多个文件
- ///
- /// url链表
- /// 下载中更新回调
- /// 下载完成回调
- public void BeginDownloadMulti(LinkedList<string> lstUrl, BaseAction<int, int, ulong, ulong> onUpdate, BaseAction onComplete = null)
- {
- //从对象池里取一个多文件下载器
- DownloadMultiRoutine multiRoutine = GameEntry.Pool.DequeueClassObject
(); -
- multiRoutine.BeginDownloadMulti(lstUrl, onUpdate, onDownloadComplete: (DownloadMultiRoutine r) =>
- {
- m_lstDownloadMultiRoutine.Remove(r);
- GameEntry.Pool.EnqueueClassObject(r);
- onComplete?.Invoke();
- });
- }
- #endregion
-
- //更新
- public void OnUpdate()
- {
- //遍历更新单文件下载器
- LinkedListNode
iterSingleRoutine = m_lstDownloadSingleRoutine.First; - for (; iterSingleRoutine != null;)
- {
- iterSingleRoutine.Value.OnUpdate();
- iterSingleRoutine = iterSingleRoutine.Next;
- }
-
- //循环更新多文件下载器
- LinkedListNode
iterMultiRoutine = m_lstDownloadMultiRoutine.First; - for (; iterMultiRoutine != null;)
- {
- iterMultiRoutine.Value.OnUpdate();
- iterMultiRoutine = iterMultiRoutine.Next;
- }
- }
-
-
- public void Dispose()
- {
- //清空单任务下载器链表
- LinkedListNode
iter = m_lstDownloadSingleRoutine.First; - for (; iter != null;)
- {
- iter.Value.Dispose();
- iter = iter.Next;
- }
- m_lstDownloadSingleRoutine.Clear();
-
- LinkedListNode
iterMultiRoutine = m_lstDownloadMultiRoutine.First; - for (; iterMultiRoutine != null;)
- {
- iterMultiRoutine.Value.Dispose();
- iterMultiRoutine = iterMultiRoutine.Next;
- }
-
- //清空多任务下载器链表
- m_lstDownloadMultiRoutine.Clear();
- }
- }