1

I am developing a card game for android. I have a OutOfMemoryError when i try to load some images. I have read many answers about this common and frequent problem in android, BUT all of them deal when the image to load is very big. I manage screen rotation by myself so I dont restart a new activity on rotation. I have already read this questions:

Out of memory exception due to large bitmap size

Android: out of memory exception in Gallery

Android handling out of memory exception on image processing

One thing that I don't understand that, is that if sum the size of all my images (png files) in res folder is about 800 kb. But in Logcat I notice that the size on creating the game(loading card images) goes rapidly over 20 mb. I use: this code to load images

Resources r = this.getContext().getResources();
CardView.loadCardImages(CardView.EAlone.CARDS, 1027, 615, r.getDrawable(R.drawable.cards));
CardView.loadCardImages(CardView.EAlone.GREEN, mCardWidth, mCardHeight, r.getDrawable(R.drawable.green));
CardView.loadCardImages(CardView.EAlone.RED, mCardWidth, mCardHeight, r.getDrawable(R.drawable.red));
CardView.loadCardImages(CardView.EAlone.WHITE, mCardWidth, mCardHeight, r.getDrawable(R.drawable.white));

CardView.LoadCardImages:

public static void loadCardImages(EAlone mode, int widthPixels, int heightPixels, Drawable tile) {

    Canvas canvas;
    switch (mode)
    {
    case GREEN:
        mAloneGreen = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mAloneGreen);
        break;
    case RED:
        mAloneRed = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mAloneRed);
        break;
    case WHITE:
        mAloneWhite = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mAloneWhite);
        break;
    default:
        mcardImages = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888);
        canvas = new Canvas(mcardImages);
    }
    tile.setBounds(0, 0, widthPixels, heightPixels);
    tile.draw(canvas);
    tile.setCallback(null);
}

mCardimages contains all the card faces. Then I use this file to create the single cards by cropping the needed card from the file. After this loadings I load some other images that have transparencies in my parent view.

dealerSign = new ImageView(getContext());
dealerSign.setImageResource(R.drawable.dealer);
dealerSign.setAdjustViewBounds(true);
dealerSign.setLayoutParams(new Gallery.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

I get the error at setImageResource line.

04-09 22:04:37.226: E/AndroidRuntime(722): FATAL EXCEPTION: main
04-09 22:04:37.226: E/AndroidRuntime(722): java.lang.OutOfMemoryError
04-09 22:04:37.226: E/AndroidRuntime(722):  at    android.graphics.Bitmap.nativeCreate(Native Method)
04-09 22:04:37.226: E/AndroidRuntime(722):  at    android.graphics.Bitmap.createBitmap(Bitmap.java:605)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:524)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:499)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:351)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:767)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.content.res.Resources.loadDrawable(Resources.java:1937)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.content.res.Resources.getDrawable(Resources.java:664)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.widget.ImageView.resolveUri(ImageView.java:537)
04-09 22:04:37.226: E/AndroidRuntime(722):  at android.widget.ImageView.setImageResource(ImageView.java:310)
04-09 22:04:37.226: E/AndroidRuntime(722):  at game.spathi.GameView.init(GameView.java:261)
04-09 22:04:37.226: E/AndroidRuntime(722):  at game.spathi.Game.onCreate(Game.java:92)
...

What am I doing wrong here?

Thank you!

Community
  • 1
  • 1
Sanandrea
  • 2,112
  • 1
  • 27
  • 45
  • Have you tried stepping through the code and observing the variables? Maybe mWidth/mHeight is not initialized to something sensible? – Tim Apr 09 '12 at 23:22
  • @Tim Sure they are. They are static ints respectively 90 and 130. – Sanandrea Apr 09 '12 at 23:27
  • 1
    Hmm. My only other random thought is that there's not a WHITE case, so you're assigning mcardImages twice, though I have no idea what consequence that would have for your program, or if it's by design. – Tim Apr 09 '12 at 23:32
  • @Tim Sorry, I edited the question I had forgot to add that piece of code in the question. – Sanandrea Apr 09 '12 at 23:38
  • What's the value of `mCardWidth` and `mCardHeight`? – dmon Apr 10 '12 at 00:15
  • Are these the only images that you are loading? Also can you try putting some logs to see how many times the loadCardImages() is being called? – Shubhayu Apr 10 '12 at 01:58
  • Try to load image with size of 50 X 50 or little bit higher, and compress it. So that it will avoid OutOfMemoryException. – Suvam Roy Apr 10 '12 at 06:07
  • @SuvamRoy What do you mean by load an image and compressing it? How is it done and with what purpose? – Sanandrea Apr 10 '12 at 07:57

1 Answers1

3

Try it -

Bitmap yourBitmap;      
ByteArrayOutputStream baos = new ByteArrayOutputStream();


offerImage.compress(Bitmap.CompressFormat.PNG, 0, baos);
byte[] img = shrinkBitmap(baos.toByteArray());

//convert this byte image to bitmap and load it...

private byte[] shrinkBitmap(byte[] data){
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inSampleSize = 2; //try to decrease decoded image
    options.inPurgeable = true; //purgeable to disk
    Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(data, 0, data.length, options), 100, 100, true);
    ByteArrayOutputStream buf = new ByteArrayOutputStream();
    bitmap.compress(CompressFormat.JPEG, 20, buf);

    return(buf.toByteArray());
}
Suvam Roy
  • 1,282
  • 2
  • 11
  • 21
  • One thing that I don't understand here is, what do I do with this byte[]? How can I change my code, can I write it to the canvas? Thank you! – Sanandrea Apr 10 '12 at 10:02
  • For eg. - case GREEN: mAloneGreen = Bitmap.createBitmap(widthPixels, heightPixels, Bitmap.Config.ARGB_8888); then follow the steps. – Suvam Roy Apr 10 '12 at 10:07
  • Thank you Suvam Roy, it really scales the bitmap and decreases the memory usage. I noticed a slight quality degradation of the images though. Anyway it does the work. – Sanandrea Apr 13 '12 at 11:13