9

I have a SurfaceView extension where the bare bones of it are implemented as in the Lunar Lander example. That is, the run() method of the drawing Thread is essentially:

public void run() {
    while (mRun) {
        Canvas c;
            try {
                c = mSurfaceHolder.lockCanvas();
                synchronized (mSurfaceHolder) {
                    doDraw(c); // Main drawing method - not included in this code snippet
                }                                   
            } 

            finally {
                // do this in a finally so that if an exception is thrown
                // during the above, we don't leave the Surface in an
                // inconsistent state
                if (c != null) {
                    mSurfaceHolder.unlockCanvasAndPost(c);
                }
            }
        }
    }
} 

And the Thread is properly stopped when the surface is destroyed:

public void surfaceDestroyed(SurfaceHolder holder) {
    // we have to tell thread to shut down & wait for it to finish, or else
    // it might touch the Surface after we return and explode
    boolean retry = true;
    thread.setRunning(false);
    while (retry) {
        try {
            thread.join();
            retry = false;
        } 
        catch (InterruptedException e) {
        }
    }
}

On devices I have usually tested on to date (HTC Desire, Desire HD, and Archos 101 which between them have OS 2.2 and 2.3.3 if I remember right) there has never been a problem with the above. That is, when the surface is destroyed because the user backs out of the Activity or another Activity is invoked on top, the code within surfaceDestroyed() always ensures that mSurfaceHolder.lockCanvas() would never be called for it to return null.

The difference I've found on my new HTC One X which is running Android 4 / ICS, however, is that during the call to method surfaceDestroyed() (i.e. code within that method is still executing) my drawing Thread would be given a null canvas from mSurfaceHolder.lockCanvas(). This would of course cause an application crash. On my One X, this will happen every single time the surface is destroyed - whether it be due to rotating the phone, backing out of the Activity, etc.

I'm confused about this because I was under the impression that mSurfaceHolder.lockCanvas() should return a non-null Canvas until surfaceDestroyed() has actually exited. Indeed, this is what the Javadoc says:

This is called immediately before a surface is being destroyed. After returning from this call, you should no longer try to access this surface. If you have a rendering thread that directly accesses the surface, you must ensure that thread is no longer touching the Surface before returning from this function.

My solution for now is to just check for null. This works fine:

if(c != null){
    doDraw(c); // Main drawing method - not included in this code snippet
}

But, any ideas why I'm suddenly having to do this for Android 4 / ICS?

Trevor
  • 10,903
  • 5
  • 61
  • 84
  • Returning `null` is a way to prevent you from touching the surface too so what you do should be fine. Idk why or what changed in the underlying implementation though. – zapl Apr 28 '12 at 14:50
  • Thanks, zapl. Yeah, it's really bizarre. Unless there is something odd somewhere I am doing to cause this apparent change in implementation, surely lots of SurfaceView-based apps are going to die on ICS. – Trevor Apr 28 '12 at 14:54
  • Does anyone find out an "official" why because I got this error also on ICS devices or emulators. – Lenciel Dec 10 '12 at 06:30
  • 3
    Glad you posted this question, thought I was doing something wrong, but the Lunar Lander example also exhibits this behaviour. There's a android bug ticket open here http://code.google.com/p/android/issues/detail?id=38658 if anyone wants to star it! :) – Andy Feb 11 '13 at 13:32
  • Very interesting that there's a bug ticket open for it. Many thanks for sharing that, Andy. – Trevor Feb 11 '13 at 15:06
  • @Andy: Thinking about it, you could quite legitimately post that as an answer. – Trevor Feb 11 '13 at 18:14
  • Nice to know I'm not facing this alone :) Was playing with surfaceview this week and have been facing this exact same issue while running on a Nexus 5 - but not on an old Samsung Galaxy Ace 2.3.6. – slott Oct 03 '14 at 06:21

2 Answers2

5

Just to elaborate on my comment, it seems that there is a bug ticket open for this change in behaviour, which you can find here: http://code.google.com/p/android/issues/detail?id=38658. If it's effected you, it might be worth staring it, just so it raises it's importance a bit!

Personally, I've seen this myself, just by using the lunar lander example which comes with the latest android SDK. I put it on my HTC Sensation XE (4.0.3) and on orientation change, I get some null canvases being returned before the surface is destroyed.

So the workaround I use is just to double check that the canvas is not null before passing it to my update and render methods.

Andy

Andy
  • 2,977
  • 2
  • 39
  • 71
  • Interesting! I had this problem also, and got around it in a similar way, the only different being that I put my check for null canvas actually inside my draw method :-) – Zippy Feb 17 '13 at 19:40
  • Just found this after struggling with the same problem for days! Thanks a lot! I did think about checking for null, but I kept on looking for another solution since it just seems messy to check for null, but I guess we have to do it that way. – mr_lou Jun 29 '13 at 15:06
0

I made small experiment. I put some notification flags in surfaceDestroyed() method like this:

public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("i","i have started");
...[the code]...
Log.i("i","i have finished");
}

What I have found out, is the fact that the nullPionterExceptionOccurs occurs after the first flag, but before the second one. The most possible anwser for your question is that you lock canvas when the old view is reachable, you draw on it but in the meantime the screen changes. So when unlock the canvas there is no place to show the results- the error is caused.

P.S. Play a bit with Lunar Lander from sample android codes and try to rotate the screen while the game works. When the error will occur just look how the background bitmap was about to be drew. You will find out, that the screen orientation changed, but the program made attempt to draw a bitmap as if nothing happend.

Tinki
  • 1,486
  • 1
  • 21
  • 32