ListView与GridView优化
前言
ListView是Android中最常用的控件,通過適配器來進行數(shù)據(jù)適配然后顯示出來,而其性能是個很值得研究的話題。本文與你一起探討Google I/O提供的優(yōu)化Adapter方案,歡迎大家交流。
?
聲明
歡迎轉(zhuǎn)載,但請保留文章原始出處:)?
博客園:http://www.cnblogs.com
農(nóng)民伯伯:?http://over140.cnblogs.com
?
正文
一、準備
1.1 了解關于Google IO大會關于Adapter的優(yōu)化,參考以下文章:
Android開發(fā)之ListView 適配器(Adapter)優(yōu)化
Android開發(fā)——09Google I/O之讓Android UI性能更高效(1)
PDF下載:Google IO.pdf
1.2 準備測試代碼:
Activity
????private?TestAdapter?mAdapter;????private?String[]?mArrData;
????private?TextView?mTV;
????@Override
????protected?void?onCreate(Bundle?savedInstanceState)?{
????????super.onCreate(savedInstanceState);
????????setContentView(R.layout.main);
????????mTV?=?(TextView)?findViewById(R.id.tvShow);
????????mArrData?=?new?String[1000];
????????for?(int?i?=?0;?i?<?1000;?i++)?{
????????????mArrData[i]?=?"Google?IO?Adapter" + i;
????????}
????????mAdapter?=?new?TestAdapter(this,?mArrData);
????????((ListView)?findViewById(android.R.id.list)).setAdapter(mAdapter);
????}
代碼說明:模擬一千條數(shù)據(jù),TestAdapter繼承自BaseAdapter,main.xml見文章末尾下載。
?
二、測試
測試方法:手動滑動ListView至position至50然后往回滑動,充分利用convertView不等于null的代碼段。
2.1 方案一
按照Google I/O介紹的第二種方案,把item子元素分別改為4個和10個,這樣效果更佳明顯。
2.1.1 測試代碼
????????private?int?count?=?0;????????private?long?sum?=?0L;
????????@Override
????????public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{
????????????//開始計時
????????????long?startTime?=?System.nanoTime();
????????????
????????????if?(convertView?==?null)?{
????????????????convertView?=?mInflater.inflate(R.layout.list_item_icon_text,
????????????????????????null);
????????????}
????????????((ImageView)?convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
????????????((TextView)?convertView.findViewById(R.id.text1)).setText(mData[position]);
????????????((ImageView)?convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
????????????((TextView)?convertView.findViewById(R.id.text2)).setText(mData[position]);
????????????
????????????//停止計時
????????????long?endTime?=?System.nanoTime();
????????????//計算耗時
????????????long?val?=?(endTime?-?startTime)?/?1000L;
????????????Log.e("Test",?"Position:"?+?position?+?":"?+?val);
????????????if?(count?<?100)?{
????????????????if?(val?<?1000L)?{
????????????????????sum?+=?val;
????????????????????count++;
????????????????}
????????????}?else
????????????????mTV.setText(String.valueOf(sum?/?100L));//顯示統(tǒng)計結(jié)果
????????????return?convertView;
????????}
? 2.1.2 測試結(jié)果(微秒除以1000,見代碼)
| 次數(shù) | 4個子元素 | 10個子元素 |
| 第一次 | ?366 | 723 |
| 第二次 | 356? | 689 |
| 第三次 | ?371 | 692 |
| 第四次 | 356? | 696 |
| 第五次 | ?371 | 662 |
按照Google I/O介紹的第三種方案,是把item子元素分別改為4個和10個。
2.2.1 測試代碼
????????private?int?count?=?0;????????private?long?sum?=?0L;
????????@Override
????????public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{
????????????//?開始計時
????????????long?startTime?=?System.nanoTime();
????????????ViewHolder?holder;
????????????if?(convertView?==?null)?{
????????????????convertView?=?mInflater.inflate(R.layout.list_item_icon_text,
????????????????????????null);
????????????????holder?=?new?ViewHolder();
????????????????holder.icon1?=?(ImageView)?convertView.findViewById(R.id.icon1);
????????????????holder.text1?=?(TextView)?convertView.findViewById(R.id.text1);
????????????????holder.icon2?=?(ImageView)?convertView.findViewById(R.id.icon2);
????????????????holder.text2?=?(TextView)?convertView.findViewById(R.id.text2);
????????????????convertView.setTag(holder);
????????????}
????????????else{
????????????????holder?=?(ViewHolder)convertView.getTag();
????????????}
????????????holder.icon1.setImageResource(R.drawable.icon);
????????????holder.text1.setText(mData[position]);
????????????holder.icon2?.setImageResource(R.drawable.icon);
????????????holder.text2.setText(mData[position]);
????????????//?停止計時
????????????long?endTime?=?System.nanoTime();
????????????//?計算耗時
????????????long?val?=?(endTime?-?startTime)?/?1000L;
????????????Log.e("Test",?"Position:"?+?position?+?":"?+?val);
????????????if?(count?<?100)?{
????????????????if?(val?<?1000L)?{
????????????????????sum?+=?val;
????????????????????count++;
????????????????}
????????????}?else
????????????????mTV.setText(String.valueOf(sum?/?100L));//?顯示統(tǒng)計結(jié)果
????????????return?convertView;
????????}
????}
????static?class?ViewHolder?{
????????TextView?text1;
????????ImageView?icon1;
????????TextView?text2;
????????ImageView?icon2;
????}
? 2.2.2 測試結(jié)果(微秒除以1000,見代碼)
| 次數(shù) | 4個子元素 | 10個子元素 |
| 第一次 | ?311 | ?417 |
| 第二次 | ?291 | ?441 |
| 第三次 | ?302 | ?462 |
| 第四次 | ?286 | ?444 |
| 第五次 | ?299 | ?436 |
?
2.3 方案三此方案為“Henry Hu”提示,API Level 4以上提供,這里順帶測試了一下不使用靜態(tài)內(nèi)部類情況下性能。
2.3.1 測試代碼 ????????@Override????????public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{
????????????//?開始計時
????????????long?startTime?=?System.nanoTime();
????????????if?(convertView?==?null)?{
????????????????convertView?=?mInflater.inflate(R.layout.list_item_icon_text,?null);
????????????????convertView.setTag(R.id.icon1,?convertView.findViewById(R.id.icon1));
????????????????convertView.setTag(R.id.text1,?convertView.findViewById(R.id.text1));
????????????????convertView.setTag(R.id.icon2,?convertView.findViewById(R.id.icon2));
????????????????convertView.setTag(R.id.text2,?convertView.findViewById(R.id.text2));
????????????}
????????????((ImageView)?convertView.getTag(R.id.icon1)).setImageResource(R.drawable.icon);
????????????((ImageView)?convertView.getTag(R.id.icon2)).setImageResource(R.drawable.icon);
????????????((TextView)?convertView.getTag(R.id.text1)).setText(mData[position]);
????????????((TextView)?convertView.getTag(R.id.text2)).setText(mData[position]);
????????????//?停止計時
????????????long?endTime?=?System.nanoTime();
????????????//?計算耗時
????????????long?val?=?(endTime?-?startTime)?/?1000L;
????????????Log.e("Test",?"Position:"?+?position?+?":"?+?val);
????????????if?(count?<?100)?{
????????????????if?(val?<?1000L)?{
????????????????????sum?+=?val;
????????????????????count++;
????????????????}
????????????}?else
????????????????mTV.setText(String.valueOf(sum?/?100L)?+?":"?+?nullcount);//?顯示統(tǒng)計結(jié)果
????????????return?convertView;
????????}
? 2.3.2 測試結(jié)果(微秒除以1000,見代碼)
第一次:450
第二次:467
第三次:472
第四次:451
第五次:441?
四、總結(jié)4.1 首先有一個認識是錯誤的,我們先來看截圖:
?
可以發(fā)現(xiàn),只有第一屏(可視范圍)調(diào)用getView所消耗的時間遠遠多于后面的,通過對
convertView == null內(nèi)代碼監(jiān)控也是同樣的結(jié)果。也就是說ListView僅僅緩存了可視范圍內(nèi)的View,隨后的滾動都是對這些View進行數(shù)據(jù)更新。不管你有多少數(shù)據(jù),他都只用ArrayList緩存可視范圍內(nèi)的View,這樣保證了性能,也造成了我以為ListView只緩存View結(jié)構不緩存數(shù)據(jù)的假相(不會只有我一人這么認為吧- - #)。這也能解釋為什么GOOGLE優(yōu)化方案一比二高很多的原因。那么剩下的也就只有findViewById比較耗時了。據(jù)此大家可以看看AbsListView的源代碼,看看 obtainView這個方法內(nèi)的代碼及RecycleBin這個類的實現(xiàn),歡迎分享。此外了解這個原理了,那么以下代碼不運行你可能猜到結(jié)果了:
????????????if?(convertView?==?null)?{????????????????convertView?=?mInflater.inflate(R.layout.list_item_icon_text,?null);
????????????????((ImageView)?convertView.findViewById(R.id.icon1)).setImageResource(R.drawable.icon);
????????????????((TextView)?convertView.findViewById(R.id.text1)).setText(mData[position]);
????????????????((ImageView)?convertView.findViewById(R.id.icon2)).setImageResource(R.drawable.icon);
????????????????((TextView)?convertView.findViewById(R.id.text2)).setText(mData[position]);
????????????}
????????????else
????????????????return?convertView;
沒錯,你會發(fā)現(xiàn)滾動時會重復顯示第一屏的數(shù)據(jù)!
子控件里的事件因為是同一個控件,也可以直接放到convertView == null 代碼塊內(nèi)部,如果需要交互數(shù)據(jù)比如position,可以通過tag方式來設置并獲取當前數(shù)據(jù)。
4.2 本文方案一與方案二對比
這里推薦如果只是一般的應用(一般指子控件不多),無需都是用靜態(tài)內(nèi)部類來優(yōu)化,使用第二種方案即可;反之,對性能要求較高時可采用。此外需要提醒的是這里也是用空間換時間的做法,View本身因為setTag而會占用更多的內(nèi)存,還會增加代碼量;而findViewById會臨時消耗更多的內(nèi)存,所以不可盲目使用,依實際情況而定。
4.3 方案三
此方案為“Henry Hu”提示,API Level 4以上支持,原理和方案三一致,減少findViewById次數(shù),但是從測試結(jié)果來看效果并不理想,這里不再做進一步的測試。
?
五、推薦文章
Android,誰動了我的內(nèi)存(1)
Android 內(nèi)存泄漏調(diào)試
?
六、后期維護 ?????????? 2011-3-30? 參見這里(http://www.javaeye.com/topic/971782)的討論,據(jù)此將計劃寫續(xù)篇。
?
結(jié)束
對于Google I/O大會這個優(yōu)化方案一直抱遲疑態(tài)度,此番測試總算是有了更進一步的了解,歡迎大家先測試后交流,看看還有什么辦法能夠再優(yōu)化一點。?
轉(zhuǎn)載于:https://www.cnblogs.com/lanzhi/p/6469678.html
總結(jié)
以上是生活随笔為你收集整理的ListView与GridView优化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [原创]状态值在数据库中的检索
- 下一篇: MyBatis 数据持久层