從Glide.with(View)開始最佳化RecyclerView掉幀問題
最近在專案中發現有個recyclerview滑動掉幀現象很嚴重,剛好最近要更新一個版本,就準備把這個問題最佳化了
一、Glide.with(View) 是低效的
Android 中對於圖片的處理 , 無非就是 Glide 、 Picasso 、 Fresco 這幾種 , 而在 imageView 直接顯示圖片 ,Glide 無疑是更好的選擇 , Glide 的實現這裡就不贅述 。 在Glide 中 with 方法有很多種 , 例如
@NonNull public static RequestManager with(@NonNull Context context) { return getRetriever(context).get(context); } @NonNull @Deprecated public static RequestManager with(@NonNull Activity activity) { return with(activity.getApplicationContext()); } @NonNull public static RequestManager with(@NonNull Fragment fragment) { return getRetriever(fragment.getContext()).get(fragment); } ....
而 recycler V iew 的 item 如果要顯示圖片 , 我們一般拿到資料後 , 會將資料直接放入 adapter 中 , 然後在進行圖片處理 , 而在 adapter 中又不應該持有 activity 、 fragment 、 context , 所以就會呼叫 Glide.with(View)
@NonNull
public static RequestManager with(@NonNull View view) {
return getRetriever(view.getContext()).get(view);
}
Glide 中關於這個方法的解釋是
其中有一句 “ This method may be inefficient aways and is definitely inefficient for large hierarchies. Consider memoizing the result after the View is attached or again, prefer the Activity and Fragment variants whenever possible. ”大致的意思就是 , 這個方法是低效的 , 除非萬不得已的情況下 ( 自定義 view 等 ) , 不然還是建議使用其他的 with () 方法
Glide 的求生慾望真的很強大 , 讓我們看看他的真實面目
1) 先是透過 findActivity 方法遞迴獲取 activity
2) 然後還要獲取 fragment , 透過 findAllSupportFragmentsWithViews 方法遞迴呼叫獲取 Activity 中所有的 Fragment ,並以 fragment.getView() 作為鍵, fragment 作為值,以鍵值對的形式存入 map 中
總結起來就是 , 獲取 view 的 activity , 然後如果找到 view 的 fragment 就使用 fragment , 如果沒有找到 fragment 但是找到了 activity 就使用 activity , 如果都沒找到就是用 application , 同時生命週期也繫結在 application 中 。
其實如果一個 item 中的圖片資源少的話 , 影響倒是也不大 , 但是我們的那個 recycler V iew 中每個 item 都要載入好幾個 imageView , 量變引發質變了屬於是
所以採用回撥的方式將 Adapter 中的 Glide 的操作放在 A ctivity / Fragment 中進行 , 對於效能來說就好了很多
二、簡化佈局的層級
其實首先排查的是佈局的層級 , 因為 ui 比較複雜 , 所以佈局這塊巢狀的層級比較多 , 這裡把該省的都省下來了 , 例如 :LinearLayout 中巢狀了 Fr amlayout , Fr amlayout 中又巢狀了 ConstraintLayout
三、 儘量使用LinearLayout 和FrameLayout
這當然是因為 LinearLayout 和 FrameLayout 的效能足夠優秀 , 在效能對比如下
以 RelativeLayout 舉例 ,RelativeLayout 會讓子 View 呼叫 2 次 onMeasure , LinearLayout 在有 weight 時, 才 會呼叫子View2 次 onMeasure 。 而 RelativeLayout 的子 View 如果高度和 RelativeLayout 不同,會導致 RelativeLayout 在 onMeasure() 方法中做橫向測量時,縱向的測量結果尚未完成,只好暫時使用自己的高度傳入子 View 系統。而父 View 給子 View 傳入的值也沒有變化就不會做無謂的測量的最佳化會失效 。
四、 滑動時不載入,停止時載入
其實到上面那一步之後 , 掉幀問題已經最佳化了很多 , 使用者正常上下滑動是沒有感覺到上面卡頓 , 但是 , 如果快速的較長時間的滑動依舊會出現卡頓 , 所以還需要繼續最佳化。 這個時候就準備從每個 item載入多個圖片這塊入手
主要思路是 : 當使用者滑動時停止載入圖片資源
Glide.with(this).pauseRequests()
在使用者停止滑動時載入圖片
Glide.with(this).resumeRequests()
體驗起來確實爽多了,不管怎麼快速滑動都是OK 的 , 但是又有新的問題 , 使用者的體驗就很差 , 因為滑動時看到的都是未載入的空白圖片 , 還得接著最佳化
前面說到 , 到第三步時 , 我們正常滑動其實已經不會卡頓了 , 無非就是要避免使用者快速滑動的問題 , 那我們只要判斷使用者滑動時速度 , 當速度過快時 , 停止載入 , 速度慢下來恢復載入 , 就可以完美解決問題 , 參考程式碼如下 :
mBinding.rvList.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState != SCROLL_STATE_IDLE && mScrolled) { Glide.with(this).pauseRequests() } else { Glide.with(this).resumeRequests() } } override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) mScrolled = dy > 80 || dy < -55 } })
五、 其他最佳化操作
參考了下別人的方案 , 如果後續還需要最佳化應該是還有最佳化的空間
1、 加大RecyclerView 的快取,用空間換時間,來提高滾動的流暢性。
mBinding.rvList.setItemViewCacheSize(20); mBinding.rvList.setDrawingCacheEnabled(true); mBinding.rvList.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
2、 增加RecyclerView 預留的額外空間
object : LinearLayoutManager(this) { override fun getExtraLayoutSpace(state: RecyclerView.State): Int { return size } }
3、 設定固定高度
如果item 高度是固定的話,可以使用 RecyclerView.setHasFixedSize(true); 來避免 requestLayout 浪費資源。
4 、 極端情況下可以採用減少 xml 檔案 inflate 時間的方式
xml 檔案包括: layout 、 drawable 的 xml , xml 檔案 inflate 出 ItemView 是透過耗時的 IO 操作。可以使用程式碼去生成佈局,即 newView() 的方式。這種方式是比較麻煩,但是在佈局太過複雜,或對效能要求比較高的時候可以使用。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/69917874/viewspace-3003768/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 小程式Android端movable-view拖拽卡頓掉幀的優化AndroidView優化
- View動畫、幀動畫View動畫
- RecyclerView問題彙總View
- Jenkins從零開始(二)- 常用外掛及問題Jenkins
- 從頭開始對ubuntu進行最佳化(轉)Ubuntu
- 2-幀動畫-從零開始寫一個武俠冒險遊戲動畫遊戲
- CSMA/CD 以太幀最小幀長計算問題
- Android 從零開始實現RecyclerView分組及粘性頭部效果AndroidView
- Win10電腦外接螢幕出現卡頓掉幀問題的解決方法Win10
- 最佳化策略:從頭開始對Linux進行最佳化(轉)Linux
- 小遊戲掉幀卡頓啟動慢執行記憶體不足……這些問題有解嗎?遊戲記憶體
- 從七橋問題開始:全面介紹圖論及其應用圖論
- Android自定義View——從零開始實現雪花飄落效果AndroidView
- 自定義View 之 RecyclerView.ItemDecorationView
- RecyclerView快取機制(scrap view)View快取
- 怎樣遮蔽掉開始按鈕? (轉)
- win10玩lol掉幀怎麼解決_win10lol掉幀卡頓的處理方法Win10
- Android自定義View——從零開始實現水波浪進度框AndroidView
- Android自定義View——從零開始實現覆蓋翻頁效果AndroidView
- Android自定義View——從零開始實現圓形進度條AndroidView
- 有獎問題徵集:一鍵解鎖解題祕籍,從向《怎樣解題》作者提問開始
- 從WinMain開始AI
- Win10膝上型電腦外接螢幕介面出現卡頓、掉幀問題如何解決Win10
- 從零開始, 開發一個 Web Office 套件(4):新的問題—— z-indexWeb套件Index
- android view background 問題AndroidView
- Flutter填坑筆記:從flutter pub get error 開始,定位Dart SDK問題Flutter筆記ErrorDart
- 聚焦資料庫安全 昂楷科技從解決最迫切問題開始資料庫
- RecyclerView常見問題解決方案,RecyclerView巢狀自動滾動,RecyclerView 高度設定wrap_content 無作用等問題...View巢狀
- Android自定義View——從零開始實現書籍翻頁效果(二)AndroidView
- Android自定義View——從零開始實現書籍翻頁效果(四)AndroidView
- Android自定義View——從零開始實現書籍翻頁效果(三)AndroidView
- 怎麼讓self.view的Y從navigationBar下面開始計算ViewNavigation
- Android自定義View——從零開始實現書籍翻頁效果(一)AndroidView
- Android自定義View——從零開始實現可展開收起的水平選單欄AndroidView
- 開啟鍵盤遮住View的問題解決方法View
- 從一個死鎖問題分析最佳化器特性
- 從零開始機器學習機器學習
- 從零開始 OpenCVOpenCV