03-03-架构篇-Runtime加载系统架构

文章来源声明: 原文作者:SmalBox; 来源站点:掘金; 原文链接:https://juejin.cn/post/7689096640691634191; 本文基于上述来源整理/加工,觅优补充点评,仅供技术学习交流。版权归原作者所有。
觅优短评

逐层拆解运行时加载链路,把引用计数与 Provider 复用讲得清楚,适合已了解 Editor 端、需排查资源泄漏或热更加载问题的 Unity 开发者精读。

Runtime 端系统架构 -------------

篇章:03-架构篇 阅读时间:约 40 分钟 前置知识:了解 Editor 端系统架构


一、引言

本章将深入解析 YooAsset 的 Runtime 端系统架构。Runtime 端是 YooAsset 的运行层,在游戏运行时工作,负责资源的加载、管理、缓存和卸载。Runtime 端是玩家实际使用的部分,其架构设计的优劣直接影响游戏的性能和体验。

YooAsset 的 Runtime 端采用了 YooAssets → ResourcePackage → ResourceManager → Provider 的分层架构设计,职责清晰,分工明确。本章将详细解析 Runtime 端的各个核心模块。


二、YooAssets 静态入口

2.1 YooAssets 的概念

YooAssets 是 YooAsset 框架的全局静态入口,提供了统一的 API 让开发者与 YooAsset 进行交互。

YooAssets 的设计理念:

YooAssets 采用静态类设计,提供了全局唯一的入口点。开发者只需要调用 YooAssets 提供的静态方法就可以完成所有资源管理操作。

YooAssets 的特点:

  • 静态入口:全局唯一的静态类,便于调用
  • 统一 API:提供统一的 API 屏蔽内部实现
  • 多 Package 支持:支持同时管理多个 ResourcePackage

YooAssets 的 API 设计:

<span>public</span> <span>static</span> <span>class</span> <span>YooAssets</span>
{
    <span><span>public</span> <span>static</span> ResourcePackage <span>CreatePackage</span>(<span><span>string</span> packageName</span>)</span>;
    <span><span>public</span> <span>static</span> <span>bool</span> <span>InitPackage</span>(<span>ResourcePackage package</span>)</span>;
    <span><span>public</span> <span>static</span> <span>void</span> <span>DestroyPackage</span>(<span>ResourcePackage package</span>)</span>;
    <span><span>public</span> <span>static</span> ResourcePackage <span>GetPackage</span>(<span><span>string</span> packageName</span>)</span>;
    <span><span>public</span> <span>static</span> <span>void</span> <span>SetDefaultPackage</span>(<span>ResourcePackage package</span>)</span>;
    <span><span>public</span> <span>static</span> ResourcePackage <span>GetDefaultPackage</span>()</span>;
}

2.2 YooAssets 的初始化流程

YooAssets 的初始化流程如下:

  1. 创建 YooAssets 设置:配置 YooAssetsSettings,包括文件服务器 URL、加密密钥等
  2. 创建 ResourcePackage:通过 YooAssets.CreatePackage 创建 ResourcePackage
  3. 初始化 ResourcePackage:通过 YooAssets.InitPackage 初始化 ResourcePackage
  4. 设置默认 Package:通过 YooAssets.SetDefaultPackage 设置默认 Package
  5. 开始使用资源:通过 ResourcePackage 加载资源

YooAssetsSettings 配置详解:

YooAssetsSettings 是 YooAssets 的全局配置,包括:

  • 文件服务器 URL:用于资源下载的服务器地址
  • 加密密钥:用于资源解密的密钥
  • 备用 URL:备用文件服务器地址
  • 超时设置:下载超时、连接超时等
  • 重试策略:下载失败时的重试策略

初始化参数详解:

<span>public</span> <span>class</span> <span>InitializeParameters</span>
{
    <span>public</span> <span>string</span> LocationToLower;         <span>// 地址转小写</span>
    <span>public</span> <span>string</span> ScreenOrientation;        <span>// 屏幕方向</span>
    <span>public</span> AssetBundleDecryptionServices AssetBundleDecryptionServices; <span>// 解密服务</span>
    <span>public</span> IDeliveryQueryServices DeliveryQueryServices;     <span>// 远端查询服务</span>
    <span>public</span> IBuildinQueryServices BuildinQueryServices;        <span>// 内置查询服务</span>
    <span>public</span> IRemoteServices RemoteServices;                     <span>// 远端服务</span>
    <span>public</span> ICacheSystem CacheSystem;                          <span>// 缓存系统</span>
}

2.3 多 Package 架构

YooAssets 支持同时管理多个 ResourcePackage:

多 Package 的使用场景:

  • 游戏分包:将游戏分成多个 Package,便于热更新
  • DLC 支持:支持 DLC 的独立加载和更新
  • 模块化设计:支持游戏模块的独立加载

多 Package 的管理:

<span>// 创建多个 Package</span>
<span>var</span> packageA = YooAssets.CreatePackage(<span>"PackageA"</span>);
<span>var</span> packageB = YooAssets.CreatePackage(<span>"PackageB"</span>);

<span>// 初始化 Package</span>
YooAssets.InitPackage(packageA);
YooAssets.InitPackage(packageB);

<span>// 切换默认 Package</span>
YooAssets.SetDefaultPackage(packageA);

<span>// 加载资源</span>
packageA.LoadAssetAsync<GameObject>(<span>"prefab_a"</span>);
packageB.LoadAssetAsync<GameObject>(<span>"prefab_b"</span>);

多 Package 的实现:

YooAssets 内部维护了一个 Dictionary<string, ResourcePackage>,用于管理所有 Package。每个 Package 都有自己的 Manifest、FileSystem 和 ResourceManager。


三、ResourcePackage 资源包

3.1 ResourcePackage 的概念

ResourcePackage 是 YooAsset 资源管理的核心抽象,封装了一组相关资源的加载、卸载和管理。

ResourcePackage 的设计理念:

ResourcePackage 抽象了"一个完整的资源包"的概念,包括资源的加载、卸载、缓存、热更新等所有功能。

ResourcePackage 的特点:

  • 封装性:封装了资源管理的所有细节
  • 独立性:每个 Package 独立管理自己的资源
  • 可配置性:Package 的配置可以独立修改

3.2 ResourcePackage 的组成

ResourcePackage 由以下核心组件组成:

ResourcePackage 组成
├── PackageManifest:包清单,记录所有 Bundle 的信息
├── <span>FileSystem</span>:文件系统,负责跨平台文件访问
├── ResourceManager:资源管理器,负责资源加载和缓存
├── DownloadManager:下载管理器,负责资源下载
├── CacheSystem:缓存系统,负责缓存管理
└── Provider Pool:Provider 池,缓存已创建的 Provider

PackageManifest 详解:

PackageManifest 是 ResourcePackage 的清单文件,包含了所有 Bundle 的信息。ResourcePackage 通过 PackageManifest 来定位资源。

FileSystem 详解:

FileSystem 是 ResourcePackage 的文件系统,负责跨平台文件访问。YooAsset 通过 IFileSystem 抽象层屏蔽了不同平台的差异。

ResourceManager 详解:

ResourceManager 是 ResourcePackage 的资源管理器,负责资源加载、缓存和引用计数管理。

DownloadManager 详解:

DownloadManager 是 ResourcePackage 的下载管理器,负责资源下载和热更新。

CacheSystem 详解:

CacheSystem 是 ResourcePackage 的缓存系统,负责缓存管理和过期清理。

Provider Pool 详解:

Provider Pool 是 ResourcePackage 的 Provider 池,缓存已创建的 Provider,避免重复创建。

3.3 ResourcePackage 的生命周期

ResourcePackage 的生命周期如下:

  1. 创建:通过 YooAssets.CreatePackage 创建 Package
  2. 初始化:通过 YooAssets.InitPackage 初始化 Package
  3. 加载资源:通过 Package.LoadAssetAsync 加载资源
  4. 热更新:通过 UpdatePackageManifestAsync 进行热更新
  5. 销毁:通过 YooAssets.DestroyPackage 销毁 Package

创建详解:

Package 的创建只是创建一个内存对象,初始化必要的字段(如 PackageName)。实际的资源加载会在初始化阶段进行。

初始化详解:

Package 的初始化会进行以下操作:

  1. 加载 PackageManifest 文件
  2. 初始化 FileSystem
  3. 初始化 ResourceManager
  4. 初始化 DownloadManager
  5. 初始化 CacheSystem

加载资源详解:

Package 提供了多种资源加载方式:

  • LoadAssetAsync:异步加载资源
  • LoadSceneAsync:异步加载场景
  • LoadSubAssetsAsync:异步加载子资源
  • LoadAllAssetsAsync:异步加载所有资源

热更新详解:

Package 的热更新流程:

  1. 请求服务器 Manifest
  2. 与本地 Manifest 对比
  3. 下载差异 Bundle
  4. 切换到新版本

销毁详解:

Package 的销毁会进行以下操作:

  1. 卸载所有已加载资源
  2. 清空 Provider Pool
  3. 关闭 FileSystem
  4. 释放内存资源

四、ResourceManager 架构

4.1 ResourceManager 的概念

ResourceManager 是 YooAsset 的资源管理器,负责资源加载、缓存、引用计数等核心功能。

ResourceManager 的设计目标:

ResourceManager 旨在提供高效、灵活、可靠的资源管理能力。它是 Runtime 层的核心模块。

ResourceManager 的职责:

  • 资源加载:根据资源地址加载 AssetBundle 中的资源
  • 资源缓存:管理已加载资源的内存缓存
  • 引用计数:使用引用计数管理资源的引用关系
  • 依赖管理:管理资源之间的依赖关系
  • 资源释放:根据引用计数释放不再使用的资源

4.2 ResourceManager 的内部结构

ResourceManager 内部维护了多个重要的集合:

<span>public</span> <span>class</span> <span>ResourceManager</span>
{
    <span>// Provider 缓存</span>
    <span>private</span> Dictionary<<span>string</span>, ProviderBase> _providerCache;
    
    <span>// 加载中队列</span>
    <span>private</span> List<ProviderOperation> _loadingQueue;
    
    <span>// 加载完成列表</span>
    <span>private</span> List<ProviderOperation> _loadingCompleted;
    
    <span>// Bundle 加载器池</span>
    <span>private</span> Dictionary<<span>string</span>, BundleLoader> _bundleLoaders;
}

Provider 缓存详解:

Provider 缓存用于存储已创建的 Provider 对象。当请求加载某个资源时,ResourceManager 会先在 Provider 缓存中查找是否已存在对应的 Provider。

加载中队列详解:

加载中队列用于管理正在加载的 Provider。当多个请求同时加载同一资源时,ResourceManager 会复用同一个 Provider。

加载完成列表详解:

加载完成列表用于在每帧结束时检查加载完成的 Provider,并触发回调通知。

Bundle 加载器池详解:

Bundle 加载器池用于管理已加载的 AssetBundle。Bundle 加载器通过引用计数管理 Bundle 的生命周期。

4.3 资源加载流程

ResourceManager 的资源加载流程如下:

  1. 接收资源加载请求:通过 Package.LoadAssetAsync 发起请求
  2. 查找 Provider:在 Provider 缓存中查找是否已存在对应的 Provider
  3. 创建 Provider:如果不存在,创建新的 Provider
  4. 将请求加入 Provider:将加载请求加入 Provider 的等待队列
  5. Provider 执行加载:Provider 开始执行实际的加载操作
  6. 触发回调通知:加载完成后触发回调通知

查找 Provider 详解:

查找 Provider 时使用资源地址作为 Key。ResourceManager 会先在缓存中查找,如果存在则复用,否则创建新的 Provider。

创建 Provider 详解:

创建 Provider 时会根据资源类型创建对应类型的 Provider。常见的 Provider 类型包括:

  • AssetProvider:普通资源 Provider
  • SceneProvider:场景资源 Provider
  • ComponentProvider:组件资源 Provider
  • RawAssetProvider:原生资源 Provider

加载请求处理详解:

Provider 使用引用计数管理加载请求。每次 LoadAsync 调用会增加引用计数,每次 Release 调用会减少引用计数。当引用计数为 0 时,Provider 会自动销毁。

4.4 引用计数机制

引用计数是 YooAsset 资源管理的核心机制:

引用计数的概念:

引用计数记录了资源被引用的次数。每次资源被引用时,引用计数加 1;每次资源被释放时,引用计数减 1。当引用计数为 0 时,资源会被卸载。

引用计数的实现:

<span>public</span> <span>abstract</span> <span>class</span> <span>ProviderOperation</span>
{
    <span>public</span> <span>int</span> RefCount;  <span>// 引用计数</span>
    
    <span><span>public</span> <span>void</span> <span>Acquire</span>()</span>
    {
        RefCount++;
        <span>// 通知子依赖增加引用</span>
    }
    
    <span><span>public</span> <span>void</span> <span>Release</span>()</span>
    {
        RefCount--;
        <span>if</span> (RefCount <= <span>0</span>)
        {
            <span>// 销毁资源</span>
        }
    }
}

引用计数的应用:

引用计数用于管理资源生命周期。当多个对象引用同一资源时,资源不会因为某个对象释放而被错误卸载。

引用计数的优点:

  • 避免内存泄漏:确保资源在不再使用时及时释放
  • 避免重复加载:相同资源只加载一次
  • 自动管理:自动管理资源的生命周期

引用计数的注意事项:

开发者必须严格遵守"Acquire-Release 配对"的原则,避免出现引用计数泄漏或重复释放。


五、Provider 体系

5.1 Provider 的概念

Provider 是 YooAsset 的资源提供者,封装了资源加载的具体逻辑。

Provider 的设计理念:

Provider 模式是 YooAsset 资源加载的核心设计模式。每个 Provider 对应一个资源,封装了资源的加载逻辑、状态管理和回调通知。

Provider 的特点:

  • 单一职责:每个 Provider 只负责一个资源的加载
  • 状态管理:Provider 维护了资源的加载状态
  • 回调通知:Provider 支持加载完成、失败等的回调通知
  • 引用计数:Provider 内置引用计数管理

5.2 Provider 类型

YooAsset 提供了多种 Provider 类型:

Provider 类型用途特点
AssetProvider普通资源最常用的 Provider
SceneProvider场景资源异步加载场景
SubAssetsProvider子资源加载包含多个资源的 AssetBundle 中的特定子资源
RawAssetProvider原生资源加载原生格式(图片、文本等)
BundleProviderBundle 资源加载整个 AssetBundle

AssetProvider 详解:

AssetProvider 是最常用的 Provider,用于加载普通资源(如 Prefab、Texture、Material 等)。它会加载包含资源的 AssetBundle,然后从 AssetBundle 中加载目标资源。

SceneProvider 详解:

SceneProvider 用于加载场景资源。它使用 Unity 的 SceneManager 进行异步场景加载,并管理场景的激活和卸载。

SubAssetsProvider 详解:

SubAssetsProvider 用于加载子资源。例如,一个 Sprite Atlas 包含多个 Sprite,可以使用 SubAssetsProvider 加载特定的 Sprite。

RawAssetProvider 详解:

RawAssetProvider 用于加载原生格式资源。它不会将资源转换为 Unity 对象,而是返回原始的字节数据。

5.3 Provider 的状态机

Provider 内部维护了一个状态机:

Provider 状态机
├── <span>None</span>:初始状态
├── Loading:加载中
├── Checking:检查中
├── Success:加载成功
├── Failed:加载失败
└── Destroyed:已销毁

状态流转详解:

Provider 的状态流转如下:

  • None → Loading:开始加载
  • Loading → Checking:加载完成,进入检查阶段
  • Checking → Success:检查通过
  • Checking → Failed:检查失败
  • Success/Failed → Destroyed:资源被销毁

状态机的作用:

状态机确保 Provider 在正确的状态下执行正确的操作。例如,只有在 Loading 状态下才能处理加载进度更新,只有在 Success 状态下才能返回资源对象。


六、BundleLoader 体系

6.1 BundleLoader 的概念

BundleLoader 是 YooAsset 的 AssetBundle 加载器,负责加载和缓存 AssetBundle。

BundleLoader 的设计理念:

BundleLoader 抽象了 AssetBundle 的加载过程,提供了统一的接口。它管理 AssetBundle 的加载、缓存、引用计数和卸载。

BundleLoader 的特点:

  • 统一接口:提供统一的接口屏蔽不同平台的差异
  • 引用计数:使用引用计数管理 AssetBundle 的生命周期
  • 缓存机制:缓存已加载的 AssetBundle,避免重复加载
  • 异步加载:支持异步加载,避免阻塞主线程

6.2 BundleLoader 类型

YooAsset 提供了多种 BundleLoader 类型:

BundleLoader 类型
├── AssetBundleLoader:标准 AssetBundle 加载器
├── RawBundleLoader:原生 Bundle 加载器
├── DecryptedBundleLoader:解密 Bundle 加载器
└── EncryptedBundleLoader:加密 Bundle 加载器

AssetBundleLoader 详解:

AssetBundleLoader 是标准的 AssetBundle 加载器,使用 Unity 的 AssetBundle.LoadFromFile 或 AssetBundle.LoadFromMemory 加载 AssetBundle。

DecryptedBundleLoader 详解:

DecryptedBundleLoader 是解密 Bundle 加载器,用于加载已解密的 AssetBundle。它会先解密数据,然后从解密后的数据加载 AssetBundle。

6.3 BundleLoader 的工作流程

BundleLoader 的工作流程如下:

  1. 接收加载请求:接收 AssetBundle 加载请求
  2. 检查缓存:检查是否已加载该 AssetBundle
  3. 执行加载:如果没有缓存,执行实际的加载操作
  4. 缓存 AssetBundle:将加载完成的 AssetBundle 缓存
  5. 管理引用计数:管理 AssetBundle 的引用计数
  6. 释放 AssetBundle:引用计数为 0 时释放 AssetBundle

检查缓存详解:

BundleLoader 维护了一个 AssetBundle 缓存字典。检查缓存时使用 Bundle 的路径或名称作为 Key。

执行加载详解:

执行加载时使用 Unity 的 AssetBundle 加载 API。具体的加载方式取决于 Bundle 的类型和位置。

引用计数管理详解:

BundleLoader 使用引用计数管理 AssetBundle 的生命周期。每个资源依赖一个 AssetBundle,资源的引用计数变化会影响 AssetBundle 的引用计数。


七、总结

本章深入解析了 YooAsset Runtime 端的系统架构,包括:

  • YooAssets 静态入口:全局统一的 API 入口
  • ResourcePackage 资源包:封装了一组相关资源的加载和管理
  • ResourceManager 资源管理器:负责资源加载、缓存和引用计数
  • Provider 资源提供者:封装了资源加载的具体逻辑
  • BundleLoader 体系:负责 AssetBundle 的加载和缓存

Runtime 端架构是 YooAsset 框架的核心,理解 Runtime 端架构是掌握 YooAsset 资源管理机制的关键。接下来的章节将分别深入解析文件系统、异步操作系统和资源下载器等核心组件。


上一篇:Editor 端系统架构下一篇:文件系统架构(IFileSystem)