最近在處理打開客戶端界面加載卡頓的問題擅憔,兩年前做cocos2dx項目的時候也注意到了這個問題,不過因為當(dāng)時的界面比較簡單倍阐,也沒有太多精力去處理获雕,不了了之。
現(xiàn)象是這樣的收捣,幾百行甚至幾千行的客戶端代碼里面,大部分時間都只卡在widgetFromJsonFile這個函數(shù)中庵楷,即對UI界面的UI元素樹進(jìn)行加載罢艾。不難猜想,里面進(jìn)行了遞歸遍歷并生成了一個個對應(yīng)的cocos widget尽纽。我們組也對此做了不少的底層優(yōu)化咐蚯,紋理的緩存優(yōu)化,json文件的加載優(yōu)化弄贿,處理文本的卡頓bug等等春锋。
拋開底層的優(yōu)化,單純地看待創(chuàng)建UI界面這個問題差凹。整個流程只有兩個入口:1. load期奔,2. destroy,非常類似于服務(wù)端開發(fā)永遠(yuǎn)都優(yōu)化不完的malloc和free函數(shù)危尿。很容易想到UI界面緩存呐萌,顯然這不是我想出來的,夢幻手游的UI界面就做了這種處理谊娇,細(xì)心的同學(xué)可以發(fā)現(xiàn)肺孤,打開商城界面操作一番,關(guān)閉界面再次打開,有些部分竟還是上次打開保留下來的赠堵,這是夢幻對界面加載貢獻(xiàn)非常大的一個優(yōu)化小渊,緩存下來的界面可以做到瞬開。
有了加載結(jié)果緩存茫叭,那什么時候清理緩存呢酬屉,我目前發(fā)現(xiàn)的是斷線重連會清理。其實清理方法很多杂靶,通過檢測內(nèi)存不足的時候清理梆惯,或者通過LRU的方式清理,或者兼而有之吗垮。最近發(fā)現(xiàn)劍俠情緣移動版是通過LRU的方式清理的垛吗。
下面來說下實現(xiàn),主要的變化是烁登,創(chuàng)建出來的root widget怯屉,增加了一個休眠狀態(tài),即三種狀態(tài):創(chuàng)建饵沧、銷毀锨络、休眠。休眠狀態(tài)就是被cache住的狀態(tài)狼牺,可以被喚醒重新利用來達(dá)到優(yōu)化的效果羡儿。每次創(chuàng)建出來的widget通過FakeUI作為代理,接管原來的removeFromParent和setVisible這兩個函數(shù)是钥,緩存的時候并不真正銷毀掠归,只是隱藏起來,并記錄下上次休眠的時間悄泥。
界面的生存周期流程圖如下:
再創(chuàng)建一個管理UIService虏冻,每次創(chuàng)建界面時候先讀緩存,有則直接激活弹囚,沒有則走正常創(chuàng)建流程厨相,創(chuàng)建完以后,判斷是否需要加入界面緩存管理鸥鹉。添加幾個接口就可以投入使用了蛮穿,如下:
class UIService(object):
add_to_cache(self, fake_ui)
get_cache(self, name)
clear(self, force=False) # 清理所有深度睡眠的ui cache
step_clear(self) # LRU的方式,一次清理一個深度睡眠的ui cache
為了減少設(shè)備的內(nèi)存占用毁渗,界面緩存提供了一個根據(jù)LRU的淘汰方式來逐步清理休眠中的界面绪撵,實現(xiàn)如下:
def step_clear(self):
""" LRU的方式,一次清理一個深度睡眠的csb cache """
oldest_cache = None
sorted_cache = self.sort_cache()
for i,cache in enumerate(sorted_cache):
deep_sleep_tiem = cache.get_deep_sleep_time()
if cache.is_deep_sleep() == True and deep_sleep_tiem > 0 and not self.check_ancestor(cache): # 只檢索深度睡眠中的cache
oldest_cache = cache
break
if oldest_cache:
name = oldest_cache.get_name()
oldest_cache.fake_do_destroy()
del self._csb_cache[name]
寫完發(fā)現(xiàn)才100來行代碼祝蝠,對原有的邏輯代碼侵害非常小音诈,但是需要注意對root_widget動態(tài)addChild的控件需要清理以便下一次利用幻碱,最后再說一下,這種是通過內(nèi)存來換取時間的方法细溅,由于UI緩存長期占用紋理褥傍,內(nèi)存占用過高降不下去,導(dǎo)致我們ios上線初期頻繁閃退喇聊。后來我們最終只在android設(shè)備上開啟了界面緩存恍风。