Android 使用NetworkImageView(Volley库)时的OOM 背景
使用是处理从web显示图像的一种方便方法 但是,它有一些bug(正如我所写的) 问题 使用它可能会遇到的一个问题是,它无法以内存效率高的方式对来自web的图像进行解码 这意味着,如果使用一个包含多个NetworkImageView的gridView,并且每个gridView都显示一个分辨率未知(可能小,也可能大)的图像,则最终会出现OOM 例如,您可以将此对象的url设置为,并亲自查看应用程序在显示位图后使用了多少内存,与之前使用的内存相比 问题 如何修改NetworkImageView解码位图的方式Android 使用NetworkImageView(Volley库)时的OOM 背景,android,bitmap,out-of-memory,android-volley,networkimageview,Android,Bitmap,Out Of Memory,Android Volley,Networkimageview,使用是处理从web显示图像的一种方便方法 但是,它有一些bug(正如我所写的) 问题 使用它可能会遇到的一个问题是,它无法以内存效率高的方式对来自web的图像进行解码 这意味着,如果使用一个包含多个NetworkImageView的gridView,并且每个gridView都显示一个分辨率未知(可能小,也可能大)的图像,则最终会出现OOM 例如,您可以将此对象的url设置为,并亲自查看应用程序在显示位图后使用了多少内存,与之前使用的内存相比 问题 如何修改NetworkImageView解码位图
我可以改变它的一种方法是让它解码位图,同时将其缩小到所需的大小(或者至少将其最大值设置为屏幕大小),例如使用缩小的方法。Volley有一种内置的方法,可以将图像适配到您提到的给定宽度和高度。您需要停止使用
NetworkImageView
提供的加载图像的方便方法,而这些方法不使用它。我建议使用以下方法来减少OOM错误的机会:
NetworkImageView
。使用常规的ImageView
并实现侦听器,以便在图像可用时应用该图像。这是步骤2的先决条件。使用NetworkImageView
和get()
方法可能会导致我的经验中出现问题ImageLoader
并使用get()
方法接收ImageRequest
。如果可以,请使用将maxHeight和maxWidth作为参数的可选构造函数ImageLoader
中使用前面提到的get()
方法时,请保存该方法返回的ImageContainer
引用,这样,如果在请求完成之前回收视图,您就可以取消请求ImageLoader
构造函数中的ImageCache
提供良好的实现。这将降低解码已有位图时的冗余度recycle()李>
编辑:添加代码示例
(2)+(4)的代码段
(3)的代码段假定ViewHolder模式和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);
默认图像加载器(您可以在此处执行以下操作):
我在搜索中找到了更好的解决方案:-)
NetworkImageView知道链接中第104行的宽度和第105行的高度
下面是在的确切代码
您只需将此信息转发到图像加载程序
第141行调用ImageLoader#get(字符串
requestUrl,最终ImageListener(侦听器)方法,不带宽度和
高度。将此调用更改为ImageLoader#get(字符串requestUrl,
ImageListener ImageListener,int-maxWidth,int-maxHeight)
将的第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);
你能给我一个示例代码吗?另外,如果谷歌不能以任何好的/定制的方式处理大型图像,为什么要创建这个类呢?NetworkImageView
是以基本方式加载远程图像的语法糖。如果你听一下主题演讲,你会看到Ficus(首席开发人员)说,NetworkImageView
基本上是为“懒惰”的开发人员准备的(他的意思很好)。这是一个很好的基本解决方案,但是如果你需要一些特定的东西,你必须使用不同的机制。我将编辑我的答案,并提供一些示例。懒惰的开发人员通常是一个好的开发人员,它需要别人的工作,而不是重新发明轮子,浪费时间在新的bug上。事实并非如此,因为这个类对于可能填满内存的图像来说不够好。它甚至没有办法以自定义方式解决这个问题。这并不完全正确,我只是发布了一些自定义修复。而且事情从来都不是黑白的。你需要考虑到Volley是新产品,和所有新产品一样,它也在不断发展和扩展。你有没有在超大图片上测试过你的代码,比如我发布的图片?你能告诉我要放的确切代码和放在哪里(不仅仅是行号)吗?这样就解决了问题?也许你应该在github网站的问题/请求上写下它?很遗憾,我无法测试你写的东西,因为我已经停止使用这个库了。也许有一天我会再次尝试。这也是我最后做的!虽然,我不确定的缓存图像虽然。。。如果您从一个Gridview开始,并对代码进行了更改,这是否意味着缓存的“图像”会更小?当您切换到“全屏”视图时,单击GridView图像后,它会重用低样本图像吗?我不确定缓存的大小是否小,但当您将图像设置为NetworkImageView时,它会设置较小的图像大小。这能防止所有内存不足错误吗?
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());
}
}
}
private void loadImageIfNecessary(final boolean isInLayoutPass) {
int width = getWidth(); // at line no 104
int height = getHeight(); // at line no 105
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);