我现在已经尝试了一千次来自己解决这个问题而没有任何成功.我已经记录并调试了应用程序,根据我收集的信息,这是一个用于动画的大型spritesheet.这是一个大小为3000×1614像素的.png. spritesheet中有10×6个精灵,对于动画来说是必不可少的.精灵之间没有任何差距,使其尽可能保持内存效率.
这是我的方法,我将我的位图加载,解码并调整大小为用户手机与手机的宽高比,我正在构建游戏:
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的内存泄漏报告:
Problem Suspect 1
One instance of “com.NAME.framework.impl.AndroidPixmap”
loaded by “dalvik.system.PathClassLoader @ 0x41a06f90” occupies 12 393
680 (54,30%) bytes. The memory is accumulated in one instance of
“byte[]” loaded by “”.Keywords dalvik.system.PathClassLoader @ 0x41a06f90
com.NAME.framework.impl.AndroidPixmap byte[]Details »
Problem Suspect 2
The class “android.content.res.Resources”, loaded by “”, occupies 4 133 808 (18,11%) bytes. The memory is accumulated
in one instance of “java.lang.Object[]” loaded by “”.Keywords java.lang.Object[] android.content.res.Resources
Details »
Problem Suspect 3
8 instances of “android.graphics.Bitmap”, loaded by “” occupy 2 696 680 (11,82%) bytes.
Biggest instances: •android.graphics.Bitmap @ 0x41462ba0 – 1 048 656
(4,59%) bytes. •android.graphics.Bitmap @ 0x41a08cf0 – 768 064
(3,37%) bytes. •android.graphics.Bitmap @ 0x41432990 – 635 872
(2,79%) bytes.Keywords android.graphics.Bitmap
Details »
附加信息:
我使用RGB565或ARGB4444作为我的图像的配置.我希望ARGL8888用于带渐变的图像,但它只占用太多内存.另外需要注意的是,当我不再需要它时,我会回收我的位图.
另一件似乎消耗大量内存的事情是我的Assets类,它包含游戏使用的所有“Pixmaps”.从资产加载的位图存储在这些“Pixmaps”中,并在不再需要时删除(位图).是否有更好的方法来存储这些对象?请告诉我:
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只发生在分辨率高于我自己(800×480)的设备上,这是因为位图得到了缩放以使应用适合更大的设备.
另请注意,图像是在画布上绘制的,因为我正在开发的是一个游戏,而且我对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;
}
};
加载BITMAP
//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的情况下加载大量缩放的位图?
最佳答案 这是一种解决方法,但这比直接缩放位图需要更长的时间.
>首先,将原始位图解码为原始大小
>拆分成小位图,在这种情况下是10位图,宽度=
originalWidth / 10和height = originalHeight.并宽度每个位图
其中,缩放到目标/ 10的大小然后保存到文件中. (我们的确是
这一个又一个,每个步骤后释放内存.)
>释放原始位图
>使用目标大小创建新位图.
>解码之前保存在文件中的每个位图并绘制到新位图上.商店
用于将来使用的新缩放位图.
方法是这样的:
public Bitmap newResizedPixmapWorkAround(Bitmap.Config format,
double width, double height) {
int[] pids = { android.os.Process.myPid() };
MemoryInfo myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (beginning) = " + myMemInfo.dalvikPss);
Config config = null;
if (format == Bitmap.Config.RGB_565) {
config = Config.RGB_565;
} else if (format == Bitmap.Config.ARGB_4444) {
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
String path = Environment.getExternalStorageDirectory()
.getAbsolutePath();
path += "/sprites.png";
File file = new File(path);
in = new FileInputStream(file);
// DECODE INPUTSTREAM
bitmap = BitmapFactory.decodeStream(in, null, options);
if (bitmap == null) {
Log.e(TAG, "bitmap == null");
}
} catch (IOException e) {
Log.e(TAG, "IOException " + e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
}
}
}
if (bitmap.getConfig() == Config.RGB_565) {
format = Bitmap.Config.RGB_565;
} else if (bitmap.getConfig() == Config.ARGB_4444) {
format = Bitmap.Config.ARGB_4444;
} else {
format = Bitmap.Config.ARGB_8888;
}
myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (before separating) = " + myMemInfo.dalvikPss);
// Create 10 small bitmaps and store into files.
// After that load each of them and append to a large bitmap
int desSpriteWidth = (int) (width + 0.5) / 10;
int desSpriteAppend = (int) (width + 0.5) % 10;
int desSpriteHeight = (int) (height + 0.5);
int srcSpriteWidth = bitmap.getWidth() / 10;
int srcSpriteHeight = bitmap.getHeight();
Rect srcRect = new Rect(0, 0, srcSpriteWidth, srcSpriteHeight);
Rect desRect = new Rect(0, 0, desSpriteWidth, desSpriteHeight);
String pathPrefix = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/sprites";
for (int i = 0; i < 10; i++) {
Bitmap sprite;
if (i < 9) {
sprite = Bitmap.createBitmap(desSpriteWidth, desSpriteHeight,
format);
srcRect.left = i * srcSpriteWidth;
srcRect.right = (i + 1) * srcSpriteWidth;
} else { // last part
sprite = Bitmap.createBitmap(desSpriteWidth + desSpriteAppend,
desSpriteHeight, format);
desRect.right = desSpriteWidth + desSpriteAppend;
srcRect.left = i * srcSpriteWidth;
srcRect.right = bitmap.getWidth();
}
Canvas canvas = new Canvas(sprite);
// NOTE: if using this drawBitmap method of canvas do not result
// good quality scaled image, you can try create tile image with the
// same size original then use Bitmap.createScaledBitmap()
canvas.drawBitmap(bitmap, srcRect, desRect, null);
String path = pathPrefix + i + ".png";
File file = new File(path);
try {
if (file.exists()) {
file.delete();
}
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
sprite.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
sprite.recycle();
} catch (FileNotFoundException e) {
Log.e(TAG,
"FileNotFoundException at tile " + i + " "
+ e.getMessage());
} catch (IOException e) {
Log.e(TAG, "IOException at tile " + i + " " + e.getMessage());
}
}
bitmap.recycle();
bitmap = Bitmap.createBitmap((int) (width + 0.5f),
(int) (height + 0.5f), format);
Canvas canvas = new Canvas(bitmap);
desRect = new Rect(0, 0, 0, bitmap.getHeight());
for (int i = 0; i < 10; i++) {
String path = pathPrefix + i + ".png";
File file = new File(path);
try {
FileInputStream fis = new FileInputStream(file);
// DECODE INPUTSTREAM
Bitmap sprite = BitmapFactory.decodeStream(fis, null, options);
desRect.left = desRect.right;
desRect.right = desRect.left + sprite.getWidth();
canvas.drawBitmap(sprite, null, desRect, null);
sprite.recycle();
} catch (IOException e) {
Log.e(TAG, "IOException at tile " + i + " " + e.getMessage());
}
}
myMemInfo = mAM.getProcessMemoryInfo(pids)[0];
Log.e(TAG, "dalvikPss (before scaling) = " + myMemInfo.dalvikPss);
// THIS IS WHERE THE OOM HAPPENS
return bitmap;
}
我已经用我的手机进行了测试,并且在缩放到1.5时它没有OOM运行(原始方法将运行到OOM中)