0. 背景
正在开发的App中,有一个功能,要在listview中显示一个书籍列表。而且每一个item中都要显示一个书籍封面,通过异步的方式进行获取。
最初,将listview的布局属性layout_height设置为wrap_content。
在adapter的getView中,启动一个自定义的AsyncTask来从网络下载图片并显示。
1. 问题
按理说,以上方式是实现图片异步加载的一个比较常规的方式,但程序运行过程中,遇到了这样一个问题:当scroll出一个新的item时,其图片要等很长时间才能加载,比如说第一页有5个item,当显示第6个item时,该item的图片很长时间才加载出来。该理说,这是不应该的,如果绘制每条item需要执行一次getView()方法的话,每个方法中启动一个AsyncTask的话,顶多有6个AsyncTask,所以不会执行的那么慢。
通过Log发现,在还未scroll列表前,getView()执行了很多次,为什么需要执行这么多次,作用是什么?
2. 解答
Romain Guy在StackOverflow上回答如下:
This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times. In your particular case you are doing the worst thing possible with a ListView by giving it a height=wrap_content. This forces ListView to measure a few children out of the adapter at layout time, to know how big it should be. This is what provides ListView with the convertViews you see passed to getView() even before you scroll.
大概意思是如果listview的layout_height设置为wrap_content,那么Listview会多次调用getView()来测量children的大小,所以这就是为什么在没有scroll的情况下会调用很多次的getView()原因。
以上还提出一个结论。对于getView(),不能假设其调用的次数和顺序, 所以写代码时,不能依赖于getView()的调用次数和顺序。
因为不能依赖于getView()的调用次数和顺序,如果在getView()中要做一些很消耗资源的工作就不合适,比如每一次都要启动一个AsyncTask,这样,当用户快速滚动listview时,会启动大量AsyncTask,这样会消耗很多资源并且使用图片加载缓慢。
改善的一种方式可以使用线程池的思想。