2020年6月25日 星期四

[Android] RecyclerView load 耗時造成動畫丟幀

markdown 又是 RecyclerView 造成的問題分析,這次問題的情境是,在進入一個 fragment 時會帶有一些動畫效果同時載入一個 RecyclerView,但是動畫會發生 junk。 因為這個 fragment 中一些比較 heavy 的 IO 操作都被移到 background thread 中去做了,所以一開始真的想不到是甚麼原因造成的,code review 了很久始終沒有頭緒,只好又請出 systrace 這神器來檢查。然後才發現,原來兇手是 RecyclerView。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy8qqyLsUxRX1pdMilL3aV2ccGN4FUPZg4wtYPtQffP-rDtXKykiheyn81jgzy9smuq6_c6-iFv5BfDvJ8yBvuHAApSud86qwi5tHzwscLqi5i_KBENjNi0uypMMKOYqmr1Ll80SXkJShu/s1600/Image+1.png =900x*) 一查才知道,原來 RecyclerView Load 花了這麼久的時間,圖中顯示一目了然,左上方紅色的 F 表示這個 Frame 被 delay 了,就是被 RecyclerView 拖累到,所以動畫會有 lag 的感覺。 後來想辦法要縮減 RecyclerView 的載入時間,想說最花時間的應該是 layout inflate,就把 inflate 改成在 background thread 去做,甚至用 custom view 就為了減少 view measure 時間,但是雖然症狀有減輕,但是還是會有 lag 的感覺,達不到預想中滑順的動畫效果。 隨著查了越來越多資料,發現動畫顯示實際上是一個非常嚴苛的條件,想一下,動畫中每個 frame 每 16ms 要顯示一次,你的 RecyclerView 載入要壓在 16ms 之內,而即使已經用了上述技巧減少每個 item load 的時間,但有很多時間是花在 RecyclerView 內部的運作,這時你再怎麼減少 item 的 onCreateViewHolder 跟 onBindViewHolder 也無濟於事。 最後面用了一個折衷的辦法,先讓動畫執行完,再來做載入 RecyclerView,這樣可以保證動畫流暢的執行完。當然,這是因為目前對 RecyclerView 還不夠了解,若是之後對 RecyclerView 了解的足夠深入,有能力改寫它裡面對 item 的載入流程,也許是可以將載入壓到 16ms 的。 --- * [The battle for jank*-less UI on Android](https://medium.com/swlh/the-battle-for-junk-less-ui-713d7680aebc) * [Developing for Android, III: The Rules: Performance](https://medium.com/google-developers/developing-for-android-iii-2efc140167fd)