fix: 修复资源释放BUG
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RemoteSprite._remoteSpriteCache.has(url)) {
|
||||||
|
const sp = RemoteSprite._remoteSpriteCache.get(url);
|
||||||
|
sp.lastAccessTime = Date.now();
|
||||||
|
sp.addRef();
|
||||||
this.release();
|
this.release();
|
||||||
const sp = await RemoteSpriteCache.getInstance().loadSpriteFrame(url);
|
this.spriteFrame = sp;
|
||||||
// 检查是否在加载过程中URL发生了变化
|
|
||||||
if (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url) {
|
|
||||||
RemoteSpriteCache.getInstance().releaseResource(url);
|
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user