4

Problem: I've a X amount of ImageViews that I'm adding dynamically like this:

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);
    }

What want to be able to click on the image and display it in another activity in full screen. I have read about a couple of ways such as passing the Uri or passing the actual Bitmap as a byte array.

Question: How do I get the Uri or the actual Bitmap I downloaded with Volley ImageLoader. The LruCache I'm using an BitmapLruCache I found here: Android Volley ImageLoader - BitmapLruCache parameter? . Can someone help me with this or any idea to accomplish my goal.

I tried this after the above code and nothing:

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

Edit: If i re-request the image with:

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

the image is loaded from the cache, BUT if I try to access the image straight from the cache with:

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

the image don't load. I want to be able to manipulate the image, such as size an stuff before a pass it to the next activity.

Community
  • 1
  • 1
chr.solr
  • 576
  • 1
  • 7
  • 19
  • Don't you already have the url from when you downloaded the image the first time? – Darwind Jul 28 '13 at 22:31
  • yes, result is an ArrayList that contains all image urls. – chr.solr Jul 28 '13 at 23:01
  • Ok if you have the list of urls, then what is the problem exactly? – Darwind Jul 29 '13 at 06:44
  • I want to retrieve the image from the cache after downloading it with volley ImageLoader. getBitmap(url) doesn't seen to work. I don't want to redownload it if it is already cached. I want to get the bitmap from cache and then pass the bitmap to next activity. – chr.solr Jul 29 '13 at 09:24
  • I believe the image will initiated from the cache if it's present in the cache, instead of downloading the image again. Did you check that this isn't what's happening already? This is how other libraries for image downloading are working. – Darwind Jul 29 '13 at 09:32
  • I just tried your code with my own url and it's working fine. Are you absolutely sure, that the `ArrayList` of urls aren't empty? – Darwind Jul 29 '13 at 21:50
  • Check the edit part in my question. – chr.solr Jul 30 '13 at 05:29

2 Answers2

5

Volley depends on your implementation of a cache for successful efficient caching. The constructor for the ImageLoader takes in an ImageCache which is a simple interface in Volley to save and load bitmaps.

public ImageLoader(RequestQueue queue, ImageCache imageCache)

A quote from the Javadoc of ImageCache interface:

Simple cache adapter interface. If provided to the ImageLoader, it will be used as an L1 cache before dispatch to Volley. Implementations must not block. Implementation with an LruCache is recommended.

Darwind is right. If you request an image and it is present in the cache it will be loaded from the cache and not from the web. This should be the case for you since you're loading and presenting an image, which if clicked should be displayed from the cache in your new activity.

You say it's not working, perhaps your implementation isn't optimized for your use case. What kind of cache are you using? Do you have one centralized RequestQueue and ImageLoader as is recommended by the Volley team?

Take a look at this question, which isn't exactly the same as yours, yet could be helpful to you. It has a simple LRU cache implementation.

Hope that helps!

Edit:

The point of Volley is not to worry about implementation details. You want an image? it will load it for you the best and fastest way possible (from memory and if it's not there via network). That's exactly the way you should look at it. Retrieving the cache and then looking in it is not the right approach IMO.

Now, if you want to manipulate the bitmap, you have a few options, the best IMO being to implement your own Image Listener pass it to the get() method instead of the default one.

Something like this:

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
        }
    }
}

from the Javadoc:

The call flow is this:

  1. Upon being attached to a request, onResponse(response, true) will be invoked to reflect any cached data that was already available. If the data was available, response.getBitmap() will be non-null.

  2. After a network response returns, only one of the following cases will happen:

    • onResponse(response, false) will be called if the image was loaded.

      or

    • onErrorResponse will be called if there was an error loading the image.

Community
  • 1
  • 1
Itai Hanski
  • 8,540
  • 5
  • 45
  • 65
  • 1
    I'm using the same LruCache from your link. I'm instantiating ImageLoader as a singleton. The picture shows just fine. What I want to do is be able to bind the image in another activity after the user clicks on it. So if I make the same Imageloader call in the next activity, it will load from cache since ImageLoader already downloaded in the previous activity? As Darwind said. Because retrieving the image manually with get(url) or getBitmap(url) doesn't seen to work. – chr.solr Jul 29 '13 at 16:39
  • Works only if you are using a view, what if you want to take the cached image and save it to file or pass it to email for sharing? – JPM Nov 06 '13 at 21:35
  • Not true. `ImageListener` is totally orthogonal to the view. Inserting the response into a view is an option that is totally up to you. You can implement it to do what you want. Once `onResponse` is called and you have a reference to the image, you can save it to file, pass it along or do whatever you want. – Itai Hanski Nov 07 '13 at 08:25
  • @ItaiHanski Is making new `NetworkImage` request in Another activity right practice? Will it load image from cache if it is there? – Roon13 Jun 20 '15 at 04:49
  • @Roon13 Not sure what you mean exactly, but generally, yeah - a `NetworkImage` works with the image cache – Itai Hanski Jun 20 '15 at 08:38
  • I have a listview in one activity and when onitemclicklistener i start another activity to show image..so the cache will work for that activity?? – Roon13 Jun 20 '15 at 08:42
  • If you're using the same instance of `RequestQueue` and `ImageLoader` then yes. – Itai Hanski Jun 20 '15 at 20:32
1

There's a bug (I'm 70% sure it's a bug) in volley currently where if a cache-expiry isn't specified (say, if you're getting an image from an S3 bucket where you have a never-expires setting), you'll always redownload from the network.

You could get around this by checking out HttpHeaderParser, and changing the relevant bits (this isn't totally crazy, as you have to include the volley source code anyway) to:

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

Then you could just pass the Uri to the activity opening the thing as a parameter. This solution has the enviable property that if something goes wrong and something happens to the image between launching the activity and displaying the bitmap, you'll still redownload it and things will work appropriately. That'll probably never/seldom happen, but it's nice to know correctness is preserved.

secureboot
  • 1,776
  • 4
  • 20
  • 32