Android java.lang.OutOfMemoryError:位图大小超过ListView和延迟加载图像中的VM预算

Android java.lang.OutOfMemoryError:位图大小超过ListView和延迟加载图像中的VM预算,android,listview,bitmap,android-listview,out-of-memory,Android,Listview,Bitmap,Android Listview,Out Of Memory,我有一个listview,它可能在无限滚动时加载了无限个项目 列表视图中的每个项目都有一个或两个我正在延迟加载的图像 一切都很好,但当我滚动很长时间,它崩溃了,这在日志猫 08-07 15:26:25.231: E/AndroidRuntime(30979): FATAL EXCEPTION: Thread-60 08-07 15:26:25.231: E/AndroidRuntime(30979): java.lang.OutOfMemoryError: bitmap size exceed

我有一个listview,它可能在无限滚动时加载了无限个项目

列表视图中的每个项目都有一个或两个我正在延迟加载的图像

一切都很好,但当我滚动很长时间,它崩溃了,这在日志猫

 08-07 15:26:25.231: E/AndroidRuntime(30979): FATAL EXCEPTION: Thread-60
08-07 15:26:25.231: E/AndroidRuntime(30979): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-07 15:26:25.231: E/AndroidRuntime(30979):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
08-07 15:26:25.231: E/AndroidRuntime(30979):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:493)
08-07 15:26:25.231: E/AndroidRuntime(30979):    at com.test.android.helper.LazyImageLoader.decodeFile(LazyImageLoader.java:171)
08-07 15:26:25.231: E/AndroidRuntime(30979):    at com.test.android.helper.LazyImageLoader.getBitmap(LazyImageLoader.java:112)
08-07 15:26:25.231: E/AndroidRuntime(30979):    at com.test.android.helper.LazyImageLoader.access$2(LazyImageLoader.java:106)
08-07 15:26:25.231: E/AndroidRuntime(30979):    at com.test.android.helper.LazyImageLoader$ImageLoader.run(LazyImageLoader.java:197)
在我的惰性图像加载器中,我将位图存储在
WeakHashMap
中。所以垃圾收集器应该收集位图,对吗

我的懒惰图像加载器的工作原理是这样的

我使用url和对imageview的引用从适配器调用
displayImage()

public void displayImage(String url, ImageView imageView, int defaultImageResourceId){

        latestImageMetaData.put(imageView, url);

        if(weakhashmapcache.containsKey(url)){
            imageView.setImageBitmap(weakhashmapcache.get(url));
        }
        else{
            enqueueImage(url, imageView, defaultImageResourceId);
            imageView.setImageResource(defaultImageResourceId);
        }
    }
因此,如果我在缓存中找到图像,我将直接对其进行设置,否则我将使用函数
enqueueImage()
对其进行排队

私有void排队映像(字符串url、ImageView、int-defaultImageResourceId){ 图像=新图像(url、imageView、defaultImageResourceId); downloadqueue.add(图像); //downloadQueue是一个等待添加图像的阻塞队列 //如果队列即将满,则删除队列中前面的元素,因为它们无论如何都不可见 迭代器迭代器=downloadQueue.Iterator(); while(iterator.hasNext()&&downloadQueue.remainingCapacity()<80){ downloadQueue.remove(iterator.next()); } }

getBitmap()
只需从url缩放中获取图像,并将其解码为位图对象
BitmapDisplayer
只是一个Runnable,它在UI线程上将图像设置为imageview

我做错了什么?

试试

这是一个开源项目,他们有关于如何使用它的博客文章。我已经在几个项目中使用过它,即使是在长长的列表中也没有问题


享受

这是一场噩梦,经过日日夜夜的研究,这里有几点可能对其他人有用

不要将所有位图存储在缓存中。保持它在磁盘缓存和内存缓存之间交换。存储的位图数量取决于通过调用

int memClass=((ActivityManager)context.getSystemService(context.ACTIVITY_SERVICE))
.getMemoryClass()

我使用了一个而不是
WeakHashMap
缓存<代码>LruCache
在支持包中提供。用
LruCache
替换现有的
WeakHashMap
实现非常容易。Android上还有一个漂亮的文档

Jake Wharton的是一种管理磁盘缓存的好方法

如果不需要,请不要下载大型位图。试着买一个刚好适合你需要的尺码

使用BitmapFactory.Options,您可以对图像质量进行一些权衡,以在内存缓存中保存更多图像


如果您认为我们还有什么可以做的,请补充。

是的。我做了,有上百条类似的线索。但大多数情况下,建议的答案是强制GC,因为位图图像没有存储在堆中,所以GC没有用处。位图有多大?如果它们大于您需要的大小,则可能值得使用
BitmapFactory.Options
将其缩小。每个图像大约为100x100。不比这个大。有那么大吗?特别是当单个ListView中有700或800个这样的图像时?我已经在使用BitmapFactory缩放图像了。
And my image loader thread is this - 

class ImageLoader extends Thread {

    public void run() {
        Image firstImageInQueue;

        try {
            while((firstImageInQueue = downloadQueue.take()) != SHUTDOWN_TOKEN)
            {
                Bitmap imageBitmap = getBitmap(firstImageInQueue.url);

                if(imageBitmap != null){
                    weakhashmap.put(firstImageInQueue.url, imageBitmap);
                    BitmapDisplayer displayer = new BitmapDisplayer(imageBitmap, firstImageInQueue.imageView, firstImageInQueue.url, firstImageInQueue.defaultImageResourceId);
                    Activity activity = (Activity)firstImageInQueue.imageView.getContext();
                    activity.runOnUiThread(displayer);
                }
            }
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        finally {
            imageLoaderTerminated = true;
        }
    } 
}