在ListView加载图片的时候,我们就会关于这里的图片加载产生一些问题。另外,我们知道针对ListView加载图片常用的优化方案之一就是,在列表的滑动过程中,暂停图片的加载任务,在滑动结束之后,则继续之前的任务。所以我们针对这些问题进行一些探讨研究(以UIL图片加载为例)。
知识点1:加载图片的线程的控制; 我们知道在ListView的adapter的getView方法中,进行图片的获取,这里为执行LoadAndDisplayImageTask,这是一个单独的线程去加载图片。但我们不会对每个View加载图片都去执行一个新的线程,所以这里采用的是一个线程池来控制线程的数目,来避免频繁地创建线程。
知识点2:在ListView的滑动过程中,是如何暂停当前的图片下载的任务的; 由于这里我们采用的是单独的线程来控制图片的加载显示的,针对线程,我们使用的是加锁来使线程阻塞,当ListView滑动结束,则使其恢复。 在UIL中,锁声明的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
在LoadAndDisplayImageTask的方法中,执行图片的加载的时候,会进行pause的判断。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* @return <b>true</b> - if task should be interrupted; <b>false</b> - otherwise /
private boolean waitIfPaused() {
AtomicBoolean pause = engine.getPause();
if (pause.get()) {
synchronized (engine.getPauseLock()) {
if (pause.get()) {
L.d(LOG_WAITING_FOR_RESUME, memoryCacheKey);
try {
engine.getPauseLock().wait();
} catch (InterruptedException e) {
L.e(LOG_TASK_INTERRUPTED, memoryCacheKey);
return true;
}
L.d(LOG_RESUME_AFTER_PAUSE, memoryCacheKey);
}
}
}
return isTaskNotActual();
}
另外,pause跟resume的调用,则是在UIL的PauseOnScrollListener进行滑动判断的调用。所以可以明显的看出是通过object的wait跟notifyAll实现线程同步,来达到暂停图片加载的任务的。
- 知识点3:当一个Item的view滑出屏幕外,应该怎么处理其对应的图片加载任务:
我们先要判断的是怎么确定一个view已经滑出屏幕之外了呢?这里有个取巧的办法就是判断view是否重用了,因为ListView显示界面所需要的ItemView是固定的(确保当前ListView是采用convertView的缓存机制),而在实现图片显示加载的时候,每个view是要绑定当前加载的图片的链接的,如果当前任务(是指当前View显示加载图片的线程)的图片链接与View要显示的图片链接不一致,则可以判定当前view已经重用了。所以则可以结束当前的显示图片的任务了,具体判断View重用的代码如下:
而在调用isViewReused的checkViewReused的如下,
1 2 3 4 5 6 7 8 9 10 11 12 13
/ @return <b>true</b> - if current ImageAware is reused for displaying another image; <b>false</b> - otherwise */ private boolean isViewReused() { String currentCacheKey = engine.getLoadingUriForView(imageAware); // Check whether memory cache key (image URI) for current ImageAware is actual. // If ImageAware is reused for another task then current task should be cancelled. boolean imageAwareWasReused = !memoryCacheKey.equals(currentCacheKey); if (imageAwareWasReused) { L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey); return true; } return false; }
最后,在线程的主体run方法中,通过调用checkTaskNotActual()判断view重用或者被回收,抛出TaskCancelledException,来结束当前的线程,并回调图片加载的cancel方法。1 2 3 4 5 6 7
/ @throws TaskCancelledException if target ImageAware is collected by GC */ private void checkViewReused() throws TaskCancelledException { if (isViewReused()) { throw new TaskCancelledException(); } }
最后,我感兴趣的几个问题记录如上,有疑问,大家不吝赐教,可以一起探讨。
PS:转载请注明来自原文链接。