I've developed an Application (called Instant Buttons) and the app has a widget feature. This widget uses PendingIntent for the onClick of the widget.
My PendingIntent code is something like this:
Intent active = new Intent(context, InstantWidget.class);
active.setAction(String.valueOf(appWidgetId));
active.putExtra("blabla", blabla); //Some data
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
actionPendingIntent.cancel();
actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
remoteViews.setOnClickPendingIntent(R.id.button, actionPendingIntent);
The onReceive gets the intent and do some stuff with the MediaPlayer class to reproduce a sound.
I have reports from some users that the widgets stop working after a while and with some research i've discovered is because the Task Killers. It seems that when you kill the app in the TaskKiller, the PendingIntent is erased from memory, so when you click the widget, it doesn't know what to do.
Is there any solution for this? Is my code wrong or something or it's the default behavior of the PendingIntent? Is there something I can use to avoid the TaskKiller to stop my widgets from working??
Greetings.
Is there any solution for this?
Ask your users not to use task killers. Or, wait for some future Android release to close the task-killer loophole.
Is my code wrong or something or it's
the default behavior of the
PendingIntent?
Your code is presumably fine. The hack the task-killers use wipes out pretty much everything, so I'm not the least bit surprised at this behavior.
Is there something I can use to avoid
the TaskKiller to stop my widgets from
working?
Not really. Your users -- those who aren't total morons, at least -- will hopefully learn to be more aware of the impacts of their use of task-killers. At present, there is no sensible defense against a task killer app.
I had this same problem with pending intents in a widget no longer connecting to their activities when the app had been dismissed. Creating, canceling, and recreating worked, but you can also specify a cancel current flag so that you do not have to explicitly call cancel yourself as the system will do it for you.
When you are creating your pending intent instead of using
PendingIntent.getBroadcast(context, 0, active, 0);
use
PendingIntent.getBroadcast(context, 0, active, PendingIntent.FLAG_CANCEL_CURRENT);
Related
I'm making an alarm clock app using PendingIntent and AlarmManager. I know in order to cancel a PendingIntent, you need to recreate it exactly. In my app, much like many other alarm clock apps, there is a list of alarms with a switch to toggle each alarm on/off. When toggled off, I currently recreate the PendingIntent and cancel it using the following:
Intent intent = new Intent(context, MyBroadcastReceiver.class);
String id = this.getId().replaceAll("[^0-9]+", "");
PendingIntent alarmIntent = PendingIntent.getBroadcast(context, id, intent, 0);
alarmIntent.cancel();
The first 3 lines of the code above I believe are unnecessary. When I toggle the alarm, I have access to the custom alarm object which contains the id along with many other details of the alarm. When I'm creating a new alarm, if I store the newly created PendingIntent in the alarm object, I can have access to the PendingIntent used to create the alarm, and just retrieve it and cancel it in 1 line, like this:
this.getPendingIntent().cancel();
I wouldn't have to recreate the intent, get the id, or recreate the PendingIntent from the code shown earlier. This would ultimately save time and resources (not much but it's good practice), so I have a couple questions:
1) Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it? It seems like a straightforward answer but I haven't seen anyone do this before.
2) Is there an advantage to recreating the PendingIntent that I'm not aware of?
Thanks!
Is there anything wrong with storing the PendingIntent in an object and use it later instead of recreating it?
Assuming that the underlying Intent doesn't have some massive payload (e.g., Bitmap in an extra), you should be OK.
It seems like a straightforward answer but I haven't seen anyone do this before.
https://github.com/commonsguy/cw-omnibus/tree/v8.7/AlarmManager/Simple, though it's a trivial example.
Is there an advantage to recreating the PendingIntent that I'm not aware of?
It works for cases where you do not have the PendingIntent. Your process does not live forever. If you want to use a cached PendingIntent as an optimization, that's fine, but you need to be in position to create the PendingIntent if it is needed.
I am developing an alarm application in Android. The flow is really simple, Im just creating a PendingIntent and then I call the setExact() method in the AlarmManager much like below.
Intent myIntent = new Intent(context, BroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + difference, pendingIntent);
After some tests I realized that with the above snippet I can set only one alarm because I set the requestCode of the pendingIntent to zero, and if I set another alarm with the requestCode set to 0 then it will overwrite the previous one. Is there a way to fix this without changing the requestCode? I was thinking maybe the flags can help me but I didn't find a flag that doesn't overwrite the previous pendingIntent.
I know that the obvious solution is to change 0 to another int and then keep track of all my ints, picking one that is not used. That solution would be fine if I was just starting the project, however I am already in the middle and I use as request codes predefined Enums. It is very difficult to change this mechanic and keep track of individual ints thats why I am asking if there is a way of not overwriting a pendingIntent when a new one with the same requestCode is registered. Thank you in advance.
You can make the Intents unique be setting a different ACTION on each one. Then you could still use the same requestCode and would have different PendingIntents.
You'll need to keep track of the ACTIONs you use if you want to be able to cancel the alarms later.
I am asking if there exists a certain type of flag that will be able to differentiate them
There is not, and it makes sense because this is already the purpose of the requestCode parameter.
For information, these are your options regarding flags:
You will have to change your mechanism to make it possible to have different requestCodes for Pending Intents. It may be a lot of work, but it is what you have to do.
I'm just looking at how to cancel an alarm and I came across these two methods. Which one should be used in what situation and why? Are they both the same?
I am currently doing this:
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
pendingAlarmIntent.cancel();
How is that different to this below?
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingAlarmIntent);
Are they both the same?
No.
If you want to cancel an alarm, call cancel() on AlarmManager.
cancel() on PendingIntent probably will appear to have the same effect -- whatever your alarm events were supposed to trigger no longer happens. However, you are then assuming that AlarmManager will detect this and clean things up on its side, which is not guaranteed. Particularly for _WAKEUP alarms, this could result in the device being woken up for no good reason, wasting battery life.
Which one should be used in what situation and why?
I am sure that cancel() on PendingIntent has use cases. I cannot name any concrete ones, as I have never seen it used. Typically when you are using a PendingIntent, any "cancel" semantics are on the use of the PendingIntent (e.g., you cancel() the alarm via AlarmManager, you cancel() the notification via NotificationManager), not on the PendingIntent itself.
One place for cancel() on PendingIntent, therefore, would be some place where you pass off a PendingIntent and there is no "cancel" to revert that, or you explicitly wish to use cancel() as the revert mechanism. For example, if you are creating some sort of plugin mechanism, and the plugin sends a PendingIntent to the host app, the plugin might use cancel() to say "stop using that PendingIntent", which the host app would find out about the next time it tried to send() the PendingIntent and got the exception. Personally, I'm not a huge fan of this -- much along the AlarmManager lines, you don't know what resources might be used by the host app if it fails to properly handle this scenario. But, used properly, it could certainly work.
How is that different to this below?
The "below" one is what I would recommend that you use.
I want show Activity when device enter the fixed zone. I have startActivity in recivier(GpsAlarmRecivier). Code below works, but when I close Activity, it crash. I know it' s because i must unregister recivier.
But I want use addProximityAlart for all application, even after close my activity(for example, move to previous). Is it possible ?
Intent myIntent = new Intent("gpsup.namespace.ProximityAlert");
PendingIntent proximityIntent = PendingIntent.getBroadcast(cxt, 0, myIntent, 0);
locationManager.addProximityAlert(records.get(pos).x, records.get(pos).y, records.get(pos).r,
-1, proximityIntent);
IntentFilter filter = new IntentFilter("gpsup.namespace.ProximityAlert");
actv.getApplicationContext().registerReceiver(new GpsAlarmReceiver(), filter);
I want use addProximityAlert, even if I close activity, when i created recivier. Thanks for any advices.
I don't believe that there is a way to directly register a system GPSBroadcastReceiver in your application. If that was the case you could just put it in your manifest and it'll get resolved when an update comes out and then you can fire off you custom intent after performing your checks.
I believe that is actually the reason why they don't allow it (I may be wrong). It would be problematic if every application was woken up when a GPS update came out. They would be spanking the battery in the background.
A suggestion that I can give is to create a Service that listens for your GPS updates and then Broadcasts your intents. While you can have it running in the background forever, it certainly has a longer life cycle than an Activity does.
There have been some similar issues discussed here, but my situation does work some of the time. I am developing a widget that when clicked should launch an activity that's part of the same package. This same activity can also be launched by a notification that may be posted. The widget updates and notification posting are done by a Service in the package. Here is the method that's called to issue the PendingIntent:
// Get pending intent for widget or notification
private PendingIntent getPendingIntent(int widgetId, int extraData) {
Intent clickIntent = new Intent(mCtx, OtdShowEvents.class);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
clickIntent.putExtra("OTDExtra", extraData);
clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendIntent = PendingIntent.getActivity(mCtx, 0, clickIntent,
0);
return pendIntent;
}
If I create an instance of the widget on a clean (rebooted) device, both the widget and notification launch the target activity as expected. However, if I remove the widget and create another instance, the Intent is no longer launched. Likewise, if I uninstall the widget altogether, then re-install it and create an instance, no Intent is fired off. However, if I power off and back on (leaving the widget in place), it works again when booted up.
One error that I saw along the way was from the PackageManager saying "Name not found", but indicating the package name "com.ghcssoftware.OTD.full", which is the correct name of my package!
Any ideas? And by the way, I have tried some of the PendingIntent flags such as FLAG_CANCEL_CURRENT and FLAG_UPDATE_CURRENT without affecting this behavior.
FWIW, I found that the code snippet provided in this article was exactly what I needed to figure out how to get this all working correctly, especially for multiple instances of my widget, etc.: PendingIntent in Widget + TaskKiller