3

I have programmed an App which I wanted to test on higher API-Levels for checking the compatibility. For API 10 (2.3.3) there were no problems, but as soon as I ran my app on API 15 (4.0.3) I got an NullPointerException in one of my SurfaceViews when I was quitting the Activity. I have to say that i solved the problem, but i can't figure out why the Exception occured actually. So maybe you could tell me.

Here is the code that worked for me on API 10: It's the common structure of the run()-method.

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }
                doDraw(c);
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

On API 15 when quitting the activity: The Exception accoured when the doDraw()-method tried to write on "c". I checked c and found out it was null, so no surprise I got an Exception. I also checked mThreadActive and found out that although i set it to false, the while-loop still triggers. Here is the Code sample:

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }

                if(!mThreadActive)    // so it really is!
                    Log.d("Thread", "mThreadActive is false!");

                if(c == null)   // so it is too!
                    Log.d("Thread", "c is null!");

                doDraw(c);   // error
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

I can imagine why mThreadActive becomes false AFTER being checked by the while-statement, but I can't figure out why "c" is null after mSurfaceHolder.lockCanvas(null). It seems that the code is not running sequential.

Well, the solution would be checking c != null before drawing on it:

public void run() {
    while (mThreadActive) {
        c = null;
        try {
            c = mSurfaceHolder.lockCanvas(null);
            synchronized (mSurfaceHolder) {
                if(mState == 1) {
                    updateValues();
                    updateAnimation();
                }
                if(c != null) // prevent drawing on c if c doesnt exist.
                    doDraw(c);
            }
        } finally {
            if (c != null) {
                mSurfaceHolder.unlockCanvasAndPost(c);
            }
        }
    }
}

So why do i get an Exception on API 15 whereas it works fine on API 10? Another funny thing is, that i have other SurfaceViews with the same structure, but in comparison to this one, they work all fine! Why is the code not running sequential? Is it because I am testing on an emulator (which is pretty laggy)?

Thank You.

user1349812
  • 73
  • 1
  • 1
  • 5

2 Answers2

2

You mention that your while() loop seems to proceed despite mThreadActive being false. Is mThreadActive marked volatile? It may need to be.

Also, lockCanvas(null) smells. Since we can't see the rest of your code, it's not exactly clear what you're trying to do. What does the API say about passing a null in to lockCanvas? (And are we talking about a raw instance of SurfaceHolder or a subclass?)

Note that the API spec for SurfaceHolder.lockCanvas() indicates it can return null:

A null is returned if the surface has not been created or otherwise cannot be edited. You will usually need to implement Callback.surfaceCreated to find out when the Surface is available for use.

From reading the API, it looks like you should be implementing the SurfaceHolder.Callback interface and responding to the surfaceCreated() event, which is really the event that tells you you're ready to go in writing to the canvas.

andersoj
  • 22,406
  • 7
  • 62
  • 73
  • The `null` in `lockCanvas()` has no meaning for my code. I dropped it now. it was meant to be `lockCanvas(Rect dirty)`. I already implemented the `SurfaceHolder.Callback` Interface. Inside of `surfaceCreated()` I am creating the thread and passing the SurfaceHolder with `getHolder()`. Right after that, i am activating (mThreadActive = true) and starting the thread (`run()`). Also I marked `mThreadActive` as volatile. It seems that `while()` is not proceeding anymore, but somehow c is still beeing called and throws its exception. Maybe it is called from a previous while-iteration? – user1349812 May 12 '12 at 13:26
0

I also get a very similar issue when calling:

public void surfaceDestroyed(SurfaceHolder holder)  {
    isAttached = false;
    this.drawthread = null;
}

When exiting my application to stop the drawing thread - the while boolean I use isAttached is false, but the drawing code within that while loop still executes - giving a nullexception on exit. Not a show stopper but something I really want to fix. Was considering a if (canvas != null) sort of solution too but I thought there must be a better way.

Now here's the weird thing with this error - it only happens some of the time - and happens less on faster devices. The problem the way I see it is that holder.lockCanvas() is returning null - meaning the holder is being destroyed before the boolean is set to false and the while has a chance to stop executing the Thread. A thread race problem?

Found a possible solution that used Thread.join() before making the while boolean null, but with holder.lockCanvas() returning null, the holder is being destroyed before the drawing is finished, so it's kind of pointless and still caused a nullexception.

Only other solution I can think of is to override the back button method and force the while bool to null before destroying the surfaceview(if that's poss), but I think if (canvas != null) is probably cleaner. Any other ideas out there keen to hear!

edit: haven't tested elsewhere, but I get the error using API 17

Kapsy
  • 1