Android应用中的Java.Lang.OutOfMemoryError

时间:2017-04-27 15:08:58

标签: java android bitmap out-of-memory

我有一个应用程序工作正常,然后突然它开始强制关闭/崩溃,并且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次,所以它不是一个会占用如此多内存的大图像

2 个答案:

答案 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类中创建一个新的精灵。我修复了它现在工作正常