更新:目前已經(jīng)寫了demo,歡迎討論:Android復(fù)雜數(shù)據(jù)流的“高效”渲染
我們知道Android中的ListView之所以可以實(shí)現(xiàn)item的無限加載,是因?yàn)閷γ總€(gè)item的View 進(jìn)行了緩存復(fù)用。ListView的高效性能使得其在App開發(fā)中使用非常頻繁,本文主要分析在復(fù)雜數(shù)據(jù)展示時(shí)如何更加高效的使用ListView,如微博、facebook、twitter等的feed流需要展示非常多的數(shù)據(jù)類型:新聞、圖片、網(wǎng)頁鏈接、視頻,這種情況下ListView進(jìn)行需要緩存各種類型的View,App的內(nèi)存占用急劇升高……
ListView復(fù)用原理
1. 簡單列表復(fù)用
首先簡單介紹一下ListView的復(fù)用原理,我們知道使用ListView時(shí)一般需要結(jié)合Adapter使用,繼承BaseAdapter時(shí),一般需要實(shí)現(xiàn)四個(gè)方法:
@Override
public int getCount() {
return 0;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
}
其中g(shù)etView是渲染每個(gè)Item時(shí)進(jìn)行回調(diào)生成View的,方法參數(shù)convertView就是ListView傳回可以復(fù)用的View,當(dāng)其不為null時(shí),無需重新創(chuàng)建View,可以直接使用convertView,進(jìn)行數(shù)據(jù)渲染即可。其原理是當(dāng)?shù)谝淮握{(diào)用時(shí)ListView直接將生成的View緩存到一個(gè)ArrayList<View>中,當(dāng)需要時(shí)直接從ArrayList中取出即可:
2. 復(fù)雜列表復(fù)用
當(dāng)列表中有多種類型的view時(shí),我們需要實(shí)現(xiàn)BaseAdapter中的:
@Override
//返回view類型數(shù)量
public int getViewTypeCount() {
return super.getViewTypeCount();
}
@Override
//返回每個(gè)Item的類型
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
這種情況下ListView實(shí)際為每種類型的Item設(shè)置了一個(gè)ArrayList進(jìn)行緩存:
復(fù)雜信息流
此處以微博為例:
-
轉(zhuǎn)發(fā)帶視頻類型
Paste_Image.png - 普通文字+卡片類型
- 轉(zhuǎn)發(fā)圖文類型
此外還有原創(chuàng)圖文類型,原創(chuàng)視頻,原創(chuàng)卡片,系統(tǒng)通知,轉(zhuǎn)發(fā)視頻,轉(zhuǎn)發(fā)圖文,……,微博有多達(dá)二十種左右的item類型,每種類型中的View可能包括頭部圖片、文字描述、正文內(nèi)容、正文圖片、正文視頻、分享操作欄等內(nèi)容,這些都緩存到內(nèi)存中,再加上二十多種類型,想想內(nèi)存的感受……
優(yōu)化
我們可以看到很多類型中都有相同可以復(fù)用的部分,如頭部、分享操作欄等很多item中都是一樣,是否可單獨(dú)拿出來呢,我們進(jìn)行簡單的拆分:
一個(gè)Item我們把它拆為來五個(gè)部分,首先頭部、評論操作欄等可以在很多不同類型的數(shù)據(jù)Item中進(jìn)行復(fù)用,文字、圖片等的View也可以單獨(dú)進(jìn)行復(fù)用,而且最重要的是:緩存ArrayList中保存的View數(shù)量將會減少,內(nèi)存消耗減了不少。
具體實(shí)現(xiàn)中的坑
看到這里,是不是很多同學(xué)覺得打開了新世界的大門,急著進(jìn)行代碼的優(yōu)化?具體的代碼不方便貼出來,這里說一下具體實(shí)現(xiàn)過程中碰到的坑:
-
item click事件
由于優(yōu)化的需求,把邏輯上的一個(gè)Item拆分為了多個(gè)item,因此每個(gè)item上都要設(shè)置ItemClick事件。具體實(shí)現(xiàn)時(shí)可以寫一個(gè)基類,在基類中對item click進(jìn)行處理。 -
cover 按壓效果
在item 點(diǎn)擊時(shí),一般需要有按壓效果,此時(shí)邏輯上的item已經(jīng)進(jìn)行了拆分,需要策略實(shí)現(xiàn)邏輯上item的整體按壓,而不是只有某個(gè)拆分后的item被按壓。 -
divider
我們知道listview的item之間是有divider的,此時(shí)需要設(shè)置divider為null,我們通過添加item的方式來實(shí)現(xiàn)divider效果。
效果
等填完拆分后的坑,運(yùn)行程序,觀察前后的效果,內(nèi)存占用可以減少10~20m,滑動流暢度也提高不少,在低端手機(jī)上的效果尤其明顯,掉幀明顯減少。非常建議有需要的同學(xué)嘗試。