AssetBundle管理機制(上)


AssetBundle內存管理機制
 
 

接上期AssetBundle打包的講解,我們今天為大家繼續探秘AssetBundle,從管理機制的角度出發,談談其資源加載和卸載的原理。

同時如果你恰有相關疑問,歡迎后台留言給UWA,或者加入QQ群(465082844)討論,當然也不要忘記關注UWA哦。

 

◆◆◆◆

 AssetBundle加載基礎


通過AssetBundle加載資源,分為兩步,第一步是獲取AssetBundle對象,第二步是通過該對象加載需要的資源。而第一步又分為兩種方式,下文中將結合常用的API進行詳細地描述。

 

第一步,獲取AssetBundle對象的常用API


方式一,先獲取WWW對象,再通過WWW.assetBundle獲取AssetBundle對象:

● public WWW(string url);

加載Bundle文件並獲取WWW對象,完成后會在內存中創建較大的WebStream(解壓后的內容,通常為原Bundle文件的4~5倍大小,紋理資源比例可能更大),因此后續的AssetBundle.Load可以直接在內存中進行。

【厚積薄發】你應該知道的AssetBundle管理機制(上)    

 ● public static WWW LoadFromCacheOrDownload(string url, int version, uint crc = 0);

加載Bundle文件並獲取WWW對象,同時將解壓形式的Bundle內容存入磁盤中作為緩存(如果該Bundle已在緩存中,則省去這一步),完成后只會在內存中創建較小的SerializedFile,而后續的AssetBundle.Load需要通過IO從磁盤中的緩存獲取。

 

● public AssetBundle assetBundle;

通過之前兩個接口獲取WWW對象后,即可通過WWW.assetBundle獲取AssetBundle對象。


方式二,直接獲取AssetBundle:

● public static AssetBundle CreateFromFile(string path);

通過未壓縮的Bundle文件,同步創建AssetBundle對象,這是最快的創建方式。創建完成后只會在內存中創建較小的SerializedFile,而后續的AssetBundle.Load需要通過IO從磁盤中獲取。

 

● public static AssetBundleCreateRequest CreateFromMemory(byte[] binary);

通過Bundle的二進制數據,異步創建AssetBundle對象。完成后會在內存中創建較大的WebStream。調用時,Bundle的解壓是異步進行的,因此對於未壓縮的Bundle文件,該接口與CreateFromMemoryImmediate等價。

 

● public static AssetBundle CreateFromMemoryImmediate(byte[] binary);

該接口是CreateFromMemory的同步版本。

 

注:5.3下分別改名為LoadFromFile,LoadFromMemory,LoadFromMemoryAsync並增加了LoadFromFileAsync,且機制也有一定的變化,可詳見Unity官方文檔。

 

第二步,從AssetBundle加載資源的常用API

 

● public Object Load(string name, Type type);

通過給定的名字和資源類型,加載資源。加載時會自動加載其依賴的資源,即Load一個Prefab時,會自動Load其引用的Texture資源。

 

● public Object[] LoadAll(Type type);

一次性加載Bundle中給定資源類型的所有資源。

 

● public AssetBundleRequest LoadAsync(string name, Type type);

該接口是Load的異步版本。

 

注:5.x下分別改名為LoadAsset,LoadAllAssets,LoadAssetAsync,並增加了LoadAllAssetsAsync。

 

◆◆◆◆

AssetBundle加載進階

 

接口對比:new WWW與WWW.LoadFromCacheOrDownload

 

前者的優勢

●  后續的Load操作在內存中進行,相比后者的IO操作開銷更小;

● 不形成緩存文件,而后者則需要額外的磁盤空間存放緩存;

● 能通過WWW.texture,WWW.bytes,WWW.audioClip等接口直接加載外部資源,而后者只能用於加載AssetBundle;

 

前者的劣勢

● 每次加載都涉及到解壓操作,而后者在第二次加載時就省去了解壓的開銷;

●  在內存中會有較大的WebStream,而后者在內存中只有通常較小的SerializedFile。(此項為一般情況,但並不絕對,對於序列化信息較多的Prefab,很可能出現SerializedFile比WebStream更大的情況)

 

內存分析

 【厚積薄發】你應該知道的AssetBundle管理機制(上) 

在管理AssetBundle時,了解其加載過程中對內存的影響意義重大。在上圖中,我們在中間列出了AssetBundle加載資源后,內存中各類物件的分布圖,在左側則列出了每一類內存的產生所涉及到的加載API:

 

● WWW對象:在第一步的方式1中產生,內存開銷小;

● WebStream:在使用new WWW或CreateFromMemory時產生,內存開銷通常較大;

● SerializedFile:在第一步中兩種方式都會產生,內存開銷通常較小;

● AssetBundle對象:在第一步中兩種方式都會產生,內存開銷小;

● 資源(包括Prefab):在第二步中通過Load產生,根據資源類型,內存開銷各有大小;

● 場景物件(GameObject):在第二步中通過Instantiate產生,內存開銷通常較小。

 

在后續的章節中,我們還將針對該圖中各類內存物件分析其卸載的方式,從而避免內存殘留甚至泄露。

 

注意點

● CreateFromFile只能適用於未壓縮的AssetBundle,而Android系統下StreamingAssets是在壓縮目錄(.jar)中,因此需要先將未壓縮的AssetBundle放到SD卡中才能對其使用CreateFromFile。

Application.streamingAsstsPath = "jar:file://" Application.dataPath "!/assets/";

 

● iOS系統有256個開啟文件的上限,因此,內存中通過CreateFromFile或WWW.LoadFromCacheOrDownload加載的AssetBundle對象也會低於該值,在較新的版本中,如果LoadFromCacheOrDownload超過上限,則會自動改為new WWW的形式加載,而較早的版本中則會加載失敗。 

 

● CreateFromFile和WWW.LoadFromCacheOrDownload的調用會增加RersistentManager.Remapper的大小,而PersistentManager負責維護資源的持久化存儲,Remapper保存的是加載到內存的資源HeapID與源數據FileID的映射關系,它是一個Memory Pool,其行為類似Mono堆內存,只增不減,因此需要對這兩個接口的使用做合理的規划。

 

● 對於存在依賴關系的Bundle包,在加載時主要注意順序。舉例來說,假設CanvasA在BundleA中,所依賴的AtlasB在BundleB中,為了確保資源正確引用,那么最晚創建BundleB的AssetBundle對象的時間點是在實例化CanvasA之前。即,創建BundleA的AssetBundle對象時、Load(“CanvasA”)時,BundleB的AssetBundle對象都可以不在內存中。

【厚積薄發】你應該知道的AssetBundle管理機制(上) 

● 根據經驗,建議AssetBundle文件的大小不超過1MB,因為在普遍情況下Bundle的加載時間與其大小並非呈線性關系,過大的Bundle可能引起較大的加載開銷。

 

● 由於WWW對象的加載是異步的,因此逐個加載容易出現下圖中CPU空閑的情況(選中幀處Vsync占了大部分),此時建議適當地同時加載多個對象,以增加CPU的使用率,同時加快加載的完成。

 【厚積薄發】你應該知道的AssetBundle管理機制(上) 

以上是AssetBundle資源加載部分,有加載自然有卸載,鑒於篇幅,我們將另起一篇,敬請關注!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM