fix:优化资源清理
This commit is contained in:
		
							
								
								
									
										5
									
								
								extensions/max-studio/assets/max-studio/core/max.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								extensions/max-studio/assets/max-studio/core/max.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| declare module "cc" { | ||||
|     interface SpriteFrame { | ||||
|         remoteUrl?: string; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| { | ||||
|   "ver": "4.0.24", | ||||
|   "importer": "typescript", | ||||
|   "imported": true, | ||||
|   "uuid": "22f3e111-a4f8-4e05-aa61-cbea5c1b9cc1", | ||||
|   "files": [], | ||||
|   "subMetas": {}, | ||||
|   "userData": {} | ||||
| } | ||||
| @@ -1,4 +1,4 @@ | ||||
| import { _decorator, Sprite } from "cc"; | ||||
| import { _decorator, isValid, Sprite } from "cc"; | ||||
| import { EDITOR } from "cc/env"; | ||||
| import { UITransform } from "cc"; | ||||
| import { StringUtils } from "../utils/StringUtils"; | ||||
| @@ -26,13 +26,13 @@ export default class RemoteSprite extends Sprite { | ||||
|         const newValue = value || ""; | ||||
|         if (this._remoteUrl !== newValue) { | ||||
|             this._remoteUrl = newValue; | ||||
|             if (this._remoteUrl !== this._currentUrl) { | ||||
|             if (StringUtils.isEmpty(this._remoteUrl)) { | ||||
|                 this.release(); | ||||
|                 return; | ||||
|             } | ||||
|             if (this.spriteFrame == null || this._remoteUrl !== this.spriteFrame.remoteUrl) { | ||||
|                 this.loadRemoteSprite(this._remoteUrl).catch((err) => { | ||||
|                     LogUtils.error( | ||||
|                         "RemoteSprite", | ||||
|                         `加载远程图片失败: ${this._remoteUrl}`, | ||||
|                         err, | ||||
|                     ); | ||||
|                     LogUtils.error("RemoteSprite", `加载远程图片失败: ${this._remoteUrl}`, err); | ||||
|                     // 可以添加默认图片或错误状态处理 | ||||
|                     if (this.isValid) { | ||||
|                         this.spriteFrame = null; | ||||
| @@ -42,29 +42,18 @@ export default class RemoteSprite extends Sprite { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // 私有属性 | ||||
|     private _currentUrl: string = ""; | ||||
|  | ||||
|     public onLoad(): void { | ||||
|         super.onLoad(); | ||||
|         if (EDITOR) { | ||||
|             return; | ||||
|         } | ||||
|         RemoteSpriteCache.getInstance().checkAndRegisterSprite( | ||||
|             this._currentUrl, | ||||
|             this.spriteFrame, | ||||
|         ); | ||||
|         if ( | ||||
|             !StringUtils.isEmpty(this._remoteUrl) && | ||||
|             StringUtils.isEmpty(this._currentUrl) | ||||
|         ) { | ||||
|         RemoteSpriteCache.getInstance().checkAndRegisterSprite(this.spriteFrame); | ||||
|         if (!StringUtils.isEmpty(this._remoteUrl) && this.spriteFrame?.remoteUrl !== this._remoteUrl) { | ||||
|             this.loadRemoteSprite(this._remoteUrl).catch((err) => { | ||||
|                 LogUtils.error( | ||||
|                     "RemoteSprite", | ||||
|                     `onLoad加载远程图片失败: ${this._remoteUrl}`, | ||||
|                     err, | ||||
|                 ); | ||||
|                 LogUtils.error("RemoteSprite", `onLoad加载远程图片失败: ${this._remoteUrl}`, err); | ||||
|             }); | ||||
|         } else if (StringUtils.isEmpty(this._remoteUrl) && !StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { | ||||
|             this.release(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -74,21 +63,19 @@ export default class RemoteSprite extends Sprite { | ||||
|         } | ||||
|  | ||||
|         // URL 相同,且已经加载完成,直接返回 | ||||
|         if (this._currentUrl === url && this.spriteFrame) { | ||||
|         if (this.spriteFrame && this.spriteFrame.remoteUrl == url) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this.release(); | ||||
|         const sp = await RemoteSpriteCache.getInstance().loadSpriteFrame(url); | ||||
|  | ||||
|         // 检查是否在加载过程中URL发生了变化 | ||||
|         if (this._remoteUrl !== url || this._currentUrl === url) { | ||||
|         if (!isValid(this.node, true) || this._remoteUrl !== url || this.spriteFrame?.remoteUrl === url) { | ||||
|             RemoteSpriteCache.getInstance().releaseResource(url); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this.spriteFrame = sp; | ||||
|         this._currentUrl = url; | ||||
|     } | ||||
|  | ||||
|     public setSize(widthOrHeight: number, height?: number): void { | ||||
| @@ -98,10 +85,9 @@ export default class RemoteSprite extends Sprite { | ||||
|     } | ||||
|  | ||||
|     public release(): void { | ||||
|         if (!StringUtils.isEmpty(this._currentUrl)) { | ||||
|             RemoteSpriteCache.getInstance()?.releaseResource(this._currentUrl); | ||||
|         if (!StringUtils.isEmpty(this.spriteFrame?.remoteUrl)) { | ||||
|             RemoteSpriteCache.getInstance()?.releaseResource(this.spriteFrame.remoteUrl); | ||||
|         } | ||||
|         this._currentUrl = ""; | ||||
|         this.spriteFrame = null; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -1,10 +1,4 @@ | ||||
| import { | ||||
|     _decorator, | ||||
|     SpriteFrame, | ||||
|     ImageAsset, | ||||
|     Texture2D, | ||||
|     assetManager, | ||||
| } from "cc"; | ||||
| import { _decorator, SpriteFrame, ImageAsset, Texture2D, assetManager } from "cc"; | ||||
| import NodeSingleton from "../NodeSingleton"; | ||||
| import LogUtils from "../utils/LogUtils"; | ||||
| import { StringUtils } from "../utils/StringUtils"; | ||||
| @@ -71,7 +65,7 @@ class CacheItem { | ||||
|  | ||||
| const TAG = "RemoteSpriteCache"; | ||||
| const CLEANUP_CHECK_INTERVAL = 10; // 10秒检查一次 | ||||
| const CACHE_EXPIRE_TIME = 1 * 60 * 1000; // 1分钟 | ||||
| const CACHE_EXPIRE_TIME = 1 * 30 * 1000; // 1分钟 | ||||
|  | ||||
| // 添加最大缓存数量限制 | ||||
| const MAX_CACHE_SIZE = 100; // 最大缓存数量 | ||||
| @@ -98,13 +92,10 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|     /** | ||||
|      * 检查并注册精灵 | ||||
|      */ | ||||
|     public checkAndRegisterSprite( | ||||
|         url: string, | ||||
|         spriteFrame: SpriteFrame, | ||||
|     ): boolean { | ||||
|         if (spriteFrame && !StringUtils.isEmpty(url)) { | ||||
|             const cacheItem = this._cache.get(url); | ||||
|             if (cacheItem && cacheItem.spriteFrame === spriteFrame) { | ||||
|     public checkAndRegisterSprite(spriteFrame: SpriteFrame): boolean { | ||||
|         if (spriteFrame && !StringUtils.isEmpty(spriteFrame.remoteUrl)) { | ||||
|             const cacheItem = this._cache.get(spriteFrame.remoteUrl); | ||||
|             if (cacheItem) { | ||||
|                 cacheItem.refCount++; | ||||
|                 return true; | ||||
|             } | ||||
| @@ -115,10 +106,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|     protected update(dt: number): void { | ||||
|         const currentTime = Date.now(); | ||||
|         // 每隔指定时间检查一次过期资源 | ||||
|         if ( | ||||
|             currentTime - this._lastCleanupTime >= | ||||
|             CLEANUP_CHECK_INTERVAL * 1000 | ||||
|         ) { | ||||
|         if (currentTime - this._lastCleanupTime >= CLEANUP_CHECK_INTERVAL * 1000) { | ||||
|             this.cleanupUnusedResources(); | ||||
|             this._lastCleanupTime = currentTime; | ||||
|         } | ||||
| @@ -130,16 +118,10 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|     public async loadSpriteFrame(url: string): Promise<SpriteFrame> { | ||||
|         let cacheItem = this._cache.get(url); | ||||
|         if (cacheItem) { | ||||
|             if ( | ||||
|                 cacheItem.loadState === LoadState.LOADED && | ||||
|                 cacheItem.spriteFrame | ||||
|             ) { | ||||
|             if (cacheItem.loadState === LoadState.LOADED && cacheItem.spriteFrame) { | ||||
|                 cacheItem.refCount++; | ||||
|                 return cacheItem.spriteFrame; | ||||
|             } else if ( | ||||
|                 cacheItem.loadState === LoadState.LOADING && | ||||
|                 cacheItem.loadPromise | ||||
|             ) { | ||||
|             } else if (cacheItem.loadState === LoadState.LOADING && cacheItem.loadPromise) { | ||||
|                 try { | ||||
|                     const spriteFrame = await cacheItem.loadPromise; | ||||
|                     if (spriteFrame) { | ||||
| @@ -155,14 +137,8 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|             } | ||||
|  | ||||
|             // 如果加载失败且重试次数未达到上限,重新尝试 | ||||
|             if ( | ||||
|                 cacheItem.loadState === LoadState.FAILED && | ||||
|                 cacheItem.retryCount < MAX_RETRY_COUNT | ||||
|             ) { | ||||
|                 LogUtils.warn( | ||||
|                     TAG, | ||||
|                     `重试加载: ${url}, 第${cacheItem.retryCount + 1}次`, | ||||
|                 ); | ||||
|             if (cacheItem.loadState === LoadState.FAILED && cacheItem.retryCount < MAX_RETRY_COUNT) { | ||||
|                 LogUtils.warn(TAG, `重试加载: ${url}, 第${cacheItem.retryCount + 1}次`); | ||||
|                 cacheItem.incrementRetry(); | ||||
|                 return this.doLoadSpriteFrame(url, cacheItem); | ||||
|             } | ||||
| @@ -178,10 +154,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|         return this.doLoadSpriteFrame(url, cacheItem); | ||||
|     } | ||||
|  | ||||
|     private async doLoadSpriteFrame( | ||||
|         url: string, | ||||
|         cacheItem: CacheItem, | ||||
|     ): Promise<SpriteFrame> { | ||||
|     private async doLoadSpriteFrame(url: string, cacheItem: CacheItem): Promise<SpriteFrame> { | ||||
|         cacheItem.loadState = LoadState.LOADING; | ||||
|         cacheItem.setLoadStartTime(); | ||||
|  | ||||
| @@ -193,6 +166,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|                 reject(new Error(`加载超时: ${url}`)); | ||||
|             }, LOAD_TIMEOUT); | ||||
|  | ||||
|             console.log(`开始加载资源: ${url}`); | ||||
|             assetManager.loadRemote(url, (err, asset: ImageAsset) => { | ||||
|                 clearTimeout(timeoutId); | ||||
|  | ||||
| @@ -209,6 +183,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|                         const spriteFrame = new SpriteFrame(); | ||||
|                         spriteFrame.texture = texture; | ||||
|                         spriteFrame.addRef(); | ||||
|                         spriteFrame.remoteUrl = url; | ||||
|  | ||||
|                         cacheItem.refCount++; | ||||
|                         cacheItem.spriteFrame = spriteFrame; | ||||
| @@ -218,11 +193,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|  | ||||
|                         resolve(spriteFrame); | ||||
|                     } catch (createErr) { | ||||
|                         LogUtils.error( | ||||
|                             TAG, | ||||
|                             `创建SpriteFrame失败: ${url}`, | ||||
|                             createErr, | ||||
|                         ); | ||||
|                         LogUtils.error(TAG, `创建SpriteFrame失败: ${url}`, createErr); | ||||
|                         cacheItem.loadState = LoadState.FAILED; | ||||
|                         cacheItem.loadPromise = null; | ||||
|                         reject(createErr); | ||||
| @@ -242,6 +213,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|             const cacheItem = this._cache.get(url); | ||||
|             if (cacheItem) { | ||||
|                 cacheItem.refCount--; | ||||
|                 console.log(`释放资源引用: ${url}, 引用计数: ${cacheItem.refCount}`); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -300,11 +272,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|     private cleanupByUsageTime(targetSize: number): void { | ||||
|         // 直接筛选并排序需要清理的资源 | ||||
|         const entriesToClean = Array.from(this._cache.entries()) | ||||
|             .filter( | ||||
|                 ([_, cacheItem]) => | ||||
|                     cacheItem.refCount <= 0 && | ||||
|                     cacheItem.loadState === LoadState.LOADED, | ||||
|             ) | ||||
|             .filter(([_, cacheItem]) => cacheItem.refCount <= 0 && cacheItem.loadState === LoadState.LOADED) | ||||
|             .sort((a, b) => a[1].lastAccessTime - b[1].lastAccessTime); | ||||
|  | ||||
|         let cleanedCount = 0; | ||||
| @@ -343,10 +311,7 @@ export class RemoteSpriteCache extends NodeSingleton { | ||||
|             let imageAsset: ImageAsset = null; | ||||
|  | ||||
|             // 如果已加入动态合图,必须取原始的Texture2D | ||||
|             if ( | ||||
|                 cacheItem.spriteFrame.packable && | ||||
|                 cacheItem.spriteFrame.original | ||||
|             ) { | ||||
|             if (cacheItem.spriteFrame.packable && cacheItem.spriteFrame.original) { | ||||
|                 texture = cacheItem.spriteFrame.original._texture as Texture2D; | ||||
|             } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 han_han9
					han_han9