我现在已经尝试了一千次自己解决这个问题,但没有任何成功。我已经记录并调试了应用程序,从我收集到的信息中,有一个用于动画的大型 spritesheet。这是一个 .png,大小为 3000x1614 像素。spritesheet 中有 10x6 的 sprite,这对于动画来说是必不可少的。精灵之间没有任何间隙,以使其尽可能节省内存。
这是我将位图加载、解码和调整大小为用户手机与我正在构建游戏的手机的纵横比的方法:
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”实例占用 12 393 680 (54,30%) 个字节。内存在“”加载的“byte[]”的一个实例中累积。
关键词 dalvik.system.PathClassLoader @ 0x41a06f90 com.NAME.framework.impl.AndroidPixmap byte[]
细节 ”
问题嫌疑人2
由“”加载的类“android.content.res.Resources”占用4 133 808 (18,11%)字节。内存在“”加载的“java.lang.Object[]”的一个实例中累积。
关键字 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 用于具有渐变的图像,但它占用了太多内存。另一件需要注意的事情是,当我不再需要它们时,我会回收我的位图。
另一个似乎消耗大量内存的东西是我的 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;
...
另一件需要注意的是,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?