# 资源缓存
# 资源缓存
小游戏在真机的文件目录分为临时文件目录和用户文件目录:
- 临时文件目录不限大小,但是会被定时清理
- 用户文件目录限定大小 200MB,但是能够持久保留。
在加载资源时,如果设置 cacheable: true
,那么文件会下载到用户文件目录,否则下载到临时文件目录。用户文件目录里的资源文件,就是资源缓存。
engine.loader.load("resource/Aircraft.prefab", {
cacheable: true
}).promise.then((spriteFrame) => {
console.log(spriteFrame)
})
对资源文件的缓存行为是资源加载模块内部控制和调度的,开发者无须关心。以如上代码为例,在 "resource/Aircraft.prefab"
加载完成后,相应的资源文件就已缓存到了用户文件目录。
# 缓存目录
wx.env.USER_DATA_PATH
是小游戏用户文件目录的根目录,${wx.env.USER_DATA_PATH}/engine-cache
是资源缓存的根目录,所有的缓存文件都在这个目录下。
可以直接用 wx 文件 API 访问这个目录。
const fs = wx.getFileSystemManager();
try {
const files = fs.readdirSync(`${wx.env.USER_DATA_PATH}/engine-cache`);
console.log(files);
}
catch (error) {
console.error("engine-cache 还没创建", error)
}
缓存目录的结构如下:
- wxfile://usr
- engine-cache(根目录)
- res_ui_accepting_applications_bid_e61c12ce@combine_0.bin
- res_ui_accepting_applications_bid_e61c12ce@assets_ui.png
- dep_00131089c86875e2ff637d09ff5038a8_9a089497@combine_0.bin
- userData.json(用户文件目录下的其他文件)
- ...
- engine-cache(根目录)
缓存文件的文件名按照 ${资源组名}@${资源文件名}
的格式保存和读取。@
字符是分割资源组名和资源文件名的分割符,因此文件名中不应该包含 @
字符,否则会导致缓存模块报错。
如果资源文件名中必须包含 @
字符,可以换一个分割符。但这一步必须在 engine.cache.init() 之前设置。
// 将分割符换成 #
engine.cache.delimiter = "#";
// 如果是手动初始化缓存模块,必须在 engine.cache.init() 之前设置
engine.cache.init();
// 如果不是手动初始化缓存模块,必须在 engine.init() 之前设置
engine.init(canvas);
# 缓存上限
除了资源缓存,游戏可能也需要在用户文件目录写入一些业务文件,比如玩家的离线存档。所以用户文件目录不能被资源文件写满。 可以通过设置 CacheManager.sizeLimit 来限制缓存大小。
engine.cache.sizeLimit = 180*1024*1024;
sizeLimit 只是一个 JS 层限制缓存文件写入的属性值,无法影响小游戏用户文件目录大小上限的设置。即使 sizeLimit 设置为 500*1024*1024
,还是会在文件总大小超过 200 MB 时写入失败。
# 缓存更新
资源组名也是资源组 id,id 末尾是 8 位 hash。当资源组内的文件内容发生变化,末尾的 hash 也会改变。 hash 不同的新旧版本资源组,会被视为两个不同的资源组。旧版本的资源会因为没有被使用,在淘汰过程中被删除。
如末尾 hash 不同的两个 res_ui_accepting_applications_bid 资源组的 combine_0.bin 文件就是两个不同的缓存文件。
res_ui_accepting_applications_bid_9a089497@combine_0.bin res_ui_accepting_applications_bid_e61c12ce@combine_0.bin
# 缓存淘汰
当有新缓存写入且当前缓存空间不足时,会触发缓存淘汰。缓存空间不足包含两种情况:
- 已经有缓存大小加上新缓存大小超过 sizeLimit。
- 因达到小游戏用户文件目录大小上限而写入失败。
当缓存空间不足时,缓存模块会调用暴露给游戏业务侧的回调函数 onNeedRelease()。游戏业务侧返回一个数值表示要释放的缓存空间大小。
engine.cache.onNeedRelease = (res) => {
console.log(`剩余缓存空间不足,已有缓存:${res.currentSize} B,新写入缓存 ${res.cacheSize}`)
// 淘汰 80 MB 缓存
return 80*1024*1024;
}
返回小于 0 的值表示本次不淘汰缓存,新缓存的写入会失败。
engine.cache.onNeedRelease = (res) => {
return -1;
}
如果不设置 engine.cache.onNeedRelease,那么要淘汰的缓存大小就是新写入的缓存的大小。
如要写入的缓存文件是 5 MB,那么就释放 >= 5MB 的缓存空间。
# 淘汰策略
除了基本的 LRU 以外,游戏业务侧还希望自定义淘汰策略。因此缓存模块暴露 compare 函数。compare 的用法和 Array.prototype.sort 一样,排序后得到一个权重升序的资源缓存数组。缓存模块从数组头部开始依次淘汰,直到满足要腾出的空间大小。
在 compare 函数的参数里,暴露了每个缓存的资源组在本次游戏中的加载信息,游戏业务侧可以据此定制加载策略。
参数 | 类型 | 说明 |
---|---|---|
size | number | 资源文件的大小 |
loadTimes | number | 资源文件被加载的次数(调用 engine.loader.load() 加载资源时涉及到这个资源文件的次数) |
lastLoadTime | Date | 最近一次资源文件被加载的时间戳(调用 engine.loader.load() 加载资源时涉及到这个资源文件的时间戳) |
useTimes | number | 资源文件被用到的次数(调用 engine.loader.getAsset() 且用到这个资源文件的内容的次数) |
lastUseTime | Date | 最近一次资源文件被用到的时间戳(最近一次调用 engine.loader.getAsset() 且用到这个资源文件的内容的时间戳) |
默认按照 useTimes 进行排序,即 LRU。