当我们需要下载热更包时,同样需要中间类来维护。
热更下载的总流程在下面的StartDownloadAB
方法内:
先获取一个下载列表List<Patch>
,默认情况下,这个List<Patch>
就是m_DownloadList
,如果是MD5校验出错的情况下,这个List<Patch>
就是需要重新下载资源的列表
根据下载列表List<Patch>
,创建List<DownloadAssetBundle>
,因为具体的下载方法我们封装在了DownloadAssetBundle
类里
DownloadItem基类 创建DownloadItem
基类,因为在游戏中需要下载的东西有很多,不一定是AssetBundle
我们在ResFrame——Frames——ResourceFrm——Download文件夹内新建DownloadItem
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 using System;using System.Collections;using System.IO;public abstract class DownloadItem { protected string m_Url; protected string m_SavePath; protected string m_FileNameWithoutExt; protected string m_FileExt; protected string m_FileName; protected string m_SaveFilePath; protected long m_FileLength; protected long m_CurFileLength; protected bool m_IsStartDownload; public string Url => m_Url; public string SavePath => m_SavePath; public string FileNameWithoutExit => m_FileNameWithoutExt; public string FileExt => m_FileExt; public string FileName => m_FileName; public string SaveFilePath => m_SaveFilePath; public long FileLength => m_FileLength; public long CurFileLength => m_CurFileLength; public bool StartDownload => m_IsStartDownload; public DownloadItem (string url, string savePath ) { m_Url = url; m_SavePath = savePath; m_IsStartDownload = false ; m_FileNameWithoutExt = Path.GetFileNameWithoutExtension(m_Url); m_FileExt = Path.GetExtension(m_Url); m_FileName = Path.GetFileName(m_Url); m_SaveFilePath = Path.Combine(m_SavePath, m_FileName); } public virtual IEnumerator Download (Action callback = null ) { yield return null ; } public abstract float GetProcess () ; public abstract long GetCurLength () ; public abstract long GetFileLength () ; public abstract void Destroy () ; }
DownloadAssetBundle类 我们在ResFrame——Frames——ResourceFrm——Download文件夹内新建DownloadAssetBundle
文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 using System;using System.Collections;using UnityEngine;using UnityEngine.Networking;public class DownloadAssetBundle : DownloadItem { UnityWebRequest m_WebRequest; public DownloadAssetBundle (string url, string savePath ) : base (url, savePath ) { } public override IEnumerator Download (Action callback = null ) { m_WebRequest = UnityWebRequest.Get(m_Url); m_IsStartDownload = true ; m_WebRequest.timeout = 30 ; yield return m_WebRequest.SendWebRequest(); m_IsStartDownload = false ; if (m_WebRequest.result == UnityWebRequest.Result.ConnectionError || m_WebRequest.result == UnityWebRequest.Result.ProtocolError || m_WebRequest.result == UnityWebRequest.Result.DataProcessingError) { Debug.LogError("Download Error" + m_WebRequest.error); } else { byte [] bytes = m_WebRequest.downloadHandler.data; FileTool.CreateFile(m_SaveFilePath, bytes); callback?.Invoke(); } } public override void Destroy () { if (m_WebRequest != null ) { m_WebRequest.Dispose(); m_WebRequest = null ; } } public override long GetCurLength () { if (m_WebRequest != null ) { return (long )m_WebRequest.downloadedBytes; } return 0 ; } public override long GetFileLength () { return 0 ; } public override float GetProcess () { if (m_WebRequest != null ) { return (long )m_WebRequest.downloadProgress; } return 0 ; } }
修改HotPatchManager 首先给HotPatchManager
添加几个全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private Dictionary<string ,string > m_DownloadMD5Dic = new Dictionary<string ,string >();public event Action ServerInfoError;public event Action<string > ItemDownloadError;public event Action LoadOver;private List<Patch> m_AlreadyDownList = new List<Patch>();private bool m_StartDownload = false ;public bool IsStartDownload => m_StartDownload;private int m_TryDownCount = 0 ;private const int MAXDOWNLOADCOUNT = 4 ;
StartDownloadAB 添加StartDownloadAB
方法,注意这是一个公共方法,需要在外部调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public IEnumerator StartDownloadAB (Action callBack,List<Patch> allPatch = null ) { m_AlreadyDownList.Clear(); m_StartDownload = true ; if (allPatch == null ) { allPatch = m_DownloadList; } if (!Directory.Exists(m_DownLoadPath)) { Directory.CreateDirectory(m_DownLoadPath); } List<DownloadAssetBundle> downloadAssetBundles = new List<DownloadAssetBundle>(); foreach (Patch patch in allPatch) { downloadAssetBundles.Add(new DownloadAssetBundle(patch.Url, m_DownLoadPath)); } foreach (DownloadAssetBundle download in downloadAssetBundles) { yield return m_Mono.StartCoroutine(download.Download()); Patch patch = FindPatchByName(download.FileName); if (patch != null ) { m_AlreadyDownList.Add(patch); } download.Destroy(); } VerifyMD5(downloadAssetBundles, callBack); } Patch FindPatchByName (string name ) { Patch patch = null ; m_DownloadDic.TryGetValue(name, out patch); return patch; }
修改ComputeDownload
和AddDownloadList
方法,主要用来添加m_DownloadMD5Dic
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void ComputeDownload () { m_DownloadList.Clear(); m_DownloadDic.Clear(); m_DownloadMD5Dic.Clear(); } void AddDownloadList (Patch patch ) { if (File.Exists(filePath)) { string md5 = MD5Manager.Instance.BuildFileMD5(filePath); if (patch.MD5 != md5) { m_DownloadList.Add(patch); m_DownloadDic.Add(patch.Name, patch); m_DownloadMD5Dic.Add(patch.Name,patch.MD5); } } else { m_DownloadList.Add(patch); m_DownloadDic.Add(patch.Name, patch); m_DownloadMD5Dic.Add(patch.Name, patch.MD5); } }
修改CheckVersion
方法,主要用来将m_TryDownCount
归零
1 2 3 4 5 public void CheckVersion (Action<bool > hotCallback = null ){ m_TryDownCount = 0 ; }
VerifyMD5 当所有的热更包下载完成之后,执行VerifyMD5
,来判断下载的文件是否正确。
它算是一种递归方法,因为VerifyMD5
在StartDownloadAB
内部被调用,又反过来调用了StartDownloadAB
。
所以我们声明了m_TryDownCount
和 MAXDOWNLOADCOUNT
来限制递归次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 void VerifyMD5 (List<DownloadAssetBundle> downLoadAssets, Action callBack ){ List<Patch> downloadList = new List<Patch>(); foreach (DownloadAssetBundle download in downLoadAssets) { string md5 = "" ; if (m_DownloadMD5Dic.TryGetValue(download.FileName, out md5)) { if (MD5Manager.Instance.BuildFileMD5(download.SaveFilePath) != md5) { Debug.Log(string .Format("此文件{0}MD5校验失败,即将重新下载" ,download.FileName)); Patch patch = FindPatchByName(download.FileName); if (patch != null ) { downloadList.Add(patch); } } } } if (downloadList.Count > 0 ) { if (m_TryDownCount > MAXDOWNLOADCOUNT) { string allName = "" ; m_StartDownload = false ; foreach (Patch patch in downloadList) { allName += patch.Name + ";" ; } Debug.LogError("资源下载后MD5校验失败,请检查资源:" + allName); ItemDownloadError?.Invoke(allName); } else { m_TryDownCount++; m_DownloadMD5Dic.Clear(); foreach (Patch patch in downloadList) { m_DownloadMD5Dic.Add(patch.Name, patch.MD5); } m_Mono.StartCoroutine(StartDownloadAB(callBack,downloadList)); } } else { m_DownloadMD5Dic.Clear(); callBack?.Invoke(); m_StartDownload = false ; LoadOver?.Invoke(); } }
获取下载总进度 我们可以通过m_AlreadyDownList
中获取到刚下载完的资源包的大小。
通过在HotPatchManager
里面添加m_CurDownload
变量,可以获取到当前正在下载的资源包的下载信息。
我们之前在HotPatchManager
里面声明了一个
1 2 3 4 private DownloadAssetBundle m_CurDownload = null ;
在StartDownloadAB
方法里指定m_CurDownload
1 2 3 4 5 6 7 8 9 10 11 12 13 public IEnumerator StartDownloadAB (Action callBack,List<Patch> allPatch = null ) { foreach (DownloadAssetBundle download in downloadAssetBundles) { m_CurDownload = download; yield return m_Mono.StartCoroutine(download.Download()); } }
最后在HotPatchManager
里面添加GetProgress
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public float GetProgress (){ float alreadySize = m_AlreadyDownList.Sum(x => x.Size); float curAlreadySize = 0 ; if (m_CurDownload != null ) { Patch patch = FindPatchByName(m_CurDownload.FileName); if (patch != null && !m_AlreadyDownList.Contains(patch)) { curAlreadySize = m_CurDownload.GetProcess() * patch.Size; } } return (alreadySize + curAlreadySize) / LoadSumSize; }
获取已经下载大小 在HotPatchManager
里面添加GetLoadSize
方法,获取下载总大小,可以用来计算下载速度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public float GetLoadSize () { float alreadySize = m_AlreadyDownList.Sum(x => x.Size); float curAlreadySize = 0 ; if (m_CurDownload != null ) { Patch patch = FindPatchByName(m_CurDownload.FileName); if (patch != null && !m_AlreadyDownList.Contains(patch)) { curAlreadySize = m_CurDownload.GetProcess() * patch.Size; } } return alreadySize + curAlreadySize; }
简化GetProgress
方法
1 2 3 4 public float GetProgress (){ return GetLoadSize() / LoadSumSize; }
有了下载总进度和已经下载的大小,我们就可以计算下载的速度了(计算出来的是有误差的,并非实际速度)
资源加载路径替换 热更包下载完成后,需要在AssetBundleManager
中指定好路径
首先在HotPatchManager
里面添加ComputeABPath
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public string ComputeABPath (string name ){ Patch patch = null ; if (m_HotFixDic.TryGetValue(name, out patch)) { return m_DownLoadPath + "/" + name; } return string .Empty; }
这时我们的m_HotFixDic
就派上用场了,它用来确认加载的包是否是目前能用的ab包,防止非法加载AB包
然后再在AssetBundleManager
里面的LoadAssetBundle
方法内添加路径的替换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private AssetBundle LoadAssetBundle (string abName ) { AssetBundleItem abItem = null ; uint abCrc = CRC32.GetCRC32(abName); if (!m_AssetBundleItemDic.TryGetValue(abCrc, out abItem)) { AssetBundle assetBundle = null ; string hotABPath = HotPatchManager.Instance.ComputeABPath(abName); string fullPath = string .IsNullOrEmpty(hotABPath)? ABLoadPath + abName : hotABPath; #if UNITY_STANDALONE || UNITY_EDITOR if (File.Exists(fullPath)) { assetBundle = AssetBundle.LoadFromFile(fullPath); } #endif } }