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 { isValid } from "cc";
import { EDITOR } from "cc/env"; import { assetManager, ImageAsset, Texture2D } from "cc";
import { SpriteFrame } from "cc";
import { UITransform } 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 { StringUtils } from "../utils/StringUtils";
import { RemoteSpriteCache } from "./RemoteSpriteCache";
import LogUtils from "../utils/LogUtils"; import LogUtils from "../utils/LogUtils";
const { ccclass, property } = _decorator; const { ccclass, property } = _decorator;
const CACHE_EXPIRE_TIME = 30000; // 30s 缓存30s
const CACHE_CHECK_INTERVAL = 5000; // 5秒检查一次
@ccclass("RemoteSprite") @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 * 远程图片URL
*/ */
@@ -42,12 +51,25 @@ export default class RemoteSprite extends Sprite {
} }
} }
// override set spriteFrame(value: SpriteFrame) {
// super.spriteFrame = value;
// }
public onLoad(): void { public onLoad(): void {
super.onLoad(); super.onLoad();
if (EDITOR) { if (EDITOR) {
return; 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) { if (!StringUtils.isEmpty(this._remoteUrl) && this.spriteFrame?.remoteUrl !== this._remoteUrl) {
this.loadRemoteSprite(this._remoteUrl).catch((err) => { this.loadRemoteSprite(this._remoteUrl).catch((err) => {
LogUtils.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err); LogUtils.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err);
@@ -67,14 +89,56 @@ export default class RemoteSprite extends Sprite {
return; return;
} }
this.release(); if (RemoteSprite._remoteSpriteCache.has(url)) {
const sp = await RemoteSpriteCache.getInstance().loadSpriteFrame(url); const sp = RemoteSprite._remoteSpriteCache.get(url);
// 检查是否在加载过程中URL发生了变化 sp.lastAccessTime = Date.now();
if (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url) { sp.addRef();
RemoteSpriteCache.getInstance().releaseResource(url); this.release();
this.spriteFrame = sp;
return; 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; this.spriteFrame = sp;
} }
@@ -86,7 +150,7 @@ export default class RemoteSprite extends Sprite {
public release(): void { public release(): void {
if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) {
RemoteSpriteCache.getInstance()?.releaseResource(this.spriteFrame.remoteUrl); this.spriteFrame.decRef(false);
} }
this.spriteFrame = null; this.spriteFrame = null;
} }
@@ -95,4 +159,48 @@ export default class RemoteSprite extends Sprite {
this.release(); this.release();
super.onDestroy(); 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);
}
} }