单个位图占用大量内存

时间:2014-09-12 08:59:08

标签: java android bitmap

我知道这个主题已经讨论了很多次了,我已经阅读了很多关于处理大位图的文章但是如果我们把这个问题放在一边,我想知道为什么单个位图占用了这么多内存。现在,我正在开发一个具有动态背景的Android应用程序。背景是类似梯度的图像,它应该从白色,灰色到黑色。我有4个背景图像,它们一起构成了所需的渐变。现在,如果我只使用一个图像并使背景静态,那么在VM堆为64 Mb的设备上,一切都能正常工作。但是,如果我决定使用所有背景,即使在VM堆为128 Mb的设备上也会出现内存错误。现在,当然,我可以理解为什么会发生这种情况,如果我加载所有4个图像,但我编辑了我的代码,以便一次只在内存中加载2个图像(如果一个背景图像不再在屏幕上,它会被回收)这是有效的,但几乎没有。如果我只添加一个与背景图像相比尺寸可忽略不计的其他图像,则会得到相同的错误。

所有这一切的另一个问题是我必须在加载后缩放我的位图。我将它们缩放到屏幕大小。我知道缩放也会占用大量内存,但是如果我进行计算,存储为ARGB888的尺寸为1280x720的位图应该使用不超过4 MB的内存。

将largeHeap设置为true也不会有任何帮助,因为有些设备的堆低至16 MB ..

即使将位图缓存到存储也无济于事,因为在某一刻我需要比某些设备可以处理的位图更多的位图。

此外,加载采样图像不会有任何帮助,因为图像不是很大(xxhdpi为1000x1000) 密度)

BitmapDrawing代码:

private void DrawBackground(Canvas canvas) {
  //temp and bgs[1] are loaded at start up, as they are required right away
         if (bg_num > 3) {
         canvas.drawBitmap(bgs[3], 0, 0, null);

         if (!bgs[0].isRecycled())
         bgs[0].recycle();
         return;
         }

         if (bgs[0] == null) {
         bgs[0] = Bitmap.createScaledBitmap(temp, getWidth(), getHeight(),
         false);
         temp.recycle();
         bgs_resized[0] = true;
         }

         if (bg_num == 2 && !bgs[0].isRecycled()) {
         bgs[0].recycle();

         bgs[2] = BitmapFactory.decodeResource(getResources(),
         R.drawable.bg3, bitmapOptions);
         }

         if (bg_num == 3 && !bgs[1].isRecycled()) {
         bgs[1].recycle();
         bgs[3] = BitmapFactory.decodeResource(getResources(),
         R.drawable.bg4, bitmapOptions);
         }

         if (!bgs_resized[bg_num]) {

         temp = bgs[bg_num];
         bgs[bg_num] = Bitmap.createScaledBitmap(temp, getWidth(),
         getHeight(), false);
         bgs_resized[bg_num] = true;
         temp.recycle();
         }

         if (bg_pos < getHeight()) {
         canvas.drawBitmap(bgs[bg_num], 0, (bg_pos += BG_TRANSITION_SPEED)
         - getHeight() + 2, null);
         canvas.drawBitmap(bgs[bg_num - 1], 0, bg_pos, null);

         } else {
         canvas.drawBitmap(bgs[bg_num], 0, 0, null);
         ++bg_num;

         bg_pos = 0;

         }

    }

我做错了什么,或者我需要转向NDK来解决问题。

感谢。

1 个答案:

答案 0 :(得分:0)

当您使用等于分辨率x 4字节的位图时,您回答了自己的问题并预留了内存。所以这很快失控了。 有几个提示。

1)使用位图工厂将您的位图解码为必要的大小。例如,如果imageview只需要512x384,则不希望在内存中加载更大的分辨率位图。

你可以这样做:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

2)确保从磁盘加载位图,然后在不同的线程上加载UI。

3)你应该尽快在你的位图上调用回收来释放内存(当你不再需要它时)

4)按照上一个提示。尝试缓存您的位图以提高响应能力

Set<SoftReference<Bitmap>> mReusableBitmaps;
private LruCache<String, BitmapDrawable> mMemoryCache;

// If you're running on Honeycomb or newer, create a
// synchronized HashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb()) {
    mReusableBitmaps =
            Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}

mMemoryCache = new LruCache<String, BitmapDrawable>(mCacheParams.memCacheSize) {

    // Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted, String key,
            BitmapDrawable oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
            // The removed entry is a recycling drawable, so notify it
            // that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable) oldValue).setIsCached(false);
        } else {
            // The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb()) {
                // We're running on Honeycomb or later, so add the bitmap
                // to a SoftReference set for possible use with inBitmap later.
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));
            }
        }
    }
....
}