文档章节

提升Android ListView性能的几个技巧

k
 kim366
发布于 2016/05/13 19:28
字数 2079
阅读 0
收藏 0
AndroidListView性能Adapter布局

目录(?)[+]

翻译 By Long Luo

原文链接:Performance Tips for Android's ListView


[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. 译者注:  
  2. 1. 由于这是技术文章,所以有些词句使用原文,表达更准确。  
  3. 2. 由于水平有效,有些地方可能翻译的不够准确,如有不当之处,敬请批评指正;  
  4. 3. inflation这个词一直找不到特别好的中文翻译。  


ListView如何运作的?

ListView是设计应用于对可扩展性和高性能要求的地方。实际上,这就意味着ListView有以下2个要求:

  1. 尽可能少的创建View;
  2. 只是绘制和布局在屏幕上可见的子View。

理解第一点很简单:通过布局xml文件在创建View并显示是很昂贵耗时耗资源的操作。尽管布局文件已经编译打包成了二进制形式以便于更高效的语法解析,但是创建View仍然需要通过一个特殊的XML树,并实例化所有需要响应的View。


ListView通过回收一些不可见的Views,通常在Android源码中称为“ScrapView(废弃的View)”来解决这个问题。这及意味着开发者只需要简单的更新每行的内容而不需要针对每个单独的行的布局来创建View。


为了实现第二点,在我们滑动屏幕时,ListView通过使用View回收器来增加低于或者高于当当前窗口的Views,并当前活动的Views移动到一个可回收池中。这样的话,ListView只需要在内存中保持足够多的Views去填充分配空间中的布局和一些额外的可回收Views,即使当你的Adapter有上百个items的适合。它会使用不同的方法去填充行之间的空间,从顶部或者底部等等,具体取决于窗口是如何变化的。


下面这个图很直观的展示了当你按下ListView的时候发生了什么:

ListView


通过上述介绍,相比我们已经熟悉了ListView的这种机制,让我们继续前往技巧部分。正如上述介绍的,在滑动时,ListView通过动态的创建和回收很多View,实现了尽可能地让Adapter的getView()轻量。所有的技巧都是通过多种方法让getView()更快。


View的回收


ListView每次需要在屏幕上显示新的一行的时候,会从其Adapter中调用getView()的方法。众所周知,getView()方法有3个参数:行的位置, convertView以及父ViewGroup。


参数convertView说穿来就是之前讲述的ScrapView。当ListView要求更新一行的布局时,convertView是一个非空值。因此,当convertView值非空时,你仅仅需要更新内容即可,而不需要重新一个新行的布局。getView()在Adapter中一般是如下的形式:


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.         if (convertView == null) {  
  3.             convertView = mInflater.inflate(R.layout.your_layout, null);  
  4.         }  
  5.       
  6.         TextView text = (TextView) convertView.findViewById(R.id.text);  
  7.         text.setText("Position " + position);  
  8.       
  9.         return convertView;  
  10.     }  


View Holder如何写的模板


Android很常见的一个操作就是在布局文件中找到一个内部的View。通常是使用一个findViewById()的View方法来实现的。这个findViewById()方法在View树中,根据一个View ID,会递归的被调用来找到其子树。虽然在静态UI布局中使用findViewById()是完全正常的。但是,在滑动时,ListView调用其Adapter中的getView()是非常频繁的。findViewById()可能会影响ListView滑动时的性能,尤其是你的行布局是很复杂的时候。


寻找一个充气布局内的内部观点是在Android上最常用的操作之一。这通常是通过一个名为findViewById(查看方法完成)。此方法将递归经过视图树寻找一个孩子用给定的ID码。静态的UI布局使用findViewById()是完全正常,但正如你所看到的,ListView中滚动时调用适配器的getView()非常频繁。 findViewById()可能perceivably击中ListViews,尤其是滚动的性能,如果你行的布局是不平凡的。


View Holder的模式就是减少在Adapter中getView()方法中调用findViewById()次数。实际上,View Holder是一个轻量级的内部类,用于直接引用到所有内部views。在创建View之后,你可以在每行的View存储为一个标签。通过这种方法,只需要在初次创建布局的时候调用findViewById()。下面是一个使用上述方法的View Holder模板的代码示例:


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public View getView(int position, View convertView, ViewGroup parent) {  
  2.     ViewHolder holder;  
  3.   
  4.     if (convertView == null) {  
  5.         convertView = mInflater.inflate(R.layout.your_layout, null);  
  6.   
  7.         holder = new ViewHolder();  
  8.         holder.text = (TextView) convertView.findViewById(R.id.text);  
  9.   
  10.         convertView.setTag(holder);  
  11.     } else {  
  12.         holder = convertView.getTag();  
  13.     }  
  14.   
  15.     holder.text.setText("Position " + position);  
  16.   
  17.     return convertView;  
  18. }  
  19.   
  20. private static class ViewHolder {  
  21.     public TextView text;  
  22. }  

异步加载

很多时候,Android应用在ListView每行中显示一些多媒体内容,比如图片等。在Adapter中的getView()使用应用内置的图片资源还是不会出什么问题的,因为可以存储在Android的高速缓存中。但当你想多态的显示来自本地磁盘或网络的内容时,例如缩略图,简历图片等。在这种情况下,你可能不希望直接在Adapter中的getView()加载它们,因为IO进程会阻塞UI线程。如果这样做的话,ListView就看起来非常卡顿。


在一个单独的线程,如果想要运行的所有行的IO操作或任何高负载CPU限制的异步操作。其中的技巧就是要做到符合ListView的回收行为。例如,如果在Adapter中的getView()中,使用AsyncTask的加载去加载资料图片,在AsyncTask完成之前,你正在加载的图片View就有可能被回收用于其他地方。所以,一旦异步操作完成的同时,需要一种机制来知道如果相应的View有没有被回收。


一个简单的方法来实现这一目标是通过附加一些标识该行与它相关的View的信息。然后,当异步操作完成的适合,检查目标行的View和标识的View是否一致。实现这一目标的方法很多。下面是实现这种方法的一个很简单的示例:


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public View getView(int position, View convertView,  
  2.         ViewGroup parent) {  
  3.     ViewHolder holder;  
  4.   
  5.     ...  
  6.   
  7.     holder.position = position;  
  8.   
  9.     new ThumbnailTask(position, holder)  
  10.             .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null);  
  11.   
  12.     return convertView;  
  13. }  
  14.   
  15. private static class ThumbnailTask extends AsyncTask {  
  16.     private int mPosition;  
  17.     private ViewHolder mHolder;  
  18.   
  19.     public ThumbnailTask(int position, ViewHolder holder) {  
  20.         mPosition = position;  
  21.         mHolder = holder;  
  22.     }  
  23.   
  24.     @Override  
  25.     protected Cursor doInBackground(Void... arg0) {  
  26.         // Download bitmap here  
  27.     }  
  28.   
  29.     @Override  
  30.     protected void onPostExecute(Bitmap bitmap) {  
  31.         if (mHolder.position == mPosition) {  
  32.             mHolder.thumbnail.setImageBitmap(bitmap);  
  33.         }  
  34.     }  
  35. }  
  36.   
  37. private static class ViewHolder {  
  38.     public ImageView thumbnail;  
  39.     public int position;  
  40. }  

人机交互知识

做到在每一行异步加载很多资源,是一个高性能的ListView的必经之路。但是,在滑动屏幕时,如果你一味的在每一个getView()调用里面都去启动一个异步的操作,造成的结果就是你会浪费大量资源。因为行被频繁回收,造成大部分返回的结果会被丢弃。


考虑到实际的人机交互情况,在ListView适配器中,在每一行中都不应该去触发任何异步操作。也就是说,在ListView中有fling(快速滑动)操作时,启动任何异步操作都没有任何意义。一旦滚动停止或即将停止,才是开始真正显示每行的内容的时候。


我不会发布一个代码示例贴在这里,因为其中涉及到的代码太多。Romain Guy写了一个很经典的应用:Shelves app,其中有一个很好的的示例。当GridView停止滑动时不做其他事情时,它就开始触发从而去异步加载书的封面资源。即使在滑动时,你也可以展示缓存中的内容,通过使用memory cache来平衡交互。这真是个好主意!


以上


我强烈推荐你看下Romain Guy和Adam Powell的关于ListView讨论,里面涵盖了很多这篇文章的东西。你可以看看Pattrn,可以看到这里面的几个技巧是如何在应用中运用的。


希望它是你在Android开发中一个很有用的参考:-)


翻译By Long Luo at PM17:30  Feb. 14th, 2014 @Shenzhen , China.

本文转载自:http://blog.csdn.net/oyangyujun/article/details/43966435

共有 人打赏支持
k
粉丝 1
博文 129
码字总数 0
作品 0
朝阳
Android开发优化之——从代码角度进行优化

通常我们写程序,都是在项目计划的压力下完成的,此时完成的代码可以完成具体业务逻辑,但是性能不一定是最优化的。一般来说,优秀的程序员在写完代码之后都会不断的对代码进行重构。重构的好...

KingMing
2015/04/03
0
0
0711 listview属性

android:cacheColorHint 控制item某个组件不出现黑色背景色 android:cacheColorHint=“#00000000” ListView 在Android中可以说随处可见,在自定义背景上使用ListView,当List滚动的时候会发现...

广阔
2012/07/11
0
0
Android开发性能优化总结

Android性能调优包含 移动网络优化 Java(Android)代码优化 布局优化 数据库性能优化 参考链接 http://www.trinea.cn/android/android-traceview/ 本文主要针对代码调优 应用程序的性能问题体...

蜗牛崛起
2017/10/19
0
0
10种技巧可提升Android应用运行效果

10种技巧可提升Android应用运行效果 技巧1:从优秀的编程开始 要采用已为用户所接受的运算法则和标准的设计样式,这些被人们长期使用的编程法则也同样适用于Android应用,尤其当这些应用使用...

庸人谷
2012/11/27
0
0
Android ListView优化实践

在看了一些vogella的文章之后,发现关于android listview性能优化这一段很有意思,于是实践了一下,经过优化,性能确实提升不少! 先看看优化前和优化后的比较: 优化前的log截图: 优化后的...

Jerikc
2013/03/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

OSX | SafariBookmarksSyncAgent意外退出解决方法

1. 启动系统, 按住⌘-R不松手2. 在实用工具(Utilities)下打开终端,输入csrutil disable, 然后回车; 你就看到提示系统完整性保护(SIP: System Integrity Protection)已禁用3. 输入reboot回车...

云迹
22分钟前
0
0
面向对象类之间的关系

面向对象类之间的关系:is-a、has-a、use-a is-a关系也叫继承或泛化,比如大雁和鸟类之间的关系就是继承。 has-a关系称为关联关系,例如企鹅在气候寒冷的地方生活,“企鹅”和“气候”就是关...

gackey
今天
1
0
读书(附电子书)|小狗钱钱之白色的拉布拉多

关注公众号,在公众号中回复“小狗钱钱”可免费获得电子书。 一、背景 之前写了一篇文章 《小狗钱钱》 理财小白应该读的一本书,那时候我才看那本书,现在看了一大半了,发现这本书确实不错,...

tiankonguse
今天
1
0
Permissions 0777 for ‘***’ are too open

异常显示: @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ ......

李玉长
今天
3
0
区块链10年了,还未落地,它失败了吗?

导读 几乎每个人,甚至是对通证持怀疑态度的人,都对区块链的技术有积极的看法,因为它有可能改变世界。然而,区块链技术问世已经10年了,我们仍然没有真正的用上区块链技术。 几乎每个人,甚...

问题终结者
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部