Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Out of memory Error on setImageResource

I'm making a board game, and I'm using a 10x10 GridView for the board. I've made a class ImageAdapter extending BaseAdapter which holds an Integer array of icons (9 patch files), and these are used to display the images for the squares of the board. The icons are stored in the res/drawable folder, are 629X629 and average about 5 KB in size.

My ImageAdapter class has the following getView() method, which essentially recycles the same view to save memory:

EDIT: (I've included the changeIcon method which gets called in the Activity for the game)

public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {  // if it's not recycled, initialize some attributes
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);
    }
    else{
        imageView = (ImageView) convertView;
    }


    imageView.setImageResource(mThumbIds[position]);
    return imageView;
}

public void changeIcon(int x, int y, Integer icon){
    int position = (9-y)*10 + x;
    mThumbIds[position] = icon;
    getView(position, currentView, null);
}

I have a separate class called Game where the logic of the game is handled. I store the pieces in a Piece[][] array, where Piece is another class I've made to hold the data for (you guessed it) pieces of the game. Of relevance is the method move(int xFrom, int yFrom, int xTo, int yTo) which handles the movement of pieces. I can move pieces around all day and everything is fine.

However, once I move one piece into another the app crashes. The intended behaviour is that a new piece gets created. The code where this happens is below:

public boolean move(int xFrom, int yFrom, int xTo, int yTo){

    boolean success = false;
    Piece pieceToMove = getPiece(xFrom,yFrom);
    Piece pieceAtDest = getPiece(xTo,yTo);
    int moveTeam = pieceToMove.getTeam();

    if(isLegalMove(xFrom, yFrom, xTo, yTo, pieceToMove)&&(turn == moveTeam)){
        if( pieceAtDest == null)
        {
           /*I do something when the destination piece is null; 
                       this block causes no problems */   
        }
        else{
            success = true;
            pieceToMove.merge();
            position[xTo][yTo] = pieceToMove;
            position[xFrom][yFrom] = null;
        }
    }
    return success;
}

So, the offending call is pieceToMove.merge(). The method merge() in the class Piece simply changes the field type in that class (the piece becomes something new) then calls the method setIcon() of that class. This method sets the icon field of the class depending on the value of type. And, as mentioned above, icons are Integers referring to the nine patch files in res/drawable.

Finally, the method move(int xFrom, int yFrom, int xTo, int yTo) was called from the Activity GameBoardActivity, and after a successful move the Activity asks the ImageAdapter (called adapter) to redraw the board, as follows:

boolean success = game.move(xFrom,yFrom,xTo,yTo);

if(success){

Integer destIcon = game.getPiece(xTo, yTo).getIcon();
Piece pieceAtDep = game.getPiece(xFrom, yFrom);
Integer depIcon;
if(pieceAtDep == null)
    depIcon = R.drawable.square;
else
    depIcon = game.getPiece(xFrom, yFrom).getIcon();
adapter.changeIcon(xTo,yTo,destIcon);
adapter.changeIcon(xFrom,yFrom,depIcon);
gridView.setAdapter(adapter);
}

The Logcat says that the line leading to the "Fatal Signal 11" and "Out of memory on a 6330272-byte allocation" is the line imageView.setImageResource(mThumbIds[position]); in the getView method of ImageAdapter.

So, like I said, everything goes along fine until I need to merge two pieces, then I get the out of memory error. It is also worth noting that this merge behaviour was working perfectly fine in an earlier iteration of the app.

I should mention the standard disclaimer now that I am a complete beginner when it comes to coding in java/android, and that I have looked at other questions relating similar issues, but no one else seems to be handling their bitmaps in the same way I am.

Any help is greatly appreciated. Thank you very much.

UPDATE

Through further testing I've noticed another oddity which is that, sometimes the problem occurs, and sometimes not, and I cannot identify what causes crashes in some instances and not others. To be more precise, doing the exact same sequence of moves may or may not lead to a crash. This is quite mysterious.

like image 905
Rookatu Avatar asked Apr 24 '13 04:04

Rookatu


1 Answers

In your code mThumbIds are Ids of drawable. What you should do is you have to create a Thumb of particular image by following code.

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) {

        // Calculate ratios of height and width to requested height and
        // width
        final int heightRatio = Math.round((float) height
                / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will
        // guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}

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);
} 

Use this code,

decodeSampledBitmapFromResource(getResources(),R.drawable.xyz, 100, 100);

Here, 100 * 100 sample size you are providing. So Thumbnail of such size will be created.

like image 187
Chintan Rathod Avatar answered Nov 15 '22 14:11

Chintan Rathod