2020年3月21日 星期六

[Android] 一次 RecyclerView 效能分析

markdown 最近遇到了一點效能問題,所以開始研究了一下要如何增進 app 效能,那要改善效能,首先要知道 app 的 bottleneck 到底是甚麼,最經典的方法就是用 profiler 看一下效能都花到哪去了。 圖形化的 profiler 最方便直觀,早期都是推薦用 [Traceview](https://developer.android.com/studio/profile/traceview),但是現在已經被 Google 列為不建議使用的工具,因為 Android Studio 在 3.0 之後推出了 [Android Profiler](https://developer.android.com/studio/profile/android-profiler) 可以讓你來觀察 CPU 與 memory 的使用量,因為我是遇到畫面 lag 的問題,就用裡面的 CPU profiler 研究了一下看 CPU 花在哪個 method 里。 在用 profiler 之前之前,其實我預期會看到的是 [setText](https://developer.android.com/reference/android/widget/TextView#setText(java.lang.CharSequence)) 這個 method 吃掉了 CPU resource,因為我有一個 RecyclerView 會頻繁刷新並且呼叫 setText。而之前曾經在 RecyclerView 最佳化的文章中看到過 setText 是很耗時的。 的確在每次畫面刷新的時候就看到 CPU 瞬間飆起來一下,但是從 Call Chart 看到了一堆 method,但都是 Android 系統的函式,看不到 setText 的耗時,後來自己在程式里計算 setText 的時間,發現才不到 1ms。耗時這麼少,被淹沒在 call stack 茫茫大海中,難怪 Call Chart 中找不到,而且也不會是 lag 的原因。後來把 TextView 的 layout class name 印出來看,發現使用了 BoringLayout,所以 setText 的耗時非常短。 後來無意中發現,當我呼叫 notifyDataSetChanged 時,onCreateViewHolder 被呼叫了很多次,這跟我原本對 RecyclerView 的認知有出入,在我原本認知下,應該只有 onBindViewHolder 會頻繁呼叫到才對,然後看了原始碼才發現,原來 RecyclerView 預設 view pool 只會暫存 5 個 item。
public static class RecycledViewPool {
    private ArrayList<ViewHolder>[] mScrap;
    private int[] mMaxScrap;
    private static final int DEFAULT_MAX_SCRAP = 5;
    // Ignore......
}
我的 list item 有 30 幾個,所以會頻繁的呼叫到 onCreateViewHolder。 看來我的 RecyclerView 有一些不必要的耗時操作,接下來就是要對 RecyclerView 作一些優化了,首先記錄一下沒優化的前的 CPU 使用率,如下圖,可以看到稍微有點高了。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlJ-h1DXYl6FIXK2bSB4q4tz1xntgF4yzjKwvDcqh1w3nyKx_tBEFZcm2pCS2SDLkoC_ddH8y8QSOiQuh8y4ZxcJtQoJn7ByOGGXAGCL5mc9v9MhzpyhtAn4Hn0vyjDAdSzk440dLyiMez/s1600/no_optimize_real.png) create view 是蠻耗時的操作,將 view pool 的 cache 數量設成 36 之後 觀察 CPU 使用率如下。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgJ12RtxDTTUC-Cjs6MRoPxff9B_4XdBmvaexscZxfwXXsEmkeyG68OlUKASqQO89k0rfzxCOaQY9kLOpeppj-d_tTFFuo6TY2u6sjfRU7Bu5He1nZ8olvp3R6evHdpI9aRL3lUsPvFlq_/s1600/view_pool_real.png) 之前 RecyclerView 最佳化的文章中還有看到 DiffUtil 這個工具類,可以進一步減少 CPU 耗時,使用後 CPU 使用率如下。 ![](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF-ZxJ9MgLQBDdey5NBxGHHsf7vtanX9f_TEdR1Fk5B_gnBIE3Ge17cED7yzqPmHaTcxdPLQEP57etV92sLpUIowdHrRZS7V8yc5oTMHsGeJGZW-TaesXX9AQQIxA69ULTFOAx304eF4_f/s1600/diffutil.png) 從圖中可以看到,CPU 使用率降低了,優化是有效果的,之後使用 RecyclerView 時可以考慮一下用這些優化方法。 --- * [Android - 性能优化方案分享](https://cloud.tencent.com/developer/article/1415758) * [Android性能优化之CPU Profiler](https://juejin.im/entry/5c0daf65f265da6150644a1d)

沒有留言:

張貼留言