一,背景
RecyclerView是谷歌官方出的一個(gè)用于大量數(shù)據(jù)展示的新控件侄非,可以用來代替?zhèn)鹘y(tǒng)的ListView蕉汪,更加強(qiáng)大和靈活。
最近逞怨,自己負(fù)責(zé)的業(yè)務(wù)者疤,也遇到這樣的一個(gè)問題,關(guān)于是否要將ListView替換為RecyclerView叠赦?
秉承著實(shí)事求是的作風(fēng)驹马,弄清楚RecyclerView是否有足夠的吸引力替換掉ListView,我從性能這一角度出發(fā)除秀,研究RecyclerView和ListView二者的緩存機(jī)制糯累,并得到了一些較有益的”結(jié)論”,待我慢慢道來鳞仙。
同時(shí)也希望能通過本文寇蚊,讓大家快速了解RecyclerView與ListView在緩存機(jī)制上的一些區(qū)別,在使用上也更加得心應(yīng)手吧棍好。
PS:相關(guān)知識(shí): ListView與RecyclerView緩存機(jī)制原理大致相似仗岸,如下圖所示:
過程中,離屏的ItemView即被回收至緩存借笙,入屏的ItemView則會(huì)優(yōu)先從緩存中獲取扒怖,只是ListView與RecyclerView的實(shí)現(xiàn)細(xì)節(jié)有差異.(這只是緩存使用的其中一個(gè)場景,還有如刷新等)
PPS:本文不貼出詳細(xì)代碼业稼,結(jié)合源碼食用更佳盗痒!
二. 正文
2.1 緩存機(jī)制對(duì)比
-
層級(jí)不同:
RecyclerView比ListView多兩級(jí)緩存,支持多個(gè)離ItemView緩存低散,支持開發(fā)者自定義緩存處理邏輯俯邓,支持所有RecyclerView共用同一個(gè)RecyclerViewPool(緩存池)。
具體來說: ListView(兩級(jí)緩存):
RecyclerView(四級(jí)緩存):
ListView和RecyclerView緩存機(jī)制基本一致:
1). mActiveViews和mAttachedScrap功能相似熔号,意義在于快速重用屏幕上可見的列表項(xiàng)ItemView稽鞭,而不需要重新createView和bindView;
2). mScrapView和mCachedViews + mReyclerViewPool功能相似引镊,意義在于緩存離開屏幕的ItemView朦蕴,目的是讓即將進(jìn)入屏幕的ItemView重用.
3). RecyclerView的優(yōu)勢在于a.mCacheViews的使用,可以做到屏幕外的列表項(xiàng)ItemView進(jìn)入屏幕內(nèi)時(shí)也無須bindView快速重用弟头;b.mRecyclerPool可以供多個(gè)RecyclerView共同使用吩抓,在特定場景下,如viewpaper+多個(gè)列表頁下有優(yōu)勢.客觀來說赴恨,RecyclerView在特定場景下對(duì)ListView的緩存機(jī)制做了補(bǔ)強(qiáng)和完善疹娶。 -
緩存不同:
1). RecyclerView緩存RecyclerView.ViewHolder,抽象可理解為: View + ViewHolder(避免每次createView時(shí)調(diào)用findViewById) + flag(標(biāo)識(shí)狀態(tài))伦连; 2). ListView緩存View雨饺。
緩存不同挣饥,二者在緩存的使用上也略有差別,具體來說: ListView獲取緩存的流程:
RecyclerView獲取緩存的流程:
1). RecyclerView中mCacheViews(屏幕外)獲取緩存時(shí)沛膳,是通過匹配pos獲取目標(biāo)位置的緩存扔枫,這樣做的好處是,當(dāng)數(shù)據(jù)源數(shù)據(jù)不變的情況下锹安,無須重新bindView:
而同樣是離屏緩存短荐,ListView從mScrapViews根據(jù)pos獲取相應(yīng)的緩存,但是并沒有直接使用叹哭,而是重新getView(即必定會(huì)重新bindView)忍宋,相關(guān)代碼如下:
//AbsListView源碼:line2345
//通過匹配pos從mScrapView中獲取緩存
final View scrapView = mRecycler.getScrapView(position);
//無論是否成功都直接調(diào)用getView,導(dǎo)致必定會(huì)調(diào)用createView
final View child = mAdapter.getView(position, scrapView, this);
if (scrapView != null) {
if (child != scrapView) {
mRecycler.addScrapView(scrapView, position);
} else {
...
}
}
2). ListView中通過pos獲取的是view,即pos–>view风罩; RecyclerView中通過pos獲取的是viewholder糠排,即pos –> (view,viewHolder超升,flag)入宦; 從流程圖中可以看出,標(biāo)志flag的作用是判斷view是否需要重新bindView室琢,這也是RecyclerView實(shí)現(xiàn)局部刷新的一個(gè)核心.
2.2 局部刷新
由上文可知乾闰,RecyclerView的緩存機(jī)制確實(shí)更加完善,但還不算質(zhì)的變化盈滴,RecyclerView更大的亮點(diǎn)在于提供了局部刷新的接口涯肩,通過局部刷新,就能避免調(diào)用許多無用的bindView.
(RecyclerView和ListView添加巢钓,移除Item效果對(duì)比)
結(jié)合RecyclerView的緩存機(jī)制病苗,看看局部刷新是如何實(shí)現(xiàn)的: 以RecyclerView中notifyItemRemoved(1)為例,最終會(huì)調(diào)用requestLayout()症汹,使整個(gè)RecyclerView重新繪制硫朦,過程為: onMeasure()–>onLayout()–>onDraw()
其中,onLayout()為重點(diǎn)烈菌,分為三步: 1. dispathLayoutStep1():記錄RecyclerView刷新前列表項(xiàng)ItemView的各種信息阵幸,如Top,Left,Bottom,Right花履,用于動(dòng)畫的相關(guān)計(jì)算芽世; 2. dispathLayoutStep2():真正測量布局大小,位置诡壁,核心函數(shù)為layoutChildren()济瓢; 3. dispathLayoutStep3():計(jì)算布局前后各個(gè)ItemView的狀態(tài),如Remove妹卿,Add旺矾,Move蔑鹦,Update等,如有必要執(zhí)行相應(yīng)的動(dòng)畫.
其中箕宙,layoutChildren()流程圖:
當(dāng)調(diào)用notifyItemRemoved時(shí)嚎朽,會(huì)對(duì)屏幕內(nèi)ItemView做預(yù)處理,修改ItemView相應(yīng)的pos以及flag(流程圖中紅色部分):
當(dāng)調(diào)用fill()中RecyclerView.getViewForPosition(pos)時(shí)柬帕,RecyclerView通過對(duì)pos和flag的預(yù)處理哟忍,使得bindview只調(diào)用一次.
需要指出,ListView和RecyclerView最大的區(qū)別在于數(shù)據(jù)源改變時(shí)的緩存的處理邏輯陷寝,ListView是”一鍋端”锅很,將所有的mActiveViews都移入了二級(jí)緩存mScrapViews,而RecyclerView則是更加靈活地對(duì)每個(gè)View修改標(biāo)志位凤跑,區(qū)分是否重新bindView爆安。
三.結(jié)論
在一些場景下,如界面初始化仔引,滑動(dòng)等扔仓,ListView和RecyclerView都能很好地工作,兩者并沒有很大的差異:
文章的開頭便拋出了這樣一個(gè)問題咖耘,微信Android客戶端卡券模塊当辐,大部分UI都是以列表頁的形式展示,實(shí)現(xiàn)方式為ListView鲤看,是否有必要將其替換成RecyclerView呢缘揪?
答案是否定的,從性能上看义桂,RecyclerView并沒有帶來顯著的提升找筝,不需要頻繁更新,暫不支持用動(dòng)畫慷吊,意味著RecyclerView優(yōu)勢也不太明顯袖裕,沒有太大的吸引力,ListView已經(jīng)能很好地滿足業(yè)務(wù)需求溉瓶。
數(shù)據(jù)源頻繁更新的場景急鳄,如彈幕:http://www.reibang.com/p/2232a63442d6等RecyclerView的優(yōu)勢會(huì)非常明顯;
進(jìn)一步來講堰酿,結(jié)論是: 列表頁展示界面疾宏,需要支持動(dòng)畫,或者頻繁更新触创,局部刷新坎藐,建議使用RecyclerView,更加強(qiáng)大完善,易擴(kuò)展岩馍;其它情況(如微信卡包列表頁)兩者都OK碉咆,但ListView在使用上會(huì)更加方便,快捷蛀恩。
Ps:僅從一個(gè)角度做了對(duì)比疫铜,盲人摸象,有誤跪求指正双谆。
四.參考資料
- ListView
a. android-23源碼 b. Android ListView工作原理解析块攒,帶你從源碼的角度徹底理解:http://blog.csdn.net/guolin_blog/article/details/44996879 c. android自己動(dòng)手寫ListView學(xué)習(xí)其原理:http://blog.csdn.net/androiddevelop/article/details/8734255 - RecyclerView
a. RecyclerView-v7-23.4.0源碼 b. RecyclerView剖析:http://blog.csdn.net/qq_23012315/article/details/50807224 c. RecyclerView剖析:http://blog.csdn.net/qq_23012315/article/details/51096696