OOM when using NetworkImageView (from Volley library)

Background

Using the Volleyball Library NetworkImageView is a convenient way to process images from the Internet.

However, there are some errors in it (as I wrote here ).

problem

One of the problems that can arise when using it is that it does not decrypt images from the Internet in an effective way.

This means that if you use a gridView with several NetworkImageViews in it, and each of them shows an image with an unknown resolution (may be small, may be large), you will get OOM.

you can set the URL of this object like this and see for yourself how much memory the application uses after displaying the bitmap, compared to how much it used to.

Question

How can I change the way NetworkImageView decodes a bitmap?

One way to change this is to make it decode the bitmap while reducing it to the required size (or at least set the maximum screen size), for example, using this scaling method .

+6
source share
2 answers

Volley has a built-in method to fit the image to a given width and height, as you mentioned. You need to stop using the convenient image loading methods provided by NetworkImageView that don't use it. I suggest using the following methods to reduce the chance of OOM errors:

  • Stop using NetworkImageView . Use a regular ImageView and implement a listener to apply the image when it is available. This is a prerequisite for step 2. Using the NetworkImageView method with the get() method can lead to problems in my experience. "
  • Create an ImageLoader and use the get() method that gets the ImageRequest . Use an optional constructor that takes maxHeight and maxWidth as parameters, if you can.
  • When you use the previously mentioned get() method in ImageLoader , save the ImageContainer link that the method returns so that you can cancel the request if the view has been processed before the request completes.
  • Provide a good implementation for ImageCache in the ImageLoader constructor. This will reduce redundancy in decoding bitmaps that are already available.
  • If your architecture allows this, try using the recycle() method for bitmaps, but be careful not to recycle the ones you still need.

EDIT: Added code examples

Code snippet for (2) + (4)

 // assuming sRequestQueue is your static global request queue // and `BitmapCache` is a good implementation for the `ImageCache` interface sImageLoader = new ImageLoader(sRequestQueue, new BitmapCache()); 

Code snippet for (3) if the ViewHolder and ImageContainer template is a member of the ViewHolder class. The principle applies to any architecture.

 // when applying a new view cancel the previous request first if (imageContainer != null) { imageContainer.cancelRequest(); } // calculate the max height and max width imageContainer = sImageLoader.get(imageUrl, new DefaultImageListener(image), maxWidth, maxHeight); 

The default image downloader (you can do what you are here):

 private class DefaultImageListener implements ImageListener { private ImageView imageView; public DefaultImageListener(ImageView view) { imageView = view } @Override public void onErrorResponse(VolleyError error) { //handle errors } @Override public void onResponse(ImageContainer response, boolean isImmediate) { if (response.getBitmap() != null) { imageView.setImageBitmap(response.getBitmap()); } } } 
+16
source

I found the best solution in my search :-)

NetworkImageView knows its width in line number 104 and the height in line number 105 at NetworkImageView.java

Below is the exact code on NetworkImageView.java

  private void loadImageIfNecessary(final boolean isInLayoutPass) { int width = getWidth(); // at line no 104 int height = getHeight(); // at line no 105 

you only need to forward this information to the image downloader.

On line 141, NetworkImageView.java calls ImageLoader # get (String requestUrl, the final ImageListener listener) with no width and height. Change this call to ImageLoader # get (String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight).

Replace code from line 141 to 172 of NetworkImageView.java with the code below

 ImageContainer newContainer = mImageLoader.get(mUrl, new ImageListener() { @Override public void onErrorResponse(VolleyError error) { if (mErrorImageId != 0) { setImageResource(mErrorImageId); } } @Override public void onResponse(final ImageContainer response, boolean isImmediate) { // If this was an immediate response that was delivered inside of a layout // pass do not set the image immediately as it will trigger a requestLayout // inside of a layout. Instead, defer setting the image by posting back to // the main thread. if (isImmediate && isInLayoutPass) { post(new Runnable() { @Override public void run() { onResponse(response, false); } }); return; } if (response.getBitmap() != null) { setImageBitmap(response.getBitmap()); } else if (mDefaultImageId != 0) { setImageResource(mDefaultImageId); } } }, width, height); 
+3
source

Source: https://habr.com/ru/post/952985/


All Articles