遇到一個從快播出來的Android開發(fā),有11年的開發(fā)經(jīng)驗,咋一看不管是資歷還是經(jīng)歷都挺嚇人的。但和他共處一段時間后,發(fā)現(xiàn)他完全沒有體現(xiàn)出11年工作經(jīng)驗的優(yōu)勢,相反還常常犯一些低級的錯誤,如在ListView中加載本地的圖片(大圖)時不使用異步線程,而是直接setImageResource。而他工作和為人都很努力,對分配的工作都很認真,但效果卻常常不盡如人意,不管是和Android特性相關(guān)的代碼還是純邏輯的代碼,他都常常犯一些低級的錯誤(這些錯誤都是普遍認為一個11年工作經(jīng)驗的人不應(yīng)該犯的)。
后來我經(jīng)常思考,是什么樣的原因?qū)е乱粋€人的工作年限和水平并不能成正比。我想到有兩個方面:
- 看待問題的眼界過窄,只能看到當前的可見的問題。(或者說他思考問題的角度和方式有較大的局限)
- 技術(shù)的要點沒有掌握,常常找不到合理的解決問題的方案。
這兩點都是需要慢慢地提升和積累,當一個人長時間在思維和眼界上沒有進步,技術(shù)也只懂些皮毛(或者只是會用),那么他工作越長時間他的優(yōu)勢反而越不明顯,甚至變成劣勢。
面試題:如何優(yōu)化ListView的性能?
在回答這個問題前,我認為很有必要和大家講幾點和getView相關(guān)的問題。我們設(shè)置或者優(yōu)化ListView的性能很多時候都是在getView中完成的,反過來說就是很多性能問題都是由于沒有正確使用getView造成的。
public View getView(int position, View convertView, ViewGroup parent)
所以我們不妨先思考一下如下的幾個問題:
在一次顯示ListView的界面時,getView會被執(zhí)行幾次?
每次getView執(zhí)行時間應(yīng)該控制在多少毫秒之內(nèi)?
getView中設(shè)置listener要注意什么?
首先我們要知道ListView的ItemView有一個復(fù)用機制,簡單看如下圖所示,ListView中有一個RecycleBin類復(fù)負回收不可見且可能被再次使用的ItemView,由ScrapView存儲。

所以我在們設(shè)置Listener進就要注意,使用convertView時需要重新設(shè)置一個Listener,保括一些數(shù)據(jù)也需要重設(shè)置,不然可能會顯示之前那個ItemView在回收前的狀態(tài)。
在繪制ListView前往往要計算它的高度,所以一個ListView界面上可以看到6個ItemView,但是getView的執(zhí)行次數(shù)卻有可能是12次,多出的次數(shù)用來計算高度(這個可以通過設(shè)置ListView的height為0來避免)。所以要避免在getView中進行邏輯運算,兩次計算同一邏輯完全是浪費。
每個getView的執(zhí)行時間更是少得可憐,很多人可能對這個時間沒有概念,我可以簡單的給大算一下:
1秒之內(nèi)屏幕可以完成30幀的繪制,人才能看到它比較流暢(蘋果是接近60幀,高于60之后人眼也無法分辨)。
每幀可使用的時間:1000ms/30 = 33.33 ms
每個ListView一般要顯示6個ListItem,加上1個重用convertView:33.33ms/7 = 4.76ms
即是說,每個getView要在4.76ms內(nèi)完成工作才會較流暢,但是事實上,每個getView間的調(diào)用也會有一定的間隔(有可能是由于handler在處理別的消息),UI的handler處理不好的話,這個間隔也可難會很大(0ms-200ms)。結(jié)論就是,留給getView使用的時間應(yīng)該在4ms之內(nèi),如果不能控制在這之內(nèi)的話,ListView的滑動就會有卡頓的現(xiàn)象。
了解了這幾個問題,現(xiàn)在我們回來這次主要考查的面試題上,如何進行ListView的性能優(yōu)化,讓它滑動更加流暢。大家一般常用如下方法:
- 重用ConvertView;
- 使用View Holder模式;
- 使用異步線程加載圖片(一般都是直接使用圖片庫加載,如Glide, Picasso);
我認為這些是面試者必備的知識點,如果連這些都說不清楚的話,也沒有必要再深入問了。針對面試者的回答,可以適當選一兩點追問一下,看是否真正明白。如:ViewHolder為什么能夠起到優(yōu)化性能的作用?
除此之前還有一些優(yōu)化建議:
- 在adapter的getView方法中盡可能的減少邏輯判斷,特別是耗時的判斷;
- 避免GC(可以從LOGCAT查看有無GC的LOG);
- 在快速滑動時不要加載圖片;
- 將ListView的scrollingCache和animateCache這兩個屬性設(shè)置為false(默認是true);
- 盡可能減少List Item的Layout層次(如可以使用RelativeLayout替換LinearLayout,或使用自定的View代替組合嵌套使用的Layout);
關(guān)于第4點,發(fā)現(xiàn)在一些型號的手機(如華為的P7)上特別管用,當其也優(yōu)化都做完之后,有無這兩項設(shè)置滑動的卡頓情況有明顯不同。
<Listview
android:scrollingCache="false"
android:animationCache="false"
小結(jié)
關(guān)于ListView有很多方面可以考察面試者,因為它實在是用的太頻繁了,雙方都能對某個問題點進行展開。如果一個面試者都沒有做過ListView優(yōu)化,那么如果不是他寫的代碼太少就是他使用ListView加載的數(shù)據(jù)太簡單(可能只有幾十項),其本上沒有其他選項。所以這一題是很能看出面試者的項目經(jīng)驗和實際的開發(fā)水平,屬于面試必考題之一。