我有一个应用程序工作正常,然后突然它开始强制关闭/崩溃,并且logcat说它是内存不足错误。
logcat如下:
04-27 10:53:05.366 8899-8899/cct.mad.lab E/AndroidRuntime: FATAL EXCEPTION: main
Process: cct.mad.lab, PID: 8899
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:587)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:422)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:445)
at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:475)
at cct.mad.lab.Sprite.<init>(Sprite.java:37)
at cct.mad.lab.GameView.createSprites(GameView.java:63)
at cct.mad.lab.GameView.surfaceCreated(GameView.java:86)
at android.view.SurfaceView.updateWindow(SurfaceView.java:572)
at android.view.SurfaceView.access$000(SurfaceView.java:86)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:175)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1871)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:544)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
精灵类是出现问题的地方(第37行)。这是一个png图像。第37行是第三行的代码部分如下:
public Sprite(GameView gameView) {
this.gameView = gameView;
spritebmp = BitmapFactory.decodeResource(gameView.getResources(),
R.drawable.bad4);
this.bmp_width = spritebmp.getWidth() / BMP_COLUMNS;
this.bmp_height = spritebmp.getHeight() / BMP_ROWS;
xSpeed = random.nextInt(15) + 1;
ySpeed = random.nextInt(15) + 1;
x = random.nextInt(gameView.getWidth() - bmp_width);
y = random.nextInt(gameView.getHeight() - bmp_height);
我尝试将以下内容添加到清单中,但没有成功:
android:largeHeap="true"
我不太确定下一步该尝试什么,这是一项任务。它进展得很顺利,但如果我无法解决这个问题,我将无法提交任何内容。
非常感谢所有帮助和建议。
由于
编辑:下面我已经添加了整个精灵类,因为人们在说什么,有些事情需要改变:
package cct.mad.lab;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.media.SoundPool;
import android.support.v7.app.AppCompatActivity;
import java.util.Random;
public class Sprite extends AppCompatActivity {
private static final int BMP_ROWS = 4;
private static final int BMP_COLUMNS = 3;
//x,y position of sprite - initial position (0,50)
private int x = 0;
private int y = 0;
private int xSpeed = 5;//Horizontal increment of position (speed)
private int ySpeed = 5;// Vertical increment of position (speed)
private GameView gameView;
private Bitmap spritebmp;
private int currentFrame = 0;
//Width and Height of the Sprite image
private int bmp_width;
private int bmp_height;
// Needed for new random coordinates.
private Random random = new Random();
private SoundPool mySound;
int zapSoundId;
public Sprite(GameView gameView) {
this.gameView = gameView;
spritebmp = BitmapFactory.decodeResource(gameView.getResources(),
R.drawable.bad3);
this.bmp_width = spritebmp.getWidth() / BMP_COLUMNS;
this.bmp_height = spritebmp.getHeight() / BMP_ROWS;
xSpeed = random.nextInt(15) + 1;
ySpeed = random.nextInt(15) + 1;
x = random.nextInt(gameView.getWidth() - bmp_width);
y = random.nextInt(gameView.getHeight() - bmp_height);
//mySound = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
//zapSoundId = mySound.load(this, R.raw.zap, 1);
}
public Sprite(GameView gameView, Bitmap bmp) {
}
//update the position of the sprite
public void update() {
x = x + xSpeed;
y = y + ySpeed;
bounce();
//y = random.nextInt(gameView.getWidth());
//wrapAround(); //Adjust motion of sprite.
}
private void bounce() {
if (x <= 0 || x >= gameView.getWidth() ) {
xSpeed = xSpeed * -1;
}
if (y <= 0 || y >= gameView.getHeight() ) {
ySpeed = ySpeed * -1;
}
currentFrame = ++currentFrame % BMP_COLUMNS;
/*if (x > gameView.getWidth() - spritebmp.getWidth() - xSpeed) {
xSpeed = -5;
if (x + xSpeed < 0) {
xSpeed = 5;
}
x = x + xSpeed;
}
if (y > gameView.getHeight() - spritebmp.getHeight() - ySpeed) {
ySpeed = -5;
if (y + ySpeed < 0) {
ySpeed = 5;
}
y = y + ySpeed;
}*/
}
public void draw(Canvas canvas) {
update();
int srcX = currentFrame * bmp_width;
int srcY;
if (xSpeed > 0) {
srcY = 0 * bmp_height;
}
else {
srcY = 1 * bmp_height;
}
// Create Rect around the source image to be drawn
Rect src = new Rect(srcX, srcY, srcX + bmp_width, srcY + bmp_height);
// Rect for destination image
Rect dst = new Rect(x, y, x + bmp_width, y + bmp_height);
//
// Draw the image frame
canvas.drawBitmap(spritebmp, src, dst, null);
}
// Checks if the sprite was touched
public boolean wasItTouched(float ex, float ey) {
boolean touched = false;
if ((x <= ex) && (ex < x + bmp_width) &&
(y <= ey) && (ey < y + bmp_height)) {
touched = true;
//mySound.play(zapSoundId, 1, 1, 1, 0, 1);
}
//
return touched;
}
}
编辑:我还想指出我使用的精灵每个都小于5kb,我有一个复制它的阵列大约8次,所以它不是一个会占用如此多内存的大图像
答案 0 :(得分:0)
请参阅本教程:https://developer.android.com/topic/performance/graphics/load-bitmap.html
总之,他们首先读取位图的大小。然后他们对文件进行子采样。
编辑:现在我看到你的文件非常小。在游戏方面 - 请确保您只需将资源加载到内存中一次,然后重复使用它。
此外,当您使用完位图后,请致电=ADDRESS()
。
理想情况下,您应该拥有一些资源管理器对象来加载资源并根据需要保留它们。任何.recycle()
都必须要求已经加载的资源,而不是每次都要读取新资源。
编辑2:
好的,让我们进一步解释一下。如果它是一个简单的游戏,我认为它是,创建一个将处理您的资产加载的类。我们称之为Sprite
。
然后让你的游戏“引擎”(无论处理游戏循环)只使AssetManager
实例化一次(单例)。准备某种映射,你可以从文件中读取它,或者你现在可以对它进行硬编码(仅为了简化本例)。
让AssetManager
保留此地图,另一个保存位图。
AssetManager
然后在Map<String, Integer> spriteRes;
Map<String, Bitmap> spriteBmps;
中使用Sprite
参数来描述要使用的位图,例如
String
每个游戏都在循环中运行并具有绘图功能。然后,对于场景中的每个Sprite ghost = new Sprite("ghost");
,您需要做的就是获取资源的名称并从Sprite
中检索位图。如果它没有存储在AssetManager
中,请调用spriteBmps
并存储它。您还可以在程序开始时预加载所有资产。
Bitmap.decodeResource()
这样,您只为每个对象类型保留一个class AssetManager {
Map<String, Integer> spriteRes;
Map<String, Bitmap> spriteBmps;
Bitmap getBitmap(GameView gameView, String key) {
if (spriteBmps.containsKey(key) && spriteBmps.get(key) != null)
return spriteBmps.get(key);
Bitmap bitmap = BitmapFactory.decodeResource(gameView.getResources(), R.drawable.bad3)
spriteBmps.put(key, bitmap);
return bitmap;
}
}
,并且只知道在画布上将其呈现在何处。
何时致电Bitmap
?当您不再需要位图时。这取决于你的案例和架构。
答案 1 :(得分:0)
问题在于我的初始for循环。我误以为它永远不会结束,总是在我的GameView类中创建一个新的精灵。我修复了它现在工作正常