由universal imageloader图片加载器为重用ImageView加载图片显示错误引起的思考...

刘和玉
2023-12-01

         一段非常普通的getview中用imageloader加载图片的代码,却怎么都会让第一个item的图片显示错误(本来应该显示blank图片,但却显示了其他网络上取回来的图片),这个listview的要求是这样的,若photo这个字段有值,则从网络上取回图片显示,没有值则从resource里面加载blank图片。

         开始我的代码是这样的:

holder.item_model_image.setImageResource(R.drawable.blankmodel);
if (!Util.fillNullStr(data.photo).equals("")){
		holder.item_model_image.
	imageLoader.displayImage((String) holder.item_model_image.getTag() , holder.item_model_image,
			mNormalImageOptions);
}

咋一看,没问题吧?但问题确实来了! 早上各种debug、各种谷歌甚至去到它的老巢https://github.com/nostra13/Android-Universal-Image-Loader里面 看issues,结果没发现有人提过这个问题。下午回来仔细分析它的代码发现,其实它有检测是否ImageView控件是否已经更其他Uri关联的,而且检查做得非常全面到位。

        在仔细翻查每行代码,发现LoadAndDisplayImageTask.java里面的这个函数,没错,就是通过它来检查ImageView是不是被重用的:

	/** @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;
	}


	/** @throws TaskCancelledException if target ImageAware is collected by GC */
	private void checkViewReused() throws TaskCancelledException {
		if (isViewReused()) {
			throw new TaskCancelledException();
		}
	}


而engine的getLoadingUriForView实现为:

String getLoadingUriForView(ImageAware imageAware) {
return cacheKeysForImageAwares.get(imageAware.getId());
}

cacheKeysForImageAwares这个实例则定义为:Map<Integer, String> cacheKeysForImageAwares = Collections
.synchronizedMap(new HashMap<Integer, String>());


        看到了吧?它是通过要加载的视图,然后在cacheKeysForImageAwares 里面找回它的Uri,然后跟当前的Uri对比,若不相等则认为是重用,而checkViewReused()则检测重用则抛出异常让任务终止,有效防止图片错位。

        但是,若这个ImageView虽然存在在listview的某个item里面(如我上面自己写的代码),之前没被imageloader加载过,当此View再次被使用并需要显示新图片时,然而cacheKeysForImageAwares表里没有记录,isViewReused()返回false,也就避开checkViewReused()的异常,顺利显示已经下载完的图片,把旧图片冲掉。



     查到这个原因后就容易修改了,不管是否需要显示图片,imageview都用imageloader加载一遍,一切问题解决:

if (!Util.fillNullStr(data.photo).equals("")){
		holder.item_model_image.
	imageLoader.displayImage((String) holder.item_model_image.getTag() , holder.item_model_image,
			mNormalImageOptions);
}
else{
	imageLoader.displayImage(null, holder.item_model_image,mNormalImageOptions);
}


 类似资料: