源码-资源缓存机制
篇章 :05-源码深度-运行时核心模块 阅读时间 :约 25 分钟 前置知识:了解引用计数机制
一、引言
本章将深入解析 YooAsset 资源缓存机制的源码实现。资源缓存是 YooAsset 资源管理的重要组成部分,通过缓存机制可以提高资源加载速度、减少网络请求。YooAsset 提供了多级缓存机制,包括内存缓存、磁盘缓存等。
资源缓存机制的设计目标是提供高效、可靠的缓存能力。本章将从源码层面深入解析 YooAsset 资源缓存机制的实现。
二、缓存机制概述
2.1 缓存的作用
缓存是资源管理的重要组成部分:
缓存的作用:
- 提高资源加载速度
- 减少网络请求
- 减少磁盘 I/O
- 提升用户体验
缓存的类型:
- 内存缓存:AssetBundle 缓存
- 磁盘缓存:文件缓存
- 预加载缓存:预下载的资源
2.2 YooAsset 中的缓存
YooAsset 提供了多级缓存机制:
markdown
YooAsset 缓存体系
├── 内存缓存
│ ├── BundleLoader 缓存
│ ├── Provider 缓存
│ └── AssetBundle 缓存
├── 磁盘缓存
│ ├── 包内资源缓存(构建内)
│ ├── 本地缓存(CacheSystem)
│ └── 临时缓存
└── 预加载缓存
└── 预下载的资源
内存缓存详解:
内存缓存是 AssetBundle 和 Provider 的内存缓存,提供最快的访问速度。
磁盘缓存详解:
磁盘缓存是已下载资源的本地缓存,提供跨会话的持久化能力。
预加载缓存详解:
预加载缓存是预下载的资源,可以在游戏空闲时预先加载。
2.3 缓存策略
YooAsset 提供了多种缓存策略:
| 缓存策略 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 无缓存 | 不缓存任何资源 | 内存占用少 | 加载慢 |
| LRU | 最近最少使用 | 自动清理 | 复杂 |
| 容量限制 | 限制缓存大小 | 可控 | 需要管理 |
| 手动缓存 | 手动管理缓存 | 灵活 | 需要管理 |
无缓存策略详解:
不缓存任何资源,每次都重新加载。适合内存紧张的小游戏。
LRU 策略详解:
使用 LRU 算法自动清理最近最少使用的资源。
容量限制策略详解:
限制缓存的总大小,避免内存溢出。
手动缓存策略详解:
由开发者手动管理缓存,提供最大的灵活性。
三、BundleLoader 缓存
3.1 BundleLoader 缓存的作用
BundleLoader 缓存用于管理 AssetBundle 的加载:
BundleLoader 缓存的作用:
- 复用已加载的 AssetBundle
- 避免重复加载
- 提高加载速度
3.2 BundleLoader 缓存的实现
csharp
public class ResourceManager
{
private Dictionary<string, BundleLoader> _bundleLoaders = new Dictionary<string, BundleLoader>();
public BundleLoader LoadBundle(BundleInfo bundleInfo)
{
string key = bundleInfo.FileName;
if (_bundleLoaders.TryGetValue(key, out var loader))
{
loader.Retain();
return loader;
}
loader = new BundleLoader(bundleInfo, Package);
loader.LoadAsync();
_bundleLoaders[key] = loader;
loader.Retain();
return loader;
}
public void UnloadBundle(BundleLoader loader)
{
loader.Release();
if (loader.RefCount <= 0)
{
_bundleLoaders.Remove(loader.BundleInfo.FileName);
}
}
}
缓存查询详解:
通过 Bundle 文件名查询缓存。
缓存复用详解:
缓存命中时复用 BundleLoader,并增加引用计数。
缓存清理详解:
引用为 0 时清理 BundleLoader。
3.3 BundleLoader 缓存策略
csharp
public class ResourceManager
{
public void ClearAllBundles()
{
foreach (var loader in _bundleLoaders.Values)
{
loader.UnloadForce();
}
_bundleLoaders.Clear();
}
public void ClearUnusedBundles()
{
var unused = _bundleLoaders.Where(b => b.Value.RefCount == 0).ToList();
foreach (var kvp in unused)
{
kvp.Value.UnloadForce();
_bundleLoaders.Remove(kvp.Key);
}
}
}
清除所有 Bundle 详解:
ClearAllBundles 清除所有 Bundle,强制卸载所有 AssetBundle。
清除未使用 Bundle 详解:
ClearUnusedBundles 清除未使用的 Bundle,保留正在使用的 Bundle。
四、Provider 缓存
4.1 Provider 缓存的作用
Provider 缓存用于管理已创建的 Provider:
Provider 缓存的作用:
- 复用已创建的 Provider
- 减少 Provider 创建开销
- 跟踪资源状态
4.2 Provider 缓存的实现
csharp
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 = CreateProvider(location, assetType);
_providerCache[key] = provider;
provider.IncreaseRefCount();
return provider;
}
public void RemoveProvider(ProviderBase provider)
{
string key = $"{provider.Location}_{provider.AssetType.Name}";
_providerCache.Remove(key);
}
}
缓存 Key 生成详解:
缓存 Key 由 location 和 assetType 组成。
Provider 复用详解:
缓存命中时复用 Provider,增加引用计数。
Provider 销毁详解:
引用为 0 时销毁 Provider。
4.3 Provider 缓存策略
csharp
public class ResourceManager
{
public void ClearAllAssets()
{
var providers = _providerCache.Values.ToList();
foreach (var provider in providers)
{
provider.UnProvide();
}
_providerCache.Clear();
}
public void ClearUnusedAssets()
{
var unused = _providerCache.Where(p => p.Value.RefCount == 0).ToList();
foreach (var kvp in unused)
{
kvp.Value.UnProvide();
_providerCache.Remove(kvp.Key);
}
}
}
清除所有资源详解:
ClearAllAssets 清除所有资源,强制销毁所有 Provider。
清除未使用资源详解:
ClearUnusedAssets 清除未使用的资源,保留正在使用的资源。
五、磁盘缓存
5.1 磁盘缓存的作用
磁盘缓存用于持久化存储已下载的资源:
磁盘缓存的作用:
- 跨会话保存资源
- 减少重复下载
- 节省网络流量
5.2 磁盘缓存的位置
csharp
public class CacheSystem
{
private string _cacheRootPath;
public CacheSystem()
{
// 缓存根目录
_cacheRootPath = Path.Combine(Application.persistentDataPath, "YooAsset/Cache");
if (!Directory.Exists(_cacheRootPath))
Directory.CreateDirectory(_cacheRootPath);
}
}
持久化路径详解:
使用 Application.persistentDataPath 作为缓存根目录。
YooAsset/Cache 目录详解:
在 persistentDataPath 下创建 YooAsset/Cache 目录用于缓存。
5.3 磁盘缓存的实现
csharp
public class CacheSystem
{
private Dictionary<string, FileInfo> _cachedFiles = new Dictionary<string, FileInfo>();
public bool IsCached(string fileName)
{
return _cachedFiles.ContainsKey(fileName);
}
public byte[] ReadCacheFile(string fileName)
{
string filePath = Path.Combine(_cacheRootPath, fileName);
if (File.Exists(filePath))
return File.ReadAllBytes(filePath);
return null;
}
public void WriteCacheFile(string fileName, byte[] data)
{
string filePath = Path.Combine(_cacheRootPath, fileName);
File.WriteAllBytes(filePath, data);
_cachedFiles[fileName] = new FileInfo(filePath);
}
public void RemoveCacheFile(string fileName)
{
string filePath = Path.Combine(_cacheRootPath, fileName);
if (File.Exists(filePath))
File.Delete(filePath);
_cachedFiles.Remove(fileName);
}
}
缓存查询详解:
通过文件名查询缓存是否存在。
缓存读取详解:
从缓存文件读取数据。
缓存写入详解:
将数据写入缓存文件。
缓存删除详解:
删除缓存文件。
5.4 磁盘缓存的清理
csharp
public class CacheSystem
{
public void ClearExpiredCache()
{
// 清理过期缓存
}
public void ClearBySize(long maxSize)
{
// 按大小清理
}
public void ClearByVersion(string currentVersion)
{
// 清理旧版本缓存
}
}
过期清理详解:
清理超过指定时间的缓存。
容量清理详解:
限制缓存总大小,超出时清理最旧的缓存。
版本清理详解:
清理旧版本的缓存,只保留当前版本的缓存。
六、内存缓存与磁盘缓存的配合
6.1 缓存配合的流程
markdown
缓存配合流程
1. 加载资源请求
2. 检查内存缓存(Provider、BundleLoader)
3. 内存缓存命中:直接返回
4. 内存缓存未命中:检查磁盘缓存
5. 磁盘缓存命中:加载到内存缓存
6. 磁盘缓存未命中:下载到磁盘缓存,再加载到内存缓存
内存缓存优先详解:
优先检查内存缓存,提供最快的访问速度。
磁盘缓存次之详解:
内存缓存未命中时,检查磁盘缓存。
下载最后详解:
磁盘缓存未命中时,从远程下载。
6.2 缓存的实现
csharp
public class ResourcePackage
{
public AssetOperationHandle LoadAssetAsync(string location, Type assetType)
{
// 1. 检查内存缓存
var provider = ResourceManager.GetProvider(location, assetType);
if (provider != null && provider.Status == EProviderStatus.Loaded)
{
return provider.GetHandle();
}
// 2. 内存缓存未命中,加载资源
provider = ResourceManager.GetOrCreateProvider(location, assetType);
provider.Provide();
return provider.GetHandle();
}
}
内存缓存检查详解:
优先检查内存缓存中是否已有 Provider。
加载资源详解:
内存缓存未命中时,创建 Provider 并加载资源。
6.3 缓存的生命周期
内存缓存的生命周期:
内存缓存在游戏会话内有效,游戏退出后自动释放。
磁盘缓存的生命周期:
磁盘缓存跨会话有效,需要主动清理。
缓存与引用的关系:
内存缓存与引用计数关联,引用为 0 时清理。
七、缓存优化
7.1 预加载
预加载是重要的缓存优化策略:
csharp
public class PreloadManager
{
public void PreloadAssets(List<string> locations)
{
foreach (var location in locations)
{
// 异步预加载
var handle = Package.LoadAssetAsync<GameObject>(location);
}
}
}
预加载的作用详解:
在游戏空闲时预先加载资源,避免游戏时加载卡顿。
预加载的应用场景详解:
- 游戏启动时预加载常用资源
- 关卡切换时预加载下一关资源
- 网络空闲时预加载未来资源
7.2 缓存预热
csharp
public class CacheWarmer
{
public void WarmCache(List<string> criticalResources)
{
foreach (var location in criticalResources)
{
var handle = Package.LoadAssetAsync<GameObject>(location);
handle.Release(); // 预热后释放
}
}
}
缓存预热的作用详解:
在游戏开始前预先加载关键资源到内存,避免第一次加载卡顿。
7.3 缓存监控
csharp
public class CacheMonitor
{
public CacheStatistics GetStatistics()
{
var stats = new CacheStatistics();
stats.MemoryCacheCount = ResourceManager.ProviderCache.Count;
stats.MemoryCacheSize = CalculateMemoryCacheSize();
stats.DiskCacheCount = CacheSystem.CachedFileCount;
stats.DiskCacheSize = CalculateDiskCacheSize();
return stats;
}
}
缓存监控的作用详解:
监控缓存的使用情况,及时发现问题。
八、总结
本章深入解析了 YooAsset 资源缓存机制的源码实现,包括:
- 缓存机制概述:缓存的作用、类型、策略
- BundleLoader 缓存:BundleLoader 缓存的作用、实现、策略
- Provider 缓存:Provider 缓存的作用、实现、策略
- 磁盘缓存:磁盘缓存的作用、位置、实现、清理
- 内存缓存与磁盘缓存的配合:配合流程、实现、生命周期
- 缓存优化:预加载、缓存预热、缓存监控
通过深入理解资源缓存机制,开发者可以优化资源加载速度,提升游戏性能。
上一篇 :引用计数机制 下一篇 :文件系统体系(IFileSystem)