05-05-YooAsset源码-Unity资源加载与Provider体系
源码-资源提供者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 Dictionarystring, ProviderBase _providerCache new Dictionarystring, 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 ListBundleLoader _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 ListBundleLoader(); 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 AssetProviderAssetProvider 是最常用的 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 SceneProviderSceneProvider 用于加载场景资源public class SceneProvider : ProviderBase { public Scene Scene { get; } public AsyncOperation LoadSceneAsync(); public AsyncOperation UnloadSceneAsync(); }SceneProvider 的用途详解加载 Unity 场景。SceneProvider 的特殊处理详解SceneProvider 使用 Unity 的 SceneManager 加载场景并管理场景的激活和卸载。5.3 ComponentProviderComponentProvider 用于加载 Component 类型的资源public class ComponentProvider : ProviderBase { public GameObject Instance { get; } public T GetComponentT() where T : Component; }ComponentProvider 的用途详解加载 Prefab 并获取其中的 Component。ComponentProvider 的实现详解ComponentProvider 在 AssetProvider 的基础上提供了 Component 获取的能力。5.4 SubAssetsProviderSubAssetsProvider 用于加载子资源public class SubAssetsProvider : ProviderBase { public Object[] SubAssets { get; } }SubAssetsProvider 的用途详解加载包含多个资源的 AssetBundle 中的特定子资源。SubAssetsProvider 的应用场景详解加载 Sprite Atlas 中的特定 Sprite、加载 AnimationClip 等。5.5 RawFileProviderRawFileProvider 用于加载原生文件public class RawFileProvider : ProviderBase { public byte[] RawData { get; } public string RawText { get; } }RawFileProvider 的用途详解加载原生格式的文件如图片、文本等。RawFileProvider 的实现详解RawFileProvider 不会将资源转换为 Unity 对象而是返回原始的字节数据。5.6 BundleProviderBundleProvider 用于加载整个 Bundlepublic class BundleProvider : ProviderBase { public AssetBundle AssetBundle { get; } }BundleProvider 的用途详解加载整个 AssetBundle不加载具体的资源。BundleProvider 的应用场景详解需要手动管理 AssetBundle 内容的场景。六、Provider 缓存机制6.1 缓存的作用Provider 缓存的作用避免重复创建相同资源只创建一次 Provider避免重复创建的开销。复用 Provider相同资源的加载请求复用同一个 Provider避免重复加载。提高性能通过缓存减少内存分配和资源加载。6.2 缓存的实现public class ResourceManager { private Dictionarystring, ProviderBase _providerCache new Dictionarystring, 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、BundleProviderProvider 缓存机制缓存的作用、实现、清理Provider 体系是 YooAsset 资源加载的核心通过深入理解 Provider 体系开发者可以更好地使用 YooAsset 的资源加载功能并实现自定义的 Provider。上一篇寻址系统下一篇异步体系OperationSystem