using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; namespace AssetManagement { // 2020.01.04 - Blastom:因为当前版本UnityWebRequest不支持下载https // 现在下载部分无法重用AsyncLoadHub // 快速解决方案是复制AsyncLoadHub 修改为支持BestHttp的流程。 public class BundleDownloadHub { // 失败后重新下载等待时间 public const float failWaitTime = 1f; // 失败后重新下载次数 public const int maxRetryCount = 5; // 后台失败后重新下载次数 public const int maxRetryLowPriority = 1; // 重新下载优先度,该优先度高于任何其他级别 public const int retryPriority = 100; public readonly List activeUnits = new List(); public readonly DownloaderQueue waitQueue = new DownloaderQueue(); protected AssetPathHub pathHub; public List rules; public void StartBackGroundDownload() { var downloadList = pathHub.GetBackgroundDownloadList(); foreach (var download in downloadList) LoadUnit(download.name, -1); } public event UnityAction onComplete; public void Init(AssetPathHub pathHub, List rules) { this.pathHub = pathHub; this.rules = rules; this.rules.Sort((a, b) => a.priority.CompareTo(b.priority)); } public BundleDownloader LoadUnit(string assetName, int priority = 0) { // 如果已经通过其他渠道下载,则直接返回当前下载的单位 var unit = activeUnits.Find(a => a.name == assetName); if (unit == null) { var waitState = waitQueue.TrySetPriority(assetName, priority, out unit); switch (waitState) { case SetPriorityResult.NoItem: { unit = new BundleDownloader(); unit.Init(pathHub, assetName, priority); // 无需检查是否有重试延迟 if (CanActive(unit.priority)) ActivateUnit(unit); else waitQueue.AddInQueue(unit); break; } case SetPriorityResult.NoUpdate: // Nothing Happened break; case SetPriorityResult.Update: if (CanActive(priority)) ActivateWaitUnits(1); break; default: throw new ArgumentOutOfRangeException(); } } else if (unit.priority < priority) unit.priority = priority; return unit; } // 注:加载中的单位是不可停止的,Unity机制限制 /// /// 停止一个加载单位,返回是否可以停止 /// public bool StopUnit(BundleDownloader unit) { var result = false; if (waitQueue.RemoveInQueue(unit.name)) result = true; else if (activeUnits.Find(a => a.name == unit.name) == null) result = true; return result; } // 仅仅在关闭App调用,没有清理Unit,会留下垃圾 public void StopAll() { for (var i = 0; i < activeUnits.Count; i++) activeUnits[i].Dispose(); } private void OnLoadComplete(BundleDownloader activeUnit) { activeUnit.onComplete -= OnLoadComplete; activeUnits.Remove(activeUnit); // 下载成功 if (string.IsNullOrEmpty(activeUnit.error)) { if (activeUnit.failCount > 0) Debug.LogWarning(string.Format("Successfully downloaded {0} at attempt {1}!", activeUnit.name, activeUnit.failCount + 1)); if (onComplete != null) onComplete.Invoke(activeUnit); } // 重试次数超过上限 else { var fail = activeUnit.priority > 0 ? activeUnit.failCount >= maxRetryCount : activeUnit.failCount >= maxRetryLowPriority; if (fail) { Debug.LogError(string.Format("Failed to download {0} after all attempts expired!", activeUnit.name)); if (onComplete != null) onComplete.Invoke(activeUnit); } else // 重新尝试下载 { activeUnit.SetRetry(Time.realtimeSinceStartup + failWaitTime); activeUnit.priority = retryPriority; activeUnit.onWaitUp += OnUnitWaitUp; Debug.Log(string.Format("Trying to restart download for bundle {0}, attempt {1}!", activeUnit.name, activeUnit.failCount)); waitQueue.AddInQueue(activeUnit); } } ActivateWaitUnits(1); } private void ActivateWaitUnits(int count) { var active = 0; for (var i = waitQueue.list.Count - 1; i >= 0; i--) { var unit = waitQueue.list[i]; if (!CanActive(unit.priority)) break; if (unit.downloadTimer == null) { active++; waitQueue.list.RemoveAt(i); ActivateUnit(unit); if (active >= count) break; } } } private void OnUnitWaitUp(BundleDownloader unit) { unit.onWaitUp -= OnUnitWaitUp; ActivateWaitUnits(1); } private void ActivateUnit(BundleDownloader unit) { activeUnits.Add(unit); unit.onComplete += OnLoadComplete; unit.Load(); } private bool CanActive(int priority) { return GetMaxActive(priority) > activeUnits.Count; } private int GetMaxActive(int priority) { var result = -1; foreach (var rule in rules) if (rule.priority > priority) result = rule.maxActive; return result; } } public class DownloaderQueue { public readonly List list = new List(); public bool RemoveInQueue(string name) { var result = false; for (var i = 0; i < list.Count; i++) if (list[i].name == name) { result = true; list.RemoveAt(i); break; } return result; } public void AddInQueue(BundleDownloader actionUnit) { var add = true; for (var i = 0; i < list.Count; i++) if (list[i].priority >= actionUnit.priority) { add = false; list.Insert(i, actionUnit); break; } if (add) list.Add(actionUnit); } public SetPriorityResult TrySetPriority(string name, int priority, out BundleDownloader unit) { unit = null; var result = SetPriorityResult.NoItem; for (var i = 0; i < list.Count; i++) if (list[i].name == name) { unit = list[i]; if (priority > list[i].priority && i < list.Count - 1) { result = SetPriorityResult.Update; var actionUnit = list[i]; actionUnit.priority = priority; list.RemoveAt(i); AddInQueue(actionUnit); } else { result = SetPriorityResult.NoUpdate; } break; } return result; } } }