Skip to content Skip to sidebar Skip to footer

Correct Pattern To Acquire A Wakelock In A Broadcastreceiver And Release It In A Service

Even after a lot of research I am still not completely sure if the way how I implement a WakeLock for a Service started by a BroadcastReceiver is correct - even though it seems to

Solution 1:

What I am unsure about is the check using isHeld() - does it really tell me if a WakeLock is acquired or not, and do I need to do this check at all?

Actually slightly tricky to answer. Looking at the source for PowerManager and PowerManager.WakeLockhere the WakeLock.acquire() and WakeLock.acquireLocked() methods are as follows...

publicvoidacquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        mHandler.postDelayed(mReleaser, timeout);
    }
}

privatevoidacquireLocked() {
    if (!mRefCounted || mCount++ == 0) {
        // Do this even if the wake lock is already thought to be held (mHeld == true)// because non-reference counted wake locks are not always properly released.// For example, the keyguard's wake lock might be forcibly released by the// power manager without the keyguard knowing.  A subsequent call to acquire// should immediately acquire the wake lock once again despite never having// been explicitly released by the keyguard.
        mHandler.removeCallbacks(mReleaser);
        try {
            mService.acquireWakeLock(mToken, mFlags, mTag, mWorkSource);
        } catch (RemoteException e) {
        }
        mHeld = true;
    }
}

...mService is an IPowerManager interface and the source for it isn't available so it's hard to tell what may or may not go wrong when attempting to call acquireWakeLock(...).

In any case, the only exception that can be caught is RemoteException and the catch block does nothing. Immediately after the try/catch, mHeld is set true regardless.

In short, if you call isHeld() immediately after acquire() the result will always be true.

Looking further into the source for PowerManager.WakeLock shows similar behaviour for release() which calls release(int flags) where the mHeld member is always set to false regardless of what happens.

In conclusion I'd suggest it is always a good idea to check isHeld() just as a best practice in case later versions of Android change this behaviour of the WakeLock methods.

Solution 2:

Manage you wakeLock inside a singleton (unique instance accessible through all your context and object)

Use a singleton instance of a custom class, then you may get wakelock object reference from call to call ,

here an singleton example

classMyData {
   privatestatic MyData mMydata= null; // unique reference ( singleton objet container)private PowerManager.Wakelock myobject = null; // inside the unique object container we have the unique working object to be use  by the application// can't make instance from outside... we want to have single instance// we want that outside use method "getInstance" to be able to use the objectprivateMyData() {
   }

   // retrieve and/or create new unique instancepublicstatic MyData getInstance() {
     if (mMydata ==  null) mMyData = new MyData();
     return   mMyData;
   }

   // Works with your memory stored object// get...public PowerManager.WakeLock getMyWakelock() {
   return myobject;
   }
   // set ...publicvoidsetMyWakeLock(PowerManager.WakeLock obj) {
    myobject = obj;
   }
}

in your application to handle your "wakelock" object your may access it like

// set a created wakelock
MyData.getInstance().setMyWakeLock(wl);
// get the saved wakelock object
PowerManager.WakeLockobj=  MyData.getInstance().getMyWakeLock();

Solution 3:

All this job can be done by a helper and native class called WakefulBroadcastReceiver

Solution 4:

I think android.os.Messenger may be a better way

for the receiver:

publicclassMessengerReceiverextendsBroadcastReceiver {


    privatestaticfinalStringTAG="MessengerReceiver";

    privatefinalMessengerHandlermHandler=newMessengerHandler();

    @OverridepublicvoidonReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving// an Intent broadcast.
        mHandler.mWakeLock = ((PowerManager)context.getSystemService(Service.POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "myreceiver");
        mHandler.mWakeLock.acquire();
        Log.e(TAG, "onReceive:: mHandler.mWakeLock=" + mHandler.mWakeLock + ", intent=" + intent + ", this=" + this);
        context.startService(newIntent(context, MessengerService.class).putExtra("messenger", newMessenger(mHandler)));
    }

    staticclassMessengerHandlerextendsHandler {

        WakeLock mWakeLock;
        @OverridepublicvoidhandleMessage(Message msg) {
            // TODO Auto-generated method stubif(mWakeLock != null){
                mWakeLock.release();
                Log.e(TAG, "handleMessage:mWakeLock=" + mWakeLock);
            }
            super.handleMessage(msg);
        }

    }
}

for the service:

publicclassMessengerServiceextendsService {
    privatestaticfinalStringTAG="MessengerService";
    publicMessengerService() {
    }

    @Overridepublic IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.thrownewUnsupportedOperationException("Not yet implemented");
    }

    @OverridepublicintonStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub      
        Log.e(TAG, "onStartCommand:: intent=" + intent);
        finalMessengermessenger= intent.getParcelableExtra("messenger");
        try {
            messenger.send(Message.obtain());
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        returnsuper.onStartCommand(intent, flags, startId);
    }
}

This method work properly even if service & receiver run in different process.

Post a Comment for "Correct Pattern To Acquire A Wakelock In A Broadcastreceiver And Release It In A Service"