Reliability of using a PendingIntent to store application state - android

I have a Service that first creates a new Intent, and calls setAction('foo') on it. After this I call putExtra("key","value123"). Then I call PendingIntent.getService(this,999,intent,0) (no flags passed), and pass the pending intent to the AlarmManager.
But then before the alarm fires (or even after, it seems) I create another Intent and again call setAction('foo') on it, but don't set any extras.
Then I again pass it to PendingIntent.getService(this,999,intent,0). However, this time I call send() on the PendingIntent so as to receive the intent immediately.
What I observe is that the original extras are delivered with the new Intent. I appear to be able to do this over and over, and even if my app is killed, when I restart it, the extras are still there.
However, I don't see anything specifically in the documentation which says whether or not this is actually expected behavior. Is this a reliable method by which I can persist a small amount of data in RAM (only!) in case my app gets terminated? Currently I'm using file on a RAMdisk, but some devices apparently don't have such a thing.

This is the way PendingIntent works ;-)
The first call to PendingIntent.getService() creates a new PendingIntent with the requestCode set to 999 and the Intent set to ACTION="foo".
The second call to PendingIntent.getService) doesn't create a new PendingIntent. It just returns a token (reference) to the first PendingIntent. When you call send() on it, the original PendingIntent is sent.
The reason is that when you call PendingIntent.getService(), Android first tries to find a PendingIntent that matches the one you've specified. To determine if the Intent matches, it checks the ACTION, COMPONENT, DATA and it also checks for matching requestCode arguments. In your case, both calls to PendingIntent.getService() have the same requestCode and the Intents have the same ACTION. NOTE: "extras" in the Intent are not considered when determining if the Intents match.
If you always want to (re)use a single PendingIntent and just override the "extras" every time you use it, you can add the flag PendingIntent.FLAG_UPDATE_CURRENT to the call to PendingIntent.getService().
If you need to create several PendingIntents in parallel, with different "extras", you need to make sure to use a unique requestCode every time you call PendingIntent.getService().

Related

How does android compare pending intents

Documentation for PendingIntent.FLAG_NO_CREATE reads:
Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
My question: What criteria are used to compare PendingIntents?
I'm guessing under the hood this flag uses PendingIntent.equals, but I'm not really sure what criteria that function is using. Is it using the action, requestCode, categories, extras (I'm guessing no), etc.?
Context:
I want to start an alarm with a pending intent if my alarm is not already setup. Specifically, I'm following this answer.
Intent i = new Intent(applicationContext, MyService.class);
i.setAction("myAction");
PendingIntent pi = PendingIntent.getService(applicationContext, /*requestCode*/0, i, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
AlarmManager alarmMgr = (AlarmManager)applicationContext.getSystemService(Context.AlarmService);
alarmMgr.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.INTERVAL_HOUR, AlarmManager.INTERVAL_HOUR, pi);
}
To determine if 2 PendingIntents match, the following must be equal:
The requestCode parameter used when the PendingIntent was created
The Intent ACTION
The Intent CATEGORIES
The Intent DATA
The Intent MIMETYPE
The Intent PACKAGE
The Intent COMPONENT
Extras are not taken into consideration.
You can read more in the PendingIntent summary documentation and Intent.filterEquals().
I'm guessing under the hood this flag uses PendingIntent.equals, but
I'm not really sure what criteria that function is using. Is it using
the action, requestCode, categories, extras (I'm guessing no), etc.?
Actually the hint is in class description:
A description of an Intent and target action to perform with it.
Instances of this class are created with
getActivity(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
getService(android.content.Context,int,android.content.Intent,int);
the returned object can be handed to other applications so that they
can perform the action you described on your behalf at a later time.
By giving a PendingIntent to another application, you are granting it
the right to perform the operation you have specified as if the other
application was yourself (with the same permissions and identity). As
such, you should be careful about how you build the PendingIntent:
almost always, for example, the base Intent you supply should have the
component name explicitly set to one of your own components, to ensure
it is ultimately sent there and nowhere else.
A PendingIntent itself
is simply a reference to a token maintained by the system describing
the original data used to retrieve it. This means that, even if its
owning application's process is killed, the PendingIntent itself will
remain usable from other processes that have been given it. If the
creating application later re-retrieves the same kind of PendingIntent
(same operation, same Intent action, data, categories, and components,
and same flags), it will receive a PendingIntent representing the same
token if that is still valid, and can thus call cancel() to remove it.
Because of this behavior, it is important to know when two Intents are
considered to be the same for purposes of retrieving a PendingIntent.
A common mistake people make is to create multiple PendingIntent
objects with Intents that only vary in their "extra" contents,
expecting to get a different PendingIntent each time. This does not
happen. The parts of the Intent that are used for matching are the
same ones defined by Intent.filterEquals. If you use two Intent
objects that are equivalent as per Intent.filterEquals, then you will
get the same PendingIntent for both of them.
There are two typical ways to deal with this.
If you truly need multiple distinct
PendingIntent objects active at the same time (such as to use as two
notifications that are both shown at the same time), then you will
need to ensure there is something that is different about them to
associate them with different PendingIntents. This may be any of the
Intent attributes considered by Intent.filterEquals, or different
request code integers supplied to
getActivity(android.content.Context,int,android.content.Intent,int),
getActivities(android.content.Context,int,android.content.Intent[],int),
getBroadcast(android.content.Context,int,android.content.Intent,int),
or getService(android.content.Context,int,android.content.Intent,int).
If you only need one PendingIntent active at a time for any of the
Intents you will use, then you can alternatively use the flags
FLAG_CANCEL_CURRENT or FLAG_UPDATE_CURRENT to either cancel or modify
whatever current PendingIntent is associated with the Intent you are
supplying.
from:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/5.0.0_r1/android/app/PendingIntent.java#PendingIntent

Cancelling Alarms with PendingIntents

I am trying to cancel an alarm that was set last time my app was run. This alarm has a PendingIntent that was set with PendingIntent.getBroadcast and an inner Intent that contains some variables set by intent.putExtra. My question is this, I know that alarms can be canceled by calling alarmManager.cancel(pendingIntent) where pendingIntent is the same as the one used to set the alarm. But, if the variables placed into the intent are changed will the alarm still be canceled? For example, I set an alarm with intent.putExtra("Joe") where Joe is a contact name. Later my app is closed and when it is re-run I try and cancel the alarm for "Joe" but the user has changed the name of the contact to "Jones". Can I cancel the alarm without knowing the variables I put into the intent?
Thanks!
I think it should cancel the alaram anyway, even though some data is different. The cancel method says:
Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.
And filterEquals says:
Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.
Anyhow, I'd still test it myself.
According to this question (which references the documentation), anything you add using putExtra is not taken into account when checking if an intent is equal to another one.
It shouldn't matter if the extra data is changed.

Same instance of pending intent is fired again and again, despite changing the extras manually

I have to do a task multiple times, and task is to broadcast an intent which will be intercepted by a different app. Lets say from App Sender to Receiver.
Receiving the intent in Receiver app is working fine, I have tested and guaranteed:
How I am doing this is,
I created a broadcast receiver in Sender app, with action SENDER_ACTION.
I create an intent with this action, set an extra with key frequency and value 4.
Get a PendingIntent from this and set an alarm of 10 sec, and fires this intent.
Idea is that sender app, will receive this intent, will fire the required intent to RECEIVER app, and then decreases the frequency value by 1, set it again to the intent, creates a pending intent and alarm and fires again.
So, ideally this counter of frequency should reach to 0 and process should terminate.
I can see that while setting the decreased value of frequency, it is happening (in logs), but when I receive its the same unchanged value.
I suspect that pending intent instance is kept on fired again and again.
Can some body please help me why is this happening?
If I had to guess -- which I do, since you provided no source code -- it is because you have not passed the right flags into getBroadcast() (or whatever factory method that you are using on PendingIntent to obtain the PendingIntent instance). Please bear in mind that Android caches PendingIntent objects and will reuse them, with their internal Intent extras unmodified, by default. Consider passing FLAG_UPDATE_CURRENT as the last parameter to getBroadcast().

There was already an alarm for this Intent scheduled,and I want add another one

I register a broadcastreceiver in AndroidMainfest.xml
And in my app, a function is that User can set a time and at this time the app will send a notification. I get the arguments User set ,and use alarmManager to set a task which will happened at the time user set.
But I find that GOOGLE API said that:
If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one.
So if I want set two or more task,the Intent will be replaced , and at end I can only get one notification,it's not the result I want.
And then I found the intent was identified by action, data, type, class, and categories,
but I can't change action(the intent's action is the intent-filter's action was registred in the AndroidMainfest.xml ),but at the time that I change the other arguments I can't even receive a broadcast.
I thought there are four ways to solve this problem,but I only made one..
create lots of broadcastreceiver and register these in
AndroidMainfest.xml,and in this way I could change the intent's
action
register the broadcastreceiver in the program ,but I didn't
make it
use service + Timer class ..
To make two intent different without change action.
Any help will be appreciated!!
Intent intent = new Intent("aaa"); //there was a broadcastreceiver's intent-filter "aaa"
intent.putExtra("title", title);
intent.putExtra("table", "计划");
PendingIntent pi = PendingIntent.getBroadcast(Alarm.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager)getSystemService(Alarm.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+time, pi);
The code is in onClickListener{onClick(){}}
Instead of Broadcasting which is not letting you to set repeat alarm you can use the calendar Object and set the alarm at whatever desired time. Once the alarm gets ON, your code will run automatically and as many times you have set the alarm.
You can also take a help of the following tutorial. I am sure it will help you out somehow.
http://blog.mikesir87.io/2013/04/android-creating-an-alarm-with-alarmmanager/

Android: Intent that is fired by AlarmManager is not containing my Flags

When my App crashes I create an Intent with a FLAG_CRASH.
I call PendingIntent.getActivity with that Intent, and hand the resulting PendingIntent over to AlarmManager.
After 1 Second it restarts my App. Yet on older API Levels (< 9) getFlags on the Intent always returns 0. API Level 15 returns my Flags. Any Idea why? Did some default behavior change happen between the Versions?
I ended up writing a broadcast receiver that I can fire an own distinct intent via pending intent. That broadcast receiver builds another intent for the activity and puts an extra into it for signaling the crash. In the Activty I have a handler method that I call from both onNewIntent and onCreate, the handler creates the error popup and resets the extra.

Categories

Resources