fix: 修复资源释放BUG

This commit is contained in:
han_han9
2025-10-30 08:01:30 +08:00
parent 41d7fe125c
commit 438aea779e

View File

@@ -1,13 +1,22 @@
import { _decorator, isValid, Sprite } from "cc";
import { EDITOR } from "cc/env";
import { isValid } from "cc";
import { assetManager, ImageAsset, Texture2D } from "cc";
import { SpriteFrame } from "cc";
import { UITransform } from "cc";
import { Sprite } from "cc";
import { _decorator } from "cc";
import { EDITOR } from "cc/env";
import { StringUtils } from "../utils/StringUtils";
import { RemoteSpriteCache } from "./RemoteSpriteCache";
import LogUtils from "../utils/LogUtils";
const { ccclass, property } = _decorator;
const CACHE_EXPIRE_TIME = 30000; // 30s 缓存30s
const CACHE_CHECK_INTERVAL = 5000; // 5秒检查一次
@ccclass("RemoteSprite")
export default class RemoteSprite extends Sprite {
export class RemoteSprite extends Sprite {
private static _remoteSpriteCache: Map<string, SpriteFrame> = new Map();
private static _loadingSpriteCache: Map<string, Promise<SpriteFrame>> = new Map();
private static _checkTimer: NodeJS.Timeout;
/**
* 远程图片URL
*/
@@ -42,12 +51,25 @@ export default class RemoteSprite extends Sprite {
}
}
// override set spriteFrame(value: SpriteFrame) {
// super.spriteFrame = value;
// }
public onLoad(): void {
super.onLoad();
if (EDITOR) {
return;
}
RemoteSpriteCache.getInstance().checkAndRegisterSprite(this.spriteFrame);
if (!RemoteSprite._checkTimer) {
RemoteSprite._checkTimer = setInterval(() => {
RemoteSprite.checkCache();
}, CACHE_CHECK_INTERVAL);
}
if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) {
this.spriteFrame.addRef();
this.spriteFrame.lastAccessTime = Date.now();
}
if (!StringUtils.isEmpty(this._remoteUrl) && this.spriteFrame?.remoteUrl !== this._remoteUrl) {
this.loadRemoteSprite(this._remoteUrl).catch((err) => {
LogUtils.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err);
@@ -67,14 +89,56 @@ export default class RemoteSprite extends Sprite {
return;
}
this.release();
const sp = await RemoteSpriteCache.getInstance().loadSpriteFrame(url);
// 检查是否在加载过程中URL发生了变化
if (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url) {
RemoteSpriteCache.getInstance().releaseResource(url);
if (RemoteSprite._remoteSpriteCache.has(url)) {
const sp = RemoteSprite._remoteSpriteCache.get(url);
sp.lastAccessTime = Date.now();
sp.addRef();
this.release();
this.spriteFrame = sp;
return;
}
let loadingPromise: Promise<SpriteFrame> = null;
if (RemoteSprite._loadingSpriteCache.has(url)) {
loadingPromise = RemoteSprite._loadingSpriteCache.get(url);
} else {
loadingPromise = new Promise<SpriteFrame>((resolve, reject) => {
LogUtils.log("RemoteSprite", `loadRemoteSprite: ${url}`);
assetManager.loadRemote(url, (err, asset: ImageAsset) => {
if (err) {
LogUtils.error(`loadRemote error: ${url}`, err);
RemoteSprite._loadingSpriteCache.delete(url);
reject(err);
} else {
try {
asset.addRef();
const texture = new Texture2D();
texture.image = asset;
const spriteFrame = new SpriteFrame();
spriteFrame.texture = texture;
spriteFrame.remoteUrl = url;
RemoteSprite._remoteSpriteCache.set(url, spriteFrame);
RemoteSprite._loadingSpriteCache.delete(url);
resolve(spriteFrame);
} catch (createErr) {
RemoteSprite._loadingSpriteCache.delete(url);
reject(createErr);
}
}
});
});
RemoteSprite._loadingSpriteCache.set(url, loadingPromise);
}
const sp = await loadingPromise;
sp.addRef();
sp.lastAccessTime = Date.now();
// // 检查是否在加载过程中URL发生了变化
if (sp && (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url)) {
sp.decRef(false);
return;
}
console.log("loadSpriteFrame:", url);
this.release();
this.spriteFrame = sp;
}
@@ -86,7 +150,7 @@ export default class RemoteSprite extends Sprite {
public release(): void {
if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) {
RemoteSpriteCache.getInstance()?.releaseResource(this.spriteFrame.remoteUrl);
this.spriteFrame.decRef(false);
}
this.spriteFrame = null;
}
@@ -95,4 +159,48 @@ export default class RemoteSprite extends Sprite {
this.release();
super.onDestroy();
}
private static checkCache() {
// 检查缓存中是否有已加载的SpriteFrame
let clearUrls = [];
let now = Date.now();
for (const [url, spriteFrame] of RemoteSprite._remoteSpriteCache) {
if (spriteFrame.refCount <= 0 && spriteFrame.lastAccessTime < now - CACHE_EXPIRE_TIME) {
let texture = spriteFrame.texture as Texture2D;
let imageAsset: ImageAsset = null;
// 如果已加入动态合图必须取原始的Texture2D
if (spriteFrame.packable && spriteFrame.original) {
texture = spriteFrame.original._texture as Texture2D;
}
// 获取ImageAsset引用
if (texture?.image) {
imageAsset = texture.image;
}
// 先销毁spriteFrame这会自动处理对texture的引用
if (spriteFrame.isValid) {
spriteFrame.destroy();
}
// 再销毁texture
if (texture?.isValid) {
texture.destroy();
}
// 最后减少ImageAsset的引用计数
if (imageAsset?.isValid) {
imageAsset.decRef();
}
clearUrls.push(url);
}
}
clearUrls.forEach((url) => {
RemoteSprite._remoteSpriteCache.delete(url);
});
LogUtils.log("清理缓存:", clearUrls.length, RemoteSprite._remoteSpriteCache.size);
}
}