OOM when using NetworkImageView ( of Volley library)

若如初见. 提交于 2019-11-30 05:36:54
Itai Hanski

Volley has a built in method for fitting an image to a given width and height like you mentioned. You need to stop using the convenience methods of loading images provided by NetworkImageView which don't use it. I suggest using the following methods to decrease the chance for OOM errors:

  1. Stop using NetworkImageView. Use a regular ImageView and implement the listener to apply the image when it is available. This is a prerequisite for step 2. Using a NetworkImageView with the get() method may lead to problems in my experience`.
  2. Create an ImageLoader and use the get() method which receives an ImageRequest. Use the optional constructor that takes in a maxHeight and maxWidth as parameters if you can.
  3. When you use the previously mentioned get() method in the ImageLoader, save the ImageContainer reference that method returns so you'll be able to cancel a request if the view is recycled before the request completes.
  4. Provide a good implementation for an ImageCache in the ImageLoader constructor. That'll lower the redundancy in decoding bitmaps that are already available.
  5. If your architecture allows it, try to use the recycle() method on the bitmaps, but be careful not to recycle the ones you might still need.

EDIT: Added Code Samples

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) assuming the ViewHolder pattern and imageContainer is a member of the ViewHolder class. The principal 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 loader (you can do what you 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());
        }
    }
}

I found a better solution in my search : -)

NetworkImageView knows its width at line no - 104 and height at line no - 105 in the link NetworkImageView.java

Below is the exact code at 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 loader.

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

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

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);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!