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

时间:2012-08-09 13:09:24

标签: android listview bitmap android-listview out-of-memory

我有一个listview,在无限滚动时可能会加载无限项。

列表视图中的每个项目都有一个或两个我懒得加载的图像。

一切都很好但是当我滚动很长时间它会在log cat中崩溃

 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从我的适配器调用displayImage()并引用imageview

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()对其进行排队。

private void enqueueImage(String url,ImageView imageView,int defaultImageResourceId){         Image image = new Image(url,imageView,defaultImageResourceId);         downloadqueue.add(图像);         // downloadQueue是一个阻塞队列,等待添加图像         //如果队列即将变满,则删除队列中前面的元素,因为它们无论如何都是不可见的         Iterator iterator = downloadQueue.iterator();         while(iterator.hasNext()&& downloadQueue.remainingCapacity()< 80){             downloadQueue.remove(iterator.next());         }     }

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

getBitmap()只是从网址缩放中获取图片并将其解码为Bitmap对象。 BitmapDisplayer只是一个Runnable,用于在UI线程上设置image到imageview。

我做错了什么?

2 个答案:

答案 0 :(得分:1)

尝试Universal Image Loader

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

享受!!

答案 1 :(得分:1)

这是一场噩梦,经过几天和几天之后。这里的研究之夜可能对其他人有用。

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

获得的堆限制

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

我使用了LruCache代替WeakHashMap缓存。支持包中提供了LruCache。使用WeakHashMap替换现有的LruCache实施非常容易。 Android上还有一个关于Caching Bitmaps的精美文档。

Jake Wharton的DiskLruCache是管理磁盘缓存的好方法。

如果您不需要,请不要下载巨大的位图。尝试获得足以满足您需求的尺寸。

使用BitmapFactory.Options可以对图像质量进行一些折衷,以便在内存缓存中容纳更多图像。

如果您认为我们还可以做更多其他事情,请添加。