使用NetworkImageView(Volley库)时的OOM

时间:2013-09-02 14:37:22

标签: android bitmap out-of-memory android-volley networkimageview

背景

使用Volley library' NetworkImageView是处理来自网络图像的便捷方式。

然而,它有一些错误(正如我写的here)。

问题

使用它时可能遇到的一个问题是它无法以内存有效的方式解码来自网络的图像。

这意味着如果你在其中使用带有多个NetworkImageView的gridView,并且每个都显示一个具有未知分辨率的图像(可能很小,可能很大),那么你最终会得到一个OOM。

作为示例,您可以将此对象的网址设置为this one并自行查看应用程序在显示位图后使用的内存量,与之前使用的内容量相比。

问题

如何修改NetworkImageView解码位图的方式?

我可以改变它的一种方法是使其解码位图,同时将其缩小到所需的大小(或至少将其最大值设置为屏幕大小),例如使用降级的this method。 / p>

2 个答案:

答案 0 :(得分:16)

Volley有一种内置的方法,可以像您提到的那样将图像拟合到给定的宽度和高度。您需要停止使用加载NetworkImageView提供的不使用它的图像的便捷方法。我建议使用以下方法来减少OOM错误的可能性:

  1. 停止使用NetworkImageView。使用常规ImageView并实现侦听器以在可用时应用图像。 这是第2步的先决条件。使用带有NetworkImageView方法的get()可能会导致我的体验出现问题。
  2. 创建ImageLoader并使用接收get()的{​​{1}}方法。如果可以的话,使用可选的构造函数,该构造函数接受 maxHeight maxWidth 作为参数。
  3. 当您使用ImageRequest中之前提到的get()方法时,请保存该方法返回的ImageLoader引用,这样您就可以在视图被回收之前取消请求请求完成。
  4. ImageContainer构造函数中的ImageCache提供良好的实现。这将降低解码已经可用的位图的冗余度。
  5. 如果您的架构允许,请尝试在位图上使用ImageLoader方法,但请注意不要回收您可能仍需要的方法。
  6. 编辑:添加了代码示例

    (2)+(4)

    的代码段
    recycle()

    (3)的代码段假设 ViewHolder 模式,// assuming sRequestQueue is your static global request queue // and `BitmapCache` is a good implementation for the `ImageCache` interface sImageLoader = new ImageLoader(sRequestQueue, new BitmapCache()); imageContainer类的成员。该主体适用于任何架构。

    ViewHolder

    默认的图像加载器(你可以在这里做):

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

答案 1 :(得分:3)

我在搜索中找到了更好的解决方案: - )

NetworkImageView在链接NetworkImageView.java

中知道第 - 104行的宽度和第 - 105行的高度

以下是NetworkImageView.java

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

您只需要将此信息转发给图像加载器。

第141行NetworkImageView.java调用ImageLoader#get(String requestUrl,final ImageListener listener)方法没有宽度和 高度。将此调用更改为ImageLoader#get(String requestUrl, ImageListener imageListener,int maxWidth,int maxHeight)。

使用以下代码替换 NetworkImageView.java 的第141行至第172行的代码

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