6

我将 4x4 imageView 放到一个活动(BoardActivity)中,用户可以通过单击它们来更改图像。使用 HTC Desire(Android 2.2.2),我在大约 30 分钟的密集使用中得到了 OOM(内存不足)-编辑:此活动的第 16 次开始-,但没有其他设备产生此问题(android 2.1 和 android 2.2.1 )。是否有可能,我在位图/图像视图的使用上犯了一些错误,导致了这个错误?首先,我将所有资源 ID 加载到地图中:

private Map<String, Integer> imageResourceMap;
imageResourceMap = new HashMap<String, Integer>();
        imageResourceMap.put("a", R.drawable.a);
        imageResourceMap.put("b", R.drawable.b);
        imageResourceMap.put("c", R.drawable.c);
//... I store 55 drawable's resourceId in this map

然后我调整每个图像的大小并将其保存到位图中,以 Map 表示:

private static Map<String, Bitmap> imageBitmap;

    private void loadBitmaps(int imagesSize) {

    for (String s : imageResourceMap.keySet()) {
        Bitmap tmp = getBitmapFromRes(imageResourceMap.get(s), imagesSize);
        imageBitmap.put(s, tmp);
    }
}

private Bitmap getBitmapFromRes(int resId, int imageSize) {
    Bitmap b = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        InputStream fis = getResources().openRawResource(resId);
        BitmapFactory.decodeStream(fis, null, o);
        fis.close();

        int scale = 1;
        if (o.outHeight > imageSize || o.outWidth > imageSize) {
            scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = getResources().openRawResource(resId);
        b = BitmapFactory.decodeStream(fis, null, o2);
        fis.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return b;
}

我将 imageViews 保存在一个数组中,并使用此函数初始化所有图像:

private static ImageView[][] imageViews;

private ImageView getImage(String name) {
        MyImageView item = new MyImageView(this, i, j, c + "");
        item.setImageBitmap(imageBitmap.get(name));
        item.setAdjustViewBounds(true);
        return item;
    }

当我需要更改图像时,我只需更改其资源:

imageViews[i][j].setImageBitmap(imageBitmap.get("a"));

在我完成活动之前,我回收了位图:

private void recycleImageBitmaps() {
    for (Bitmap b : imageBitmap.values()) {
        if (b != null) {
            b.recycle();
        }
    }

}

在 AndroidManifest.xml 我声明了这个活动“singleTask”:

<activity android:name=".game.board.BoardActivity" android:launchMode="singleTask">
    </activity>

在这个应用程序(游戏)中,我们多次重新打开这个活动......我做错了什么?这会导致内存不足错误吗?

更正 像这样更正了 getBitmapFromRes:

private Bitmap getBitmapFromRes(int resId, int imageSize) {
    Bitmap tmp = null;
    Bitmap b = null;
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        InputStream fis = getResources().openRawResource(resId);
        tmp = BitmapFactory.decodeStream(fis, null, o);
        fis.close();

        int scale = 1;
        if (o.outHeight > imageSize || o.outWidth > imageSize) {
            scale = (int) Math.pow(2, (int) Math.round(Math.log(imageSize / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = getResources().openRawResource(resId);
        b = BitmapFactory.decodeStream(fis, null, o2);
        fis.close();
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        if(tmp != null){
            tmp.recycle();
            tmp = null;
        }
    }
    return b;
}

HTC 在此活动的第 11 次开始时仍然崩溃。

编辑: 此活动(BoardActivity)从具有 4 个 imageButton 的 Activity(MenuActivity)启动,并且位于 Tabhost 活动中。imageButtons 声明如下所示:

<ImageButton
    android:id="@+id/game_menu_CreateButton"
    android:layout_width="120dip" 
    android:layout_height="120dip"
    android:layout_alignRight="@+id/textView1"
    android:layout_alignTop="@+id/textView1"
    android:background="@drawable/create" 
    android:layout_marginRight="1sp"
    android:layout_marginTop="10sp"
     />

当我从MenuActivity 启动BoardActivity 时,我不会调用finish()MenuActivity,当我调用finish()BoardActivity 时,我不会启动新的intent,所以它只是返回到已经打开的MenuActivity。而这16轮,我拿到了OOM。

4

1 回答 1

9

要减少内存,您可以尝试以下方法:

  • 将可绘制对象转换为位图后,您可以将 imageResourceMap 设置为 null。这将卸载 3 个可绘制对象。
  • 避免存储对 imageViews 的引用。即使从 UI 中删除它们,您也可能会存储 imageViews
  • 更频繁地回收位图。而不仅仅是 onDestroy,只要你知道一个位图没有被使用,你就可以回收它。

编辑:基于评论中的对话: BitmapFactory.decodeStream(fis, null, o) 返回的位图未分配给任何变量,因此不会被回收。Android 2.2 和 2.3 在这行会有漏洞。

于 2012-12-02T10:21:47.303 回答