在看了郭霖大神关于ListView图片错乱的分析以后,收获不少。但是自己平时使用多的是findViewWithTag或者是使用的一些开源控件,所以说弱引用相关比findViewWithTag效率要高很多,不是很理解。所以就自己来分析一下(最后的结果确实是比findViewWithTag要好很多)。
直接在郭霖的findViewWithTag解决方案中修改相关的代码,BitmapWorkerTask的onPostExecute方法,修改成下面的样子
@Override
protected void onPostExecute(BitmapDrawable drawable) {
ImageView imageView = (ImageView) mListView.findViewWithTag(imageUrl);
if (imageView != null && drawable != null) {
imageView.setImageDrawable(drawable);
System.out.println("Egos------> 加载成功");
}
else{
System.out.println("Egos------> 加载错乱");
}
}
也就是说成功或者失败都会打印出相应的相应的信息。
看上面的打印信息,发现大部分都是加载错乱的信息,只有少量的加载成功的信息。也就是在这个过程中做了很多的无用功,也可能下载了很多无用的图片,这样对于流量来说就是一个巨大的浪费。
刚好这几天有看了下universal-image-loader这个库,这个库使用的是弱引用相关的方法解决的这个问题(具体怎么使用弱引用相关看郭霖大神的博客就行了)。我首先在最后显示图片时添加了打印的信息。过程分为3种,imageAware是否回收,是否被复用(从郭大神的博客中可以知道,如果复用了就会错乱),是否成功。
<span style="white-space:pre"> </span>@Override
public void run() {
if (imageAware.isCollected()) {
<span style="white-space:pre"> </span>System.out.println("Egos---Collected");
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else if (isViewWasReused()) {
<span style="white-space:pre"> </span>System.out.println("Egos---Reused");
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
} else {
<span style="white-space:pre"> </span>System.out.println("Egos---Success");
L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
displayer.display(bitmap, imageAware, loadedFrom);
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
}
}
看看打印的信息。发现基本上都是成功的。当然,这只是显示时候打印的信息,并不能说明什么,但从侧面来说也体现了成功的几率。
找找加载图片时的操作,在LoadAndDisplayImageTask的run的方法中就有加载图片的图片,可以发现有一个方法checkTaskNotActual,从单词中也可以大致知道意思就是检查本次的加载任务是否是正确的。看看里面的实现。
<span style="white-space:pre"> </span>/**
* @throws TaskCancelledException if task is not actual (target ImageAware is collected by GC or the image URI of
* this task doesn't match to image URI which is actual for current ImageAware at
* this moment)
*/
private void checkTaskNotActual() throws TaskCancelledException { //检查是否是正确的图片下载
<span style="white-space:pre"> </span>System.out.println("Egos======>Can't Load");
checkViewCollected();
checkViewReused();
}
/**
* @return <b>true</b> - if task is not actual (target ImageAware is collected by GC or the image URI of this task
* doesn't match to image URI which is actual for current ImageAware at this moment)); <b>false</b> - otherwise
*/
private boolean isTaskNotActual() {
return isViewCollected() || isViewReused();
}
/** @throws TaskCancelledException if target ImageAware is collected */
private void checkViewCollected() throws TaskCancelledException {
if (isViewCollected()) { //如果View已经回收了,则直接抛出异常。
throw new TaskCancelledException();
}
}
/** @return <b>true</b> - if target ImageAware is collected by GC; <b>false</b> - otherwise */
private boolean isViewCollected() { //判断View是否回收
if (imageAware.isCollected()) {
L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
return true;
}
return false;
}
/** @throws TaskCancelledException if target ImageAware is collected by GC */
private void checkViewReused() throws TaskCancelledException {
if (isViewReused()) { //如果View重复使用,抛出异常
throw new TaskCancelledException();
}
}
/** @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;
}
也就是图片加载之前,就会判断是否是一个正确的图片,如果不是不去下载,如果是就去下载。这意味着什么呢?这意味着可以减少很多不必要的图片的下载。这也是为什么比findViewWithTag效率要高的原因。
最后还有一点要说明的是,最好在加载图片的时候加一张默认图,不然下载比较大的图片的时候,看起来会有加载错乱的错觉。