前面自定义了Bundle的打包方式,现在就要自定义加载方式了,不然就会走原来设置的远程加载的原本逻辑,我们多做了一个内置包列表和复制移动Bundle到Library中的准备工作就没有用了。
注意如果是UnityWebRequestAssetBundle,则不会支持访问data去保存,不过自行缓存,此时只需要判断是不是内置Bundle就行,其他走web。需要自己缓存Bundle的,需要修改UnityWebRequestAssetBundle为UnityWebRequest
新建一个cs文件MyAssetBundleProvider,复制原内容的AssetBundlePrivider到新建cs脚本中,修改相关类名为自己自定义类名。主要修改IAssetBundleResource的BeginOperation方法和WebRequestOperationCompleted方法
//MyAssetBundleProvider.cs private void BeginOperation() { string path = m_ProvideHandle.Location.InternalId; var url = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location); string bundleName = Path.GetFileName(url); // if a path starts with jar:file, it is an android embeded resource. The resource is a local file but cannot be accessed by // FileStream(called in LoadWithDataProc) directly // Need to use webrequest's async call to get the content. if (AssetBundleManager.GetInstance().buildInData != null && AssetBundleManager.GetInstance().buildInData.BuildInBundleNames.Contains(bundleName))//本地资源,内置包 { string streamPath = UnityEngine.AddressableAssets.Addressables.RuntimePath + "/" + PlatformMappingService.GetPlatformPathSubFolder() + "/" + bundleName; Debug.Log("LoadOne:" + streamPath); var crc = m_Options == null ? 0 : m_Options.Crc; CompleteBundleLoad(AssetBundle.LoadFromFile(streamPath)); } else if (AssetBundleManager.GetInstance().IsCache(bundleName)) //已经下载过 缓存到本地的Bundle { string cachePath = Path.Combine(AssetBundleManager.GetInstance().GetBundleCachePath(), bundleName); Debug.Log("LoadTwo:" + cachePath); var crc = m_Options == null ? 0 : m_Options.Crc; CompleteBundleLoad(AssetBundle.LoadFromFile(cachePath)); } else if (ResourceManagerConfig.ShouldPathUseWebRequest(path)) //真正需要下载的Bundle { Debug.Log("DownloadThree:" + url); var req = CreateWebRequest(m_ProvideHandle.Location); req.disposeDownloadHandlerOnDispose = false; m_WebRequestQueueOperation = WebRequestQueue.QueueRequest(req); if (m_WebRequestQueueOperation.IsDone) { m_RequestOperation = m_WebRequestQueueOperation.Result; m_RequestOperation.completed += WebRequestOperationCompleted; } else { m_WebRequestQueueOperation.OnComplete += asyncOp => { m_RequestOperation = asyncOp; m_RequestOperation.completed += WebRequestOperationCompleted; }; } } else { m_RequestOperation = null; m_ProvideHandle.Complete<MyAssetBundleResource>(null, false, new Exception(string.Format("Invalid path in AssetBundleProvider: '{0}'.", path))); } } private void WebRequestOperationCompleted(AsyncOperation op) { UnityWebRequestAsyncOperation remoteReq = op as UnityWebRequestAsyncOperation; var webReq = remoteReq.webRequest; if (!UnityWebRequestUtilities.RequestHasErrors(webReq, out UnityWebRequestResult uwrResult)) { m_downloadHandler = webReq.downloadHandler; string path = m_ProvideHandle.ResourceManager.TransformInternalId(m_ProvideHandle.Location); string bundleName = Path.GetFileName(path); AssetBundleManager.GetInstance().CacheBundle(bundleName, m_downloadHandler.data);//主要是在这里加了一个保存到本地的方法 if (!m_Completed) { m_ProvideHandle.Complete(this, true, null); m_Completed = true; } } else { m_downloadHandler = webReq.downloadHandler; m_downloadHandler.Dispose(); m_downloadHandler = null; string message = string.Format("Web request {0} failed with error '{1}', retrying ({2}/{3})...", webReq.url, webReq.error, m_Retries, m_Options.RetryCount); if (m_Retries < m_Options.RetryCount) { Debug.LogFormat(message); BeginOperation(); m_Retries++; } else { var exception = new Exception(string.Format( "RemoteAssetBundleProvider unable to load from url {0}, result='{1}'.", webReq.url, webReq.error)); m_ProvideHandle.Complete<MyAssetBundleResource>(null, false, exception); } } webReq.Dispose(); }
读取内置包列表,缓存Bundle到本地,检查是否有缓存,都是使用 AssetBundleManager.cs
using System.Collections.Generic; using UnityEngine; using System; using System.IO; [Serializable] public class BuildInBundleData { public List<string> BuildInBundleNames = new List<string>(); } public class AssetBundleManager { private static AssetBundleManager _instance = null; private string cachePath = ""; public BuildInBundleData buildInData; public static AssetBundleManager GetInstance() { if (_instance == null) _instance = new AssetBundleManager(); return _instance; } AssetBundleManager() { cachePath = Path.Combine(Application.persistentDataPath, "ab"); if (!Directory.Exists(cachePath)) { Directory.CreateDirectory(cachePath); } } public void Init() { #if !UNITY_EDITOR var json = Resources.Load<TextAsset>("Version/BuildInBundleName"); buildInData = JsonUtility.FromJson<BuildInBundleData>(json.text); #endif } public String GetBundleCachePath() { return cachePath; } public bool IsCache(string bundleName) { string filePath = Path.Combine(AssetBundleManager.GetInstance().GetBundleCachePath(), bundleName); return File.Exists(filePath); } public void CacheBundle(string bundlename, byte[] bytes) { string filePath = Path.Combine(GetBundleCachePath(), bundlename); if (File.Exists(filePath)) File.Delete(filePath); File.WriteAllBytes(filePath, bytes); string realName = GetRealBundleName(bundlename); string oldPath = PlayerPrefs.GetString(realName, ""); if (oldPath != "") File.Delete(oldPath); PlayerPrefs.SetString(realName, filePath); } public string GetRealBundleName(string bundlename) { if (string.IsNullOrEmpty(bundlename)) return ""; int index = bundlename.LastIndexOf("_"); return bundlename.Substring(0, index); } }
自定义写完后,需要应用使用上这个自定义加载Provider
public static void SetAllGroupToRemote() { var s = AASUtility.GetSettings(); var groups = s.groups; foreach (var group in groups) { BundledAssetGroupSchema bundledAssetGroupSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundledAssetGroupSchema == null) { bundledAssetGroupSchema = group.AddSchema<BundledAssetGroupSchema>(); } bundledAssetGroupSchema.BuildPath.SetVariableByName(group.Settings, AddressableAssetSettings.kRemoteBuildPath); bundledAssetGroupSchema.LoadPath.SetVariableByName(group.Settings, AddressableAssetSettings.kRemoteLoadPath); bundledAssetGroupSchema.SetAssetBundleProviderType(typeof(MyAssetBundleProvider)); //主要是这 EditorUtility.SetDirty(bundledAssetGroupSchema); } AssetDatabase.Refresh(); }