setImageResource导致内存不足错误

5
我正在制作一个棋盘游戏,并使用一个10x10的GridView来展示棋盘。我创建了一个ImageAdapter类,继承自BaseAdapter,其中包含一个整数数组图标(.9.png文件),这些图标用于显示棋盘上每个方格的图像。这些图标存储在res/drawable文件夹中,尺寸为629X629,平均大小约为5 KB。
我的ImageAdapter类有以下getView()方法,它基本上重复使用相同的视图以节省内存: 编辑:(我已经包括了在游戏活动中调用的changeIcon方法)
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);
}

我有一个名为Game的独立类,处理游戏逻辑。我将棋子存储在Piece[][]数组中,其中Piece是另一个我创建的类,用于保存游戏的棋子数据。move(int xFrom, int yFrom, int xTo, int yTo)方法处理棋子的移动,这很重要。我可以整天移动棋子,一切都很好。
然而,一旦我将一个棋子移动到另一个棋子里,应用程序就会崩溃。预期的行为是创建一个新的棋子。下面是发生这种情况的代码:
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;
}

所以,有问题的调用是pieceToMove.merge()。Piece类中的merge()方法仅更改该类中的type字段(棋子变成新的东西),然后调用该类的setIcon()方法。该方法根据type值设置类的icon字段。正如上面提到的,图标是指res/drawable中的九宫格文件的整数引用。
最后,从GameBoardActivity活动调用了move(int xFrom, int yFrom, int xTo, int yTo)方法,在成功移动后,活动要求ImageAdapter(称为adapter)重新绘制棋盘,如下所示:
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);
}

Logcat显示导致“Fatal Signal 11”和“在6330272字节分配上内存不足”的行是ImageAdapter的getView方法中的imageView.setImageResource(mThumbIds[position]);
所以,就像我说的一样,一切都很顺利,直到我需要合并两个部分,然后我就会遇到内存不足的错误。值得注意的是,在应用程序的早期版本中,这种合并行为完全正常。
我应该提醒一下,我完全是一个java/android编码的初学者,并且我已经查看了其他关于类似问题的问题,但没有人像我一样处理他们的位图。
非常感谢您的任何帮助。
更新
通过进一步测试,我注意到另一个奇怪的地方,有时候会发生问题,有时候不会,我无法确定什么原因会导致某些情况下崩溃而其他情况则不会。更精确地说,进行完全相同的移动序列可能会导致崩溃,也可能不会。这是相当神秘的。

在将图像视图显示之前,您应该先创建缩略图。 - Chintan Rathod
你是指mThumbIds吗?它已经在之前被创建了,只是不在那段代码片段中。 - Rookatu
如果您的意思是实际引用图像本身应该被创建,我已经包含了changeIcon方法,它在GameBoardActivity中被调用。 - Rookatu
1个回答

7
在你的代码中,mThumbIds 是 drawable 的 Id。你需要按照以下代码创建特定图像的 Thumb
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);
} 

使用此代码:
decodeSampledBitmapFromResource(getResources(),R.drawable.xyz, 100, 100);

在这里,您提供了100 * 100的样本大小。因此将创建此类大小的缩略图。


1
那个像魔法一样好用!谢谢你,Chintan!只是为了我能明白,位图的大小正在被缩小,但是我是否仍然有未回收的内存问题?当我在屏幕上放更多东西时,这个问题会再次出现吗?为什么这个问题有时候发生,有时候又不发生呢? - Rookatu
在使用任何位图时,您可以使用SoftReferenceWeakReference,但有时会返回null,因此我不建议在此示例中使用。但是,在创建任何位图并执行任何操作后,您可以使用它来释放不再需要的位图。 - Chintan Rathod
1
@ChintanRathod 这是解决问题的方法。别忘了在 onDestory() 中调用 recycle() 方法释放位图资源。 - FindOutIslamNow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接