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

次に、すべての画像のサイズを変更して、マップで表されるビットマップに保存します。

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を持ち、TabhostアクティビティにあるActivity(MenuActivity)から起動します。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を開始するときは、MenuActivityを呼び出さず、finish()BoardActivityを呼び出すときはfinish()、新しいインテントを開始しないため、すでに開いているMenuActivityに戻るだけです。そしてこの16ラウンドで、私はOOMを手に入れました。

4

1 に答える 1

9

メモリを削減するには、次のことを試すことができます。

  • ドローアブルをビットマップに変換した後、imageResourceMap を null に設定できます。これにより、3 つのドローアブルがアンロードされます。
  • imageViews への参照を保存しないでください。UI から削除された後でも、imageView を保存している可能性があります。
  • ビットマップをより頻繁にリサイクルします。onDestroy だけでなく、1 つのビットマップが使用されていないことがわかったらすぐに、それをリサイクルできます。

編集:コメントの会話に基づいて: BitmapFactory.decodeStream(fis, null, o) によって返されるビットマップはどの変数にも割り当てられていないため、リサイクルされません。Android 2.2 および 2.3 では、この行にリークがあります。

于 2012-12-02T10:21:47.303 に答える