ListView与GridView优化

前言

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

 

声明

  欢迎转载,但请保留文章原始出处:) 

    博客园:http://www.cnblogs.com

    农民伯伯: http://over140.cnblogs.com

 

正文

  一、准备

    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次数,但是从测试结果来看效果并不理想,这里不再做进一步的测试。

 

  五、推荐文章

    Android,谁动了我的内存(1)

    Android 内存泄漏调试

 

六、后期维护

           2011-3-30  参见这里(http://www.javaeye.com/topic/971782)的讨论,据此将计划写续篇。

 

结束

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

转载于:https://www.cnblogs.com/lanzhi/p/6469678.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/547450.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2021安徽舒城中学高考成绩查询,2021安徽省地区高考成绩排名查询,安徽省高考各高中成绩喜报榜单...

怀宁中学芜湖2017年芜湖市高考文科头名是来自于芜湖师大附中的唐逸云&#xff0c;高考成绩653分。2017年芜湖市高考理科头名是来自于芜湖师大附中的茅志鹏&#xff0c;高考成绩676分。师大附中芜湖一中淮南淮南二中文科考生最高分631(市应届生第一名)&#xff0c;理科考生最高分…

jsonp的简单学习

前言&#xff1a;说到AJAX就会不可避免的面临两个问题&#xff0c;第一个是AJAX以何种格式来交换数据&#xff1f;第二个是跨域的需求如何解决&#xff1f;这两个问题目前都有不同的解决方案&#xff0c;比如数据可以用自定义字符串或者用XML来描述&#xff0c;跨域可以通过服务…

win7台式计算机怎么连热点,怎么用台式电脑开热点,电脑怎么开热点win7

导读&#xff1a;手机是每个人的随身携带之物&#xff0c;手机的功能让越来越多的人痴迷其中&#xff0c;也正是因为这个原因&#xff0c;手机流量的消耗也越来越多&#xff0c;购买流量让我们花费了很多金钱&#xff0c;我们该如何解决这个问题呢?下面我们就一起来看看电脑的…

WebLogic11g-创建域(Domain)及基本配置

2019独角兽企业重金招聘Python工程师标准>>> 最近看到经常有人提问weblogic相关问题&#xff0c;所以闲暇之际写几篇博文&#xff08;基于weblogic11&#xff09;&#xff0c;仅供大家参考。 具体weblogic的介绍以及安装&#xff0c;这里就不赘述了。 以域的创建开篇…

计算机教室规则英语作文,班级规则初中英语作文

【导语】无规则不成方圆&#xff0c;班级亦是如此&#xff0c;也拥有属于它的规章制度。下面是无忧考网为你整理的有关班级规则初中英语作文&#xff0c;希望你喜欢!【篇一】班级规则初中英语作文Students should get to school for a half-hour reading before 7:30. Everyone…

高拍仪拍照SDK开发(良田影像S300L|S500L)

高拍仪拍照SDK开发下载地址&#xff1a;点击下载 本SDK适用于&#xff1a;良田影像S300L|S500L 高拍仪如图&#xff1a; SDN开发包安装之后找到安装目录&#xff0c;如图&#xff1a; 大家找到各自需要的版本即可&#xff0c;需要注意的是如果需要上传图片到服务器的话&#…

gitlab的用户使用手册

最近比较忙&#xff0c;没时间写博客&#xff0c;今天来的早所以写一篇关于gitlab的使用手册分享给大家。目录一、账户/项目申请二、登陆与修改密码三、SSH Key导入四、上传/下载代码1、上传&#xff08;用户系统为linux&#xff09;2、下载&#xff08;用户系统为linux&#x…

学计算机多久能拿证,计算机证书多久能领取 什么时候拿

计算机证书多久能领取&#xff0c;什么时候能拿&#xff0c;小编整理了相关信息&#xff0c;希望会对大家有所帮助&#xff01;计算机证书什么时候能领取1、一般情况下是考试结束后50个工作日左右出成绩及出证。全国计算机等级考试证书一年两次考试。每年的3月与9月考试。2、证…

MySQL_0

2019独角兽企业重金招聘Python工程师标准>>> 网址&#xff1a; http://www.mysqlpub.com 转载于:https://my.oschina.net/u/194743/blog/160145

TensorFlow 2.0 mnist手写数字识别(CNN卷积神经网络)

TensorFlow 2.0 (五) - mnist手写数字识别(CNN卷积神经网络) 源代码/数据集已上传到 Github - tensorflow-tutorial-samples 大白话讲解卷积神经网络工作原理&#xff0c;推荐一个bilibili的讲卷积神经网络的视频&#xff0c;up主从youtube搬运过来&#xff0c;用中文讲了一遍。…

职业梦想是计算机的英语作文,理想职业英语作文2篇

篇一&#xff1a;大学英语作文之我理想的工作my ideal jobMy Ideal JobAs college students, we will step into the society, and now we need to prepare for our future and arrange for our future career life, we need to take into consideration what to do in the fut…

C语言二维数组中的指针问题

#include "stdio.h" void main() {int a[5][5];int i,j;for (i0;i<5;i){for (j0;j<5;j){a[i][j] i;}} for (i0;i<5;i){for (j0;j<5;j){printf("%d ",a[i][j]);}printf("\n");} }转载于:https://blog.51cto.com/shamrock/12…

爬取微信小程序源码

爬取微信小程序源码 想知道爬取微信小程序有多简单吗&#xff1f;一张图、三个步骤&#xff0c;拿到你想要的任何微信小程序源码。

马老师 生产环境mysql主从复制、架构优化方案

Binlog日志(主服务器) > 中继日志(从服务器 运行一遍,保持一致)。从服务器是否要二进制日志取决于架构设计。如果二进制保存足够稳定&#xff0c;从性能上来说&#xff0c;从服务器不需要二进制日志。默认情况下&#xff0c;mysql主从复制是异步的。 异步&#xff1a;命令写…

10分钟带你学会微信小程序的反编译

以xxxxx小程序为例10分钟带你学会微信小程序的反编译 2019-11-28 12:59:26 以一个简单的例子介绍下小程序反编译操作流程 实验环境前置准备模拟器内软件安装获取小程序包开始解包导入开发者工具补充注意事项技术交流群有偿解包uniapp 逆向服务逆向教程小程序分包教程#实验环境…

反编译Android APK详细操作指南

早在4年前我曾发表过一篇关于《Android开发之反编译与防止反编译》的文章&#xff0c;在该文章中我对如何在Windows平台反编译APK做了讲解&#xff0c;如今用Mac系统的同学越来越多&#xff0c;也有很多朋友问我能否出一篇关于如何在Mac平台上反编译APK的文章&#xff0c;今天呢…

用idea新建springboot项目遇到的@Restcontroller不能导入的问题

我个人的解决方法如下&#xff1a; 1.springboot默认有 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>o…

Ext1.X的CheckboxSelectionModel默认全选之后不允许编辑的BUG解决方案

Ext1.X的CheckboxSelectionModel默认全选之后不允许编辑的BUG解决方案&#xff0c;ext 的CheckboxSelectionModel在后台默认选中之后&#xff0c;前台就不允许编辑的bug是存在的&#xff0c;因为CheckboxSelectionModel没有Disabled"true"的设置&#xff0c;只能想办…

广州海珠区计算机学校,2019广州海珠区电脑派位和对口直升表

点击即可领取期末各科试卷预约课程还可获赠免费的学习复习诊断— — 学而思爱智康课程优势 — —12年本地化教研沉淀个性化学习方式专属教学服务优质的教学系统2019广州海珠区电脑派位和对口直升表&#xff0c;各位的爸爸妈妈们看过来&#xff01;&#xff01;看看目标学校都招…

android的消息队列机制

android下的线程&#xff0c;Looper线程&#xff0c;MessageQueue&#xff0c;Handler&#xff0c;Message等之间的关系&#xff0c;以及Message的send/post及Message dispatch的过程。 Looper线程 我们知道&#xff0c;线程是进程中某个单一顺序的控制流&#xff0c;它是内核…