Android中ListView常見優(yōu)化方案

我們?cè)O(shè)置或者優(yōu)化ListView的性能很多時(shí)候都是在getView中完成的檩奠,反過(guò)來(lái)說(shuō)就是很多性能問(wèn)題都是由于沒(méi)有正確使用getView造成的骄酗。

public View getView(int position, View convertView, ViewGroup parent)

那么問(wèn)題就來(lái)了。

在一次顯示ListView的界面時(shí),getView會(huì)被執(zhí)行幾次拙友?

在繪制ListView前往往要計(jì)算它的高度,所以一個(gè)ListView界面上可以看到6個(gè)ItemView,但是getView的執(zhí)行次數(shù)卻有可能是12次其做,多出的次數(shù)用來(lái)計(jì)算高度(這個(gè)可以通過(guò)設(shè)置ListView的height為0來(lái)避免)渊季。所以要避免在getView中進(jìn)行邏輯運(yùn)算,兩次計(jì)算同一邏輯完全是浪費(fèi)。

每次getView執(zhí)行時(shí)間有多久黎茎?

1秒之內(nèi)屏幕大概可以完成30幀的繪制,人才能看到它比較流暢,每幀可使用的時(shí)間:1000ms/30 = 33.33 ms炊林,每個(gè)ListView一般要顯示6個(gè)ListItem棺榔,加上1個(gè)重用convertView:33.33ms/7 = 4.76ms,即是說(shuō)缩幸,每個(gè)getView要在4.76ms內(nèi)完成工作才會(huì)較流暢难咕,但是事實(shí)上爆土,每個(gè)getView間的調(diào)用也會(huì)有一定的間隔(有可能是由于handler在處理別的消息)步势,UI的handler處理不好的話氧猬,這個(gè)間隔也可難會(huì)很大(0ms-200ms)桑腮。結(jié)論就是提陶,留給getView使用的時(shí)間應(yīng)該在4ms之內(nèi)撑柔,如果不能控制在這之內(nèi)的話,ListView的滑動(dòng)就會(huì)有卡頓的現(xiàn)象累舷。
(轉(zhuǎn)載:Android面試一天一題(11 Day) —— goeasyway

有哪些常見的優(yōu)化方案浩考?

1. 使用ConvertView

也是最普通的優(yōu)化,就在MyAdapter類中的getView方法中被盈,我們注意到析孽,上面的寫法每次需要一個(gè)View對(duì)象時(shí),都是去重新inflate一個(gè)View出來(lái)返回去只怎,沒(méi)有實(shí)現(xiàn)View對(duì)象的復(fù)用袜瞬,而實(shí)際上對(duì)于ListView而言,只需要保留能夠顯示的最大個(gè)數(shù)的view即可身堡,其他新的view可以通過(guò)復(fù)用的方式使用消失的條目的view邓尤,而getView方法里也提供了一個(gè)參數(shù):convertView,這個(gè)就代表著可以復(fù)用的view對(duì)象贴谎,當(dāng)然這個(gè)對(duì)象也可能為空汞扎,當(dāng)它為空的時(shí)候,表示該條目view第一次創(chuàng)建擅这,所以我們需要inflate一個(gè)view出來(lái),所以在這里澈魄,我們使用下面這種方式來(lái)重寫getView方法:

@Override  
public View getView(int position, View convertView, ViewGroup parent) {   
   View view;   
   // 判斷convertView的狀態(tài),來(lái)達(dá)到復(fù)用效果   
   if (null == convertView) {    
       //如果convertView為空仲翎,則表示第一次顯示該條目痹扇,需要?jiǎng)?chuàng)建一個(gè)view    
       view = View.inflate(MainActivity.this, R.layout.listview_item,null);   
    } else {    
       //否則表示可以復(fù)用convertView    
       view = convertView;   
    }   
    // listview_item里只有一個(gè)textview   
    TextView tv_item = (TextView) view.findViewById(R.id.tv_item);     
    tv_item.setText(list.get(position));   
    return view;  
}
2. 使用View Holder模式

經(jīng)過(guò)上面的優(yōu)化之后,我們不需要每一個(gè)view都重新生成了溯香。下面我們來(lái)解決下一個(gè)每一次都需要做的工作鲫构,那就是view中組件的查找:

TextView tv_item = (TextView) view.findViewById(R.id.tv_item);

實(shí)際上,findViewById是到xml文件中去查找對(duì)應(yīng)的id玫坛,可以想象如果組件多的話也是挺費(fèi)事的芬迄,如果我們可以讓view內(nèi)的組件也隨著view的復(fù)用而復(fù)用,就能對(duì)ListView進(jìn)行很好的優(yōu)化

private static class ViewHolder {  
   private TextView tvHolder; 
}

@Override  
public View getView(int position, View convertView, ViewGroup parent) {   
   View view;   
   ViewHolder holder;   
   // 判斷convertView的狀態(tài)昂秃,來(lái)達(dá)到復(fù)用效果   
   if (null == convertView) {    
       // 如果convertView為空禀梳,則表示第一次顯示該條目,需要?jiǎng)?chuàng)建一個(gè)view    
       view = View.inflate(MainActivity.this, R.layout.listview_item, null);    
       //新建一個(gè)viewholder對(duì)象    
       holder = new ViewHolder();    
       //將findviewbyID的結(jié)果賦值給holder對(duì)應(yīng)的成員變量    
       holder.tvHolder = (TextView) view.findViewById(R.id.tv_item);    
       // 將holder與view進(jìn)行綁定    
       view.setTag(holder);   
    } else {    
       // 否則表示可以復(fù)用convertView    
       view = convertView;    
       holder = (ViewHolder) view.getTag();  
    }   
    // 直接操作holder中的成員變量即可肠骆,不需要每次都findViewById   
    holder.tvHolder.setText(list.get(position));   
    return view;  
}

ps:
這里的ViewHolder類需要不需要定義成static算途,根據(jù)實(shí)際情況而定,如果item不是很多的話蚀腿,可以使用嘴瓤,這樣在初始化的時(shí)候扫外,只加載一次,可以稍微得到一些優(yōu)化廓脆。
不過(guò)筛谚,如果item過(guò)多的話,建議不要使用停忿。因?yàn)閟tatic是Java中的一個(gè)關(guān)鍵字驾讲,當(dāng)用它來(lái)修飾成員變量時(shí),那么該變量就屬于該類席赂,而不是該類的實(shí)例吮铭。所以用static修飾的變量,它的生命周期是很長(zhǎng)的颅停,如果用它來(lái)引用一些資源耗費(fèi)過(guò)多的實(shí)例(比如Context的情況最多)谓晌,這時(shí)就要盡量避免使用了。

3. 分批加載與分頁(yè)加載相結(jié)合

我們需要進(jìn)行分批加載癞揉,比如說(shuō)1000條新聞的List集合纸肉,我們一次加載20條,等到用戶翻頁(yè)到底部的時(shí)候喊熟,我們?cè)偬砑酉旅娴?0條到List中毁靶,再使用Adapter刷新ListView,這樣用戶一次只需要等待20條數(shù)據(jù)的傳輸時(shí)間逊移,不需要一次等待好幾分鐘把數(shù)據(jù)都加載完再在ListView上顯示预吆。其次這樣也可以緩解很多條新聞一次加載進(jìn)行產(chǎn)生OOM應(yīng)用崩潰的情況。

實(shí)際上胳泉,分批加載也不能完全解決問(wèn)題拐叉,因?yàn)殡m然我們?cè)诜峙幸淮沃辉黾?0條數(shù)據(jù)到List集合中,然后再刷新到ListView中去扇商,假如有10萬(wàn)條數(shù)據(jù)凤瘦,如果我們順利讀到最后這個(gè)List集合中還是會(huì)累積海量條數(shù)的數(shù)據(jù),還是可能會(huì)造成OOM的情況案铺,這時(shí)候我們就需要用到分頁(yè)蔬芥,比如說(shuō)我們將這10萬(wàn)條數(shù)據(jù)分為1000頁(yè),每一頁(yè)100條數(shù)據(jù)控汉,每一頁(yè)加載時(shí)都覆蓋掉上一頁(yè)中List集合中的內(nèi)容笔诵,然后每一頁(yè)內(nèi)再使用分批加載,這樣用戶的體驗(yàn)就會(huì)相對(duì)好一些姑子。

除此之前還有一些優(yōu)化建議:

  1. 使用異步線程加載圖片(一般都是直接使用圖片庫(kù)加載乎婿,如Glide, Picasso);
  1. 在adapter的getView方法中盡可能的減少邏輯判斷街佑,特別是耗時(shí)的判斷谢翎;
  2. 避免GC(可以從LOGCAT查看有無(wú)GC的LOG)捍靠;
  3. 在快速滑動(dòng)時(shí)不要加載圖片;
  4. 將ListView的scrollingCache和animateCache這兩個(gè)屬性設(shè)置為false(默認(rèn)是true);
  5. 盡可能減少List Item的Layout層次(如可以使用RelativeLayout替換LinearLayout森逮,或使用自定的View代替組合嵌套使用的Layout)榨婆;

(轉(zhuǎn)載:Android面試一天一題(11 Day) —— goeasyway

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市褒侧,隨后出現(xiàn)的幾起案子良风,更是在濱河造成了極大的恐慌,老刑警劉巖璃搜,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異鳞上,居然都是意外死亡这吻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門篙议,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唾糯,“玉大人,你說(shuō)我怎么就攤上這事鬼贱∫魄樱” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵这难,是天一觀的道長(zhǎng)舟误。 經(jīng)常有香客問(wèn)我,道長(zhǎng)姻乓,這世上最難降的妖魔是什么嵌溢? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蹋岩,結(jié)果婚禮上赖草,老公的妹妹穿的比我還像新娘。我一直安慰自己剪个,他們只是感情好秧骑,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著扣囊,像睡著了一般乎折。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侵歇,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天笆檀,我揣著相機(jī)與錄音,去河邊找鬼盒至。 笑死酗洒,一個(gè)胖子當(dāng)著我的面吹牛士修,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播樱衷,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼棋嘲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了矩桂?” 一聲冷哼從身側(cè)響起沸移,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侄榴,沒(méi)想到半個(gè)月后雹锣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡癞蚕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蕊爵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桦山。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡攒射,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恒水,到底是詐尸還是另有隱情会放,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布钉凌,位于F島的核電站咧最,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏御雕。R本人自食惡果不足惜窗市,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望饮笛。 院中可真熱鬧咨察,春花似錦、人聲如沸福青。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)无午。三九已至媒役,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宪迟,已是汗流浹背酣衷。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留次泽,地道東北人穿仪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓席爽,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親啊片。 傳聞我的和親對(duì)象是個(gè)殘疾皇子只锻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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