展会信息港展会大全

Android ListView性能优化之视图缓存
来源:互联网   发布日期:2015-11-26 10:22:29   浏览:1665次  

导读:前言ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流。正文一、准备1.1 了解关于Go......

前言

ListView是Android中最常用的控件,通过适配器来进行数据适配然后显示出来,而其性能是个很值得研究的话题。本文与你一起探讨Google I/O提供的优化Adapter方案,欢迎大家交流。

正文

一、准备

1.1 了解关于Google IO大会关于Adapter的优化,参考以下文章:

Android开发之ListView 适配器(Adapter)优化

Android开发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);

}

代码说明:模拟一千条数据,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));//显示统计结果

return convertView;

}

2.1.2 测试结果(微秒除以1000,见代码)

次数

4个子元素

10个子元素

第一次

366

723

第二次

356

689

第三次

371

692

第四次

356

696

第五次

371

662

2.2 方案二

按照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));// 显示统计结果

return convertView;

}

}

static class ViewHolder {

TextView text1;

ImageView icon1;

TextView text2;

ImageView icon2;

}

2.2.2 测试结果(微秒除以1000,见代码)

次数

4个子元素

10个子元素

第一次

311

417

第二次

291

441

第三次

302

462

第四次

286

444

第五次

299

436

2.3 方案三

此方案为 Henry Hu 提示,API Level 4以上提供,这里顺带测试了一下不使用静态内部类情况下性能。

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);// 显示统计结果

return convertView;

}

2.3.2 测试结果(微秒除以1000,见代码)

第一次:450

第二次:467

第三次:472

第四次:451

第五次:441

四、总结

4.1 首先有一个认识是错误的,我们先来看截图:

可以发现,只有第一屏(可视范围)调用getView所消耗的时间远远多于后面的,通过对convertView == null内代码监控也是同样的结果。也就是说ListView仅仅缓存了可视范围内的View,随后的滚动都是对这些View进行数据更新。 不管你有多少数据,他都只用ArrayList缓存可视范围内的View,这样保证了性能,也造成了我以为ListView只缓存View结构不缓存数据 的假相(不会只有我一人这么认为吧- - #)。这也能解释为什么GOOGLE优化方案一比二高很多的原因。那么剩下的也就只有findViewById比较耗时了。据此大家可以看看 AbsListView的源代码,看看obtainView这个方法内的代码及RecycleBin这个类的实现,欢迎分享。

此外了解这个原理了,那么以下代码不运行你可能猜到结果了:

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;

没错,你会发现滚动时会重复显示第一屏的数据!

子控件里的事件因为是同一个控件,也可以直接放到convertView == null 代码块内部,如果需要交互数据比如position,可以通过tag方式来设置并获取当前数据。

4.2 本文方案一与方案二对比

这里推荐如果只是一般的应用(一般指子控件不多),无需都是用静态内部类来优化,使用第二种方案即可;反之,对性能要求较高时可采用。此外需要提醒的是这 里也是用空间换时间的做法,View本身因为setTag而会占用更多的内存,还会增加代码量;而findViewById会临时消耗更多的内存,所以不 可盲目使用,依实际情况而定。

4.3 方案三

此方案为 Henry Hu 提示,API Level 4以上支持,原理和方案三一致,减少findViewById次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。

结束

对于Google I/O大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。

赞助本站

人工智能实验室

相关热词: 性能优化 视图缓存

相关内容
AiLab云推荐
推荐内容
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港