清空缓存链表
如果我们有一个资源,第一次Release它时我们让它缓存在m_NoReferenceAssetMapList
链表中,第二次Release时我们让它彻底清除,这时这个资源ResourceItem
对应的Obj引用和AssetBundle引用都会清除,但是ResorceItem
本身还是被缓存在m_NoReferenceAssetMapList
链表中,我们需要添加清空缓存链表的方法。
首先给ResourceItem
添加是否Clear的变量:
1 2 3 4 5 6 7 8 9 10
|
public class ResourceItem { public bool m_Clear = true; }
|
修改DestroyResourceItem
方法,之前我们把AssetDic.Remove
放在了m_NoReferenceAssetMapList.InsertToHead
前面,这就导致了无论是否完全删除资源,缓存字典都会删除这个资源记录;再这里我们移到后面,这样我们只有完全删除资源时才会删除缓存字典的资源记录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| protected void DestroyResourceItem(ResourceItem item, bool destroyCache = false) { if(item == null||item.RefCount>0) { return; } if(!destroyCache) { m_NoReferenceAssetMapList.InsertToHead(item); return; } if (!AssetDic.Remove(item.m_Crc)) { return; } AssetBundleManager.Instance.ReleaseAsset (item); if(item.m_Obj != null) { item.m_Obj = null; } }
|
添加ClearCache
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
public void ClearCache() { List<ResourceItem> tempList = new List<ResourceItem>(); foreach (ResourceItem item in AssetDic.Values) { if (item.m_Clear) { tempList.Add(item); } } tempList.ForEach(item => { DestroyResourceItem(item,true); }); tempList.Clear(); }
|
由于我们完全使用AssetDic
作为临时缓存,它能明确指出哪些资源是已经缓存的,哪些资源是完全删除的,所以m_NoReferenceAssetMapList
链表目前用来缓存资源没有意义了,我们在DestroyResourceItem
里面先注释掉添加进链表的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| protected void DestroyResourceItem(ResourceItem item, bool destroyCache = false) { if(item == null||item.RefCount>0) { return; } if(!destroyCache) { return; } if (!AssetDic.Remove(item.m_Crc)) { return; }
}
|
这些只是临时的操作,双向链表肯定是要用到的,否则用不到的资源会一直放在AssetDic里
预加载
预加载就是先同步加载资源,再卸载资源(但是资源卸载参数是false),故意把ReourceItem
(包括Object和相应的AssetBundle)缓存在内存里。
资源预加载时,GetCacheResourceItem(crc,0)
设置的引用计数为零,这表示预加载的资源随时可以卸载
我们还添加了根据路径卸载资源的ReleaseResource
重载方法,这样能省去根据资源卸载时需要遍历缓存字典的麻烦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| public void PreLoadRes(string path) { if (string.IsNullOrEmpty(path)) { return; } uint crc = CRC32.GetCRC32(path); ResourceItem item = GetCacheResourceItem(crc,0); if (item != null) { return; } Object obj = null; #if UNITY_EDITOR if(!m_LoadFromAssetBundle) { item = AssetBundleManager.Instance.FindResourceItem (crc); if (item != null && item.m_Obj != null) { obj = item.m_Obj as T; } else { if (item == null) { item = new ResourceItem { m_Crc = crc, };
} obj = LoadAssetByEditor<T>(path); } } #endif if (obj == null) { item = AssetBundleManager.Instance.LoadResourceAssetBundle(crc); if (item != null && item.m_AssetBundle != null) { if (item.m_Obj != null) { obj = item.m_Obj; } else { obj = item.m_AssetBundle.LoadAsset<Object>(item.m_AssetName); } } } CacheResource(path, ref item, crc, obj); item.m_Clear = false; ReleaseResource(path, false); } public bool ReleaseResource(string path,bool destroyObj = false) { if (string.IsNullOrEmpty(path)) return false; uint crc = CRC32.GetCRC32(path); ResourceItem item = null; if (!AssetDic.TryGetValue(crc, out item) || item == null) { Debug.LogError("AssetDic里不存在该资源:" + path + " 可能释放了多次"); } item.RefCount--; DestroyResourceItem(item, destroyObj); return true; }
|
预加载演示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| using UnityEngine;
public class GameStart : MonoBehaviour { public AudioSource m_AudioSource; private AudioClip m_AudioClip; private void Awake() { AssetBundleManager.Instance.LoadAssetBundleConfig(); ResourceManager.Instance.Init(this); } private void Start() { ResourceManager.Instance.PreLoadRes("Assets/GameData/Sounds/menusound.mp3"); } private void Update() { if(Input.GetKeyDown(KeyCode.A)) { m_AudioSource.Stop(); ResourceManager.Instance.ReleaseResource(m_AudioClip,true); m_AudioSource.clip = null; m_AudioClip = null; }if (Input.GetKeyDown(KeyCode.D)) { m_AudioClip = ResourceManager.Instance.LoadResource<AudioClip>("Assets/GameData/Sounds/menusound.mp3"); m_AudioSource.clip = m_AudioClip; m_AudioSource.Play(); } } private void OnApplicationQuit() { ResourceManager.Instance.ClearCache(); Resources.UnloadUnusedAssets(); } }
|
预加载之后,我们再调用ResourceManager.Instance.LoadResource
加载资源时,耗时几乎为零,如果我们在Start
里将ResourceManager.Instance.PreLoadRes
注释掉,直接加载就会有几毫秒的耗时。