I need to plan sheduled task every 10 minutes.
As in Lollipop and higher version setRepeating()
is inexact, I use setExact()
and (on alarm
You can call the method from support.v4:
AlarmManagerCompat.setExact(...);
The internal implementation contains checks by sdk version.
To answer the question on the system alarm...
Android's stock Alarm Clock/Desk Clock app uses a combination of setAlarmClock and setExactAndAllowWhileIdle.
The following code is used to update notifications:
final PendingIntent operation = PendingIntent.getBroadcast(context, 0,
AlarmStateManager.createIndicatorIntent(context), flags);
final AlarmClockInfo info = new AlarmClockInfo(alarmTime, viewIntent);
alarmManager.setAlarmClock(info, operation);
While at the same time the following code is used to schedule the actual alarm:
if (Utils.isMOrLater()) {
// Ensure the alarm fires even if the device is dozing.
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent)
}
The Pending intent set in setExactAndAllowWhileIdle triggers the alarm while setAlarmClock's intent is then simply ignored.
Android Googlesource
You can try use AlarmManager.setAlarmClock maybe it can help you.
Another thing you need to check which type of BroadcastReceiver you are using, it will be better to use WakefulBroadcastReceiver
Btw you need to change logic for work with Alarm Manager for support Android M, you can you something like this:
if(Build.VERSION.SDK_INT < 23){
if(Build.VERSION.SDK_INT >= 19) {
setExact(...);
} else {
set(...);
}
} else {
setExactAndAllowWhileIdle(...);
}
Probably a possible workaround could be something like this: you schedule the Alarm about 1 minute before the expected time, than you use a Handler.postDelayed to cover the remaining time.
Here you can find an example of this kind of implementation. The activity just set-up the first alarm:
public class MainActivity extends AppCompatActivity {
private static int WAIT_TIME = 60*1000; //1 minute
public static int DELAY_TIME = 10*60*1000; // delay between iterations: 10min
public static String UPDATE_TIME_KEY = "update_time_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAlarm(this,(new Date().getTime())+DELAY_TIME);
}
public static void setAlarm(Context context, long delay) {
long fireDelay = delay-WAIT_TIME;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putLong(UPDATE_TIME_KEY,delay).apply();
Intent startIntent = new Intent(context, UpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, startIntent,PendingIntent.FLAG_UPDATE_CURRENT );
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(ALARM_TYPE, fireDelay, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, fireDelay, pendingIntent);
}
}
}
than the receiver continues the loop:
public class UpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
Log.e("RECEIVED","RECEIVED");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
long fireTime = sharedPreferences.getLong(MainActivity.UPDATE_TIME_KEY, (new Date()).getTime());
long fireDelay =(fireTime-(new Date().getTime())>0)?fireTime-(new Date().getTime()):0;
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
Log.e("RECEIVED","PERFORMED");
MainActivity.setAlarm(context,(new Date()).getTime()+MainActivity.DELAY_TIME);
}
},fireDelay);
}
}
I hope it helped.
From android documentation of AlarmManager
Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.
Also while using setExact() :
The alarm will be delivered as nearly as possible to the requested trigger time.
So its still not guaranteed that setExact will be Exact.
The OS chooses how the alarms will work, with consideration of the time you've specified. Because of that, when the phone gets into a 'semi-sleep' mode, it won't necessary use the resource at the time you wish it to. Basically, it waits for 'windows' that the OS opens for it, and only then the alarm you want to run will run, that's why you're experiencing time gaps.
This was introduced on Marshmallow OS and will continue on Nougat OS as well, as part of Google trying to improve the device's battery.
Here's the thing, you have 2 options:
If you choose option 2, here's the start:
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
am.setExactAndAllowWhileIdle(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
am.setExact(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else
am.set(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);