Android app crashes on reopen - java

I made a 2D android game for android and the game works perfectly fine but upon reopening it after closing, the game crashes once (Unfortunately, game has stopped working appears) before working again. This only occurs on newer devices, as I tested it on an API level 8 smaller screen and this does not happen.
I suspect this has something to do with how I use the Bitmap resources for the game. I do not release the resources anywhere in the code, is this something that I should be doing or does the garbage collector automatically handle it?
The game reopens fine on all emulator devices except for the occasional
Exception locking surface
java.lang.IllegalStateException: Surface was already locked
that is thrown on reopen. I do not know the fix for this but it happens very rarely.
Here is the code in the MainThread.java class
while (running) {
startTime = System.nanoTime();
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
// update game state
this.gamePanel.update();
// draws the canvas on the panel
this.gamePanel.draw(canvas);
}
} catch(Exception e){e.printStackTrace();}finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
}
catch(Exception e){e.printStackTrace();}
}
}
method that starts the thread:
#Override
public void surfaceCreated(SurfaceHolder holder) {
player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.helicopter),65,25,3);
bg = new Background(BitmapFactory.decodeResource(getResources(), R.drawable.grassbg1));
smoke = new ArrayList<Smokepuff>();
missiles = new ArrayList<Missile>();
botborder = new ArrayList<BotBorder>();
topborder = new ArrayList<TopBorder>();
explosions = new ArrayList<Explosion>();
smokeStartTime = System.nanoTime();
missileStartTime = System.nanoTime();
thread.setRunning(true);
thread.start();
}
and method that shuts the thread down:
#Override
public void surfaceDestroyed(SurfaceHolder holder)
{
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
retry = false;
}
}
Any help would be greatly appreciated!

Related

Android Java draw to canvas each millisecond

I want to draw in some Activity. I've used threads with SurfaceViews, but it doesn't push changes at all! Here you can see my previous things:
Canvas canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (canvas) {
this.gameSurface.update();
this.gameSurface.draw(canvas);
}
} catch (Exception e) {
// Do nothing.
} finally {
if (canvas != null) {
canvas.drawColor(Color.BLACK);
this.surfaceHolder.unlockCanvasAndPost(canvas);
}
}
try {
sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
(Thread handling example - I've checked, it'd drown only first time, but then it had not pushed any changes). So, should I change SurfaceView to something else, or this is a good solution and I have to find errors?
I've decided to stay on clear android engine. I've fixed all my problems with canvas redrowing. If anyone have some issues: just push changes inside SurfaceView to itself, just like this:
public void update() {
invalidate();
Canvas canvas = getHolder().lockCanvas(null);
this.draw(canvas);
getHolder().unlockCanvasAndPost(canvas);
}

Needing JAVA help in animation

I'm new in Java (still learning) all the other works have gone well but only this animation gives my headache and the coffee won't even help=(!
I should make an animation of Javaman (10 gif pictures named as T1, T2,...T10) I should use Thread, MediaTracker-class and addImage-method. Then I should specify the speed of the animation by using sleep-method (I used join-method if that's right??).
(MY JAVA CODE GOES LIKE THIS)
import java.applet.Applet;
import java.awt.*;
public class Animaatio extends Applet implements Runnable {
Image[] images = null;
MediaTracker tracker = null;
int current = 0;
Thread animThread;
#Override
public void init() {
// Creating a new media tracker, to track loading images
tracker = new MediaTracker(this);
// Creating an array of ten images
images = new Image[10];
// Downloading the images
for (int i = 0; i < 10; i++) {
// Loading the images
images[i] = getImage(getCodeBase(), (i + 1) + "T.gif");
tracker.addImage(images[i], 0);
}
try {
tracker.waitForAll();
} catch (InterruptedException e) {
}
}
#Override
public void start() {
if (animThread == null) {
animThread = new Thread(this);
animThread.start();
}
try {
animThread.join();
} catch (InterruptedException e) {
}
}
#Override
public void paint(Graphics g) {
g.drawImage(images[current++], 0, 0, this);
}
#Override
public void run() {
while (true) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
}
}
}
The problem is that I don't see any animation just an empty applet viewer which just keeps running. Problem might be caused if the images are storaged in the wrong place? If someone could wreally help me, I would be really thankful for my knight=).

Android SurfaceView UI Thread Not Working

I have got problems with my Android application (Android ANR keyDispatchingTimedOut). I know the reason behind, but I can't solve the problem. My UI thread is probably overloaded and I wondering how to move expensive code/methods to a background thread. I also wonder, what kind of graphics/update methods can be moved to a background thread?
This is my UI thread:
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // The time when the cycle begun
long timeDiff; // The time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // Number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
// Send the thread to sleep for a short period,
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
And here are the two methods, update() and render()
public void update() {
// Gets the start level if it isn't initialized (when you
// enter the game for the first time)
if (startLvl == 0) {
startLvl = ((SingleGameActivity)getContext()).getStartLvl();
currentLvl = startLvl;
}
if (playing) {
// Update the life icons
updatePlayerLives();
updateComputerLives();
// Checks if you have won or lost
outOfBounds();
// Update the position of the ball, player and computer including
// collision detection and reaction.
ball.moveWithCollisionDetection(box, player, computer);
player.moveWithCollisionDetection(box);
computer.setDirectionWithAI(ball);
computer.moveWithCollisionDetection(box);
} else {
// Create new objects
newObjects();
if (newRound) {
playing = true;
newRound = false;
}
}
}
// //////////////////////////////////////////////////////////////////////////////////
// The render() method renders the UI graphics on the screen
public void render(Canvas canvas) {
super.onDraw(canvas);
if (playing) {
// Draw components
box.draw(canvas);
// Draw booster/s
if (racketTouches > 4) {
if (b1.isUsed()) {
b1 = new Booster();
}
canvas.drawBitmap(b1.booster, b1.boosterX, b1.boosterY, null);
}
if (racketTouches > 14) {
if (b2.isUsed()) {
b2 = new Booster();
}
canvas.drawBitmap(b2.booster, b2.boosterX, b2.boosterY, null);
}
if (racketTouches > 24) {
if (b3.isUsed()) {
b3 = new Booster();
}
canvas.drawBitmap(b3.booster, b3.boosterX, b3.boosterY, null);
}
// Draw rackets and ball
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
} else {
// Draw components
box.draw(canvas);
player.draw(canvas);
computer.draw(canvas);
ball.draw(canvas);
}
}
Are the methods overloaded? If so, what can I move to a background thread? And how?
FYI: I'm creating a retro pong/tennis game with boosters included.
You never want to do expensive tasks like drawing and animations on the UI thread. There are two ways to utilize a worker thread...
Thread with a runnable (this can be created inside your drawing surface, drawing surface must implement Runnable):
new Thread(new Runnable() {
public void run() {
update();
render();
}
}).start();
Extend Thread in a separate class:
class myThread extends Thread {
Context context;
//Everything that you currently have in your UI thread should be in here...
/* Constructor for thread. Pass useful things like context, drawing surface, etc */
public myThread(Context context) {
this.context = context;
}
public void setRunning(Boolean running) {
this.running = running;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // The time when the cycle begun
long timeDiff; // The time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // Number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// Try locking the canvas
try {
canvas = this.surfaceHolder.lockCanvas();
if (canvas != null) {
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// Update game state here!
this.gameView.update();
// Render state to the screen
// Draws the canvas on the panel
this.gameView.render(canvas);
// Calculate how long time the cycle took
timeDiff = System.currentTimeMillis() - beginTime;
// Calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
try {
// Send the thread to sleep for a short period,
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// Need to catch up by updating without rendering
// Update game state here!
this.gameView.update();
// Add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
} finally {
// In case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // End finally
}
}
}
And when you create your drawing surface, assign the thread:
Thread thread;
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread = new myThread(getContext());
thread.setRunning(true);
thread.start();
}
So that's pretty much it - note that you'll probably have to pass some other things to the thread in the second example through its constructor for it to work with your game, such as the surface on which you are drawing and the SurfaceHolder, but that's the idea.
Good luck with your game!

I have an issue with a NullPointerException upon resuming my Android app and rendering the canvas

my app currently crashes when you press the home or back button saying "Unfortunately, Rocks has crashed". I have narrowed it down to the line of code that reads:
// draws the canvas on the panel
this.gamePanel.render(canvas);
in this context:
package com.background.rocks;
import android.graphics.Canvas;
import android.util.Log;
import android.view.SurfaceHolder;
public class MainThread extends Thread {
private static final String TAG = MainThread.class.getSimpleName();
// desired fps
private final static int MAX_FPS = 50;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
// Surface holder that can access the physical surface
private SurfaceHolder surfaceHolder;
// The actual view that handles inputs
// and draws to the surface
private Graphics gamePanel;
// flag to hold game state
private boolean running;
public void setRunning(boolean running) {
this.running = running;
}
public MainThread(SurfaceHolder surfaceHolder, Graphics gamePanel) {
super();
this.surfaceHolder = surfaceHolder;
this.gamePanel = gamePanel;
}
#Override
public void run() {
Canvas canvas;
Log.d(TAG, "Starting game loop");
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
sleepTime = 0;
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
beginTime = System.currentTimeMillis();
framesSkipped = 0; // resetting the frames skipped
// update game state
this.gamePanel.update();
// render state to the screen
// draws the canvas on the panel
this.gamePanel.render(canvas);
// calculate how long did the cycle take
timeDiff = System.currentTimeMillis() - beginTime;
// calculate sleep time
sleepTime = (int) (FRAME_PERIOD - timeDiff);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// we need to catch up
this.gamePanel.update(); // update without rendering
sleepTime += FRAME_PERIOD; // add frame period to check if in next frame
framesSkipped++;
}
}
} finally {
// in case of an exception the surface is not left in
// an inconsistent state
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
} // end finally
}
}
}
But I'm new to using canvas so I don't know how to fix it. It creates a NullPointerException so I'm assuming it's to do with when resuming the app or pausing the app the canvas isn't stored properly.
Edit: Graphics class (without imports):
package com.background.rocks;
public class Graphics extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = Graphics.class.getSimpleName();
private MainThread thread;
private Player player;
private ArrayList<Rock> rocks = new ArrayList<Rock>();
private Random random = new Random();
private CountDownTimer countdown;
public Graphics(Context context) {
super(context);
// adding the callback (this) to the surface holder to intercept events
getHolder().addCallback(this);
// create shape and load bitmap
player = new Player(BitmapFactory.decodeResource(getResources(), R.drawable.player_orange), 540, 1500);
// create the game loop thread
thread = new MainThread(getHolder(), this);
// make the GamePanel focusable so it can handle events
setFocusable(true);
timer();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// at this point the surface is created and
// we can safely start the game loop
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Surface is being destroyed");
// tell the thread to shut down and wait for it to finish
// this is a clean shutdown
boolean retry = true;
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
// try again shutting down the thread
}
}
Log.d(TAG, "Thread was shut down cleanly");
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// delegating event handling to the shape
player.handleActionDown((int) event.getX(), (int) event.getY());
// check if in the lower part of the screen we exit
if (event.getY() > getHeight() - 50) {
thread.setRunning(false);
((Activity) getContext()).finish();
} else {
Log.d(TAG, "Coords: x=" + event.getX() + ",y=" + event.getY());
}
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
// the gestures
if (player.isTouched()) {
// the shape was picked up and is being dragged
player.setX((int) event.getX());
player.setY((int) event.getY());
}
}
if (event.getAction() == MotionEvent.ACTION_UP) {
// touch was released
if (player.isTouched()) {
player.setTouched(false);
}
}
return true;
}
**public void render(Canvas canvas) {
if (canvas != null) {
canvas.drawColor(Color.WHITE);
player.draw(canvas);
Rock[] rockArray = rocks.toArray(new Rock[0]);
for (Rock rock : rockArray) {
rock.draw(canvas);
}
}
}**
/**
* This is the game update method. It iterates through all the objects and
* calls their update method if they have one or calls specific engine's
* update method.
*/
public void update() {
Rock[] rockArray = rocks.toArray(new Rock[0]);
for (Rock rock : rocks) {
rock.update();
}
}
public void timer() {
if (countdown != null) {
countdown.cancel();
}
countdown = new CountDownTimer(30000, 800) {
public void onTick(long millisUntilFinished) {
rocks.add(new Rock(BitmapFactory.decodeResource(getResources(), R.drawable.rock), random.nextInt(1080 - 1) + 1, 0));
}
public void onFinish() {
countdown.start();
}
}.start();
}
}
Edit 2: So I managed to change my Render method in Graphics and so the app doesn't crash now when you press home or back, however when I try and resume the app I get a black screen. I'm assuming because it hasn't loaded the canvas back in, but I'm not sure how to do this, any help?
Start running the thread once the surface gets created. i.e. when you get a callback surfaceCreated(), start the thread.
Code Snippet
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
This might be too late but what I did to fix it was I put a n if statement to test whether the canvas is still their before rendering and updating. Should look like this:
If (canvas != null){
this.gamePanel.render (canvas);
this.gamePanel.update ();
}
I think I followed the same tutorial as you, you will probobly encounter another issue after this on is solved. Im still looking for an answer to that issue. Hope this helps!

SurfaceView, Black screen after reopening application

I have an activity which calls a class that extends a SurfaceView and implement Runnable
and sets the contentView() of the activity class to the instance of the surfaceview class. After minimizing the activity, i pause and destroy the thread:
public void pause(){
running = false;
while(true){
try{
renderThread.join();
break;
}catch(InterruptedException e){
//retry
}
}
}
when the activity resumes i recreate the thread:
public void resume(){
running = true;
renderThread = new Thread(this);
renderThread.start();
}
note that those are called within the onPause() and onResume() activity methods.
public void run(){
while(running){//thred loop
if(!holder.getSurface().isValid())
continue;
if(puzzleDrawn!=true) {
canvas = holder.lockCanvas();
drawPuzzle(canvas);
holder.unlockCanvasAndPost(canvas);
}
}
}
public void drawPuzzle(canvas){
//draws on canvas
}
when i try to reopen the application i see a black screen.
I need the drawPuzzle(canvas) method to be drawn just once.
any tips?
if u need more info let me know!
Thanks
In your pause method you are calling break in the wrong spot. What you should do is call break at the end of the while loop after the try and catch brackets and set your thread to null again outside the scope of the loop and just before you end your pause method, like this:
public void pause(){
running = false;
while(true){
try{
renderThread.join();
}catch(InterruptedException e){
//retry
}
break;
}
renderThread = null;
}

Categories