Sridhar Chidurala
Sridhar Chidurala

Reputation: 576

Usage of PendingIntent.cancel() and AlarmManager.cancel()

How does PendingIntent.cancel() affect AlarmManager if there is a pending alarm.

Should I call cancel on both objects(intent and alarmmanager) to cancel an alarm? Can someone explain how they work together.

Thanks in advance.

Upvotes: 6

Views: 2052

Answers (2)

pcodex
pcodex

Reputation: 1940

Adding my 2 cents to the excellent answer from dawid gdanski.

Calling a cancel on the Alarm Manager is sufficient to stop the alarm from firing. You will need to however pass the same Pending Intent that was used while setting the alarm.

so

PendingIntent pc = PendingIntent.getBroadcast(getApplicationContext(),
                    someid,
                    someintent,
                    PendingIntent.FLAG_UPDATE_CURRENT);

//to set
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime(),
                        60000, //1 min
                        pc);

//to cancel, use the same Pending Intent
if(pc != null) {
                    alarmManager.cancel(pc);
}

You can test this yourself by reducing the periodic interval of your alarm to around 30 secs. Notice how cancelling just the alarm is sufficient to stop the alarm from firing.

However remember that the Pending Intent has still not been cancelled and if you wish to do that then you will need to explicitly cancel it.

pc.cancel() 

Upvotes: 0

dawid gdanski
dawid gdanski

Reputation: 2452

Registering PendingIntents

PendingIntent instance can be obtained via factory methods PendingIntent.getActivity(), PendingIntent.getService(), PendingIntent.getBroadcast().

However, apart from just obtaining PendingIntent instance, ActivityManager registers the PendingIntent in internal cache/meta data file if it does not exist. In contrary, if it does exist, then previously registered instance is returned.

For instance,

 public static PendingIntent getActivity(Context context, int requestCode,
        Intent intent, int flags) {
    String packageName = context.getPackageName();
    String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
            context.getContentResolver()) : null;
    try {
        intent.setAllowFds(false);
        IIntentSender target =
            ActivityManagerNative.getDefault().getIntentSender(
                IActivityManager.INTENT_SENDER_ACTIVITY, packageName,
                null, null, requestCode, new Intent[] { intent },
                resolvedType != null ? new String[] { resolvedType } : null, flags);
        return target != null ? new PendingIntent(target) : null;
    } catch (RemoteException e) {
    }
    return null;
}

As the documentation states:

/**
 * Retrieve a PendingIntent that will start a new activity, like calling
 * {@link Context#startActivity(Intent) Context.startActivity(Intent)}.
 * Note that the activity will be started outside of the context of an
 * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
 * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent. 
...
* @return Returns an existing or new PendingIntent matching the given
 * parameters.  May return null only if {@link #FLAG_NO_CREATE} has been
 * supplied.

Cancelling PendingIntent only

Here's how the cancellation looks like:

 /**
 * Cancel a currently active PendingIntent.  Only the original application
 * owning an PendingIntent can cancel it.
 */
public void cancel() {
    try {
        ActivityManagerNative.getDefault().cancelIntentSender(mTarget);
    } catch (RemoteException e) {
    }
}

The documentation states that the application can cancel the PendingIntent for you. Essentially, it means that ActivityManager attempts to match the PendingIntent and remove the meta data /cache entry on condition that the matching PendingIntent exists.

If you attempt to get the previously cancelled or unregistered PendingIntent with the flag FLAG_NO_CREATE applied, a null is returned then.

Cancelling PendingIntent via AlarmManager

Cancellation via AlarmManager differs obviously because it removes registered PendingIntent in IAlarmManager's cache/meta data files and from what I noticed diving deeper into Android source code there is no cancellation being done via ActivityManager when an alarm is removed.

public void cancel(PendingIntent operation) {
    try {
        mService.remove(operation); IAlarmManager instance
    } catch (RemoteException ex) {
    }
}

Conclusion

You must cancel your alarm via AlarmManager once you register it, cancelling PendingIntent itself and the AlarmManager's alarms cancellation process have nothing in common.

Hope, that I clarified your doubts.

Upvotes: 5

Related Questions