我已经尝试了一千次,想自己解决这个问题,但没有任何成功。我已经记录并调试了应用程序,从我收集到的信息中,有一个用于动画的大型精灵表。png格式,大小为3000x1614像素。在精灵表中有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
"com.NAME.framework.impl.AndroidPixmap"的一个实例由"dalvik.system"加载。PathClassLoader @ 0x41a06f90"占用12393680(54,30%)字节。的一个实例中积累了内存"byte[]"被".
关键字dalvik.system.PathClassLoader @ 0x41a06f90com.NAME.framework.impl.AndroidPixmap byte []
详情»
问题疑点2
类"android.content.res. "由"加载的资源"占用4 133 808(18.11%)字节。记忆是积累起来的在"java.lang. net"的一个实例中。对象[]"加载".
关键字. lang。对象[]android.content.res.Resources
详情»
问题疑点3
8个"android.graphics。由"加载的位图"占用2 696 680(11.82%)字节。
最大实例:•android.graphics。位图@ 0x41462ba0 - 1 048 656(4 59%)字节。•android.graphics。位图@ 0x41a08cf0 - 768 064(3 37%)字节。•android.graphics。位图@ 0x41432990 - 635 872(2 79%)字节。
关键字android.graphics.Bitmap
详情»
附加信息:我使用RGB565或ARGB4444作为图像的配置。我希望使用ARGB8888的图像与梯度,但它只是占用太多的内存。另一件需要注意的事情是,当我不再需要位图时,我确实会回收它们。
另一个似乎消耗大量内存的东西是我的Assets类,它包含游戏使用的所有"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只发生在分辨率高于我自己的设备上(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的情况下加载大量缩放位图,同时保持它们的质量?
这是一种解决方法,但这将比直接缩放位图花费更长的时间。
- 首先,将原始位图解码为原始大小
- 分割成小位图,在这种情况下是10位图与宽度=originalWidth/10和height = originalHeight。以及每个位图的宽度其中,缩放到target/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运行
为什么要放大位图?画的时候按比例缩放。
3000x1614还挺大的,如果能放下支持<= 2.2;http://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html可以加载位图的特定部分(精灵)。
你正在传递新的位图到AndroidPixmap的构造函数中,看起来你在保持对它们的静态引用-你到底是如何回收它们的?
作为逃避,您可以在清单中使用<application ... android:largeHeap="true" ...>
请求更多内存;http://developer.android.com/guide/topics/manifest/application-element.html#largeHeap我相信这需要> =3.0
只是为了帮助(但不是真正的解决方案),你读过这个吗:
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html:
http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html也许可以帮助你!如果您测试交付的样例项目并使用大图更改URL路径!
并且,当我有工作的时候,我读了这个:
http://twigstechtips.blogspot.com/2011/10/android-resize-bitmap-image-while.html
试试这个函数
void scaleBitmap()
{
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
o.inPurgeable = true;
o.inScaled = true;
BitmapFactory.decodeStream(new FileInputStream(f), null, o);
int width_temp = o.outWidth, height_temp = o.outHeight;
int scale = 1;
while (true) {
if (width_temp / 2 < 80 || height_temp / 2 < 80)
break;
width_temp /= 2;
height_temp /= 2;
scale *= 2;
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null,
o2);
}
将您的图像转换为位图对象。并且使用Bitmap类的api来缩放图像,以避免内存不足错误。
Bitpmap bm = BitmapFactory.decode ("image file path");
bm.setDensity(integer);
Bitmap bm1 = Bitmap.createScaledBitmap (bm, width, height, false);
imageView.setImageBitmap(bm1);
尝试设置width和height的值,这样就不会泄漏内存。希望这能帮到你。
使用-Xmx VM参数允许JVM使用更多内存。
例如,允许JVM使用1gb (1024mb)内存:
java -Xmx1024m
或
使用任意图像上传器并在demo
中传递图像url1) Nostra的通用图像加载器。
2)费多的懒人清单。
;3) Novoda的ImageLoader.