multithreading - Android Game Thread Crashing on Draw() -
i have implemented basic game loop in android "conway's game of life" implementation. works pretty occasionally, crashes. me looks draw() called when view no longer valid (sometimes when home pressed or when options pressed).
so did research , discovered not implementing onpause()/onresume() correctly. have tried correct still crashing intermittently. not time, enough.
i have been working on longer care admit @ point , hoping knows more i, @ , tell me if i'm doing wrong, maybe life-cycle problem or something.
here code (note have removed non-related methods brevity):
// here main android activity public class mainactivity extends activity implements onsharedpreferencechangelistener { game gameview; string mspeed, malivecolor, mdeadcolor, mboardsize; @override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); string deviceid = secure.getstring(getcontentresolver(), secure.android_id); sharedpreferences prefs = preferencemanager.getdefaultsharedpreferences(this); mspeed = prefs.getstring("sim_speed", "fast"); int ispeed = 0; if(mspeed.equals("warp speed")) ispeed = 50; if(mspeed.equals("fast")) ispeed = 250; if(mspeed.equals("medium")) ispeed = 500; if(mspeed.equals("slow")) ispeed = 1000; if(mspeed.equals("really slow")) ispeed = 2500; // create game object gameview = new game(this, ispeed); // register preference change listener prefs.registeronsharedpreferencechangelistener(this); // , set remembered preferences string bs = prefs.getstring("sim_board_size", "large"); gameview.setboardsize(bs); malivecolor = prefs.getstring("alive_color", "yellow"); gameview.setcolor(malivecolor, "alive"); mdeadcolor = prefs.getstring("dead_color", "blue"); gameview.setcolor(mdeadcolor, "dead"); setcontentview(gameview); } @override protected void onpause() { //gameview.issimrunning = false; gameview.thread.onpause(); super.onpause(); } @override protected void onresume() { //gameview.issimrunning = true; gameview.thread.onresume(); //gameview.initview(); super.onresume(); } // handle updates preferences public void onsharedpreferencechanged(sharedpreferences prefs, string key) { if(key.equals("sim_speed")) { mspeed = prefs.getstring("sim_speed", "fast"); if(mspeed.equals("warp speed")) gameview.setspeed(50); if(mspeed.equals("fast")) gameview.setspeed(250); if(mspeed.equals("medium")) gameview.setspeed(500); if(mspeed.equals("slow")) gameview.setspeed(1000); if(mspeed.equals("really slow")) gameview.setspeed(5000); } if(key.equals("sim_board_size")) { mboardsize = prefs.getstring("sim_board_size", "large"); gameview.setboardsize(mboardsize); } if(key.equals("alive_color")) { malivecolor = prefs.getstring("alive_color", "yellow"); gameview.setcolor(malivecolor, "alive"); } if(key.equals("dead_color")) { mdeadcolor = prefs.getstring("dead_color", "blue"); gameview.setcolor(mdeadcolor, "dead"); } } @override public boolean oncreateoptionsmenu(menu menu) { // create menu inflater menuinflater inflater = getmenuinflater(); // generate menu xml menu resource file inflater.inflate(r.menu.main_menu, menu); return true; } } // here surfaceview class public class game extends surfaceview implements surfaceholder.callback { long lastupdate = 0; long sleeptime=0; public int num_cols = 51; public int max_cols = 51; public int num_rows = 81; public int max_rows = 81; public long gmdelay = 0; private int grid_cell_size = 9; public boolean [][] current_life = new boolean [max_cols][max_rows]; private boolean [][] successor_gen = new boolean [max_cols][max_rows]; public boolean issimrunning = false; public boolean isthreadstarted = false; paint dead_paint = new paint(); paint alive_paint = new paint(); paint background = new paint(); private gamethread thread; surfaceholder surfaceholder; context context; public game(context context, int dly) { super(context); dead_paint.setstrokewidth(0); dead_paint.setcolor(color.blue); alive_paint.setstrokewidth(0); alive_paint.setcolor(color.yellow); background.setstrokewidth(0); background.setcolor(color.black); gmdelay = dly; initview(); initlifearray(); } public void setspeed(long s) { gmdelay = s; initview(); thread.delay = s; } public void setboardsize(string s) { thread.state = 2; issimrunning = false; if(s.equals("small")) num_cols = 10; else if(s.equals("medium")) num_cols = 25; else if(s.equals("large")) num_cols = 51; thread.state = 1; issimrunning = true; initview(); } public void setcolor(string color, string type) { int c = 0; // violet, white , orange if(color.equals("black")) c = color.black; else if(color.equals("blue")) c = color.blue; else if(color.equals("green")) c = color.green; else if(color.equals("purple")) c = color.rgb(109, 6, 108); else if(color.equals("orange")) c = color.rgb(255, 157, 30); else if(color.equals("red")) c = color.red; else if(color.equals("yellow")) c = color.yellow; else if(color.equals("white")) c = color.white; else c = color.yellow; if(type.equals("alive")) alive_paint.setcolor(c); else dead_paint.setcolor(c); } @override public boolean ontouchevent(motionevent event) { if(event != null) { int x = (int) event.getx()/grid_cell_size; int y = (int) event.gety()/grid_cell_size; int max_x = current_life.length; if(x < max_x) { int max_y = current_life[x].length; if(y < max_y) current_life[x][y] = true; } return true; } return super.ontouchevent(event); } void initview() { // initialize our screen holder surfaceholder holder = getholder(); holder.addcallback(this); // initialize our thread class. call made start later thread = new gamethread(holder, context, new handler(), this); setfocusable(true); } public void draw(canvas canvas) { int x = canvas.getwidth(); int y = canvas.getheight(); canvas.drawrect(0, 0, x, y, background); grid_cell_size = (int) math.ceil((double) (x / num_cols) * 1.0); int gap = grid_cell_size - 1; for(int col = 0; col < num_cols; col++) { for(int row = 0; row < num_rows; row++) { if(current_life[col][row]) canvas.drawrect(col*grid_cell_size, row*grid_cell_size, col*grid_cell_size+gap, row*grid_cell_size+gap, alive_paint); else canvas.drawrect(col*grid_cell_size, row*grid_cell_size, col*grid_cell_size+gap, row*grid_cell_size+gap, dead_paint); } } } // these methods overridden surfaceview super class. automatically called // when surfaceview created, resumed or suspended. @override public void surfacechanged(surfaceholder arg0, int format, int width, int height) { } @override public void surfacedestroyed(surfaceholder arg0) { } @override public void surfacecreated(surfaceholder arg0) { if (!isthreadstarted) { thread.start(); isthreadstarted = true; } thread.onresume(); } } // finally, have thread class public class gamethread extends thread { // flag hold game state // private static final string tag = gamethread.class.getsimplename(); private game game; private surfaceholder msurfaceholder; //for consistent rendering private long sleeptime; public long delay=250; //state of game (running or paused). int state = 1; public final static int running = 1; public final static int paused = 2; private object mpauselock = new object(); private boolean mpaused = false; public gamethread(surfaceholder surfaceholder, context context, handler handler, game g) { super(); //data screen msurfaceholder = surfaceholder; delay = g.gmdelay; this.game = g; } public void onpause() { state = 2; synchronized (mpauselock) { mpaused = true; } } public void onresume() { state = 1; synchronized (mpauselock) { mpaused = false; mpauselock.notifyall(); } } @override public void run() { while (state == running && ! mpaused) { delay = this.game.gmdelay; //time before update long beforetime = system.nanotime(); // update simulation 1 generation game.createnextgeneration(); canvas c = null; try { //lock canvas nothing else can use c = msurfaceholder.lockcanvas(null); synchronized(msurfaceholder) { //if(game.issimrunning) game.draw(c); } } { // in if exception thrown // during above, don't leave surface in inconsistent state if (c != null) { msurfaceholder.unlockcanvasandpost(c); } } synchronized (mpauselock) { while (mpaused) { try { mpauselock.wait(); } catch (interruptedexception e) { } } } // sleep time. time required sleep keep game consistent // starts specified delay time (in milliseconds) subtracts actual // time took update , render game. allows simulation render smoothly. this.sleeptime = delay-((system.nanotime()-beforetime)/1000000l); try { //actual sleep code if(sleeptime>0) { thread.sleep(sleeptime); } } catch (interruptedexception ex) { logger.getlogger(gamethread.class.getname()).log(level.severe, null, ex); } } } }
i have solved issue checking see if canvas null in draw() before performing operations.
thanks suggestions , comments!!
Comments
Post a Comment