由绘制到位图引起的Android OOM

时间:2012-09-26 01:26:38

标签: android bitmap

这是我的代码:

int[] image = {R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2,
        R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2,
        R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2,
        R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2,
        R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2,
        R.drawable.image1, R.drawable.image2, R.drawable.image1, R.drawable.image2};

getView:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {

        convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);
        ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);
        imageView.setBackgroundResource(image[position]);

        return convertView;
    }

一切都很好。没有OOM。

然后,我想首先对drawable进行采样:

public static Bitmap sampleImage(Context context, int resourceId, int sampleSize) {        
            Bitmap resizeBmp = null;
            BitmapFactory.Options opts = new BitmapFactory.Options();
            opts.inSampleSize = sampleSize;
            resizeBmp = BitmapFactory.decodeResource(context.getResources(), resourceId, opts);

            return resizeBmp;
        }

新的getView:

@Override
    public View getView(int position, View convertView, ViewGroup parent) {

        convertView=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);
        ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);
        Bitmap item = BitmapUtil.sampleMaskInShelf(getApplicationContext(), image[position], 4);
        imageView.setImageBitmap(item);
        //      imageView.setBackgroundResource(image[position]);

        return convertView;
    }  

事情发生了。首先,应用程序不会崩溃。但是我使用的内存超过50MB,在我向上和向下滚动后,它会导致OOM。

问题是: 1)为什么采样图像会增加使用的内存。 2)如何减少使用的内存,还有其他方法吗?

4 个答案:

答案 0 :(得分:5)

您好像在使用ListView吗?

在这种情况下,您可以通过以下几个步骤获得更好的服务(并希望节省大量内存):

  1. 您希望尽可能尝试重复使用convertView。关于如何做到这一点,已经写了很多文章。 Here is one
  2. 您似乎只有两个不同的图像(image1image2)。每次调用getView()时,您似乎都在对位图进行采样。您可能应该预先计算两个采样位图,然后一遍又一遍地使用它们。

答案 1 :(得分:2)

有三篇优秀的Android培训文章涵盖了这个问题:

  1. Loading Large Bitmaps Efficiently
  2. Caching Bitmaps
  3. Displaying Bitmaps(特别是使用GridView)
  4. 我可能会尝试阅读所有这三种技术,了解他们使用的技术并下载sample application,它将所有训练代码封装到一个示例中。我认为在您的情况下,将图像预先采样到显示尺寸然后使用LruCache可能就足够了。

答案 2 :(得分:0)

垃圾收集器不会自动为您处理位图释放,因此每当您将位图直接分配给变量时,您还需要在完成后调用bitmap.recycle()。如果不这样做会导致内存泄漏,这就是你所拥有的。但是,如果您将位图分配给drawable然后调用bitmap.recycle(),然后尝试使用drawable,您将获得异常,因为drawable无法访问回收的位图。

我正在写一个类似于你所拥有的ListView,我最终做的是在当前视口上方十多个插槽的任何位图上调用bitmap.recycle,在该ListView项上设置一个标志来显示它的位图已经被回收,然后让用户在那里向后滚动时重新加载位图。

答案 3 :(得分:0)

您不应每次都创建新视图,而应尝试重用视图,如..

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if(v == null)  {   
        v=LayoutInflater.from(getApplicationContext()).inflate(R.layout.item, null);
    } 
    ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView);
    imageView.setBackgroundResource(image[position]);

    return v;
}

要在OOM上进行更多操作,您应该在以下链接上进行操作.. http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

或者您可以使用UniversalImage Loader Library加载图像..您可以从中找到它的库 UniversalImageLoader

希望这有帮助...