接近年底,想分享點(diǎn)兒東西給大家水评。
Android UI繪制過程
開發(fā)中的卡頓我想沒跟人都遇到過猩系,之前也是搜博客看看怎么個解決辦法,沒有認(rèn)真研究過中燥,今天我打算跟大家聊一聊寇甸。
先從View 說吧。相信大家應(yīng)該都知道View的繪制過程疗涉,measure幽纷,layout,draw博敬。丟幀一定是在16ms內(nèi)沒有把這些事兒干完就對了友浸,這里我們簡單的分一下,主要是計(jì)算時間偏窝,以及繪圖時間收恢。
計(jì)算時間:這里的measure武学,layout的過程,都是會向下遞歸計(jì)算的伦意,學(xué)過數(shù)據(jù)結(jié)構(gòu)的話火窒,應(yīng)該知道,深搜的代價是很大的驮肉。所以盡量讓樹的高度降低熏矿,這里就引出扁平化布局。
繪圖時間:這里需要著重講一下离钝,因?yàn)橛袝r候這才是我們UI卡頓的主要原因票编。在這里我們要把a(bǔ)ndroid的試圖看成是三維的,就像photoshop的圖層一樣卵渴。android在繪制的時候就會一層一層的“粉刷”慧域,好了,那么造成卡頓浪读,也就是丟幀昔榴,說白了最后沒有在16ms內(nèi)做完。好了碘橘,讓我們剖析一下:
1.invalidate():
我們知道invalidate 是用來請求View 重繪的互订,
這里可以看出來draw的過程其實(shí)就是拿到AttachInfo 里面包含著繪制信息,以及將繪制區(qū)域拿到痘拆,通過parent去繪制屁奏。讓我們跟進(jìn)去蜈出。
這里的dirty代表你繪制的這塊區(qū)域是否透明当纱。
這里我們看到了個關(guān)鍵函數(shù) scheduleTraversals 张症,為什么說神奇宴合。我們看一下
這里最重要的是Choreographer 這個蝌箍,我們最終算出來的繪制信息都要通過它回調(diào)跋选,開始他會注冊一個廣播用來接收時鐘信息棍好,然后他會在內(nèi)部建立一個UI繪制隊(duì)列:CallbackQueue斤寂,我們在外部CallBack的時候识颊,會將我們的繪制信息作為CallbackRecord 然后會在接收到一個時鐘信號的時候進(jìn)行doFrame操作诚镰,并打印Traces信息,從而來繪制一幀祥款。
可以看到這里我們把我們的繪制內(nèi)容扔到隊(duì)列里清笨,等待輪訓(xùn)。
接收時鐘脈沖信號的廣播刃跛,16ms一次抠艾,我們的目的就是在這個時鐘脈沖里搞定整個 view
2.Android 動畫
Animator,ScrollTo桨昙,offsetLeftAndRight检号,這里面我們先單列這幾項(xiàng)腌歉,都是同一個原理。這里我們可以大膽的猜想齐苛,一定是頻繁執(zhí)行我們的 Choreographer.CallBack 來繪制翘盖,因?yàn)橹灰?6ms內(nèi)繪制成功,那就是流暢的動畫凹蜂。下面我們驗(yàn)證一下
ScrollTo:
我們先看一下 View 中這個方法
很簡單馍驯,我們都可以看懂,開始位置玛痊,結(jié)束位置汰瘫,這里我們重點(diǎn)關(guān)注 postInvalidateOnAnimation() ?這個方法
我們可以看到,這里的動畫過程繪制他還是扔到了ViewRootImpl 代理做這件事卿啡。
這里我們看到他開了個線程 mInvalidateOnAnimationRunnable 去添加我們這個將要繪制的 view吟吝,接下來我們繼續(xù)庖丁解牛
終于菱父,應(yīng)了我們的猜想颈娜,ViewRootImpl 有一個專門執(zhí)行動畫繪制操作的線程,我們可以看到 run() 里面不斷地CallBack浙宜,然后回收官辽,當(dāng)然里面有些線程鎖啥的不涉及本文就不細(xì)說了。
3.ValueAnimator:
這里我們有個 AnimationHandler 來執(zhí)行動畫操作粟瞬,這其中我們可以看到
這里在不斷循環(huán)我們所有的anim同仆,并在不斷執(zhí)行 scheduleAnimation 方法
剩下的大家自己翻閱源碼把。
這里總結(jié)一下裙品。我們所有界面上視圖的變化都是都是 ViewRootImpl 把需要重繪的東西填充 Choreographer 中的 mCallbackQueues 隊(duì)列俗批,然后在時鐘脈沖的廣播下進(jìn)行輪訓(xùn)執(zhí)行。
既然提到隊(duì)列市怎,假如我們在16ms內(nèi)大量的填充 AttachInfo 之類的繪制OBJ岁忘,就會導(dǎo)致無法再一次時鐘脈沖內(nèi)繪制完畢,就會在造成丟幀区匠,UI阻塞干像。
避免 Android UI 卡頓解決辦法
解決辦法:分析了好多,這里說兩個方法驰弄。
1.避免重繪麻汰,這里避免圖層(View)迭代。這里我們可以去開發(fā)者模式中對“顯示GPU視圖更新”打鉤
這里引用 http://hukai.me/android-performance-render 這篇博客的作者戚篙,盜個圖五鲫。??
這里可以進(jìn)行,選擇制定畫布繪制岔擂,而不是整個view去繪制臣镣「ㄔ福可以在onDraw中進(jìn)行限制,去限制繪制區(qū)域忆某,例如
canvas.clipRect(100,100,350,600, Region.Op.INTERSECT);
2.扁平化布局点待,歸根結(jié)底也是減少 mCallbackQueues 隊(duì)列大小。保證盡量在16ms內(nèi)繪制完畢弃舒,再有就是可以減少視圖 ViewTree 的高度癞埠,減少時間復(fù)雜度,從而優(yōu)化計(jì)算過程
*附:
通過打開剛才說的開發(fā)者選項(xiàng)聋呢,來根據(jù)顏色來判斷頁面繪制情況苗踪。
距離回家還有8 個小時,17年希望可以發(fā)覺更多的東西給大家削锰,并且希望大家可以積極執(zhí)政文章中的錯誤通铲。祝大家新年快樂!??