当前位置:   article > 正文

记ImageLoader 引发的问题_edan.asp.imageviewloader.core.loadanddisplayimaget

edan.asp.imageviewloader.core.loadanddisplayimagetask$taskcancelledexception

以问题的形式记录ImageLoader 图片加载器相关知识

问题描述:app有使用ImageLoader加载图片,假如加载某个场景图片A,后来场景换了又产生了一张图片,此时又命名为A,这时候其实图片已经换了,但是显示还是之前的图片?


分析:要解决这个问题就要了解ImageLoader的图片加载机制(参考文章)和实现原理,下面来分析一下这个国民程序员加载图片库。

从ImageLoader的disPlayImage说起

核心代码

  1. public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener, FileImageDecoder mFileImageDecoder) {
  2. this.checkConfiguration();
  3. ........
  4. // uri为空 展示逻辑
  5. if(TextUtils.isEmpty(uri)) {
  6. this.engine.cancelDisplayTaskFor(imageAware);
  7. listener.onLoadingStarted(uri, imageAware.getWrappedView());
  8. if(options.shouldShowImageForEmptyUri()) {
  9. imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources));
  10. } else {
  11. imageAware.setImageDrawable((Drawable)null);
  12. }
  13. listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null);
  14. } else {
  15. ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
  16. String memoryCacheKey;
  17. //根据uri生成key
  18. if(options.isMemoryCacheKeySplitQuestionMark()) {
  19. String keyUri = StorageUtils.generateKeyUri(uri);
  20. memoryCacheKey = MemoryCacheUtils.generateKey(TextUtils.isEmpty(keyUri)?uri:keyUri, targetSize);
  21. } else {
  22. memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
  23. }
  24. this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
  25. listener.onLoadingStarted(uri, imageAware.getWrappedView());
  26. //用此key来从缓存中获取图片
  27. Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
  28. ImageLoadingInfo imageLoadingInfo;
  29. // 获取到了图片
  30. if(bmp != null && !bmp.isRecycled()) {
  31. if(this.configuration.writeLogs) {
  32. L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
  33. }
  34. if(options.shouldPostProcess()) {
  35. imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
  36. ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, defineHandler(options));
  37. if(options.isSyncLoading()) {
  38. displayTask.run();
  39. } else {
  40. this.engine.submit(displayTask);
  41. }
  42. } else {
  43. options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
  44. listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
  45. }
  46. } else {
  47. // 没有获取到,图片展示逻辑
  48. if(options.shouldShowImageOnLoading()) {//是否设置了加载图片时候的回调
  49. imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));
  50. } else if(options.isResetViewBeforeLoading()) {
  51. imageAware.setImageDrawable((Drawable)null);
  52. }
  53. imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
  54. LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options), mFileImageDecoder);
  55. if(options.isSyncLoading()) {
  56. displayTask.run();
  57. } else {
  58. this.engine.submit(displayTask);
  59. }
  60. }
  61. }
  62. }
  63. }

其大致逻辑是 

先缓存,在磁盘,最后没有就网络获取;这里的顺序和代码的逻辑一样的,根据图片uri生成一个key,然后用key去找图片,看看这个key是怎么生成的?

  1. ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
  2. String memoryCacheKey;
  3. if(options.isMemoryCacheKeySplitQuestionMark()) {
  4. String keyUri = StorageUtils.generateKeyUri(uri);
  5. memoryCacheKey = MemoryCacheUtils.generateKey(TextUtils.isEmpty(keyUri)?uri:keyUri, targetSize);
  6. } else {
  7. memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
  8. }
  9. public static String generateKey(String imageUri, ImageSize targetSize) {
  10. return imageUri + "_" + targetSize.getWidth() + "x" + targetSize.getHeight();
  11. }

关键是generateKey 方法,这里根据uri 和 内部定义的 targetSize来去生成key,这样假如图片的名称没有改变那这样得到同一个key,再回到之前displayImage方法,就会看到ImageLoader会从内存中去加载图片,这时候就不会走下面的逻辑了,导致虽然图片内容变了但是显示的图片还是之前的原因,所以这个问题到此也就分析出来了;

解决方法

很简单的,既然你是从缓存中获取图片,那我在设置Options 的时候禁用缓存就可以了。

.cacheInMemory(false).cacheOnDisk(false)

ImageLoader 同步加载图片

同步的概念理解:就是顺序执行,直到图片加载请求结束后,在去执行其他代码;可以百度 “同步网络请求什么意思” ,或者参考一下 同步和异步

使用场景:假如推送中包含文字信息和图片信息,由于及时性的特性,要求推送产生后,先把文字推送出去(文字信息包含图片的url),并同时上传图片(这个图片上传是耗时的,等图片上传完了后url才会有图片),这样会造成两者的不同步,但app端要同时展示文字和图片?

解决办法,见代码示例:

  1. // 由于推送文字消息和图片有时间差,当推送过来时就同步加载图片10次,若10次之后还没有加载到,则认为图片有问题
  2. Bitmap bigBitmap = null;
  3. for (int i = 0; i < 10; i++) {
  4. bigBitmap = ImageLoader.getInstance().loadImageSync(largeIconUrl);
  5. if (bigBitmap != null){
  6. break;
  7. }
  8. LogUtil.debugLog("tag", "for循环: " + i);
  9. }
  10. if (null != bigBitmap){
  11. 图片加载出来了,并设置推送大图
  12. builder.setLargeIcon(bigBitmap);
  13. builder.setStyle(new Notification.BigPictureStyle().bigPicture(bigBitmap));
  14. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  15. notification = builder.build();
  16. } else {
  17. notification = builder.getNotification();
  18. }
  19. notificationManager.notify(xx, notification);
  20. }else {
  21. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
  22. notification = builder.build();
  23. } else {
  24. notification = builder.getNotification();
  25. }
  26. notificationManager.notify(xx, notification);
  27. }

由于,可以得出同步和异步的区别

  • 一般的,异步中我们需要从回调接口中拿到我们需要对象,而同步方法会直接返回你所要的对象;
  • 不管同步和异步其实他们都是在子线程中实现的;(但同步是的具体实现机制是什么样的呢?有待深入探讨)
  • 之前一直疑惑同步请求没有用,其实也是有用的

这个大图获取是在service中实现的,自己验证了一下在actvity中直接用 loadImageSync 会报异常android.os.NetworkOnMainThreadException,所以这个后面继续研究一下?

 

ImageLoader 配置

参考文章 点击链接,可以看看相关配置介绍。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/105832
推荐阅读
相关标签
  

闽ICP备14008679号