Search code examples
androidbroadcastreceiverandroid-servicealarmmanagerintentservice

Polling via an IntentService and using AlarmManager


I am just having a hard time understanding the AlarmManager system, tied in with future notifications etc. I can't seem to get the AlarmManager to set alarms at the correct frequency.

I'm also unsure where I should actually create an alarm, and what type is most suited to my needs.

Updates are based on Preferences which will be detailed below.


Launching the Service

When my application is launched, the service is called when a FragmentActivity is created. Within the onCreate(), I have the following:

Intent mServiceIntent = new Intent(this, ServiceUpdater.class);
if(startService(mServiceIntent)==null)
startService(mServiceIntent);

Lots of other stuff happens and fragments are shown ect, but this is essentially to ensure the service is running. Note, I also have it set to call the BroadcastReceiver on System startup.

ServiceUpdater.class

public class ServiceUpdater extends IntentService{

    private SharedPreferences defaultPrefs;
    private SharedPreferences userPrefs;
    private SharedPreferences.Editor editor;

    // Alarm Service
    private AlarmManager alarmManager;
    private PendingIntent alarmIntent;

    public ServiceUpdater() {
            super("MyService");
}

    @Override
    protected void onHandleIntent(Intent intent) {

            defaultPrefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
            userPrefs = getSharedPreferences("user", Context.MODE_PRIVATE);


            long updateFrequency = (Long.parseLong(defaultPrefs.getString("updateFrequencyPref", "24")))*1000*60*60; 
            // in hours = 1000*60*60*'24'        

            long thirtySecondsAfterWake = SystemClock.elapsedRealtime() + 30*1000; // 30 seconds after the device boots

            Intent intent1 = new Intent(this, UpdateReceiver.class);
            alarmIntent = PendingIntent.getBroadcast(this, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
            alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
            alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, thirtySecondsAfterWake, updateFrequency, alarmIntent); 

            if(defaultPrefs.getBoolean("updatePref", true))
                    new Update().execute();
    }


    private class Update extends AsyncTask<Void, Void, Integer>{
        private int newNumber;

    @Override
    protected Integer doInBackground(Void... params) {
        newNumber= new HttpServices().getNewNumber(); 
        return newNumber;
    }

    @Override
    protected void onPostExecute(Integer noJobs){
        if(newNumber > 0){ //userPrefs.getInt("oldNumber", 0)){
            editor = userPrefs.edit();
            editor.putInt("oldNumber", newNumber);
            editor.commit();
            if(!FragActivity.active)
                new MyNotif(getApplicationContext(), "NewData");
        }
    }

} // end Async
} // end service class

UpdateReceiver.class

public class UpdateReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent updater = new Intent(context, ServiceUpdater.class);
        context.startService(updater);
    }

}

---------------------

My guess is that when the alarm calls the Receiver, it creates a new Alarm, and then updates the old one, setting the next update time to be 30 seconds in the future; which explains why the HTTP request is happening (and I'm getting notifications every 30 seconds or so.

I want to ensure my alarm runs even when the application is not running.

My aim for this application is to do the following.

  1. Run the App / or / On Phone Startup
  2. Start a service that polls (e.g. Daily) a single Http Request
  3. Regardless of the response, ensure the Polling happens again the following iteration timeframe
  4. A notification will fire from onPostExecute based on the results of the polling

Where do I correctly set the Alarm so that it is not recreated every time the service is - but runs correctly even when the application isn't running?

Thanks for any help in advance.


Solution

  • but this is essentially to ensure the service is running

    I have no idea why you are starting the service twice.

    ServiceUpdater.class

    I have no idea why you are using an AsyncTask inside an IntentService. This is bad on several levels. onHandleIntent() is already called on a background thread, and I would expect your app to flat-out crash when trying to create an AsyncTask on a background thread. Just move all the background logic into onHandleIntent() (or methods called by onHandleIntent()).

    My guess is that when the alarm calls the Receiver, it creates a new Alarm, and then updates the old one, setting the next update time to be 30 seconds in the future; which explains why the HTTP request is happening (and I'm getting notifications every 30 seconds or so.

    I have no idea why you are setting up a repeating alarm on every run.

    Where do I correctly set the Alarm so that it is not recreated every time the service is - but runs correctly even when the application isn't running?

    Your code to set up the AlarmManager schedule needs to be executed:

    • On the first run of your app (since you have not had an opportunity to set up the alarms yet)

    • After the user force-stops your app from Settings (since your previously-scheduled alarms are wiped out)

    • After a reboot (since your previously-scheduled alarms are wiped out)

    A typical pattern for handling the first two is to keep track of when your alarm code last ran, such as in a SharedPreference. Then, either in a custom Application or in a likely entry point of your app (e.g., launcher activity), check the SharedPreference value for your last alarm run time:

    • If there is no value, that means it's the first run of your app, so you schedule the alarms

    • If there is a value, and it is significantly longer than your polling period, you assume that your app was force-stopped, and so you schedule the alarms

    • Otherwise, there is a plausible value, so you assume that your alarms are working just fine

    Handling the reboot scenario is a matter of running through your AlarmManager scheduling logic in response to an ACTION_BOOT_COMPLETED broadcast.

    Note that you are using ELAPSED_REALTIME, which means that the alarm will be delayed if the device is in sleep mode.

    Also note that you are using setInexactRepeating(), which means that the alarm will go off sometime within the polling period, but not at a precise interval.