/// <summary> /// 强制卸载指定资源(不管AB加载后属于哪一种类型,强制卸载) /// </summary> /// <param name="abname"></param> public void forceUnloadSpecificResource(string abname) { ResourceLoadType resourceloadtype = ResourceLoadType.NormalLoad; AbstractResourceInfo arinfo = null; foreach (var loadedabinfomap in mAllLoadedResourceInfoMap) { if (arinfo != null) { break; } foreach (var loadedabinfo in loadedabinfomap.Value) { if (loadedabinfo.Key.Equals(abname)) { arinfo = loadedabinfo.Value; resourceloadtype = loadedabinfomap.Key; break; } } } if (arinfo != null) { mAllLoadedResourceInfoMap[resourceloadtype].Remove(arinfo.AssetBundleName); arinfo.dispose(); ResourceLogger.log(string.Format("AB资源 : {0}已强制卸载!", abname)); } else { ResourceLogger.logErr(string.Format("AB资源 : {0}未被加载,无法强制卸载!", abname)); } }
/// <summary> /// AB加载携程 /// </summary> /// <returns></returns> private IEnumerator assetBundleLoadAsync() { while (true) { if (ABAsyncQueue.Count > 0) { CurrentLoadingAssetBundleLoader = ABAsyncQueue.Dequeue(); //检查是否已经同步加载完成 //如果异步加载AB时,同步请求来了,打断异步后续逻辑 //LoadState == ResourceLoadState.None表明同步加载该资源已经完成,无需再异步返回 if (CurrentLoadingAssetBundleLoader.LoadState == ResourceLoadState.None) { //ResourceLogger.logWar("有资源还未开始异步加载就被同步加载打断!"); } else { CurrentLoadingAssetBundleLoader.LoadState = ResourceLoadState.Loading; var abname = CurrentLoadingAssetBundleLoader.AssetBundleName; var abpath = AssetBundlePath.GetABLoadFullPath(abname); AssetBundleCreateRequest abrequest = null; #if UNITY_EDITOR //因为资源不全,很多资源丢失,导致直接报错 //这里临时先在Editor模式下判定下文件是否存在,避免AssetBundle.LoadFromFileAsync()直接报错 if (System.IO.File.Exists(abpath)) { abrequest = AssetBundle.LoadFromFileAsync(abpath); } else { Debug.LogError(string.Format("AB : {0}文件不存在!", CurrentLoadingAssetBundleLoader.AssetBundleName)); } #else abrequest = AssetBundle.LoadFromFileAsync(abpath); #endif yield return(abrequest); //如果异步加载AB时,同步请求来了,打断异步后续逻辑 //LoadState == ResourceLoadState.None表明同步加载该资源已经完成,无需再异步返回 if (CurrentLoadingAssetBundleLoader.LoadState == ResourceLoadState.None) { ResourceLogger.log(string.Format("资源 : {0}加载已完成,异步加载被打断!", abname)); } else { var assetbundle = abrequest.assetBundle; if (assetbundle == null) { ResourceLogger.logErr(string.Format("Failed to load AssetBundle : {0}!", CurrentLoadingAssetBundleLoader.AssetBundleName)); } CurrentLoadingAssetBundleLoader.onSelfABLoadComplete(assetbundle); } } CurrentLoadingAssetBundleLoader = null; } else { yield return(null); } } }
/// <summary> /// 卸载资源(Resources.UnloadAsset遍历卸载) /// </summary> private void unloadResource() { ResourceLogger.log(string.Format("卸载资源:{0}", AssetBundleName)); foreach (var loadedasset in mLoadedAssetMap) { var asset = loadedasset.Value; if (asset is GameObject) { ResourceLogger.log(string.Format("无法通过Resources.UnloadAsset卸载GameObject : {0}资源,后续会清空后通过Resources.UnloadUnsedAsset卸载!", asset.name)); } else if (asset is Component) { ResourceLogger.log(string.Format("无法通过Resources.UnloadAsset卸载Component : {0}资源,后续会清空后通过Resources.UnloadUnsedAsset卸载!", asset.name)); } else { //AssetDatabase模式不支持卸载资源, //因为并非真实的模拟AssetBundle资源加载行为,只是单纯的把所需资源自身加载进来 //Resources.UnloadAsset(loadedasset.Value); ResourceLogger.log(string.Format("假卸载资源:{0}的Asset : {1}", AssetBundleName, loadedasset.Value.name)); } } mLoadedAssetMap.Clear(); mIsReady = false; }
/// <summary> /// 获取AB加载全路径(含热更加载逻辑判定) /// </summary> /// <param name="abname"></param> /// <returns></returns> public static string GetABLoadFullPath(string abname) { //TODO: //热更逻辑路径判定 //if(包外有) // Application.persistentDataPath //{ // 返回包外资源路径 //} //else // Application.streamingAssetsPath //{ // 返回包内资源路径 //} var outterabfullpath = ABHotUpdatePath + abname; if (IsABExitInOutterPath(abname)) { ResourceLogger.log(string.Format("使用包外资源 : {0}", abname)); return(outterabfullpath); } else { ResourceLogger.log(string.Format("使用包内资源 : {0}", abname)); return(ABBuildinPath + abname); } }
/// <summary> /// 真正的请求资源 /// </summary> /// <param name="resname">资源AB名</param> /// <param name="completehandler">加载完成上层回调</param> /// <param name="loadtype">资源加载类型</param> /// <param name="loadmethod">资源加载方式</param> protected override void realRequestResource(string resname, LoadResourceCompleteHandler completehandler, ResourceLoadType loadtype = ResourceLoadType.NormalLoad, ResourceLoadMethod loadmethod = ResourceLoadMethod.Sync) { // 如果资源已经加载完成,直接返回 if (mAllLoadedResourceInfoMap[ResourceLoadType.NormalLoad].ContainsKey(resname)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.NormalLoad][resname]); if (loadtype > ResourceLoadType.NormalLoad) { updateLoadedResourceInfoLoadType(resname, ResourceLoadType.NormalLoad, loadtype); } } else if (mAllLoadedResourceInfoMap[ResourceLoadType.Preload].ContainsKey(resname)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.Preload][resname]); if (loadtype > ResourceLoadType.Preload) { updateLoadedResourceInfoLoadType(resname, ResourceLoadType.Preload, loadtype); } } else if (mAllLoadedResourceInfoMap[ResourceLoadType.PermanentLoad].ContainsKey(resname)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.PermanentLoad][resname]); } else { // 确保同一个资源加载的Loader是同一个 // 保证同一个资源加载完成时上层所有加载该资源的回调正确 AssetBundleLoader abloader = null; if (mABRequestTaskMap.ContainsKey(resname)) { abloader = mABRequestTaskMap[resname]; // 之前有请求resname资源,但还未完成 // 比如先异步请求resname,在异步完成前来了一个同步请求resname // 修改加载方式并添加回调,调用同步加载方式,异步加载会在同步加载完成时一起回调 abloader.LoadMethod = loadmethod; abloader.LoadType = loadtype; abloader.LoadABCompleteCallBack += completehandler; abloader.LoadSelfABCompleteNotifier = onABLoadCompleteNotifier; if (loadmethod == ResourceLoadMethod.Sync) { ResourceLogger.log(string.Format("请求同步加载一个正在异步加载的资源 : {0}", abloader.AssetBundleName)); //重置AB加载状态,走同步加载模式 abloader.LoadState = ResourceLoadState.None; abloader.startLoad(); } } else { abloader = createABLoader(resname); abloader.LoadMethod = loadmethod; abloader.LoadType = loadtype; abloader.LoadABCompleteCallBack = completehandler; abloader.LoadSelfABCompleteNotifier = onABLoadCompleteNotifier; mABRequestTaskMap.Add(resname, abloader); abloader.startLoad(); } } }
/* * /// <summary> * /// 为AB添加指定owner的引用 * /// 所有owner都销毁则ab引用计数归零可回收 * /// </summary> * /// <param name="owner"></param> * private void retainOwner(UnityEngine.Object owner) * { * if (owner == null) * { * ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}添加引用!", AssetBundleName)); * return; * } * * foreach (var referenceowner in mReferenceOwnerList) * { * if (owner.Equals(referenceowner)) * { * return; * } * } * * System.WeakReference wr = new System.WeakReference(owner); * mReferenceOwnerList.Add(wr); * } * * /// <summary> * /// 获取AB有效的引用对象计数 * /// </summary> * /// <returns></returns> * private int updateOwnerReference() * { * for (int i = 0; i < mReferenceOwnerList.Count; i++) * { * UnityEngine.Object o = (UnityEngine.Object)mReferenceOwnerList[i].Target; * if (!o) * { * mReferenceOwnerList.RemoveAt(i); * i--; * } * } * return mReferenceOwnerList.Count; * } */ /// <summary> /// 卸载AB(AssetBundle.Unload(true)的形式) /// </summary> private void unloadAssetBundle() { ResourceLogger.log(string.Format("卸载AB:{0}", AssetBundleName)); if (Bundle != null) { // 索引计数为零时调用释放AB和Asset Bundle.Unload(true); } Bundle = null; mIsReady = false; }
/// <summary> /// 打印所有AB依赖信息 /// </summary> public void printAllResourceDpInfo() { foreach (var abinfo in mAssetBundleDpMap) { ResourceLogger.log(string.Format("AB Name:{0}", abinfo.Key)); foreach (var dpfile in abinfo.Value) { ResourceLogger.log(string.Format(" DP AB Name:{0}", dpfile)); } } }
/// <summary> /// 检查AB包外目录,不存在则创建一个 /// </summary> public static void CheckAndCreateABOutterPathFolder() { if (Directory.Exists(ABHotUpdatePath)) { ResourceLogger.log(string.Format("AB包外目录:{0}已存在!", ABHotUpdatePath)); } else { ResourceLogger.log(string.Format("AB包外目录:{0}不存在,新创建一个!", ABHotUpdatePath)); Directory.CreateDirectory(ABHotUpdatePath); } }
/// <summary> /// 开始资源加载卸载统计 /// </summary> public void startResourceLoadAnalyse() { if (ResourceLoadAnalyseSwitch) { ResourceLoadAnalyseStart = true; ResourceLoadAnalyseMap.Clear(); ResourceLogger.log("开启AB加载统计!"); } else { ResourceLogger.logErr("请先开启AB资源加载分析开关!"); } }
/// <summary> /// 更新已加载AB的加载类型 /// </summary> /// <param name="resname">资源名</param> /// <param name="oldloadtype">旧的加载类型</param> /// <param name="newloadtype">新的家在类型</param> protected void updateLoadedResourceInfoLoadType(string resname, ResourceLoadType oldloadtype, ResourceLoadType newloadtype) { if (mAllLoadedResourceInfoMap[oldloadtype].ContainsKey(resname)) { var abi = mAllLoadedResourceInfoMap[oldloadtype][resname]; mAllLoadedResourceInfoMap[newloadtype].Add(resname, abi); mAllLoadedResourceInfoMap[oldloadtype].Remove(resname); ResourceLogger.log(string.Format("已加载的资源 : {0}从资源类型 : {1}更新到资源类型 : {2}!", resname, oldloadtype, newloadtype)); } else { ResourceLogger.logErr(string.Format("资源类型 : {0}里找不到已加载的资源 : {1},无法更新该资源的加载类型!", oldloadtype, resname)); } }
/// <summary> /// AB资源加载完成 /// </summary> public void onSelfABLoadComplete(AssetBundle ab = null) { ResourceLogger.log(string.Format("AB:{0}自身加载完成!", AssetBundleName)); mABInfo = AssetBundleModule.createAssetBundleInfo(AssetBundleName, ab); mABInfo.updateLastUsedTime(); LoadState = ResourceLoadState.SelfComplete; // 通知上层自身AB加载完成,移除加载任务 LoadSelfABCompleteNotifier(this); LoadSelfABCompleteNotifier = null; loadDepAssetBundle(); }
/// <summary> /// 加载自身AB /// </summary> private void loadSelfAssetBundle() { ResourceLogger.log(string.Format("加载AB:{0}", AssetBundleName)); if (LoadMethod == ResourceLoadMethod.Sync) { LoadState = ResourceLoadState.Loading; loadAssetBundleSync(); } else { //异步加载AB修改成限制携程数量,入队列的形式 //ModuleManager.Singleton.getModule<ResourceModuleManager>().StartCoroutine(loadAssetBundleAsync()); AssetBundleAsyncQueue.enqueue(this); } }
/// <summary> /// 加载资源 /// </summary> private void loadAsset() { ResourceLogger.log(string.Format("加载资源:{0}", AssetBundleName)); //暂时默认都当同步处理 if (LoadMethod == ResourceLoadMethod.Sync) { LoadState = ResourceLoadState.Loading; loadAssetSync(); } else { LoadState = ResourceLoadState.Loading; loadAssetSync(); } }
/// <summary> /// 依赖AB加载完成回调 /// </summary> /// <param name="resinfo">ab加载信息</param> private void onDepABLoadComplete(AbstractResourceInfo resinfo) { var abinfo = resinfo as AssetBundleInfo; ResourceLogger.log(string.Format("依赖AB:{0}加载成功!", abinfo.AssetBundleName)); mDepAssetBundleInfoList.Add(abinfo); #if UNITY_EDITOR //Editor模式下的调试功能 // 移除已经加载过的,存储来测试查看用 mUnloadedAssetBundleName.Remove(abinfo.AssetBundleName); #endif mLoadedDepABCount++; // 作为依赖AB时并不会触发getAsset || instantiateAsset之类的接口, // 依赖于Unity加载依赖AB自动还原的机制,所以这里我们需要手动更新AB资源的最近使用时间 abinfo.updateLastUsedTime(); if (mLoadedDepABCount == mDepABCount) { allABLoadedComplete(); } }
/// <summary> /// 打印当前资源所有使用者信息以及索引计数(开发用) /// </summary> public void printAllLoadedResourceOwnersAndRefCount() { ResourceLogger.log("Normal Loaded AssetDatabase Info:"); foreach (var adi in mAllLoadedResourceInfoMap[ResourceLoadType.NormalLoad]) { adi.Value.printAllOwnersNameAndRefCount(); } ResourceLogger.log("Preload Loaded AssetDatabase Info:"); foreach (var adi in mAllLoadedResourceInfoMap[ResourceLoadType.Preload]) { adi.Value.printAllOwnersNameAndRefCount(); } ResourceLogger.log("Permanent Loaded AssetDatabase Info:"); foreach (var adi in mAllLoadedResourceInfoMap[ResourceLoadType.PermanentLoad]) { adi.Value.printAllOwnersNameAndRefCount(); } }
/// <summary> /// 打印当前AB所有使用者信息以及索引计数(开发用) /// </summary> public void printAllOwnersNameAndRefCount() { ResourceLogger.log(string.Format("AB Name: {0}", AssetBundlePath)); ResourceLogger.log(string.Format("Ref Count: {0}", RefCount)); if (mReferenceOwnerList.Count == 0) { ResourceLogger.log("Owners Name : None"); } else { ResourceLogger.log("Owners Name :"); for (int i = 0, length = mReferenceOwnerList.Count; i < length; i++) { if (mReferenceOwnerList[i].Target != null) { ResourceLogger.log(string.Format("owner[{0}] : {1}", i, mReferenceOwnerList[i].Target.ToString())); } } } ResourceLogger.log(string.Format("Last Used Time: {0}", LastUsedTime)); }
/// <summary> /// 停止资源加载卸载统计并输出 /// </summary> public void endResourceLoadAnalyse() { if (ResourceLoadAnalyseSwitch) { if (ResourceLoadAnalyseStart) { ResourceLoadAnalyseStart = false; outputResourceLoadedInfoDetail(); ResourceLoadAnalyseMap.Clear(); ResourceLogger.log("结束资源加载统计!"); } else { ResourceLogger.logErr("请先启动资源加载统计!"); } } else { ResourceLogger.logErr("请先开启资源资源加载分析开关!"); } }
/// <summary> /// 所有AB加载完成(自身和依赖AB) /// </summary> private void allABLoadedComplete() { ResourceLogger.log(string.Format("AB:{0}所有AB加载完成!", AssetBundleName)); // 所有AB加载完添加依赖AB索引信息并通知上层可以使用了 foreach (var dploader in mDepAssetBundleInfoList) { mABInfo.addDependency(dploader); } LoadState = ResourceLoadState.AllComplete; // 所有AB加载完成才算AB Ready可以使用 mABInfo.mIsReady = true; // 通知上层ab加载完成,可以开始加载具体的asset LoadABCompleteCallBack(mABInfo); LoadABCompleteCallBack = null; // 自身AB以及依赖AB加载完成后,AssetBundleLoader的任务就完成了,回收重用 AssetBundleLoaderFactory.recycle(this); }
/// <summary> /// 移除指定拥有者绑定(用于解决上层绑定对象一直存在导致资源无法释放的问题) /// </summary> /// <param name="owner"></param> /// <returns></returns> public bool releaseOwner(UnityEngine.Object owner) { if (owner == null) { ResourceLogger.logErr(string.Format("引用对象不能为空!无法为资源:{0}解除绑定!", AssetBundlePath)); return(false); } var ownerindex = mReferenceOwnerList.FindIndex((ow) => ow.Target.Equals(owner)); if (ownerindex != -1) { ResourceLogger.log(string.Format("资源:{0}找到指定绑定对象:{1},解除绑定!", AssetBundlePath, owner)); mReferenceOwnerList.RemoveAt(ownerindex); return(true); } else { ResourceLogger.log(string.Format("资源:{0}找不到指定绑定对象:{1},解除绑定失败!", AssetBundlePath, owner)); return(false); } }
/// <summary> /// 资源请求携程 /// </summary> /// <returns></returns> private IEnumerator resourcesRequest() { foreach (var hotupdateres in mTestHotUpdateResourceList) { var resurl = TestResourceURL + hotupdateres; ResourceLogger.log(string.Format("下载资源 : {0}", resurl)); var webrequest = UnityWebRequest.Get(resurl); yield return(webrequest.SendWebRequest()); if (webrequest.isNetworkError) { ResourceLogger.logErr(string.Format("{0}资源下载出错!", hotupdateres)); ResourceLogger.logErr(webrequest.error); } else { if (webrequest.isDone) { ResourceLogger.log(string.Format("{0}资源下载完成!", hotupdateres)); var data = webrequest.downloadHandler.data; //检查包外是否存在同名资源,存在的话需要先删除再存储最新到包外 var outterabfullpath = AssetBundlePath.ABHotUpdatePath + hotupdateres; if (AssetBundlePath.IsABExitInOutterPath(hotupdateres)) { ResourceLogger.log(string.Format("删除包外资源 : {0}", hotupdateres)); File.Delete(outterabfullpath); } using (var fs = File.Create(outterabfullpath)) { fs.Write(data, 0, data.Length); fs.Flush(); fs.Close(); ResourceLogger.log(string.Format("包外资源 : {0}写入完成!", hotupdateres)); } } } } }
/// <summary> /// 卸载资源(Resources.UnloadAsset遍历卸载) /// </summary> private void unloadResource() { ResourceLogger.log(string.Format("卸载资源:{0}", AssetBundleName)); foreach (var loadedasset in mLoadedAssetMap) { var asset = loadedasset.Value; if (asset is GameObject) { ResourceLogger.log(string.Format("无法通过Resources.UnloadAsset卸载GameObject : {0}资源,后续会清空后通过Resources.UnloadUnsedAsset卸载!", asset.name)); } else if (asset is Component) { ResourceLogger.log(string.Format("无法通过Resources.UnloadAsset卸载Component : {0}资源,后续会清空后通过Resources.UnloadUnsedAsset卸载!", asset.name)); } else { //AssetDatabase模式下不会真正卸载资源(因为没有真正打AB没有真正的依赖关系,只是假的模拟AB加载) //Resources.UnloadAsset(loadedasset.Value); ResourceLogger.log(string.Format("卸载资源:{0}的Asset : {1}", AssetBundleName, loadedasset.Value.name)); } } mLoadedAssetMap.Clear(); mIsReady = false; }
/// <summary> /// 真正的请求资源 /// </summary> /// <param name="respath">资源AB路径</param> /// <param name="completehandler">加载完成上层回调</param> /// <param name="loadtype">资源加载类型</param> /// <param name="loadmethod">资源加载方式</param> protected override void realRequestResource(string respath, LoadResourceCompleteHandler completehandler, ResourceLoadType loadtype = ResourceLoadType.NormalLoad, ResourceLoadMethod loadmethod = ResourceLoadMethod.Sync) { // AB运行时统一转成小写,避免和AB打包那方输出的信息不一致 respath = respath.ToLower(); var abpath = string.Empty; // 因为依赖AB加载也是走统一入口,所以要区分是AB路径还是Asset路径 if (!mAssetBundleBuildInfo.isABPath(respath)) { // AB依赖信息文件和AB打包 abpath = getAssetPathAssetBundleName(respath); } else { abpath = respath; } // 如果资源已经加载完成,直接返回 if (mAllLoadedResourceInfoMap[ResourceLoadType.NormalLoad].ContainsKey(abpath)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.NormalLoad][abpath]); if (loadtype > ResourceLoadType.NormalLoad) { updateLoadedResourceInfoLoadType(abpath, ResourceLoadType.NormalLoad, loadtype); } } else if (mAllLoadedResourceInfoMap[ResourceLoadType.Preload].ContainsKey(abpath)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.Preload][abpath]); if (loadtype > ResourceLoadType.Preload) { updateLoadedResourceInfoLoadType(abpath, ResourceLoadType.Preload, loadtype); } } else if (mAllLoadedResourceInfoMap[ResourceLoadType.PermanentLoad].ContainsKey(abpath)) { completehandler(mAllLoadedResourceInfoMap[ResourceLoadType.PermanentLoad][abpath]); } else { // 确保同一个资源加载的Loader是同一个 // 保证同一个资源加载完成时上层所有加载该资源的回调正确 AssetBundleLoader abloader = null; if (mABRequestTaskMap.ContainsKey(abpath)) { abloader = mABRequestTaskMap[abpath]; // 之前有请求resname资源,但还未完成 // 比如先异步请求resname,在异步完成前来了一个同步请求resname // 修改加载方式并添加回调,调用同步加载方式,异步加载会在同步加载完成时一起回调 abloader.LoadMethod = loadmethod; abloader.LoadType = loadtype; abloader.LoadABCompleteCallBack += completehandler; abloader.LoadSelfABCompleteNotifier = onABLoadCompleteNotifier; if (loadmethod == ResourceLoadMethod.Sync) { ResourceLogger.log(string.Format("请求同步加载一个异步加载状态:{0}的资源 : {1}", abloader.LoadState.ToString(), abloader.AssetBundlePath)); //重置AB加载状态,走同步加载模式 abloader.LoadState = ResourceLoadState.None; abloader.startLoad(); } } else { abloader = createABLoader(abpath); abloader.LoadMethod = loadmethod; abloader.LoadType = loadtype; abloader.LoadABCompleteCallBack = completehandler; abloader.LoadSelfABCompleteNotifier = onABLoadCompleteNotifier; mABRequestTaskMap.Add(abpath, abloader); abloader.startLoad(); } } }