如何获取使用Volley ImageLoader下载的缓存位图?

4
问题:我有一定数量的ImageViews,我是通过以下方式动态添加它们的:
for (int i=2; i < result.size(); i++) {
        // instantiate image view
        ImageView mImageView = new ImageView(this);
        mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
        mImageView.setBackgroundResource(R.drawable.selectable_background_theme);
        mImageView.setOnClickListener(this);

        // download image and display it
        mImageLoader.get(result.get(i), ImageLoader.getImageListener(mImageView, R.drawable.ic_logo, R.drawable.ic_action_refresh));

        // add images to container view
        mLlDescContent.addView(mImageView);
    }

我希望能够点击图片并在另一个全屏活动中显示它。我阅读了一些方法,比如传递Uri或将实际的位图作为字节数组传递。

问题:如何获取使用Volley ImageLoader下载的Uri或实际位图。我正在使用LruCache和找到的BitmapLruCache:Android Volley ImageLoader - BitmapLruCache parameter?。有人能帮我解决这个问题或者提供实现我的目标的任何想法吗?

我尝试了上面的代码后却没有作用:

Bitmap mBitmap = VolleyInstance.getBitmapLruCache().getBitmap(result.get(2));
mIvAuthorImg.setImageBitmap(mBitmap);

编辑:如果我使用以下方式重新请求图像:

mImageLoader.get(result.get(i), ImageLoader.getImageListener(mImageView, R.drawable.ic_logo, R.drawable.ic_action_refresh));

图片从缓存中加载,但是如果我尝试直接从缓存访问图片:
Bitmap mBitmap = VolleyInstance.getBitmapLruCache().getBitmap(result.get(2));
mIvAuthorImg.setImageBitmap(mBitmap);

图片无法加载。我希望能在将其传递到下一个活动之前对其进行操作,例如调整大小等。

你第一次下载图片的时候不是已经有了URL吗? - Darwind
是的,结果是一个包含所有图像URL的ArrayList<String>。 - chr.solr
我想在使用Volley ImageLoader下载图片后从缓存中检索图像。但是,getBitmap(url)似乎无法正常工作。如果已经缓存,我不想重新下载它。我想从缓存中获取位图,然后将位图传递到下一个活动。 - chr.solr
我相信如果图片已经存在于缓存中,那么它将从缓存中启动,而不是再次下载图片。你确定这不是已经发生的事情吗?其他用于图像下载的库都是这样工作的。 - Darwind
我刚刚使用了您的代码和我的网址,它可以正常工作。您确定 ArrayList<String> 中的网址不是空的吗? - Darwind
显示剩余2条评论
2个回答

5
Volley依赖于您实现的缓存以实现有效的缓存。 ImageLoader的构造函数接受一个ImageCache,这是Volley中用于保存和加载位图的简单接口。
public ImageLoader(RequestQueue queue, ImageCache imageCache)

ImageCache接口的Javadoc中引用了以下内容:

简单的缓存适配器接口。如果提供给ImageLoader,则会在调度到Volley之前作为L1缓存使用。实现不能阻塞。建议使用带有LruCache的实现。

Darwind是正确的。如果您请求一张图像并且它存在于缓存中,则将从缓存中加载它而不是从网络中加载。这应该是您的情况,因为您正在加载和展示一张图片,如果点击则应在您的新活动中从缓存中显示。

您说它不起作用,也许您的实现对您的用例没有进行优化。您使用什么样的缓存?您是否拥有一个中央的RequestQueueImageLoader,就像Volley团队推荐的那样?

请查看this question, 这个问题与您的问题不完全相同,但可能对您有所帮助。它有一个简单的LRU缓存实现。

希望这有所帮助!

编辑:

Volley的重点不在于担心实现细节。你想要一张图片吗?它会以最佳和最快的方式为你加载它(从内存中,如果没有则通过网络)。这正是你应该考虑的方式。在检索缓存然后查找其中内容并不是我认为正确的方法。
现在,如果你想操作位图,你有几个选项,我认为最好的选项是实现自己的“图像监听器”,将其传递给“get()”方法,而不是默认的监听器。
像这样:
public class MyImageListener implements ImageListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        // handle errors
    }

    @Override
    public void onResponse(ImageContainer response, boolean isImmediate) {
        Bitmap bitmap = response.getBitmap();
        if (bitmap != null) {

            //                    
            // manipulations
            //

            // assuming mView is a reference to your ImageView
            mView.setImageBitmap(bitmap);
        } else {
            // display placeholder or whatever you want
        }
    }
}

来自Javadoc:

调用流程如下:

  1. 当附加到请求时,将调用onResponse(response, true)以反映任何已经可用的缓存数据。 如果数据可用,则response.getBitmap()将不为null。

  2. 在网络响应返回后,只会发生以下情况之一:

    • 如果加载了图像,则将调用onResponse(response, false)。

      或者

    • 如果加载图像时出现错误,则将调用onErrorResponse。


1
我正在使用您提供的LruCache。我将ImageLoader实例化为单例。图片显示得很好。我想做的是在用户点击后能够在另一个活动中绑定图像。所以,如果我在下一个活动中进行相同的Imageloader调用,它将从缓存中加载,因为ImageLoader已经在上一个活动中下载了?正如Darwind所说。因为手动使用get(url)或getBitmap(url)检索图像似乎不起作用。 - chr.solr
仅在使用视图时才有效,如果您想将缓存的图像保存到文件或传递给电子邮件进行共享怎么办? - JPM
不是这样的。ImageListener与视图完全无关。将响应插入视图是完全由您决定的选项。您可以实现它以执行您想要的操作。一旦调用了onResponse并且您有了对图像的引用,您可以将其保存到文件中,传递它或者进行任何您想要的操作。 - Itai Hanski
我在一个活动中有一个ListView,当onitemclicklistener被调用时,我启动另一个活动来显示图片。所以缓存会对那个活动起作用吗? - Roon13
如果您正在使用相同的“RequestQueue”和“ImageLoader”实例,则是的。 - Itai Hanski
显示剩余2条评论

1

目前在volley中存在一个bug(我70%确定这是一个bug),如果没有指定缓存过期时间(比如,如果你从S3存储桶获取图像,并且设置为永不过期),那么你将总是从网络重新下载。

你可以通过查看HttpHeaderParser并更改相关部分来解决此问题(这并不完全疯狂,因为你必须包含volley源代码),例如:

// Cache-Control takes precedence over an Expires header, even if both exist and Expires
// is more restrictive.
if (hasCacheControl) {
    softExpire = now + maxAge * 1000;
} else if (serverDate > 0 && serverExpires >= serverDate) {
    // Default semantic for Expire header in HTTP specification is softExpire.
    softExpire = now + (serverExpires - serverDate);
} else if (serverExpires == 0) {
    softExpire = Long.MAX_VALUE;
}

那么,您只需将Uri作为参数传递给打开该内容的活动即可。这种解决方案具有令人羡慕的特性,即如果在启动活动和显示位图之间发生任何故障并且图像发生了变化,您仍将重新下载它,并且事情将适当地工作。这可能永远不会/很少发生,但知道正确性得到保留是很好的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接