私はこれを自分で解決しようと何千回も試みましたが、まったく成功しませんでした。アプリケーションをログに記録してデバッグしました。収集した情報から、アニメーションに使用される大きなスプライトシートが 1 つあります。サイズが 3000x1614 ピクセルの .png です。スプライトシートには、アニメーションに不可欠な 10x6 のスプライトがあります。可能な限りメモリ効率を高めるために、スプライト間にギャップはまったくありません。
これは、ビットマップを読み込んでデコードし、ユーザーの電話とゲームを構築している私の電話の縦横比にサイズ変更する方法です。
public Pixmap newResizedPixmap(String fileName, PixmapFormat format, double Width, double Height) {
// TODO Auto-generated method stub
Config config = null;
if (format == PixmapFormat.RGB565)
config = Config.RGB_565;
else if (format == PixmapFormat.ARGB4444)
config = Config.ARGB_4444;
else
config = Config.ARGB_8888;
Options options = new Options();
options.inPreferredConfig = config;
options.inPurgeable=true;
options.inInputShareable=true;
options.inDither=false;
InputStream in = null;
Bitmap bitmap = null;
try {
//OPEN FILE INTO INPUTSTREAM
in = assets.open(fileName);
//DECODE INPUTSTREAM
bitmap = BitmapFactory.decodeStream(in, null, options);
if (bitmap == null)
throw new RuntimeException("Couldn't load bitmap from asset '"+fileName+"'");
} catch (IOException e) {
throw new RuntimeException("Couldn't load bitmap from asset '"+fileName+"'");
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
if (bitmap.getConfig() == Config.RGB_565)
format = PixmapFormat.RGB565;
else if (bitmap.getConfig() == Config.ARGB_4444)
format = PixmapFormat.ARGB4444;
else
format = PixmapFormat.ARGB8888;
//THIS IS WHERE THE OOM HAPPENS
return new AndroidPixmap(Bitmap.createScaledBitmap(bitmap, (int)(Width+0.5f), (int)(Height+0.5f), true), format);
}
これは、エラーの LogCat 出力です。
05-07 12:57:18.570: E/dalvikvm-heap(636): Out of memory on a 29736016-byte allocation.
05-07 12:57:18.570: I/dalvikvm(636): "Thread-89" prio=5 tid=18 RUNNABLE
05-07 12:57:18.580: I/dalvikvm(636): | group="main" sCount=0 dsCount=0 obj=0x414a3c78 self=0x2a1b1818
05-07 12:57:18.580: I/dalvikvm(636): | sysTid=669 nice=0 sched=0/0 cgrp=apps handle=705796960
05-07 12:57:18.590: I/dalvikvm(636): | schedstat=( 14462294890 67436634083 2735 ) utm=1335 stm=111 core=0
05-07 12:57:18.590: I/dalvikvm(636): at android.graphics.Bitmap.nativeCreate(Native Method)
05-07 12:57:18.590: I/dalvikvm(636): at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
05-07 12:57:18.590: I/dalvikvm(636): at android.graphics.Bitmap.createBitmap(Bitmap.java:586)
05-07 12:57:18.590: I/dalvikvm(636): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
05-07 12:57:18.590: I/dalvikvm(636): at com.NAME.framework.impl.AndroidGraphics.newResizedPixmap(AndroidGraphics.java:136)
05-07 12:57:18.590: I/dalvikvm(636): at com.NAME.GAME.GameScreen.<init>(GameScreen.java:121)
05-07 12:57:18.600: I/dalvikvm(636): at com.NAME.GAME.CategoryScreen.update(CategoryScreen.java:169)
05-07 12:57:18.600: I/dalvikvm(636): at com.NAME.framework.impl.AndroidFastRenderView.run(AndroidFastRenderView.java:34)
05-07 12:57:18.600: I/dalvikvm(636): at java.lang.Thread.run(Thread.java:856)
05-07 12:57:18.620: I/Choreographer(636): Skipped 100 frames! The application may be doing too much work on its main thread.
05-07 12:57:18.670: W/dalvikvm(636): threadid=18: thread exiting with uncaught exception (group=0x40a13300)
05-07 12:57:18.720: E/AndroidRuntime(636): FATAL EXCEPTION: Thread-89
05-07 12:57:18.720: E/AndroidRuntime(636): java.lang.OutOfMemoryError
05-07 12:57:18.720: E/AndroidRuntime(636): at android.graphics.Bitmap.nativeCreate(Native Method)
05-07 12:57:18.720: E/AndroidRuntime(636): at android.graphics.Bitmap.createBitmap(Bitmap.java:640)
05-07 12:57:18.720: E/AndroidRuntime(636): at android.graphics.Bitmap.createBitmap(Bitmap.java:586)
05-07 12:57:18.720: E/AndroidRuntime(636): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:466)
05-07 12:57:18.720: E/AndroidRuntime(636): at com.NAME.framework.impl.AndroidGraphics.newResizedPixmap(AndroidGraphics.java:136)
05-07 12:57:18.720: E/AndroidRuntime(636): at com.NAME.GAME.GameScreen.<init>(GameScreen.java:121)
05-07 12:57:18.720: E/AndroidRuntime(636): at com.NAME.GAME.CategoryScreen.update(CategoryScreen.java:169)
05-07 12:57:18.720: E/AndroidRuntime(636): at com.NAME.framework.impl.AndroidFastRenderView.run(AndroidFastRenderView.java:34)
05-07 12:57:18.720: E/AndroidRuntime(636): at java.lang.Thread.run(Thread.java:856)
05-07 12:57:18.847: D/Activity:(636): Pausing...
これは、DDMS を使用したメモリ リーク レポートです。
問題の容疑者 1
「dalvik.system.PathClassLoader @ 0x41a06f90」によってロードされた「com.NAME.framework.impl.AndroidPixmap」の 1 つのインスタンスは、12 393 680 (54,30%) バイトを占有します。メモリは、"" によってロードされた "byte[]" の 1 つのインスタンスに蓄積されます。
キーワード dalvik.system.PathClassLoader @ 0x41a06f90 com.NAME.framework.impl.AndroidPixmap バイト[]
詳細 "
問題の容疑者 2
「」によってロードされるクラス「android.content.res.Resources」は、4 133 808 (18,11%) バイトを占有します。メモリは、"" によってロードされた "java.lang.Object[]" の 1 つのインスタンスに蓄積されます。
キーワード java.lang.Object[] android.content.res.Resources
詳細 "
問題の容疑者 3
「」によってロードされた「android.graphics.Bitmap」の 8 つのインスタンスは、2 696 680 (11,82%) バイトを占有します。
最大のインスタンス: •android.graphics.Bitmap @ 0x41462ba0 - 1 048 656 (4.59%) バイト。•android.graphics.Bitmap @ 0x41a08cf0 - 768 064 (3,37%) バイト。•android.graphics.Bitmap @ 0x41432990 - 635 872 (2,79%) バイト。
キーワード android.graphics.Bitmap
詳細 "
追加情報: イメージの構成として RGB565 または ARGB4444 を使用しています。グラデーションのある画像に ARGB8888 を使用したいのですが、メモリを消費しすぎます。注意すべきもう 1 つの点は、ビットマップが不要になったときにビットマップをリサイクルすることです。
多くのメモリを消費しているように見えるもう 1 つのことは、ゲームが使用するすべての "Pixmaps" で構成される Assets クラスです。アセットからロードされたビットマップはこれらの「ピックスマップ」に保存され、不要になったときに削除されます (ビットマップ)。これらのオブジェクトを保存するためのより良い方法はありますか? 私にお知らせください:
public static Pixmap image1;
public static Pixmap image2;
public static Pixmap image3;
public static Pixmap image4;
public static Pixmap image5;
public static Pixmap image6;
public static Pixmap image7;
public static Pixmap image8;
public static Pixmap image9;
public static Pixmap image10;
public static Pixmap image11;
...
もう 1 つ注意すべき点は、OOM は私のデバイス (800x480) よりも解像度が高いデバイスでのみ発生することです。これは、アプリがより大きなデバイスに適合するようにビットマップがスケーリングされるためです。
また、私が開発しているのはゲームであり、OpenGL の経験があまりないため、画像がキャンバスに描かれていることに注意してください。
編集#1:私の問題が何であるかを知っていることを確認するためだけに
次の行で OOM を取得しています。
Bitmap.createScaledBitmap(bitmap, (int)(Width), (int)(Height), true)
私は助けを求めています!これは、この血まみれのゲームのリリースに対する私の最後の封鎖です!
編集#2:うまくいかなかった私が試した新しい方法
デコードされたビットマップを使用する前に LruCache に追加してみました。また、ビットマップのマップを一時ファイルに作成してから再度ロードする方法も試しました。このような:
アクティビティ
// Get memory class of this device, exceeding this amount will throw an
// OutOfMemory exception.
final int memClass = ((ActivityManager) getSystemService(
Context.ACTIVITY_SERVICE)).getMemoryClass();
// Use 1/8th of the available memory for this memory cache.
final int cacheSize = 1024 * 1024 * memClass / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return (bitmap.getRowBytes() * bitmap.getHeight()) / 1024;
}
};
ビットマップのロード
//Copy the byte to the file
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888;
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = null;
try {
map = channel.map(MapMode.READ_WRITE, 0, width*height*4);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bitmap.copyPixelsToBuffer(map);
//recycle the source bitmap, this will be no longer used.
bitmap.recycle();
//Create a new bitmap to load the bitmap again.
bitmap = Bitmap.createBitmap(width, height, config);
map.position(0);
//load it back from temporary
bitmap.copyPixelsFromBuffer(map);
//close the temporary file and channel , then delete that also
try {
channel.close();
randomAccessFile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
((AndroidGame) activity).addBitmapToMemoryCache(""+fileName, Bitmap.createScaledBitmap(bitmap, (int)(Width), (int)(Height), true));
return new AndroidPixmap(((AndroidGame) activity).getBitmapFromMemCache(""+fileName), format);
編集#3:あなたが知らなかった場合に備えて...
ビットマップを縮小するのではなく拡大しているので、この状況では inSampleSize は役に立ちません。私はすでに inSampleSize を試したので、すべてがぼやけてしまい、何が何であるかさえわかりません。それが、inSampleSize を 2 に設定したときです。
では、この問題を解決するにはどうすればよいでしょうか。品質を維持しながら、OOM を取得せずにスケーリングされたビットマップを大量にロードするにはどうすればよいですか?