android – WakeLock finalized while still held-ThrowExceptions

Exception or error:

The pm and keepScreenOn variables are globally defined.

I grab the PowerManager.WakeLock in my OnCreate method:

pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
keepScreenOn = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_LOCK,"tpd");

in my onStart, onResume, and onRestart I grab the lock with

if (keepScreenOn == null) {
    keepScreenOn = pm.newakeLock(PowerManager,SCREEN_BRIGHT_LOCK,"tpd");
}
keepScreenOn.acquire();

in my onDestroy, onPause, and onStop I release the lock with:

if (keepScreenOn != null) {
  keepScreenOn.release();
  keepScreenOn = null
}

After my app exits I get a failure screen and adb complains that

java.lang.Exception: WakeLock finalized while still held: tpd

Tracing shows that I released the lock before exit.
What have I missed?

There is no way out of the app without crossing at least one of
onPause, onStop, or onDestroy. I can see that the app called
release() as often as it called acquire() so even though the
wakelock is reference counted it should still have zero refs.

How to solve:

Ok I believe I found the problem.

The WakeLock is reference counted. That means that if a second acquire()
happens it will just bump the reference count. Every call to acquire()
needs to be protected by a call to isHeld() as in:

if ((keepScreenOn != null) &&           // we have a WakeLock
    (keepScreenOn.isHeld() == false)) {  // but we don't hold it 
  keepScreenOn.acquire();
}

I had assumed that acquire() on a lock I held did nothing so
multiple acquire() calls caused the problem. Since the reference
count is not zero the GC throws an error.

###

I know this question is old, but keep in mind that WakeLocks are ‘reference counted’ by default. You can turn off reference counting using setReferenceCounted(boolean), see http://developer.android.com/reference/android/os/PowerManager.WakeLock.html#setReferenceCounted(boolean)

###

No, there is only one declaration at the global scope and all
calls to the acquire() and release() occur in that scope. I
println when they happen and the acquire() occurs once and the
release occurs once.

Leave a Reply

Your email address will not be published. Required fields are marked *