Addressables踩坑記錄
首先應(yīng)該了解ResourceManager的基本流程,核心主要是以下幾塊耸采。
- ResourceManager 資源管理類,目的是提供一種通用的方法來訪問資源,同時(shí)抽象出特定的加載實(shí)現(xiàn)忠怖。
- IResourceLocation 資源定位類,包含了加載資源的全部信息 (what/where/how/dependencies)
- IResourceProvider 資源提供者抄瑟,提供不同資源的加載解析方式
- IAsyncOperation 異步操作類凡泣,通過ResourceManager.StartOperation調(diào)用向管理器里面注冊(cè)異步操作枉疼,再由ResourcecManager進(jìn)行統(tǒng)一調(diào)度管理(看上去賊蛋疼)。
下面走進(jìn)Addressables.InitializeAsync()鞋拟,探究Addressables 初始化流程骂维。
- InitializationOperation.CreateInitializationOperation
internal static AsyncOperationHandle<IResourceLocator> CreateInitializationOperation(AddressablesImpl aa, string playerSettingsLocation, string providerSuffix)
{
var jp = new JsonAssetProvider();
jp.IgnoreFailures = true; //!!!注意,這里坑得很
aa.ResourceManager.ResourceProviders.Add(jp);
var tdp = new TextDataProvider();
tdp.IgnoreFailures = true;//!!!注意贺纲,這里坑得很
aa.ResourceManager.ResourceProviders.Add(tdp);
aa.ResourceManager.ResourceProviders.Add(new ContentCatalogProvider(aa.ResourceManager));
//以上是一些后面會(huì)用到解析catalog.json和catalog.hash對(duì)應(yīng)Provider的初始化
var runtimeDataLocation = new ResourceLocationBase("RuntimeData", playerSettingsLocation, typeof(JsonAssetProvider).FullName, typeof(ResourceManagerRuntimeData));
var initOp = new InitializationOperation(aa);
initOp.m_rtdOp = aa.ResourceManager.ProvideResource<ResourceManagerRuntimeData>(runtimeDataLocation);
initOp.m_ProviderSuffix = providerSuffix;
initOp.m_InitGroupOps = new InitalizationObjectsOperation();
initOp.m_InitGroupOps.Init(initOp.m_rtdOp, aa);
var groupOpHandle = aa.ResourceManager.StartOperation(initOp.m_InitGroupOps, initOp.m_rtdOp);
return aa.ResourceManager.StartOperation<IResourceLocator>(initOp, groupOpHandle);
}
忽略前面provider初始化航闺,經(jīng)過理性分析,這里注冊(cè)了三個(gè)異步操作猴誊,并且有依賴關(guān)系潦刃,分別為:initOp.m_rtdOp,initOp.m_InitGroupOps懈叹,以及initOp乖杠。
- initOp.m_rtdOp 首先initOp.m_rtdOp是加載settings.json,包裝在ProviderOperation澄成,主要操作為異步調(diào)用m_Provider.Provide(new ProvideHandle(m_ResourceManager, this));provider的方法處理對(duì)應(yīng)的資源胧洒,這里處理完成得到了setting.json數(shù)據(jù),序列化到ResourceManagerRuntimeData數(shù)據(jù)結(jié)構(gòu)對(duì)象里面墨状,這里我們先打開一份setting.json
{
"m_buildTarget": "StandaloneWindows64",
"m_SettingsHash": "",
"m_CatalogLocations": [{
"m_Keys": ["AddressablesMainContentCatalogRemoteHash"],
"m_InternalId": "http://localhost/StandaloneWindows64/catalog_2020.10.14.15.32.29.hash",
"m_Provider": "UnityEngine.ResourceManagement.ResourceProviders.TextDataProvider",
"m_Dependencies": [],
"m_ResourceType": {
"m_AssemblyName": "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"m_ClassName": "System.String"
}
}, {
"m_Keys": ["AddressablesMainContentCatalogCacheHash"],
"m_InternalId": "{UnityEngine.Application.persistentDataPath}/com.unity.addressables/catalog_2020.10.14.15.32.29.hash",
"m_Provider": "UnityEngine.ResourceManagement.ResourceProviders.TextDataProvider",
"m_Dependencies": [],
"m_ResourceType": {
"m_AssemblyName": "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089",
"m_ClassName": "System.String"
}
}, {
"m_Keys": ["AddressablesMainContentCatalog"],
"m_InternalId": "{UnityEngine.AddressableAssets.Addressables.RuntimePath}/catalog.json",
"m_Provider": "UnityEngine.AddressableAssets.ResourceProviders.ContentCatalogProvider",
"m_Dependencies": ["AddressablesMainContentCatalogRemoteHash", "AddressablesMainContentCatalogCacheHash"],
"m_ResourceType": {
"m_AssemblyName": "Unity.Addressables, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null",
"m_ClassName": "UnityEngine.AddressableAssets.ResourceLocators.ContentCatalogData"
}
}],
"m_ProfileEvents": false,
"m_LogResourceManagerExceptions": true,
"m_ExtraInitializationData": [],
"m_DisableCatalogUpdateOnStart": false,
"m_IsLocalCatalogInBundle": false,
"m_CertificateHandlerType": {
"m_AssemblyName": "",
"m_ClassName": ""
},
"m_AddressablesVersion": "1.16.1",
"m_maxConcurrentWebRequests": 500
}
- initOp.m_InitGroupOps卫漫,有了ResourceManagerRuntimeData數(shù)據(jù)之后執(zhí)行InitalizationObjectsOperation異步操作,這里主要是初始化m_ExtraInitializationData(setting.json)內(nèi)的數(shù)據(jù)(目前沒用到)
- 最后執(zhí)行initOp肾砂,也就是InitializationOperation列赎,初始化setting.json中的一些設(shè)置,初始化catalogLocations通今,從上面可以看到catalog有3個(gè)粥谬,如果沒看Remote,也就只有一個(gè)辫塌,就沒有接下來的步驟咯漏策。
- 初始化catalog,主要是包含三個(gè):
- 服務(wù)器上對(duì)于的catalog.hash
- 本地持續(xù)化目錄的catalog.hash
- 對(duì)比本地與服務(wù)器上catalog的hash值臼氨,判斷用本地的catalog.json還是遠(yuǎn)程的catalog.json掺喻,加載遠(yuǎn)程的catalog并替換本地的catalog,并解析出ContentCatalogData文件储矩,具體json解析邏輯在ContentCatalogData.CreateCustomLocator中感耙。
- 最后解析出catalog.json中所有的資源location信息,并注冊(cè)到ResourceManager中持隧。
解讀Addressables中的資源加載
Addressables中主要是通過本地和遠(yuǎn)端catalog.hash對(duì)比即硼,并加載出最新的catalog.json文件,而catalog.json讀取的location信息會(huì)指明你需要加載的資源在本地還是服務(wù)器屡拨,從而AssetBundleProvider中通過不同的加載方式進(jìn)行加載只酥,本地目錄加載就不多說褥实,遠(yuǎn)程加載使用了UnityWebRequestAssetBundle做加載,并搭配CachedAssetBundle做本地緩存裂允,緩存使用BundleName以及資源的hash值损离,如果資源有變化,hash同樣也會(huì)發(fā)生變化绝编,本地命中失敗僻澎,具體邏輯可參考AssetBundleProvider
關(guān)于Addressables中的熱更
Addressables想依靠本地的catalog.hash與Remote的catalog.hash做對(duì)比,從而加載到我們打出的最新catalog.json文件十饥,從而解析出資源的location窟勃,這里Addressables有一個(gè)bug,上文代碼中標(biāo)注了IgnoreFailures設(shè)置為了true绷跑,它的本意是做第一次加載本地緩存時(shí)拳恋,因?yàn)楸镜乜沙掷m(xù)目錄是沒有catalog文件的凡资,所以忽略了加載失敗的情況砸捏,但是這也會(huì)導(dǎo)致我們從Remote加載hash文件的時(shí)候,如果加載失敗也并不會(huì)有異常拋出隙赁,而會(huì)直接使用可持續(xù)目錄下的catalog.json垦藏。