RecyclerView 效能優化 | 安卓 offer 收割基

blankj發表於2019-03-03

阿里四面有三面都問了這個問題,在此做了整理,希望可以幫助到大家,歡迎查漏補缺。

資料處理和檢視載入分離

我們知道,從遠端拉取資料肯定是要放在非同步的,在我們拉取下來資料之後可能就匆匆把資料丟給了 VH 處理,其實,資料的處理邏輯我們也應該放在非同步處理,這樣 Adapter 在 notify change 後,ViewHolder 就可以簡單無壓力地做資料與檢視的繫結邏輯,比如:

mTextView.setText(Html.fromHtml(data).toString());
複製程式碼

這裡的 Html.fromHtml(data) 方法可能就是比較耗時的,存在多個 TextView 的話耗時會更為嚴重,這樣便會引發掉幀、卡頓,而如果把這一步與網路非同步執行緒放在一起,站在使用者角度,最多就是網路重新整理時間稍長一點。

資料優化

分頁拉取遠端資料,對拉取下來的遠端資料進行快取,提升二次載入速度;對於新增或者刪除資料通過 DiffUtil 來進行區域性重新整理資料,而不是一味地全域性重新整理資料。

佈局優化

減少過渡繪製

減少佈局層級,可以考慮使用自定義 View 來減少層級,或者更合理地設定佈局來減少層級,不推薦在 RecyclerView 中使用 ConstraintLayout,有很多開發者已經反映了使用它效果更差,相關連結有:Is ConstraintLayout that slow?constraintlayout 1.1.1 not work well in listview

減少 xml 檔案 inflate 時間

這裡的 xml 檔案不僅包括 layout 的 xml,還包括 drawable 的 xml,xml 檔案 inflate 出 ItemView 是通過耗時的 IO 操作,尤其當 Item 的複用機率很低的情況下,隨著 Type 的增多,這種 inflate 帶來的損耗是相當大的,此時我們可以用程式碼去生成佈局,即 new View() 的方式,只要搞清楚 xml 中每個節點的屬性對應的 API 即可。

減少 View 物件的建立

一個稍微複雜的 Item 會包含大量的 View,而大量的 View 的建立也會消耗大量時間,所以要儘可能簡化 ItemView;設計 ItemType 時,對多 ViewType 能夠共用的部分儘量設計成自定義 View,減少 View 的構造和巢狀。

其他

其他並不代表不重要,而是我不能把他們進行分類哈,其中可能某些操作會對你的 RecyclerView 有很大的優化。

  • 升級 RecycleView 版本到 25.1.0 及以上使用 Prefetch 功能,可參考 RecyclerView 資料預取

  • 如果 Item 高度是固定的話,可以使用 RecyclerView.setHasFixedSize(true); 來避免 requestLayout 浪費資源;

  • 設定 RecyclerView.addOnScrollListener(listener); 來對滑動過程中停止載入的操作。

  • 如果不要求動畫,可以通過 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false); 把預設動畫關閉來提升效率。

  • TextView 使用 String.toUpperCase 來替代 android:textAllCaps="true"

  • TextView 使用 StaticLayout 或者 DynamicLayout 的自定義 View 來代替它。

  • 通過重寫 RecyclerView.onViewRecycled(holder) 來回收資源。

  • 通過 RecycleView.setItemViewCacheSize(size); 來加大 RecyclerView 的快取,用空間換時間來提高滾動的流暢性。

  • 如果多個 RecycledViewAdapter 是一樣的,比如巢狀的 RecyclerView 中存在一樣的 Adapter,可以通過設定 RecyclerView.setRecycledViewPool(pool); 來共用一個 RecycledViewPool

  • ItemView 設定監聽器,不要對每個 Item 都呼叫 addXxListener,應該大家公用一個 XxListener,根據 ID 來進行不同的操作,優化了物件的頻繁建立帶來的資源消耗。

  • 通過 getExtraLayoutSpace 來增加 RecyclerView 預留的額外空間(顯示範圍之外,應該額外快取的空間),如下所示:

    new LinearLayoutManager(this) {
        @Override
        protected int getExtraLayoutSpace(RecyclerView.State state) {
            return size;
        }
    };
    複製程式碼

結語

我正在打造一個幫助 Android 開發者們拿到更好 offer 的面試庫————安卓 offer 收割基,歡迎 star,覺得不錯的可以持續關注,有興趣的可以一起加入進來和我一同打造。

相關文章