前言

  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大会这个优化方案一直抱迟疑态度,此番测试总算是有了更进一步的了解,欢迎大家先测试后交流,看看还有什么办法能够再优化一点。

ListView与GridView优化的更多相关文章

  1. Android之ListView/GridView 优化

    一.效率最低的getView实现 我们知道,ListView和GridView的显示都是通过Adapter的getView实现的. ListView/GridView数据量较小时,我们的处理方式一般是 ...

  2. [Android Pro] listView和GridView的item设置的高度和宽度不起作用

    referece to : http://blog.csdn.net/beibeixiao/article/details/9032569 1.     在Android开发中会发现,有时listVi ...

  3. android 有弹性的ScrollView 简单实现,与处理ScrollView和ListView,GridView之间的冲突

    处理ScrollView和ListView,GridView之间的冲突, 最好的办法就是继承这两个类,重写他们的onMeasure方法即可: ListView: import android.widg ...

  4. Android之ScrollView嵌套ListView和GridView冲突

    由于ListView,GridView本身都继承于ScrollView,一旦在ScrollView中嵌套ScrollView, 在ScrollView中嵌套使用ListView或者GridView,L ...

  5. [安卓] 16、ListView和GridView结合显示单元实现自定义列表显示效果

    List在各种手机应用中都有体现,是安卓UI设计的必修课. 本文将介绍在开发中如何利用ListView和GridView设计自定义列表. 下面分别是用ListView和GridView做的效果: 上面 ...

  6. android listView嵌套gridview的使用心得

    在开发的过程中可能需要用到listview嵌套gridview的场景,但是在Android中, 不能在一个拥有Scrollbar的组件中嵌入另一个拥有Scrollbar的组件,因为这不科学,会混淆滑动 ...

  7. (转)解决ScrollView嵌套ListView或者GridView导致只显示一行的方法

    即动态获取ListView和GridView的高度 一.对于ListView ListView listview= new ListView(this); setListViewHeightBased ...

  8. Listview和Gridview自定义分割线

    1,ListView和GridView中的每一个条目都有分割线,属性android:footerDividersEnabled表示是否显示分割线,默认是为true,即可见.可以自定义的设置分割线的颜色 ...

  9. android 项目学习随笔十七(ListView、GridView显示组图)

    ListView.GridView显示组图,处理机制相同 <?xml version="1.0" encoding="utf-8"?> <Li ...

随机推荐

  1. [Android 4.4.2] 泛泰A870 Mokee4.4.2 20140531 RC1.0 by syhost

    欢迎关注泛泰非盈利专业第三方开发团队 VegaDevTeam  (本team 由 syhost suky zhaochengw(z大) xuefy(大星星) tenfar(R大师) loogeo cr ...

  2. Android中ImageView.ScaleType属性值

    1 android:scaleType="center" (1)当图片大于ImageView的宽高:以图片的中心点和ImageView的中心点为基准,按照图片的原大小居中显示,不缩 ...

  3. 4、linux下应用创建线程

    1.linux创建线程之pthread_create 函数简介 pthread_create是UNIX环境创建线程函数 头文件 #include<pthread.h> 函数声明 int p ...

  4. 【LeetCode-面试算法经典-Java实现】【104-Maximum Depth of Binary Tree(二叉树的最大深度)】

    [104-Maximum Depth of Binary Tree(二叉树的最大深度)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a binary t ...

  5. php 文件夹是否存在,不存在就创建

    $lujing = "./nihao/wohao"; if(!is_dir($liujing)){ mkdir(iconv("UTF-8", "GBK ...

  6. 【2005】N只猴子选大王

    Time Limit: 3 second Memory Limit: 2 MB N只猴子选大王.选举办法如下:从头到尾1.2.3报数,凡报3的退出,余下的从尾到头1.2.3报数,凡报3退出:余下的又从 ...

  7. 数学分析告诉偶们什么(vamei)

    1]人生的痛苦在于追求错误的东西.所谓追求错误的东西,就是你在无限趋近于它的时候,才猛然发现,你和它是不连续的. 2]人和人就像数轴上的有理数点,彼此能够靠得非常近非常近,但你们之间始终存在隔阂. 3 ...

  8. Android动态修改图片颜色的实现方式分析

    版权声明:本文为博主原创文章,未经博主允许不得转载. 1.修改色相.饱和度.亮度 参看:http://blog.csdn.NET/sjf0115/article/details/7267063 2.使 ...

  9. 用DOM4J包实现对xml文件按属性分离。

    转自本人博客:http://www.xgezhang.com/dom4j_xml_separata.html dom4j是一个Java的XML API.类似于jdom.用来读写XML文件的. dom4 ...

  10. C语言高速入门系列(五)

    C语言高速入门系列(五) C语言指针初涉                                           ------转载请注明出处:coder-pig 本节引言: 上一节我们对C ...