UnityWebRequest多个下载器并行下载AB包

发布时间:2026/10/27 19:04:15
UnityWebRequest多个下载器并行下载AB包 多个UnityWebRequest并行比单个串行更快程序更复杂。必须弄一个池管理UnityWebRequest还需要一个AB包状态池记录每个包是没下载、下载中、下载完成、下载失败。逻辑是输入一个AB包信息列表对UnityWebRequest池循环如果当前是空闲对AB包状态池循环如果还没人下载则给此下载器下载并且break循环。我们可以用Update轮询也可以让下载器下载完通知管理器再分配任务。在Update里做先判断AB包是否全部下载完了就对AB包状态池循环。那么就先对AB包状态池循环我们发现如果有AB包没分配下载就要分配下载如果都分配下载了没下载完就等待如果都是下载完或失败则返回结果发现需要分配下载是最先知道的全部处理完毕要最后才知道然后我们要做成一个模块化的功能不依赖外部就输入一个AB包信息列表最后执行回调把结果结构体输入结果包括结果枚举还有下载失败的AB包清单。看了一下API创建UnityWebRequest就要输入url了那么UnityWebRequest个数和AB包数量一样下载器开始下载时UnityWebRequest是当场创建的。那么我们维护的是下载器池。关于下载过程之前是从开始到结束全部在一个IEnumerator函数里using (UnityWebRequest request UnityWebRequest.Get(url)) { request.downloadHandler new DownloadHandlerFile(savePath); var op request.SendWebRequest(); while (!op.isDone) { showBytes?.Invoke(request.downloadedBytes); yield return null; } if (request.result UnityWebRequest.Result.Success) { result.ok true; } else现在我们改成开始Update架构在Update里查看下载器的进度那么这个UnityWebRequest不能封在using里否则cpu下载完前都不会离开这个using。我们在Update里写两个函数一个是检查是否同时有未下载文件和空闲下载器有则分配下载一个是检查每个下载器的状态如果下载完毕就把UnityWebRequest DisposeUnityWebRequestAsyncOperationnull。然后这样有问题就是我们想每次的下载分配和下载完成都打印二者一一对应但是我们在检查下载器状态里打印下载完毕分配下载时通过UnityWebRequestAsyncOperation.isDone判断下载器空闲就有下载器没轮到检查状态就又分配下载了导致二者对不上。那么我们需要下载器有一个唯一的入口管理器检查时发现下载完了设一个标志isDone分配时下载器要么从没下载过要么isDone才能再分配保证分配、完成打印对上。就是说不是UnityWebRequest下载完了代表下载器就下载完了还需要管理器检查打标记。显示下载的字节数如果要每帧一更新下载字节数逻辑有点复杂因为在下载过程中管理器需要把已下载完成的每个下载器当前请求已下载的加起来有文件下载完成需要把这些字节数加入管理器的已下载字节数。还有一种比较简单的逻辑是一个下载器在下载完成时更新字节数UI上看起来更新会不频繁一些遇到大文件半天不更新。但是这样要处理的累加逻辑、要把字节数翻译成M、k为单位的文本工作量会少很多。然后最终还要保证下载的字节数加起来和预期的相同。代码using MyFW; using System; using System.Collections.Generic; using UnityEngine; namespace MyFarm { public class ParallelDownloadMgr: MonoSingletonDontDestroyParallelDownloadMgr { ListDownloadAgent agents; ListABDownloadState ABStates; public Actionstring debugAction; [SerializeField] /// summary /// uint最多表示4G可能不够 /// /summary ulong downloadedByteNum; public string resFolderUrl; public string localFolder; /// summary /// 在一次轮询中已经为某下载器分配了任务 /// /summary bool assigned; bool hasLeft; DownloadUIInfo[]UIInfos; public void Init(int downloaderNum) { agentsnew(downloaderNum); for (int i 0; i downloaderNum; i) { agents.Add(new DownloadAgent((uint)i)); } } public void AssignABList(ListABInfo list) { if(listnull|| list.Count 0) { debugAction?.Invoke(传入的AB列表为空); return; } ABStates new(list.Count); for (int i 0; i list.Count; i) { ABDownloadState state new(list[i]); ABStates.Add(state); } hasLeft true; } private void Update() { if (ABStates ! null ABStates.Count 0hasLeft) { CheckAssign(); foreach (var agent in agents) { agent.Update(); } } } void CheckAssign() { hasLeft false; assigned false; foreach (var abState in ABStates) { if (abState.downloadState DownloadState.NotBegin) { for(int i 0;iagents.Count;i) { var agent agents[i]; if (agent.IsIdle()) { debugAction?.Invoke($为{i}号下载器分配{abState.ABInfo.name}); agent.Download(abState); assigned true; break; } } } if (abState.downloadState ! DownloadState.OK) { hasLeft true; } if (assigned) { break; } } if (!hasLeft) { debugAction?.Invoke(下载完毕); ABStates.Clear(); } } public void AddDownloadedByteNum(ulong val) { downloadedByteNum val; } public DownloadUIInfo[] GetInfo() { if (agents null || agents.Count 0) { return null; } if (UIInfos null) { UIInfos new DownloadUIInfo[agents.Count]; } for (int i 0; i agents.Count; i) { UIInfos[i].Id (uint)i; UIInfos[i].progress agents[i].DownloadedProgress(); UIInfos[i].downloadedByteNum agents[i].DownloadedByteNum(); } return UIInfos; } } }using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using UnityEngine.Networking; namespace MyFarm { public class DownloadAgent { uint Id; ABDownloadState _state; UnityWebRequestAsyncOperation op; UnityWebRequest request; public DownloadAgent(uint Id) { this.Id Id; } public void Download(ABDownloadState state) { if (request ! null) { request.Dispose(); opnull; } _state state; _state.downloadState DownloadState.Downloading; string fileName state.ABInfo.name; string url ${ParallelDownloadMgr.Single.resFolderUrl}{fileName}; string savePath Path.Combine(Application.persistentDataPath, ParallelDownloadMgr.Single.localFolder, fileName); AsyncReturnbyte[] ret new AsyncReturnbyte[](); request UnityWebRequest.Get(url); request.downloadHandler new DownloadHandlerFile(savePath); op request.SendWebRequest(); } public void Update() { if (op ! null op.isDone) { Debug.Log($color#FFA500{Id}下载完了{request.uri}/color); ParallelDownloadMgr.Single.AddDownloadedByteNum(request.downloadedBytes); request.Dispose(); _state.downloadState DownloadState.OK; op null; } } public bool IsIdle() { return _state null||(opnull); } } }using System.Collections; using System.Collections.Generic; using UnityEngine; namespace MyFarm { public class ABDownloadState { ABInfo _ABInfo; public ABInfo ABInfo_ABInfo; public DownloadState downloadState; public ABDownloadState(ABInfo info) { _ABInfo info; } } }using System.Collections; using System.Collections.Generic; using UnityEngine; namespace MyFarm { public enum DownloadState:ushort { NotBegin, Downloading, OK, Fail, } }检查器进度条更新并不灵敏。using System.Collections; using System.Collections.Generic; using UnityEditor; using UnityEngine; namespace MyFarm { [CustomEditor(typeof(ParallelDownloadMgr))] public class ParalellDownloadEditor:Editor { ParallelDownloadMgr _Mgr; DownloadUIInfo[] UIInfos; private void OnEnable() { _Mgr (ParallelDownloadMgr)target; } public override void OnInspectorGUI() { base.OnInspectorGUI(); UIInfos _Mgr.GetInfo(); if (UIInfos ! null) { foreach (var info in UIInfos) { EditorGUI.ProgressBar(EditorGUILayout.GetControlRect(false, 20), info.progress, $下载器{info.Id}已下载{info.downloadedByteNum}byte); } } } } }namespace MyFarm { public struct DownloadUIInfo { public uint Id; public float progress; public ulong downloadedByteNum; } }

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询