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;理科考生最高分…

CentOS7挂载nfs盘快速指南

A服务器做服务端&#xff1a; 1、安装nfs相关软件&#xff0c;实际上是基于rpc协议的 yum install -y nfs-utils rpcbind2、设置共享目录 mkdir -p /data/vi /etc/exports/data 192.168.139.71(rw,async,no_root_squash)#rw(来访者的权限&#xff0c;rw表示可读写&#xff0…

带毫秒的字符转换成时间(DateTime)格式的通用方法

C#自身有更好的方式&#xff0c;Net任意String格式转换为DateTime类型 原文 好久没更新日志了&#xff0c;添加个方法吧&#xff0c;本身没有什么技术可言&#xff0c;为了能方便大家&#xff0c;我稍微整理一下咯~ 带毫秒的字符转换成时间&#xff08;DateTime&#xff09…

陕西省2021年高考成绩结果查询,陕西招生考试信息网:2021年陕西高考成绩查询入口、查分系统...

【摘要】为了方便陕西高考考生能及时查询到2021年高考成绩&#xff0c;高考频道特别整理了的陕西招生考试信息网2021年陕西高考成绩查询入口、查分系统&#xff0c;考生可在成绩公布时直接点击下面的链接进行查分&#xff0c;预祝大家金榜题名&#xff01;自陕西招生考试信息网…

SpringBoot 2.3.x 分层构建 Docker 镜像实践

目录[-] . 一、什么是镜像分层. 二、SpringBoot 2.3.x 新增对分层的支持. 三、创建测试的 SpringBoot 应用. 1、Maven 中引入相关依赖和插件. 2、创建测试的 Controller 类. 3、创建 SpringBoot 启动类. 四、创建两种构建镜像的 Dockerfile 脚本. 1、普通镜像构建脚本文件 doc…

Net任意String格式转换为DateTime类型

方式一&#xff1a;Convert.ToDateTime(string) Convert.ToDateTime(string) 注意&#xff1a;string格式有要求&#xff0c;必须是yyyy-MM-dd hh:mm:ss 方式二&#xff1a;Convert.ToDateTime(string, IFormatProvider) DateTimeFormatInfo dtFormat new System.Globalizatio…

jsonp的简单学习

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

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

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

Jenkins 与 Kubernetes 的 CI 与 CD Git + Maven + Docker+Kubectl

目录[-] . 一、Kubernetes 部署 Jenkins. 1、NFS 存储卷创建 Jenkins 目录. 2、创建 Jenkins 用于存储的 PV、PVC. 3、创建 ServiceAccount & ClusterRoleBinding. 4、创建 Service & Deployment. 5、获取 Jenkins 生成的 Token. 6、启动 Jenkins 进行初始化. 二、Jen…

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

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

Asp.Net回车键触发Button的OnClick事件解决方案

在aspx页面有textbox文本框&#xff0c;还有三个button按钮。启用textbox的TextChanged事件和button的click事件。 问题&#xff1a; 现在在textbox文本框输入完数据按“回车”后&#xff0c;会触发TextChanged事件&#xff0c;但同时还会触发该页面第一个button按钮的Click事…

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

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

Pandas 统计分析基础 笔记4 任务4.4 使用分组聚合进行组内计算

文章目录 pandas_任务4.4 使用分组聚合进行组内计算4.4.1 使用groupby方法拆分数据 代码 4-51 对菜品订单详情表依据订单编号分组代码 4-52 GroupBy 类求均值,标准差,中位数代码 4-53 agg和aggregate函数的参数及其说明代码 4-54 使用agg分别求字段的不同统计量代码 4-55 使用…

陕西理工大学计算机科学与技术系,陕西理工大学计算机科学与技术专业2016年在陕西理科高考录取最低分数线...

类似问题答案南京理工大学计算机类(计算机科学与技术、软件工程、网络工程、智能科学与工程)专业2...学校 地 区 专业 年份 批次 类型 分数 南京理工大学 陕西 计算机类(计算机科学与技术、软件工程、网络工程、智能科学与工程) 2016 一批 理科 563 南京理工大学 陕西 计算机类…

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

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

Pandas 文件读取和导出

Pandas 文件读取和导出 更新时间&#xff1a;2020-12-28 00:16:20标签&#xff1a;pandas io 说明 Pandas 中文教程修订中&#xff0c;欢迎加微信 sinbam 提供建议、纠错、催更。查看更新日志 我们拿到的数据一般是 CSV、Excel 等格式&#xff0c;将文件加载到 Pandas 的 D…

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;用中文讲了一遍。…