Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update the data send to a service using intent when service is started by alarm manager?

I am writing an Android application where the user can choose several stocks to watch and gets alerted if an predefined alert condition is matched. The stock data is saved to 5 objects of a custom Parcelable class "alert" (one object per stock and condition). The periodic data update is done via a service started by an AlarmManager. The alert objects are passed to the service via putting them into the Intent which is put into the PendingIntent of the AlarmManager.

    Intent intent = new Intent(this, UpdateService.class);
    Bundle b = new Bundle();
    saveAlertsToBundle(b);      
    intent.putExtras(b);
    intent.setData(Uri.parse("updateManager"));
    PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);

    // 1min intervall
    long intervall = DateUtils.MINUTE_IN_MILLIS * 1;
    // time of first start
    long firstStartDelay = DateUtils.SECOND_IN_MILLIS * 30;
    long firstStart = System.currentTimeMillis() + firstStartDelay;

    AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    // kill running
    am.cancel(pendIntent);
    //start new
    am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);

My problem is:

When starting the service for the first time when there is only one object of alert passed to the service everything works fine. As soon as there are more alerts objects existing they also need to be passed to the service but this does not work with the code above. The service does not receive the updated intent with the additional alert objects , but only the initial one with only one alert object. The code above correctly creates an Intent holding the additional alert object, but they never get to the service.

So my question is, how to pass the updated intent to the already running AlarmManager.

I already tried stopping the AlarmManager (the line at the // kill running comment) and restarting it, but this does not work. Perhaps because of the intent not holding the same alert objects as at the time when he was created ? I tried to fix this by setting an uri in the data part of the intent but this also did not help.

Thanks for help.

like image 344
user1695117 Avatar asked Nov 03 '22 15:11

user1695117


1 Answers

Your problem is the way PendingIntent works. The system manages a pool of PengingIntents. When your code does:

PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);

This causes the system to search for a PendingIntent that matches the parameters you've passed in (in this case, your Intent. However, the matching algorithm that PendingIntent uses only compares certain fields of the Intent to determine if it is the one that you are looking for. In particular, it does not compare extras. So this means after you've created the first PendingIntent, the call to PendingIntent.getService() will always return the same PendingIntent from the pool (and not create a new one, which is what you want).

In order to make the call to PendingIntent.getService() create a new PendingIntent every time you call it, try making the parameters you pass to the call unique, like this:

int requestCode = (int) System.currentTimeMillis(); // Create unique request code
PendingIntent pendIntent = PendingIntent.getService(this, requestCode, intent, 0);

Since requestCode will be different for each call to PendingIntent.getService(), this should solve your problem.

EDIT Based on OP's comments below

You want to cancel the existing alarm and create a new one with new data. In that case you don't need to use unique identifiers because you only want to have a single PendingIntent in the pool. But, you want to change the data for that. Try this:

// Create a PendingIntent (or update the existing PendingIntent with new values
PendingIntent pendIntent = PendingIntent.getService(this, 0, intent,
                      PendingIntent.FLAG_UPDATE_CURRENT);

AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// cancel any pending alarms
am.cancel(pendIntent);
//start new
am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);
like image 56
David Wasser Avatar answered Nov 08 '22 14:11

David Wasser