Android bitmaps reuse

2019-08-21 17:51发布

问题:

I am a newbie in Android ! I have some huge problems with Android's SDK, as I'm developing an app that uses huge bitmaps (> 1200 * 1000) and the problem is during the app I need multiple layers/bitmaps of the same size (3-5 of them). The problem is memory heap is consumed very quickly on one of these allocations and OutOfMemory exception is triggered.

The good part is that I don't need all of the bitmaps in the same time.

I tried the recycle() method calling, although I understood it's obsolete, as the memory for the bitmap now resides on the heap, not on the native memory. I also tried to remove all references from a specific bitmap and even call System.gc() manually but no use, the bitmap couldn't be removed. Ok, maybe there would be memory leaks I don't know about, although I doubt it.

I tried all sorts of solutions and none of them work.

The best choice for me would have been to reuse a bitmap, but the decode method of BitmapFactory does only know to allocate a new Bitmap which consumes memory.

My question is, do you know a method of reusing the bitmaps pixels (overwrite a bitmap's pixels without allocating another Bitmap/pixels array) that can work with Android ??? the problem is I can't reside on a GarbageCollector solution because I have specific moments in my app when I have to be certain those bitmaps are removed from memory/other bitmaps won't be allocated.

Another solution would be: do you know a method of drawing some bitmaps directly on sdcard, without passing through the heap memory? On of the moments it crashes it when I try to allocate a big bitmap (double of the original size) to concatenate two other processed bitmaps) ... that's why I'm asking. Could there be a possibility to instantiate a Canvas r other drawing object directly from a data stream ?

Other solution would be to make use of something like virtual swap memory which would simulate heap memory but with caching on SDCARD or something. But can't find a way on doing this also.

回答1:

Write a separate class for performing Custom Bitmap operations:

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;


class BitmapLoader
{
public static int getScale(int originalWidth,int originalHeight,
   final int requiredWidth,final int requiredHeight)
{
    //a scale of 1 means the original dimensions 
    //of the image are maintained
    int scale=1;

    //calculate scale only if the height or width of 
    //the image exceeds the required value. 
    if((originalWidth>requiredWidth) || (originalHeight>requiredHeight)) 
    {
        //calculate scale with respect to
        //the smaller dimension
        if(originalWidth<originalHeight)
            scale=Math.round((float)originalWidth/requiredWidth);
        else
          scale=Math.round((float)originalHeight/requiredHeight);

    }

    return scale;
}

public static BitmapFactory.Options getOptions(String filePath,
        int requiredWidth,int requiredHeight)
{

    BitmapFactory.Options options=new BitmapFactory.Options();
    //setting inJustDecodeBounds to true
    //ensures that we are able to measure
    //the dimensions of the image,without
    //actually allocating it memory
    options.inJustDecodeBounds=true;

    //decode the file for measurement
    BitmapFactory.decodeFile(filePath,options);

    //obtain the inSampleSize for loading a 
    //scaled down version of the image.
    //options.outWidth and options.outHeight 
    //are the measured dimensions of the 
    //original image
    options.inSampleSize=getScale(options.outWidth,
            options.outHeight, requiredWidth, requiredHeight);

    //set inJustDecodeBounds to false again
    //so that we can now actually allocate the
    //bitmap some memory
    options.inJustDecodeBounds=false;

    return options;

}



public static Bitmap loadBitmap(String filePath,
        int requiredWidth,int requiredHeight){


    BitmapFactory.Options options= getOptions(filePath,
            requiredWidth, requiredHeight);

    return BitmapFactory.decodeFile(filePath,options);
}
}

Then from your activity, call Bitmap reqBitmap = loadBitmap(String filePath,int requiredWidth,int requiredHeight) method of this class providing the filepath of the bitmap obtained from the sd card, and setting the requiredWidth and requiredHeight to the dimensions you wish to scale the bitmap to. Now use the reqBitmap.

This way large bitmaps will be scaled down before they are loaded into the heap memory. I had the same problem and this solved it for me. :)



回答2:

I have had such similar problems as wel where recycle() did not work for me. So i did something as trivial as set myBitmap = null. This indicates to the Garbage Collector that myBitmap has been dereferenced and it is no longer needed. It will thus do the job of freeing up heap. You can then go ahead and use myBitmap to load new Bitmaps without memory exceptions.



回答3:

This might be too late, but I'd recommend using "BitmapFactory.Options.inBitmap". If set, skia will try to re-use this bitmap to decode the image. http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inBitmap