关于Universal-Image-Loader加载同一张图只成功一张的问题

ImageLoader#loadImage(java.lang.String, ImageLoadingListener);

当使用这个方法加载图片的时候,如果同一本地路径下的图片被加载多次。
你会发现除了最后一个加载任务成功收到onComplete回调,前面的加载任务
都是onCanceled方法被回调。

研究了一下ImagerLoader的源码,会发现最后调用的还是displayImage方法。并且构造了一个NonViewAware对象,该对象的成员变量imageUri被赋值为你需要
加载的图片的路径。

public void loadImage(String uri, ImageLoadingListener listener) {
    loadImage(uri, null, null, listener, null);
}
public void loadImage(String uri, ImageSize targetImageSize, DisplayImageOptions options,
        ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
    checkConfiguration();
    if (targetImageSize == null) {
        targetImageSize = configuration.getMaxImageSize();
    }
    if (options == null) {
        options = configuration.defaultDisplayImageOptions;
    }
    // 在这里组装了一个NonViewAware对象
    NonViewAware imageAware = new NonViewAware(uri, targetImageSize, ViewScaleType.CROP);
    // 接着调用了displayImage方法
    displayImage(uri, imageAware, options, listener, progressListener);
}
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
                         ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
    checkConfiguration();
    if (imageAware == null) {
        throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
    }
    if (listener == null) {
        listener = defaultListener;
    }
    if (options == null) {
        options = configuration.defaultDisplayImageOptions;
    }

    if (TextUtils.isEmpty(uri)) {
        // 取消imageAware对象对应的加载任务
        engine.cancelDisplayTaskFor(imageAware);
        listener.onLoadingStarted(uri, imageAware.getWrappedView());
        if (options.shouldShowImageForEmptyUri()) {
            imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
        } else {
            imageAware.setImageDrawable(null);
        }
        listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
        return;
    }

    if (targetSize == null) {
        targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
    }
    String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
    engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
    // 回调加载任务开始
    listener.onLoadingStarted(uri, imageAware.getWrappedView());
    // 从缓存中获取
    Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
    if (bmp != null && !bmp.isRecycled()) {
        L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);

        if (options.shouldPostProcess()) {
            ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                    options, listener, progressListener, engine.getLockForUri(uri));
            ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
                    defineHandler(options));
            if (options.isSyncLoading()) {
                displayTask.run();
            } else {
                engine.submit(displayTask);
            }
        } else {
            options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
            listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
        }
    } else {
        if (options.shouldShowImageOnLoading()) {
            imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
        } else if (options.isResetViewBeforeLoading()) {
            imageAware.setImageDrawable(null);
        }

        ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
                options, listener, progressListener, engine.getLockForUri(uri));
        LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
                defineHandler(options));
        if (options.isSyncLoading()) {
            displayTask.run();
        } else {
        // 提交任务到加载队列,也就是之后displayTask这个对象的run方法会被调用
            engine.submit(displayTask);
        }
    }
}

加载任务的run方法有点长,我只把关键部分抽取出来:
LoadAndDisplayImageTask#run

@Override
public void run() {
    // ....
    // 省略一堆堆

    DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
    // 继续提交一个DisplayBitmapTask任务
    runTask(displayBitmapTask, syncLoading, handler, engine);
}

接着看DisplayBitmapTask的run方法:
com.nostra13.universalimageloader.core.DisplayBitmapTask#run

@Override
public void run() {
    if (imageAware.isCollected()) {
        L.d(LOG_TASK_CANCELLED_IMAGEAWARE_COLLECTED, memoryCacheKey);
        listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
    } else if (isViewWasReused()) {
        // isViewWasResued为true的话,cancel掉该任务
        L.d(LOG_TASK_CANCELLED_IMAGEAWARE_REUSED, memoryCacheKey);
        listener.onLoadingCancelled(imageUri, imageAware.getWrappedView());
    } else {
        L.d(LOG_DISPLAY_IMAGE_IN_IMAGEAWARE, loadedFrom, memoryCacheKey);
        displayer.display(bitmap, imageAware, loadedFrom);
        engine.cancelDisplayTaskFor(imageAware);
        listener.onLoadingComplete(imageUri, imageAware.getWrappedView(), bitmap);
    }
}

isViewWasResued:

/** Checks whether memory cache key (image URI) for current ImageAware is actual */
private boolean isViewWasReused() {
    String currentCacheKey = engine.getLoadingUriForView(imageAware);
    return !memoryCacheKey.equals(currentCacheKey);
}
/**
 * Returns URI of image which is loading at this moment into passed {@link com.nostra13.universalimageloader.core.imageaware.ImageAware}
 */
String getLoadingUriForView(ImageAware imageAware) {
    return cacheKeysForImageAwares.get(imageAware.getId());
}

最后调用了NonViewAware的getId方法:

@Override
public int getId() {
    return TextUtils.isEmpty(imageUri) ? super.hashCode() : imageUri.hashCode();
}

而这里的getId返回的结果正是加载的图片路径,也就是说,如果同一路径下
的图片,同一个任务如果在队列中存在的话,已存在的会被cancel掉,
当被cancel掉的任务调用run方法是,直接回调onCancel方法,不会调用onComplete方法。

解决方法:
调用displayImage方法,传递一个NonViewAware对象,其iamgeUrl成员变赋值成一个递增的数字,这样的话,即使是重复路径的任务也会执行。

public void displayImage(String uri, ImageAware imageAware, ImageLoadingListener listener) {
    displayImage(uri, imageAware, null, listener, null);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值