RecyclerView 優(yōu)化——滾動(dòng)性能提升

前言

在這篇文章中辰斋,我們將學(xué)習(xí)如何優(yōu)化 Android 中的 RecyclerView 性能。通過這些優(yōu)化瘸味,我們可以使 RecyclerView 滾動(dòng)流暢宫仗。

當(dāng)我們?cè)谖覀兊?Android 應(yīng)用程序中實(shí)現(xiàn) RecyclerView 時(shí),有時(shí)我們會(huì)遇到這樣的問題: RecyclerView 項(xiàng)目滾動(dòng)不順暢旁仿。這會(huì)導(dǎo)致糟糕的用戶體驗(yàn)藕夫,因?yàn)槲覀兊?Android 應(yīng)用程序似乎很滯后。

讓我們看看我們可以做些什么來提高 RecyclerView 的性能枯冈,從而獲得平滑的滾動(dòng)毅贮。

RecyclerView 優(yōu)化技巧

讓我們開始吧

使用圖像加載庫

由于垃圾回收(GC)運(yùn)行在主線程上,UI無響應(yīng)的原因之一是內(nèi)存的不斷分配和釋放尘奏,導(dǎo)致GC運(yùn)行非常頻繁滩褥。通過使用位圖池的概念,我們可以避免它炫加。

最好的部分是像 Glide瑰煎、Fresco 這樣的 Image-Loading 庫使用了這個(gè)位圖池概念。因此琢感,請(qǐng)始終使用圖像加載庫丢间。

將所有與圖像相關(guān)的任務(wù)委托給這些庫探熔。

設(shè)置圖像寬度和高度

如果我們的圖像寬度和高度是動(dòng)態(tài)的(不是固定的)驹针,并且我們imageUrl從服務(wù)器獲取。

如果我們事先沒有設(shè)置正確的圖像寬度和高度诀艰,UI 會(huì)在加載(下載圖像)和設(shè)置圖像到 ImageView 的過渡過程中閃爍(實(shí)際上是在下載完成時(shí)使其可見)柬甥。

所以饮六,我們應(yīng)該要求我們的后端開發(fā)者發(fā)送圖像大小或縱橫比,據(jù)此苛蒲,我們可以計(jì)算出所需的 ImageView 的寬度和高度卤橄。

然后,我們將只能先設(shè)置寬度和高度臂外。因此沒有閃爍窟扑。問題解決了!

少做 onBindViewHolder 方法

我們的 onBindViewHolder 方法應(yīng)該做的工作很少漏健。我們應(yīng)該檢查我們的 onBindViewHolder 方法并對(duì)其進(jìn)行優(yōu)化嚎货。通過這樣做,我們可以看到 RecyclerView 的改進(jìn)蔫浆。

使用通知項(xiàng)目 RecyclerView API

每當(dāng)我們有刪除殖属、更新、添加項(xiàng)目的用例時(shí)瓦盛,使用通知項(xiàng)目 API洗显。

<pre class="public-DraftStyleDefault-pre" data-offset-key="7inds-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="7inds-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

adapter.notifyItemRemoved(position)
adapter.notifyItemChanged(position)
adapter.notifyItemInserted(position)
adapter.notifyItemRangeInserted(start, end)

</pre>

</pre>

另外,檢查DiffUtil原环。

萬一我們被迫使用notifyDataSetChanged()基于我們的用例挠唆,我們可以嘗試setHasStableIds(true).

<pre class="public-DraftStyleDefault-pre" data-offset-key="rvf3-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="rvf3-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

adapter.setHasStableIds(true)

</pre>

</pre>

它指示數(shù)據(jù)集中的每個(gè)項(xiàng)目是否可以用 Long 類型的唯一標(biāo)識(shí)符表示。

即使我們調(diào)用notifyDataSetChanged()嘱吗,它也不必處理整個(gè)適配器的完全重新排序损搬,因?yàn)樗梢哉业侥硞€(gè)位置的項(xiàng)目是否與以前相同并且做更少的工作。

避免嵌套視圖

如果可能柜与,我們應(yīng)該避免嵌套視圖巧勤,并盡可能創(chuàng)建平面視圖。嵌套會(huì)降低 RecyclerView 的性能弄匕。平面視圖提高了性能颅悉。

使用 setHasFixedSize

如果所有項(xiàng)目的高度相等,我們應(yīng)該使用這種方法迁匠。添加以下內(nèi)容并檢查性能剩瓶。

<pre class="public-DraftStyleDefault-pre" data-offset-key="c13hk-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="c13hk-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

recyclerView.setHasFixedSize(true)

</pre>

</pre>

使用 setRecycledViewPool 優(yōu)化嵌套 RecyclerView

眾所周知,RecyclerView 盡可能地遵循“Reuse View Using Pool”的原則城丧。

檢查 RecyclerView 如何在內(nèi)部工作延曙?

如果我們有嵌套 RecyclerView 的用例。

<pre class="public-DraftStyleDefault-pre" data-offset-key="8lih4-0-0" style="margin: 1.4em 0px; padding: 0.88889em; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: auto; background: rgb(246, 246, 246); border-radius: 4px; color: rgb(18, 18, 18); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">

<pre class="Editable-styled" data-block="true" data-editor="b345q" data-offset-key="8lih4-0-0" style="margin: 0px; padding: 0px; font-size: 0.9em; word-break: normal; overflow-wrap: normal; white-space: pre; overflow: initial; background: rgb(246, 246, 246); border-radius: 0px;">

- OuterRecyclerView
    - InnerRecyclerViewOne
    - InnerRecyclerViewTwo

</pre>

</pre>

但默認(rèn)情況下亡哄,優(yōu)化適用于特定的 RecyclerView枝缔,因?yàn)樘囟ǖ?RecyclerView 有自己的視圖池。

池不會(huì)在具有相同視圖類型的兩個(gè) RecyclerView 之間共享。

所以愿卸,我們可以做的是灵临,我們可以創(chuàng)建一個(gè) ViewPool 并將其傳遞給所有內(nèi)部的 RecyclerViews,以便它像下面這樣共享:

class OuterRecyclerViewAdapter() : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    // code removed for brevity

    private val viewPool = RecyclerView.RecycledViewPool()

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

        // code removed for brevity
        
        viewHolderOne.innerRecyclerViewOne.setRecycledViewPool(viewPool)
        
        // code removed for brevity

        viewHolderTwo.innerRecyclerViewTwo.setRecycledViewPool(viewPool)
        
    }
    
    // code removed for brevity
}

這將提高滾動(dòng)性能趴荸,因?yàn)樗鼘㈤_始重用共享 ViewPool 中的視圖儒溉。

使用 setItemViewCacheSize

我們可以通過設(shè)置 ItemView Cache Size來進(jìn)行實(shí)驗(yàn)。

recyclerView.setItemViewCacheSize(cacheSize)

根據(jù)官方文檔:它設(shè)置在將它們添加到可能共享的回收視圖池之前要保留的屏幕外視圖的數(shù)量发钝。屏幕外視圖緩存保持對(duì)附加適配器中的更改的感知顿涣,允許 LayoutManager 重用這些未修改的視圖,而無需返回到適配器重新綁定它們酝豪。

這意味著當(dāng)我們滾動(dòng) RecyclerView 以使視圖幾乎完全脫離屏幕時(shí)园骆,RecyclerView 將保留它,以便我們可以將其滾動(dòng)回視圖而無需onBindViewHolder()再次調(diào)用寓调。

一般來說锌唾,我們不會(huì)改變 View Cache 的大小,而是嘗試一下夺英,如果它適合你晌涕,就實(shí)現(xiàn)它。

這些是我們可以做的事情來提高 RecyclerView 的性能⊥疵酰現(xiàn)在就是這樣余黎。

作者:Amit Shekhar
鏈接:https://blog.mindorks.com/recyclerview-optimization

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市载萌,隨后出現(xiàn)的幾起案子惧财,更是在濱河造成了極大的恐慌,老刑警劉巖扭仁,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垮衷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡乖坠,警方通過查閱死者的電腦和手機(jī)搀突,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熊泵,“玉大人仰迁,你說我怎么就攤上這事⊥绶郑” “怎么了徐许?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長卒蘸。 經(jīng)常有香客問我雌隅,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任澄步,我火速辦了婚禮冰蘑,結(jié)果婚禮上和泌,老公的妹妹穿的比我還像新娘村缸。我一直安慰自己,他們只是感情好武氓,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布梯皿。 她就那樣靜靜地躺著,像睡著了一般县恕。 火紅的嫁衣襯著肌膚如雪东羹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天忠烛,我揣著相機(jī)與錄音属提,去河邊找鬼。 笑死美尸,一個(gè)胖子當(dāng)著我的面吹牛冤议,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播师坎,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼恕酸,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了胯陋?” 一聲冷哼從身側(cè)響起蕊温,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遏乔,沒想到半個(gè)月后义矛,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盟萨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年症革,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸯旁。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡噪矛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出铺罢,到底是詐尸還是另有隱情艇挨,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布韭赘,位于F島的核電站缩滨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜脉漏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一苞冯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侧巨,春花似錦舅锄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坦仍,卻和暖如春鳍烁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背繁扎。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國打工幔荒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人梳玫。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓爹梁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親汽纠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卫键,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容