我现在已经尝试过一千次来自己解决这个问题而没有任何成功。我已经记录并调试了应用程序,根据我收集的信息,这是一个用于动画的大型spritesheet。这是一个大小为3000x1614像素的.png。 spritesheet中有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”加载占用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作为我的图像的配置。我希望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只发生在分辨率高于我自己(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;
}
};
加载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?
答案 0 :(得分:5)
这是一种解决方法,但这比直接缩放位图需要更长的时间。
方法是这样的:
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运行
答案 1 :(得分:1)
为什么要扩大位图?只需在绘制时缩放它们。
3000x1614非常大,如果你可以放弃&lt; = 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我认为这需要&gt; = 3.0
答案 2 :(得分: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
答案 3 :(得分:0)
尝试此功能。
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);
}
答案 4 :(得分:-1)
将图像转换为Bitmap对象。并使用Bitmap类的API来缩放图像,以避免Out of Memory错误。
Bitpmap bm = BitmapFactory.decode ("image file path");
bm.setDensity(integer);
Bitmap bm1 = Bitmap.createScaledBitmap (bm, width, height, false);
imageView.setImageBitmap(bm1);
尝试放置宽度和高度的值,以便它不会泄漏内存。愿这可以帮到你。
答案 5 :(得分:-3)
允许JVM使用-Xmx VM参数使用更多内存。
例如,允许JVM使用1 GB(1024 MB)的内存:
java -Xmx1024m
OR
使用任何图片上传器并在演示
中传递图片网址1)Nostra的Universal Image Loader。
2)Fedor的LazyList。和;
3)Novoda的ImageLoader。