7

背景

使用Volley 库NetworkImageView是一种处理来自网络的显示图像的便捷方式。

但是,它有一些错误(正如我在这里写的)。

问题

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

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

例如,您可以将此对象的 url 设置为这个并亲自查看应用程序在显示位图后使用了多少内存,与之前使用的内存相比。

问题

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

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

4

2 回答 2

16

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

  1. 停止使用NetworkImageView。使用常规ImageView并实现侦听器以在图像可用时应用图像。这是第 2 步的先决条件。使用 a NetworkImageViewwithget()方法可能会导致我的经验出现问题`。
  2. 创建一个ImageLoader并使用get()接收ImageRequest. 如果可以,请使用将maxHeightmaxWidth作为参数的可选构造函数。
  3. 当您在 中使用前面提到get()的方法时ImageLoader,请保存该ImageContainer方法返回的引用,这样如果在请求完成之前视图被回收,您就可以取消请求。
  4. ImageCache为构造函数中的a 提供良好的实现ImageLoader。这将降低解码已经可用的位图的冗余。
  5. 如果您的架构允许,请尝试recycle()在位图上使用该方法,但请注意不要回收您可能仍需要的那些。

编辑:添加代码示例

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

(3) 的代码片段假设ViewHolder模式并且imageContainerViewHolder该类的成员。原则适用于任何架构。

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

默认图像加载器(您可以在此处执行操作):

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());
        }
    }
}
于 2013-09-03T07:19:10.800 回答
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);
于 2014-01-16T13:07:24.760 回答