Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AlarmManager repeating alarm missing randomly when phone is not in use

I am calling a background Service at interval of 30 min to read the latitude/longitude of current location and sending it to server by POST API .

I am using setRepeating() method of AlarmManager class to schedule alarm every 30 minutes. But some times the alarm is getting missed and service is not called. To monitor if alarm gets called or not in every 30 min I have generated Log.txt file in the sdcard. For every time alarm called the entry for the current time will be written in the Log.txt file. But after comparing 4 to 5 devices Log.txt files I noticed that for some of devices alarm is not calling onCreate() method of UserTrackingReceiver.java (Background service) . Full code chunks mentioned below.

When app start registerUserTrackingReceiver() method has been call which is below:

public static void registerUserTrackingReceiver(Context context) {
        try {
            Intent intent = new Intent(context, UserTrackingReceiver.class);

            boolean alarmUp = (PendingIntent.getService(context, 1001, intent, PendingIntent.FLAG_NO_CREATE) == null);

            if (alarmUp) {
                Calendar calendar = Calendar.getInstance();

                if (calendar.get(Calendar.MINUTE) > 0 && calendar.get(Calendar.MINUTE) <= 30) {
                    calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
                    calendar.set(Calendar.MINUTE, 30);
                    calendar.set(Calendar.SECOND, 0);
                } else if (calendar.get(Calendar.MINUTE) > 30) {
                    if (calendar.get(Calendar.HOUR_OF_DAY) == 23) {
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                    } else {
                        calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1);
                    }
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                } else {
                    calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                }

                PendingIntent sender = PendingIntent.getService(context, 1001, intent, 0);
                AlarmManager alarmManager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);
                alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                        AlarmManager.INTERVAL_HALF_HOUR, sender);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
}

UserTrackingReceiver.java is below:

public class UserTrackingReceiver extends Service
        implements LocationListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    @Override
    public void onCreate() {
        super.onCreate();

        Calendar calendar = Calendar.getInstance();
        Util.appendLog("Tracking Alarm Called on: " + calendar.get(Calendar.HOUR_OF_DAY) + " : " + calendar.get(Calendar.MINUTE) + " : " + calendar.get(Calendar.SECOND));
        stopSelf();
    }
}

In Util.java there is appendLog() method which is below:

public static void appendLog(String text) {

        String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();

        File logFile = new File(baseDir + "/" + Constant.AppNameSuper + "/log.txt");
        if (!logFile.exists()) {
            try {
                logFile.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            //BufferedWriter for performance, true to set append to file flag
            BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
            buf.append(text);
            buf.newLine();
            buf.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

If alarm called every 30 min as per above code it should be written in Log.txt file in SDCARD. But the issue is it cannot write log file every 30 min that means alarm is missing. As per taking reading for two days I have noticed that alarm is not missing in day time as user continuously use their phone but it is missed in night when phone is not in use.

Out put log files with different devices are below:

Device A Log.txt

  • Tracking Alarm Called on: 0 : 0 : 31(Start From 12:00 Night)
  • Tracking Alarm Called on: 1 : 10 : 27
  • Tracking Alarm Called on: 3 : 5 : 25
  • Tracking Alarm Called on: 6 : 55 : 31
  • Tracking Alarm Called on: 7 : 0 : 6
  • Tracking Alarm Called on: 7 : 30 : 0
  • Tracking Alarm Called on: 8 : 0 : 6
  • Tracking Alarm Called on: 8 : 30 : 0
  • Tracking Alarm Called on: 9 : 0 : 6
  • Tracking Alarm Called on: 9 : 30 : 0
  • Tracking Alarm Called on: 10 : 0 : 0

Device B Log.txt

  • Tracking Alarm Called on: 0 : 0 : 27(Start From 12:00 Night)
  • Tracking Alarm Called on: 0 : 30 : 1
  • Tracking Alarm Called on: 1 : 0 : 1
  • Tracking Alarm Called on: 1 : 30 : 2
  • Tracking Alarm Called on: 2 : 0 : 1
  • Tracking Alarm Called on: 2 : 30 : 1
  • Tracking Alarm Called on: 3 : 0 : 1
  • Tracking Alarm Called on: 3 : 30 : 1
  • Tracking Alarm Called on: 4 : 0 : 1
  • Tracking Alarm Called on: 4 : 30 : 29
  • Tracking Alarm Called on: 5 : 0 : 1
  • Tracking Alarm Called on: 5 : 30 : 2
  • Tracking Alarm Called on: 6 : 0 : 30
  • Tracking Alarm Called on: 6 : 30 : 1
  • Tracking Alarm Called on: 7 : 0 : 1
  • Tracking Alarm Called on: 7 : 30 : 1
  • Tracking Alarm Called on: 8 : 0 : 1
  • Tracking Alarm Called on: 8 : 30 : 1
  • Tracking Alarm Called on: 9 : 0 : 32
  • Tracking Alarm Called on: 9 : 30 : 1

Device C Log.txt

  • Tracking Alarm Called on: 0 : 0 : 7(Start From 12:00 Night)
  • Tracking Alarm Called on: 0 : 30 : 3
  • Tracking Alarm Called on: 1 : 0 : 6
  • Tracking Alarm Called on: 1 : 30 : 1
  • Tracking Alarm Called on: 2 : 0 : 32
  • Tracking Alarm Called on: 2 : 30 : 3
  • Tracking Alarm Called on: 3 : 1 : 50
  • Tracking Alarm Called on: 3 : 30 : 5
  • Tracking Alarm Called on: 4 : 1 : 58
  • Tracking Alarm Called on: 4 : 31 : 14
  • Tracking Alarm Called on: 5 : 0 : 1
  • Tracking Alarm Called on: 5 : 30 : 1
  • Tracking Alarm Called on: 6 : 2 : 1
  • Tracking Alarm Called on: 6 : 30 : 1
  • Tracking Alarm Called on: 7 : 0 : 1
  • Tracking Alarm Called on: 7 : 30 : 1
  • Tracking Alarm Called on: 8 : 0 : 1
  • Tracking Alarm Called on: 8 : 30 : 4
  • Tracking Alarm Called on: 9 : 1 : 44
  • Tracking Alarm Called on: 9 : 30 : 1

Device D Log.txt

  • Tracking Alarm Called on: 0 : 1 : 25(Start From 12:00 Night)
  • Tracking Alarm Called on: 0 : 30 : 0
  • Tracking Alarm Called on: 1 : 31 : 41
  • Tracking Alarm Called on: 2 : 39 : 52
  • Tracking Alarm Called on: 3 : 0 : 25
  • Tracking Alarm Called on: 3 : 30 : 58
  • Tracking Alarm Called on: 4 : 0 : 25
  • Tracking Alarm Called on: 4 : 30 : 56
  • Tracking Alarm Called on: 5 : 30 : 51
  • Tracking Alarm Called on: 7 : 18 : 55
  • Tracking Alarm Called on: 7 : 30 : 0
  • Tracking Alarm Called on: 8 : 0 : 25
  • Tracking Alarm Called on: 8 : 30 : 43
  • Tracking Alarm Called on: 9 : 0 : 3
  • Tracking Alarm Called on: 9 : 30 : 25
  • Tracking Alarm Called on: 10 : 0 : 25
  • Tracking Alarm Called on: 10 : 30 : 4
  • Tracking Alarm Called on: 11 : 1 : 52
  • Tracking Alarm Called on: 11 : 30 : 27
  • Tracking Alarm Called on: 12 : 1 : 6⁠⁠⁠⁠
like image 836
Himanshu Avatar asked Jun 21 '16 09:06

Himanshu


People also ask

How do I stop my Android phone from repeating an alarm?

Open your phone's Clock app . At the bottom, tap Alarm. On the alarm you want, tap the Down arrow . Cancel: To cancel an alarm scheduled to go off in the next 2 hours, tap Dismiss.

How do I set repeating alarm on Android?

At the top of the main panel you should see an option to Add alarm. Tap this and you'll be presented with a time in the top half of the screen, with various settings in the lower half. Scroll the hours up or down until you reach the one you want, then repeat the process with the minutes.


1 Answers

The problem might be your PendingIntent calling a Service. The device can go back to sleep before your Service finishes (or even starts) execution.

I'd suggest you to use a BroadcastReceiver instead (since a WakeLock is guaranteed during onReceive()).

Acquire a WakeLock in onReceive(), start your Service from there and release the WakeLock from the Service, when appropriate.

To simplify this process you can use the WakefulBroadcastReceiver helper class:

  1. Call PendingIntent.getBroadcast() instead of PendingIntent.getService().
  2. Start an IntentService from onReceive() by calling WakefulBroadcastReceiver.startWakefulService().
  3. Do your stuff in onHandleIntent() and call WakefulBroadcastReceiver.completeWakefulIntent() when finished.

For example, a BroadcastReceiver that starts a wakeful Service:

public class ExampleReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent wakefulServiceIntent = new Intent(context,
            ExampleWakefulService.class);

        WakefulBroadcastReceiver.startWakefulService(context,
            wakefulServiceIntent);
    }
}

And the Service:

public class ExampleWakefulService extends IntentService {

    private static final String NAME = "com.example.ExampleWakefulService";

    public ExampleWakefulService() {
        super(NAME);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        // doing stuff

        WakefulBroadcastReceiver.completeWakefulIntent(intent);
    }
}

Also, check out this article from the developer's guide on keeping the device awake.

On API level 23+ you have to deal with Doze.

From the documentation:

To help with scheduling alarms, Android 6.0 (API level 23) introduces two new AlarmManager methods: setAndAllowWhileIdle() and setExactAndAllowWhileIdle(). With these methods, you can set alarms that will fire even if the device is in Doze.

Unfortunately there is no alternative for setRepeating(), so you have two choices:

  • Set exact alarms (using the appropriate method depending on the API level of the device, check out this answer for an example) and reschedule them every time they fire.
  • Whitelist your app (not recommended because of the strict revision policy of Google).
like image 162
earthw0rmjim Avatar answered Nov 03 '22 01:11

earthw0rmjim