My game entities consist of several frames that are updated as time passes. Each frame is a different image.
Note that I'm not using the Bitmap class directly, but a wrapper I made called "GreenBitmap". It has a Bitmap inside and uses reference counting to reuse it when needed (so that it will not be loaded more than once). So for example if I have three Actors, the frames for all of them will be loaded only once and reused.
package com.greenbits.RiverCross;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.util.SparseArray;
public class GreenBitmap
{
private static SparseArray<Entry> map = new SparseArray<Entry>();
public Bitmap bitmap;
private int id;
/**
* @param drawableID The ID of the image
* @return The width of the image
*/
public static float getBitmapWidth(Resources res, int drawableID)
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, drawableID, o);
return o.outWidth;
}
/**
* @param drawableID The ID of the image
* @return The height of the image
*/
public static float getBitmapHeight(Resources res, int drawableID)
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, drawableID, o);
return o.outHeight;
}
public GreenBitmap(Resources res, int drawableID)
{
id = drawableID;
Entry e = null;
// if the ID doesn't exist (i.e the bitmap isn't stored), load it
if( (e = map.get(drawableID)) == null )
{
bitmap = BitmapFactory.decodeResource(res, drawableID); // load the bitmap
e = new Entry(bitmap, 1); // create a new entry
map.put(drawableID, e); // put it in the map
}
else
{
bitmap = e.bitmap;
++e.count;
}
}
public GreenBitmap(Resources res, int drawableID, int requiredWidth, int requiredHeight)
{
// first use Options with inJustDecodeBounds to get the image's dimensions
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, drawableID, options);
// calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, requiredWidth, requiredHeight);
options.inJustDecodeBounds = false;
id = drawableID;
Entry e = null;
// if the ID doesn't exist (i.e the bitmap isn't stored), load it
if( (e = map.get(drawableID)) == null )
{
bitmap = BitmapFactory.decodeResource(res, drawableID, options); // load the bitmap
e = new Entry(bitmap, 1); // create a new entry
map.put(drawableID, e); // put it in the map
}
else
{
bitmap = e.bitmap;
++e.count;
}
}
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
{
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth)
{
if (width > height)
inSampleSize = Math.round((float)height / (float)reqHeight);
else
inSampleSize = Math.round((float)width / (float)reqWidth);
}
return inSampleSize;
}
/**
* @return The internal Bitmap object. You can also use the public 'bitmap' member.
*/
public Bitmap get()
{
return bitmap;
}
public void recycle()
{
Entry e = map.get(id);
--e.count;
//Log.e("GreenBitmap", "count of ID " + id + " was lowered to " + e.count);
if( e.count == 0 )
{
e.bitmap.recycle();
map.remove(id);
//Log.e("GreenBitmap", "Bitmap " + id + " was recycled");
}
}
private static class Entry
{
public Bitmap bitmap;
public int count;
public Entry(Bitmap _b, int _c)
{
bitmap = _b;
count = _c;
}
}
}
I'm also calculating how many samples to use to reduce memory consumption.
Here's the problem. I have a class named "Human" that has 20 frames of animation. The frames are only a few KB big. On the emulator the game runs, but it gives me OutOfMemoryException (or Error, I don't remember) on HTC One X (!).
I've read somewhere that images aren't stored on the garbage collected heap (which is guaranteed to be 16MB) but on a different, native heap that has limited memory.
Does anyone know what I can do to solve this? Why does it run on the emulator and not on a top class phone?
Is there a way to load the images in the garbage collected heap?
Thanks.