fix: 修复资源释放BUG
This commit is contained in:
		| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 han_han9
					han_han9