源码-资源提供者(Provider 体系)
篇章:05-源码深度-运行时核心模块
阅读时间:约 30 分钟
前置知识:了解寻址系统
一、引言
本章将深入解析 YooAsset 资源提供者(Provider 体系)的源码实现。Provider 是 YooAsset 资源加载的核心,封装了不同类型资源的加载逻辑。YooAsset 提供了多种 Provider 类型,包括 AssetProvider、SceneProvider、ComponentProvider 等。
Provider 体系的设计目标是提供灵活、可扩展的资源加载能力。本章将从源码层面深入解析 Provider 体系的设计和实现。
二、Provider 概述
2.1 Provider 的概念
Provider 是 YooAsset 中负责加载单个资源的核心组件:
Provider 的定义:
Provider 是一个抽象类,封装了资源加载的所有逻辑。每个 Provider 对应一个资源的加载。
Provider 的特点:
- 单一职责:每个 Provider 只负责一个资源
- 状态管理:Provider 维护资源加载的状态
- 引用计数:Provider 内置引用计数管理
- 异步加载:Provider 支持异步加载资源
Provider 的作用:
- 接收资源加载请求
- 创建和加载 Bundle
- 从 Bundle 中加载资源
- 管理资源生命周期
2.2 Provider 状态机
Provider 内部维护了一个状态机:
Provider 状态机 ├── None:初始状态 ├── Loading:加载中 ├── Loaded:已加载 ├── Releasing:释放中 └── Destroyed:已销毁None 详解:
None 是 Provider 的初始状态,Provider 创建后即进入 None 状态。
Loading 详解:
Loading 是 Provider 的加载中状态。Provide 方法被调用后进入 Loading 状态。
Loaded 详解:
Loaded 是 Provider 的已加载状态。资源加载完成后进入 Loaded 状态。
Releasing 详解:
Releasing 是 Provider 的释放中状态。UnProvide 方法被调用后进入 Releasing 状态。
Destroyed 详解:
Destroyed 是 Provider 的已销毁状态。资源销毁后进入 Destroyed 状态。
2.3 Provider 状态详解
public enum EProviderStatus { None = 0, // 初始状态 Loading = 1, // 加载中 Loaded = 2, // 已加载 Failed = 3, // 加载失败 Destroyed = 4, // 已销毁 }状态转换详解:
- None → Loading:调用 Provide 方法
- Loading → Loaded:资源加载成功
- Loading → Failed:资源加载失败
- Loaded/Failed → Destroyed:调用 Destroy 方法
2.4 Provider 创建
public abstract class ProviderBase { public string Location { get; } public EProviderStatus Status { get; protected set; } public float Progress { get; protected set; } public object AssetObject { get; protected set; } public int RefCount { get; protected set; } public abstract void Provide(); public abstract void UnProvide(); public abstract void Update(); }Location 详解:
Location 是 Provider 加载的资源地址。
Status 详解:
Status 是 Provider 的当前状态。
Progress 详解:
Progress 是 Provider 的加载进度。
AssetObject 详解:
AssetObject 是 Provider 加载的资源对象。
RefCount 详解:
RefCount 是 Provider 的引用计数。
三、Provider 创建/获取
3.1 Provider 的创建
Provider 的创建由 ResourceManager 负责:
public class ResourceManager { public AssetProvider CreateAssetProvider(string location, Type assetType) { AssetProvider provider = new AssetProvider(this, location, assetType); return provider; } }Provider 实例化详解:
ResourceManager 根据资源的类型创建对应的 Provider。
Provider 参数详解:
创建 Provider 时需要传入 ResourceManager、location、assetType 等参数。
3.2 Provider 的获取
public class ResourceManager { private Dictionary<string, ProviderBase> _providerCache = new Dictionary<string, ProviderBase>(); public ProviderBase GetOrCreateProvider(string location, Type assetType) { string key = $"{location}_{assetType.Name}"; if (_providerCache.TryGetValue(key, out var provider)) { provider.IncreaseRefCount(); return provider; } provider = CreateAssetProvider(location, assetType); _providerCache[key] = provider; provider.IncreaseRefCount(); return provider; } }Provider 复用详解:
GetOrCreateProvider 方法会先在缓存中查找已存在的 Provider。如果存在则复用,否则创建新的。
引用计数增加详解:
每次获取 Provider 时,引用计数加 1。
3.3 Provider 的销毁
public class ResourceManager { public void DestroyProvider(ProviderBase provider) { string key = provider.Location; _providerCache.Remove(key); provider.Destroy(); } }Provider 销毁详解:
当 Provider 的引用计数为 0 时,会从缓存中移除并销毁。
四、Provider 加载/卸载
4.1 加载流程
Provider 的加载流程如下:
Provider 加载流程 1. 接收加载请求 2. 增加引用计数 3. 启动加载流程 a. 创建 BundleLoader b. 加载 Bundle c. 加载依赖的 Bundle d. 从 Bundle 中加载资源 4. 加载完成 5. 触发回调接收加载请求详解:
用户调用 LoadAssetAsync 时,ResourceManager 会创建或复用 Provider。
增加引用计数详解:
每次加载请求会增加 Provider 的引用计数。
启动加载流程详解:
Provide 方法启动加载流程。
Bundle 加载详解:
Provider 创建 BundleLoader 加载 Bundle。
资源加载详解:
从 Bundle 中加载资源。
4.2 加载实现
public class AssetProvider : ProviderBase { private BundleLoader _bundleLoader; private List<BundleLoader> _dependLoaders; private AssetBundleRequest _request; public override void Provide() { Status = EProviderStatus.Loading; Progress = 0; // 1. 获取 AssetInfo var assetInfo = Package.GetAssetInfo(Location); if (assetInfo == null) { Status = EProviderStatus.Failed; Error = "AssetInfo not found"; return; } // 2. 加载依赖 Bundle _dependLoaders = new List<BundleLoader>(); foreach (var depBundleID in assetInfo.DependIDs) { var depBundle = Package.GetBundleInfo(depBundleID); var loader = Package.ResourceManager.LoadBundle(depBundle); _dependLoaders.Add(loader); } // 3. 加载主 Bundle var mainBundle = Package.GetBundleInfo(assetInfo.BundleID); _bundleLoader = Package.ResourceManager.LoadBundle(mainBundle); } public override void Update() { if (Status != EProviderStatus.Loading) return; // 计算进度 float total = _dependLoaders.Count + 1; float current = 0; foreach (var loader in _dependLoaders) { if (loader.IsDone) current++; } if (_bundleLoader.IsDone) current++; Progress = current / total; // 检查是否完成 if (Progress < 1) return; // 加载资源 _request = _bundleLoader.AssetBundle.LoadAssetAsync(AssetInfo.AssetPath, AssetType); } }加载依赖 Bundle 详解:
加载资源前,先加载所有依赖的 Bundle。
加载主 Bundle 详解:
加载资源所属的主 Bundle。
进度计算详解:
进度 = 已加载的 Bundle 数 / 总 Bundle 数。
资源加载详解:
所有 Bundle 加载完成后,从主 Bundle 中加载资源。
4.3 卸载流程
Provider 的卸载流程:
Provider 卸载流程 1. 接收卸载请求 2. 减少引用计数 3. 引用计数为 0 时启动销毁 4. 释放资源 5. 销毁 Bundle 引用 6. 销毁 Provider接收卸载请求详解:
用户调用 UnloadAsset 时,ResourceManager 减少 Provider 的引用计数。
引用计数减少详解:
每次卸载请求会减少 Provider 的引用计数。
销毁触发详解:
引用计数为 0 时,Provider 启动销毁流程。
4.4 卸载实现
public class AssetProvider : ProviderBase { public override void UnProvide() { DecreaseRefCount(); if (RefCount > 0) return; Status = EProviderStatus.Destroyed; // 释放资源 if (AssetObject != null) { if (AssetObject is GameObject) { // 释放 GameObject 资源 } Resources.UnloadAsset(AssetObject); } // 减少 Bundle 引用 Package.ResourceManager.UnloadBundle(_bundleLoader); foreach (var loader in _dependLoaders) { Package.ResourceManager.UnloadBundle(loader); } } }资源释放详解:
使用 Resources.UnloadAsset 释放资源。
Bundle 引用减少详解:
减少 Bundle 的引用计数,引用为 0 时销毁 Bundle。
五、Provider 类型
5.1 AssetProvider
AssetProvider 是最常用的 Provider,用于加载普通资源:
public class AssetProvider : ProviderBase { public override void Provide(); public override void UnProvide(); public override void Update(); }AssetProvider 的用途详解:
加载 Prefab、Texture、Material、AudioClip 等普通资源。
AssetProvider 的实现详解:
AssetProvider 继承自 ProviderBase,实现了 Provide、UnProvide、Update 方法。
5.2 SceneProvider
SceneProvider 用于加载场景资源:
public class SceneProvider : ProviderBase { public Scene Scene { get; } public AsyncOperation LoadSceneAsync(); public AsyncOperation UnloadSceneAsync(); }SceneProvider 的用途详解:
加载 Unity 场景。
SceneProvider 的特殊处理详解:
SceneProvider 使用 Unity 的 SceneManager 加载场景,并管理场景的激活和卸载。
5.3 ComponentProvider
ComponentProvider 用于加载 Component 类型的资源:
public class ComponentProvider : ProviderBase { public GameObject Instance { get; } public T GetComponent<T>() where T : Component; }ComponentProvider 的用途详解:
加载 Prefab 并获取其中的 Component。
ComponentProvider 的实现详解:
ComponentProvider 在 AssetProvider 的基础上,提供了 Component 获取的能力。
5.4 SubAssetsProvider
SubAssetsProvider 用于加载子资源:
public class SubAssetsProvider : ProviderBase { public Object[] SubAssets { get; } }SubAssetsProvider 的用途详解:
加载包含多个资源的 AssetBundle 中的特定子资源。
SubAssetsProvider 的应用场景详解:
加载 Sprite Atlas 中的特定 Sprite、加载 AnimationClip 等。
5.5 RawFileProvider
RawFileProvider 用于加载原生文件:
public class RawFileProvider : ProviderBase { public byte[] RawData { get; } public string RawText { get; } }RawFileProvider 的用途详解:
加载原生格式的文件,如图片、文本等。
RawFileProvider 的实现详解:
RawFileProvider 不会将资源转换为 Unity 对象,而是返回原始的字节数据。
5.6 BundleProvider
BundleProvider 用于加载整个 Bundle:
public class BundleProvider : ProviderBase { public AssetBundle AssetBundle { get; } }BundleProvider 的用途详解:
加载整个 AssetBundle,不加载具体的资源。
BundleProvider 的应用场景详解:
需要手动管理 AssetBundle 内容的场景。
六、Provider 缓存机制
6.1 缓存的作用
Provider 缓存的作用:
避免重复创建:
相同资源只创建一次 Provider,避免重复创建的开销。
复用 Provider:
相同资源的加载请求复用同一个 Provider,避免重复加载。
提高性能:
通过缓存减少内存分配和资源加载。
6.2 缓存的实现
public class ResourceManager { private Dictionary<string, ProviderBase> _providerCache = new Dictionary<string, ProviderBase>(); public ProviderBase GetProvider(string location, Type assetType) { string key = GenerateProviderKey(location, assetType); if (_providerCache.TryGetValue(key, out var provider)) { return provider; } return null; } public void CacheProvider(ProviderBase provider) { string key = GenerateProviderKey(provider.Location, provider.AssetType); _providerCache[key] = provider; } }缓存 Key 生成详解:
缓存 Key 由 location 和 assetType 组成,确保相同资源使用相同的 Key。
缓存查询详解:
加载资源时先查询缓存,命中则复用,未命中则创建。
6.3 缓存的清理
public class ResourceManager { public void ClearUnusedProviders() { var unused = _providerCache.Where(p => p.Value.RefCount == 0).ToList(); foreach (var kvp in unused) { kvp.Value.Destroy(); _providerCache.Remove(kvp.Key); } } }清理条件详解:
引用计数为 0 的 Provider 是未使用的 Provider。
清理时机详解:
可以在资源切换、关卡切换等时机调用清理方法。
七、总结
本章深入解析了 YooAsset 资源提供者(Provider 体系)的源码实现,包括:
- Provider 概述:Provider 的概念、状态机、状态详解
- Provider 创建/获取:Provider 的创建、获取、销毁
- Provider 加载/卸载:加载流程、加载实现、卸载流程、卸载实现
- Provider 类型:AssetProvider、SceneProvider、ComponentProvider、SubAssetsProvider、RawFileProvider、BundleProvider
- Provider 缓存机制:缓存的作用、实现、清理
Provider 体系是 YooAsset 资源加载的核心,通过深入理解 Provider 体系,开发者可以更好地使用 YooAsset 的资源加载功能,并实现自定义的 Provider。
上一篇:寻址系统
下一篇:异步体系(OperationSystem)