YooAsset源码阅读-Downloader

继续 YooAsset 的 Downloader ,本文将详细介绍如何创建下载器相关代码

CreateResourceDownloaderByAll

关键类

  • PlayModeImpl.cs
  • ResourceDownloaderOperation.cs
  • DownloaderOperation.cs
  • BundleInfo.cs

CreateResourceDownloaderByAll 方法用于创建下载所有需要更新资源的下载器。

关键源代码
// PlayModeImpl.cs Line 110-115
ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByAll(int downloadingMaxNumber, int failedTryAgain)
{List<BundleInfo> downloadList = GetDownloadListByAll(ActiveManifest);var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain);return operation;
}// PlayModeImpl.cs Line 245-264
public List<BundleInfo> GetDownloadListByAll(PackageManifest manifest)
{if (manifest == null)return new List<BundleInfo>();List<BundleInfo> result = new List<BundleInfo>(1000);foreach (var packageBundle in manifest.BundleList){var fileSystem = GetBelongFileSystem(packageBundle);if (fileSystem == null)continue;if (fileSystem.NeedDownload(packageBundle)){var bundleInfo = new BundleInfo(fileSystem, packageBundle);result.Add(bundleInfo);}}return result;
}

流程图

调用
遍历
每个Bundle
检查
所有Bundle处理完
CreateResourceDownloaderByAll
GetDownloadListByAll
manifest.BundleList
GetBelongFileSystem
fileSystem.NeedDownload
创建BundleInfo
跳过该Bundle
添加到下载列表
继续下一个Bundle
返回下载列表
创建ResourceDownloaderOperation
返回下载器

CreateResourceDownloaderByTags

关键类

  • PlayModeImpl.cs
  • ResourceDownloaderOperation.cs
  • PackageBundle.cs

CreateResourceDownloaderByTags 方法用于创建下载指定标签资源的下载器,支持DLC(可下载内容)场景。

关键源代码
// PlayModeImpl.cs Line 116-121
ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByTags(string[] tags, int downloadingMaxNumber, int failedTryAgain)
{List<BundleInfo> downloadList = GetDownloadListByTags(ActiveManifest, tags);var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain);return operation;
}// PlayModeImpl.cs Line 265-297
public List<BundleInfo> GetDownloadListByTags(PackageManifest manifest, string[] tags)
{if (manifest == null)return new List<BundleInfo>();List<BundleInfo> result = new List<BundleInfo>(1000);foreach (var packageBundle in manifest.BundleList){var fileSystem = GetBelongFileSystem(packageBundle);if (fileSystem == null)continue;if (fileSystem.NeedDownload(packageBundle)){// 如果未带任何标记,则统一下载if (packageBundle.HasAnyTags() == false){var bundleInfo = new BundleInfo(fileSystem, packageBundle);result.Add(bundleInfo);}else{// 查询DLC资源if (packageBundle.HasTag(tags)){var bundleInfo = new BundleInfo(fileSystem, packageBundle);result.Add(bundleInfo);}}}}return result;
}

流程图

传入tags数组
遍历
每个Bundle
检查
HasAnyTags = false
HasAnyTags = true
匹配
不匹配
处理完成
CreateResourceDownloaderByTags
GetDownloadListByTags
manifest.BundleList
GetBelongFileSystem
fileSystem.NeedDownload
跳过该Bundle
检查Bundle标签
无标签Bundle
统一下载
检查HasTag匹配
DLC资源
添加到下载列表
跳过该Bundle
创建BundleInfo
添加到结果列表
继续下一个Bundle
返回下载列表
创建ResourceDownloaderOperation

CreateResourceDownloaderByPaths

关键类

  • PlayModeImpl.cs
  • ResourceDownloaderOperation.cs
  • AssetInfo.cs
  • PackageManifest.cs

CreateResourceDownloaderByPaths 方法用于创建下载指定资源路径及其依赖的下载器,支持递归下载选项。

关键源代码
// PlayModeImpl.cs Line 122-127
ResourceDownloaderOperation IPlayMode.CreateResourceDownloaderByPaths(AssetInfo[] assetInfos, bool recursiveDownload, int downloadingMaxNumber, int failedTryAgain)
{List<BundleInfo> downloadList = GetDownloadListByPaths(ActiveManifest, assetInfos, recursiveDownload);var operation = new ResourceDownloaderOperation(PackageName, downloadList, downloadingMaxNumber, failedTryAgain);return operation;
}// PlayModeImpl.cs Line 298-359 (核心逻辑)
public List<BundleInfo> GetDownloadListByPaths(PackageManifest manifest, AssetInfo[] assetInfos, bool recursiveDownload)
{if (manifest == null)return new List<BundleInfo>();// 获取资源对象的资源包和所有依赖资源包List<PackageBundle> checkList = new List<PackageBundle>();foreach (var assetInfo in assetInfos){if (assetInfo.IsInvalid){YooLogger.Warning(assetInfo.Error);continue;}// 获取主资源包PackageBundle mainBundle = manifest.GetMainPackageBundle(assetInfo.Asset);if (checkList.Contains(mainBundle) == false)checkList.Add(mainBundle);// 获取依赖资源包List<PackageBundle> mainDependBundles = manifest.GetAssetAllDependencies(assetInfo.Asset);foreach (var dependBundle in mainDependBundles){if (checkList.Contains(dependBundle) == false)checkList.Add(dependBundle);}// 递归下载主资源包内所有资源对象依赖的资源包if (recursiveDownload){foreach (var otherMainAsset in mainBundle.IncludeMainAssets){var otherMainBundle = manifest.GetMainPackageBundle(otherMainAsset.BundleID);if (checkList.Contains(otherMainBundle) == false)checkList.Add(otherMainBundle);List<PackageBundle> otherDependBundles = manifest.GetAssetAllDependencies(otherMainAsset);foreach (var dependBundle in otherDependBundles){if (checkList.Contains(dependBundle) == false)checkList.Add(dependBundle);}}}}// 筛选需要下载的资源包List<BundleInfo> result = new List<BundleInfo>(1000);foreach (var packageBundle in checkList){var fileSystem = GetBelongFileSystem(packageBundle);if (fileSystem == null)continue;if (fileSystem.NeedDownload(packageBundle)){var bundleInfo = new BundleInfo(fileSystem, packageBundle);result.Add(bundleInfo);}}return result;
}

流程图

AssetInfo数组
遍历
每个AssetInfo
所有AssetInfo处理完
每个PackageBundle
处理完成
CreateResourceDownloaderByPaths
GetDownloadListByPaths
AssetInfo数组
AssetInfo.IsInvalid?
记录警告
跳过该资源
获取主资源包
添加到checkList
获取依赖资源包
添加依赖到checkList
recursiveDownload?
处理下一个AssetInfo
递归处理主资源包内
所有资源的依赖
获取其他主资源包
获取其他依赖资源包
添加到checkList
遍历checkList
GetBelongFileSystem
fileSystem.NeedDownload?
创建BundleInfo
添加到结果
跳过该Bundle
继续下一个Bundle
返回下载列表
创建ResourceDownloaderOperation

下载器核心机制

ResourceDownloaderOperation 继承关系

继承
继承
继承
继承
AsyncOperationBase
异步操作基类
DownloaderOperation
下载器操作基类
ResourceDownloaderOperation
资源下载器
ResourceUnpackerOperation
资源解压器
ResourceImporterOperation
资源导入器

下载器状态机

DownloaderOperation 使用状态机管理下载流程:

InternalStart
验证下载列表
无效
有效
创建下载器
检查下载结果
DownloaderOperation.BeginDownload
外部使用
Check
下载列表有效?
Done
Status=Failed
Loading
动态管理下载器池
有失败的下载?
Done
Status=Failed
所有下载完成?
继续下载
Done
Status=Succeed

下载器池管理机制

DownloaderOperation 采用动态下载器池来优化下载性能,主要特性:

  1. 最大并发限制:通过 MAX_LOADER_COUNT = 64_downloadingMaxNumber 控制同时下载的文件数量
  2. 失败重试机制:支持 _failedTryAgain 参数控制失败重试次数
  3. 动态调度:当有下载器完成时,自动从待下载列表中选择新的文件开始下载
  4. 暂停/恢复:支持 PauseDownload()ResumeDownload() 控制下载状态
  5. 进度回调:提供详细的下载进度、错误、开始等事件回调

需要注意 DownloaderOperation.InternalUpdate 方法中的关键逻辑:

// 动态创建新的下载器到最大数量限制
// 注意:如果期间有下载失败的文件,暂停动态创建下载器
if (_bundleInfoList.Count > 0 && _failedList.Count == 0)
{if (_isPause)return;if (_downloaders.Count < _downloadingMaxNumber){int index = _bundleInfoList.Count - 1;var bundleInfo = _bundleInfoList[index];var downloader = bundleInfo.CreateDownloader(_failedTryAgain);downloader.StartOperation();this.AddChildOperation(downloader);_downloaders.Add(downloader);_bundleInfoList.RemoveAt(index);}
}

从用户调用到UnityWebRequest的完整调用链

看完上面的分析,我觉得还需要把整个调用链梳理清楚,这样更容易理解整个下载流程。

调用链概述

用户调用下载器到最终发起UnityWebRequest的完整调用链是这样的:

用户代码 → downloader.BeginDownload() → OperationSystem.Update() → DownloaderOperation.InternalUpdate() 
→ BundleInfo.CreateDownloader() → DefaultCacheFileSystem.DownloadFileAsync() → DownloadPackageBundleOperation
→ DownloadCenter.DownloadFileAsync() → UnityDownloadFileOperation → UnityWebFileRequestOperation → UnityWebRequest

详细调用链分析

1. 用户入口点
// 用户代码
var downloader = package.CreateResourceDownloaderByAll(10, 3);
downloader.BeginDownload(); // 开始下载
2. BeginDownload → InternalUpdate
// DownloaderOperation.cs
public void BeginDownload()
{// 开始下载会调用 OperationSystem.StartOperationOperationSystem.StartOperation(PackageName, this);
}// OperationSystem每帧会调用
internal override void InternalUpdate()
{// 在Loading状态时会动态创建下载器if (_bundleInfoList.Count > 0 && _failedList.Count == 0){var bundleInfo = _bundleInfoList[index];var downloader = bundleInfo.CreateDownloader(_failedTryAgain); // 关键调用downloader.StartOperation();this.AddChildOperation(downloader);}
}
3. BundleInfo.CreateDownloader
// BundleInfo.cs Line 39-44
public FSDownloadFileOperation CreateDownloader(int failedTryAgain)
{DownloadFileOptions options = new DownloadFileOptions(failedTryAgain);options.ImportFilePath = _importFilePath;return _fileSystem.DownloadFileAsync(Bundle, options); // 委托给FileSystem
}
4. DefaultCacheFileSystem.DownloadFileAsync
// DefaultCacheFileSystem.cs Line 172-191
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{// 获取下载地址string mainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);string fallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);options.SetURL(mainURL, fallbackURL);// 创建具体的下载操作var downloader = new DownloadPackageBundleOperation(this, bundle, options);return downloader;
}
5. DownloadPackageBundleOperation
// DownloadPackageBundleOperation.cs Line 58-72
if (_steps == ESteps.CreateRequest)
{string url = GetRequestURL();// 委托给DownloadCenter创建Unity下载器_unityDownloadFileOp = _fileSystem.DownloadCenter.DownloadFileAsync(Bundle, url);_steps = ESteps.CheckRequest;
}
6. DownloadCenter → UnityDownloadFileOperation
// DownloadCenterOperation.cs
public UnityDownloadFileOperation DownloadFileAsync(PackageBundle bundle, string url)
{// 根据断点续传等条件创建不同的下载器if (bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize){return new ResumeDownloadOperation(_fileSystem, bundle, url);}else{return new NormalDownloadOperation(_fileSystem, bundle, url);}
}
7. UnityDownloadFileOperation → UnityWebRequest
// UnityWebFileRequestOperation.cs Line 72-81
private void CreateWebRequest()
{DownloadHandlerFile handler = new DownloadHandlerFile(_fileSavePath);handler.removeFileOnAbort = true;// 最终创建UnityWebRequest_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);_webRequest.timeout = _timeout;_webRequest.downloadHandler = handler;_webRequest.disposeDownloadHandlerOnDispose = true;// 发起网络请求_requestOperation = _webRequest.SendWebRequest();
}

完整调用链流程图

downloader.BeginDownload
OperationSystem.Update
bundleInfo.CreateDownloader
_fileSystem.DownloadFileAsync
new DownloadPackageBundleOperation
DownloadCenter.DownloadFileAsync
创建具体下载器
CreateWebRequest
_webRequest.SendWebRequest
用户代码
DownloaderOperation
InternalUpdate循环
BundleInfo
DefaultCacheFileSystem
DownloadPackageBundleOperation
DownloadCenterOperation
UnityDownloadFileOperation
UnityWebFileRequestOperation
UnityWebRequest
关键步骤说明
用户调用BeginDownload启动下载
OperationSystem每帧Update驱动
BundleInfo适配不同FileSystem
FileSystem创建具体下载操作
DownloadCenter管理并发控制
UnityWebRequest执行实际网络请求

关键调用点解析

1. 责任分离设计
  • DownloaderOperation:管理下载器池和并发控制
  • BundleInfo:适配器,统一不同FileSystem的接口
  • DefaultCacheFileSystem:处理URL获取和参数配置
  • DownloadPackageBundleOperation:状态机管理,重试逻辑
  • DownloadCenter:并发限制和下载器复用
  • UnityDownloadFileOperation:Unity网络请求的具体实现
2. 异步调用链

整个调用链是异步的,通过 OperationSystem 的 Update 循环驱动:

// 主要更新循环
YooAsset.Update() → OperationSystem.Update() → AsyncOperationBase.Update()InternalUpdate()
3. 错误处理和重试

在调用链的每一层都有相应的错误处理:

  • DownloaderOperation:失败隔离策略
  • DownloadPackageBundleOperation:重试逻辑和超时处理
  • UnityWebRequest:网络层错误处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92025.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92025.shtml
英文地址,请注明出处:http://en.pswp.cn/web/92025.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

豆包新模型与 PromptPilot 实操体验测评,AI 辅助创作的新范式探索

摘要&#xff1a;在 AI 技术飞速发展的当下&#xff0c;各类大模型及辅助工具层出不穷&#xff0c;为开发者和创作者带来了全新的体验。2025 年 7 月 30 日厦门站的火山方舟线下 Meetup&#xff0c;为我们提供了近距离接触豆包新模型与 PromptPilot 的机会。本次重点体验了实验…

深入探讨AI在测试领域的三大核心应用:自动化测试框架、智能缺陷检测和A/B测试优化,并通过代码示例、流程图和图表详细解析其实现原理和应用场景。

引言随着人工智能技术的飞速发展&#xff0c;软件测试领域正在经历一场深刻的变革。AI技术不仅提高了测试效率&#xff0c;还增强了测试的准确性和覆盖范围。本文将深入探讨AI在测试领域的三大核心应用&#xff1a;自动化测试框架、智能缺陷检测和A/B测试优化&#xff0c;并通过…

音视频学习笔记

0.vs应用其他库配置1基础 1.1视频基础 音视频录制原理音视频播放原理图像表示rgb图像表示yuvhttps://blog.51cto.com/u_7335580/2059670 https://blog.51cto.com/cto521/1944224 https://blog.csdn.net/mandagod/article/details/78605586?locationNum7&fps1 视频主要概念…

LLM隐藏层状态: outputs.hidden_states 是 MLP Residual 还是 Layer Norm

outputs.hidden_states 是 MLP Residual 还是 Layer Norm outputs.hidden_states 既不是单纯的 MLP Residual,也不是单纯的 Layer Norm,而是每一层所有组件(包括 Layer Norm、注意力、MLP、残差连接等)处理后的最终隐藏状态。具体需结合 Transformer 层的结构理解: 1. T…

XML 用途

XML 用途 引言 XML&#xff08;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言。自1998年推出以来&#xff0c;XML因其灵活性和可扩展性&#xff0c;在众多领域得到了广泛应用。本文将详细介绍XML的用途&#xff0c;帮助读者全面了解这一重要技术。 一、数据存…

亚马逊撤离Google购物广告:重构流量生态的战略博弈

战略突变&#xff1a;从渐进收缩到全面退潮的背后逻辑亚马逊在2025年7月突然全面停止Google Shopping广告投放&#xff0c;这场看似 abrupt 的决策实则经历了一年多的战略铺垫&#xff0c;从2024年Q1开始的预算削减&#xff0c;到2025年Q2美国市场支出减半&#xff0c;直至核心…

【QT】常⽤控件详解(三)常用按钮控件PushButton RadioButton CheckButton Tool Button

文章目录前言一、PushButton1.1 QAbstractButton1.2 添加图标的按钮1.3 给按钮添加快捷键1.4 代码⽰例:按钮的重复触发二、 RadioButtion2.1简介2.2 几个槽函数 click,press,release, toggled 的区别2.2 模拟分组点餐三、 CheckBox四、Tool Button&#x1f6a9;总结前言 一、P…

数据结构:反转链表(reverse the linked list)

目录 通过交换元素值实现反转&#xff08;reverse by swapping elements&#xff09; 滑动指针&#xff08;sliding pointers&#xff09; 使用滑动指针反转链表&#xff08;Reversing a Linked List using Sliding Pointers&#xff09; 对比分析 如何用递归&#xff08;R…

【C#】基于SharpCompress实现压缩包解压功能

1.SharpCompress安装 在vs的nuget下搜索安装SharpCompress&#xff0c;如图所示2.解压缩包功能实现 /// <summary> /// 解压压缩包 /// </summary> /// <param name"filePath">压缩包文件路径</param> /// <param name"directoryPat…

mybatis连接PGSQL中对于json和jsonb的处理方法

pgsql数据库表字段设置了jsonb格式&#xff1b;在java的实体里使用String或者对象转换会一直提示一个错误&#xff1a; Caused by: org.postgresql.util.PSQLException: ERROR: column “xx” is of type jsonb but expression is of type character varying 需要加一个转换方法…

Spring AI Alibaba Graph 深度解析:原理、架构与应用实践

1. 引言概述 1.1 什么是 Spring AI Alibaba Graph Spring AI Alibaba Graph 是阿里云团队基于 Spring AI 生态开发的一个强大的工作流编排框架&#xff0c;专门用于构建复杂的 AI 应用。它采用声明式编程模型&#xff0c;通过图结构来定义和管理 AI 工作流&#xff0c;让开发…

C++少儿编程(二十一)—软件执行流程

让我们将以下程序视为用C编写的示例程序。步骤1&#xff1a;预处理器将源代码转换为扩展代码。当您运行程序时&#xff0c;源代码首先被发送到称为预处理器的工具。预处理器主要做两件事&#xff1a;它会从程序中删除注释。它扩展了预处理器指令&#xff0c;如宏或文件包含。它…

精通Webpack搭建Vue2.0项目脚手架指南

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;在Web应用程序开发中&#xff0c;Vue 2.0因其虚拟DOM、单文件组件、增强的生命周期钩子和Vuex及Vue Router状态管理与路由解决方案&#xff0c;成为了提高开发效率和代码组织性的关键。Webpack作为必不可少的模…

无偿分享120套开源数据可视化大屏H5模板

数据可视化跨越了语言、技术和专业的边界&#xff0c;是能够推动实现跨界沟通&#xff0c;实现国际间跨行业的创新的工具。正如画家用颜料表达自我&#xff0c;作者用文字讲述故事&#xff0c;而统计人员用数字沟通 ...... 同样&#xff0c;数据可视化的核心还是传达信息。而设…

Qt按键响应

信号与槽机制是一个非常强大的事件通信机制&#xff0c;是 Qt 最核心的机制之一&#xff0c;初学者掌握它之后&#xff0c;几乎可以做任何交互操作。信号&#xff08;Signal&#xff09; 是一种“事件”或“通知”&#xff0c;比如按钮被点击、文本改变、窗口关闭等。 槽&#…

【Git】常见命令整理

Git分区与操作关系&#xff1a;Working Directory&#xff08;工作区&#xff0c;对于本地的编辑和修改在此进行&#xff09;->Staging Area&#xff08;暂存区/Index&#xff0c;在工作区进行git add操作后的位置&#xff09;->Git Repository&#xff08;本地仓库&…

Linux-Shell脚本基础用法

1.变量定义变量命名规则&#xff1a;可以包含字母&#xff0c;数字&#xff0c;下划线&#xff0c;首字母不能用数字开头&#xff0c;中间不能又空格&#xff1b;为变量赋值等号之间不能为空格&#xff1b;变量命名不能使用标点符号&#xff0c;不能使用bash的关键字&#xff1…

JS中的Map和WeakMap区别和联系

JavaScript 中 Map 与 WeakMap 的区别、联系及示例核心区别特性MapWeakMap键的类型允许任意类型的键&#xff08;对象、原始值&#xff09;键必须是对象&#xff08;非原始值&#xff09;垃圾回收强引用键 → 阻止垃圾回收弱引用键 → 不影响垃圾回收可遍历性支持遍历&#xff…

Linux 环境 libpq加载异常导致psql 连接 PostgreSQL 库失败失败案例

文章目录局点现象定位结论局点环境补充知识点如下库文件加载顺序关键事实&#xff1a;您系统中的证据&#xff1a;优先级对比表&#xff1a;解决方案强化&#xff1a;最终检查&#xff1a;本局点解决方法局点现象 数据库 mdm 升级失败检查日志, 发现是由于 psql 连接数据库报错…

C# XML 文件

在 C# 中处理 XML 文件是非常常见的操作&#xff0c;可以使用System.Xml命名空间中的类来实现。以下是一些常用的 XML 操作示例&#xff1a; 手册链接&#xff1a; System.Xml 命名空间 XmlDocument 创建一个xml数据格式的文档 XmlDocument xml new XmlDocument(); Xml…