由universal imageloader图片加载器为重用ImageView加载图片显示错误引起的思考...
2014-09-04 15:05
585 查看
一段非常普通的getview中用imageloader加载图片的代码,却怎么都会让第一个item的图片显示错误(本来应该显示blank图片,但却显示了其他网络上取回来的图片),这个listview的要求是这样的,若photo这个字段有值,则从网络上取回图片显示,没有值则从resource里面加载blank图片。
开始我的代码是这样的:
咋一看,没问题吧?但问题确实来了! 早上各种debug、各种谷歌甚至去到它的老巢https://github.com/nostra13/Android-Universal-Image-Loader里面 看issues,结果没发现有人提过这个问题。下午回来仔细分析它的代码发现,其实它有检测是否ImageView控件是否已经更其他Uri关联的,而且检查做得非常全面到位。
在仔细翻查每行代码,发现LoadAndDisplayImageTask.java里面的这个函数,没错,就是通过它来检查ImageView是不是被重用的:
而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加载一遍,一切问题解决:
开始我的代码是这样的:
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); }
相关文章推荐
- Glide与CircleImageView加载圆形图片显示不正常的问题
- [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画
- 异步加载图片显示到ImageView控件上
- 聊天界面自定义cell中的image view加载网络图片不显示
- ImageView加载图片 路径没问题,图片不显示
- [Android]异步加载图片,内存缓存,文件缓存,imageview显示图片时增加淡入淡出动画
- 使用自定义的item、Adapter和AsyncTask、第三方开源框架PullToRefresh联合使用实现自定义的下拉列表(从网络加载图片显示在item中的ImageView)
- 加载sdcard的图片显示在ImageView中
- 加载网络图片显示进度条,可支持所有继承 imageview 的控件
- 在Android中ImageView无法显示加载的本地SDCard图片。
- 关于在RecyclerView中使用UniversalImageLoader加载图片刷新数据时引起的闪烁问题
- 小米手机上ImageView加载超大本地图片不显示的问题
- ListView的Item高度不确定,导致最后一条item中ImageView 加载图片时不能完全显示
- 使用自定义的item、Adapter和AsyncTask、第三方开源框架PullToRefresh联合使用实现自定义的下拉列表(从网络加载图片显示在item中的ImageView)
- flex image组件加载图片出错时显示默认图片
- android 怎么把网络图片生成一个Bitmap显示在ImageView中
- Android手机开发:ImageView使用和从内存读取图片显示
- 同一位置(同一个ImageView)显示不同的图片--level-list (转)
- Android用ImageView显示本地和网上的图片
- tableviewcell里面imageview在下载图片之前显示loading,下载后显示指定图片