According to the documentation:
Note: Beginning with API 19 (KITKAT) alarm delivery is inexact...
There are new APIs to support applications which need strict delivery
guarantees; see... setExact(int, long, PendingIntent).
So if I need exact repeating alarm- need to use setExact - sets one time alarm. So I have:
public static void setAlarm(Context context) {
AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarm.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + ALARM_INTERVAL, pendingIntent);
}
public static void cancelAlarm(Context context) {
AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyService.class);
PendingIntent sender = PendingIntent.getService(context, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarm.cancel(sender);
}
The order of calling is:
User enables alarm via Activity, so the activity calls setAlarm(getApplicationContext()).
The alarm causing some service to run in the background-This is the purpose of the alarm: to run this service in the background in exact interval of time.
Inside onDestroy() of the service I call setAlarm(getApplicationContext()) and in such way I have the effect of repeating the alarm in exact time. So in the end I have repeats not every interval but interval + execution time of the service until onDestroy is called.
The user can from some activity to cancel the alarm via calling cancelAlarm(getApplicationContext()). In this way the repeating effect of the alarm canceled: The service will not be executed, and specifically the onDestroy() method with the call to set new alarm will not be executed, so the chain is ended.
UPDATE: Result of adb shell dumpsys alarm when should run is:
RTC_WAKEUP #0: Alarm{... type 0 when ... code.dev}
tag=*walarm*:code.dev/.MyService
operation=PendingIntent{...: PendingIntentRecord{... code.dev startService}}
...:code.dev +17ms running, 3 wakeups:
*walarm*:code.dev/.MyService
And after cancel:
...:code.dev +359ms running, 14 wakeups:
*walarm*:code.dev/.MyService
Problem: The service keeps waking up periodically, which means the alarm not canceled.
Related
I have an Alarm Manager that runs periodically , but I want to have a specific set of time that it will be running. For Example , lets say that we have a periodic Alarm Manager that is registered with a broadcast receiver and an Action is being performed every 30 minutes. The thing is that I want the Alarm Manager to be active for a specific time lets say 3 hours, so the Alarm manager should goes off 3 hours / 30 minutes or 6 times.
Code to start the define the Alarm Manager:
TimerPeriodic = (AlarmManager)getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(),AlarmReceiver.class);
intent.putExtra(Constants.ALARM_ID, Constants.TIMER_PERIODIC_ID);
TimerPeriodicPendingIntent = PendingIntent.getBroadcast(getActivity(), 0, intent, 0);
Fire Alarm Manager:
long start = TimeUnit.MINUTES.toMillis(StartMinutes);
TimerPeriodic.setRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + start, start, TimerPeriodicPendingIntent);
Also the alarm Manager should be active if the Application is killed.
Thank you for any help!
It can be acheived by using Sqlite Db. where you store the Alaram ID,count,and repeation (How many time you want to repeat).
when Alarm is trigger (AlarmReceiver.onReceive()) increment the count check with the condition with repeation. if it exceed just cancel it. Hope It will help :)
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(
context.ALARM_SERVICE);
Intent myIntent = new Intent(context, AlarmReceiver.class);
cancelAlarm(reminder.getId(),myIntent,alarmManager);
}
private void cancelAlarm(int notiId,Intent myIntent,AlarmManager alarmManager) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, notiId, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.cancel(pendingIntent);
}
I have some existing code that spawns a service intent which does a bunch of stuff in the background. This code does work...
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startService(serviceIntent);
My question is: how to change this to use the AlarmManager.setInexactRepeating(...) methods?
I have changed the above code to this:
Intent serviceIntent = new Intent(context, APMService.class);
serviceIntent.putExtra("STARTED_BY", starter);
serviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//Set up recurring alarm that restarts our service if
// it crashes or if it gets killed by the Android OS
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getService(context, 0, serviceIntent, 0);
//am.cancel(pi);
am.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP //wake up the phone if it's asleep
, cal.getTimeInMillis()
, 10000
, pi);
And I have added these permissions to AndroidManifest.xml...
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="com.android.alarm.permission.WAKE_LOCK"/>
My understanding is that this is supposed to start the service immediately and then try to restart it again every 10 seconds. But this code isn't working properly.
Using this new code, the service never starts at all and I cannot see why not. To complicate matters the debugger never seems to attach to the app in time to see what's going on.
Can anyone see what I'm doing wrong?
Put AlarmManager code under onDestroy() function of service to schedule start of service as below:
#Override
public void onDestroy() {
/**
* Flag to restart service if killed.
* This flag specify the time which is ued by
* alarm manager to fire action.
*/
final int TIME_TO_INVOKE = 5 * 1000; // try to re-start service in 5 seconds.
// get alarm manager
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AutoStartServiceReceiver.class);
PendingIntent pendingIntent = PendingIntent
.getBroadcast(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// set repeating alarm.
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() +
TIME_TO_INVOKE, TIME_TO_INVOKE, pendingIntent);
}
And handle starting of your service in AutoStartServiceReceiver as below:
public class AutoStartServiceReceiver extends BroadcastReceiver {
private static final String TAG = AutoStartServiceReceiver.class.getSimpleName();
#Override
public void onReceive(Context context, Intent intent) {
// check broadcast action whether action was
// boot completed or it was alarm action.
if (intent.getAction().equals(AppConstants.ACTION_ALARM_INTENT)) {
context.startActivity(new Intent(context, YourActivity.class));
// handle service restart event
LockerServiceHelper.handleServiceRestart(context);
}
}
}
Kindly note that, your service will not restart if you stop it manually from settings-apps-running apps-your app.
Your service is not starting because of AlarmManager.ELAPSED_REALTIME_WAKEUP, while it should be using AlarmManager.RTC_WAKEUP
If you want to run every 10s keep in mind that above API 21 alarm intervals below 60s are rounded up to 60s.
Also, consider using WakefulIntentService
https://github.com/commonsguy/cwac-wakeful
I have a big problem with Android KitKat and Alarm Manager.
All my apps work with a service that always run in background without Android kill it.
Before Android 4.4 KitKat the solution I found was to start the service through a BroadcastReceiver triggered by an AlarmManager.
...
Intent intent = new Intent(c, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(c, 0, intent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager am = (AlarmManager) c.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT<Build.VERSION_CODES.KITKAT) {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), pendingIntent);
} else {
setAlarmFromKitkat(am, System.currentTimeMillis(), pendingIntent);
}
...
#TargetApi(19)
private static void setAlarmFromKitkat(AlarmManager am, long ms, PendingIntent pi){
am.setExact(AlarmManager.RTC_WAKEUP, ms, pi);
}
...
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, MyService.class);
context.startService(service);
}
}
On Android 4.4 KitKat by this solution I can start service but after some time Android kill it!
Is there a way to have a Service that works in background without Android 4.4 KitKat kill it?
Many Thanks
For Android Kitkat version
If your app uses AlarmManager...
When you set your app's targetSdkVersion to "19" or higher, alarms that you create using either set() or setRepeating() will be inexact.
To improve power efficiency, Android now batches together alarms from all apps that occur at reasonably similar times so the system wakes the device once instead of several times to handle each alarm.
If your alarm is not associated with an exact clock time, but it's still important that your alarm be invoked during a specific time range (such as between 2pm and 4pm), then you can use the new setWindow() method, which accepts an "earliest" time for the alarm and a "window" of time following the earliest time within which the system should invoke the alarm.
If your alarm must be pinned to an exact clock time (such as for a calendar event reminder), then you can use the new setExact() method.
This inexact batching behavior applies only to updated apps. If you've set the targetSdkVersion to "18" or lower, your alarms will continue behave as they have on previous versions when running on Android 4.4.
Original Source:
http://developer.android.com/about/versions/android-4.4.html
in kitkat,use the code snippet below to restart te service automatically:
#Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
Intent restartService = new Intent(getApplicationContext(),
this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(
getApplicationContext(), 1, restartService,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +1000, restartServicePI);
}
I have a function where I set a 15 minute repeating alarm to start my service, which calls a webservice, processes the result and closes. Simplified:
public static void setAlarm(Context cx) {
try{
//My service is running, no need to reset the alarm
if (isServiceRunning())
return;
Intent intent = new Intent(cx, ResultService.class);
PendingIntent sender = PendingIntent.getService(cx, 0, intent, PendingIntent.FLAG_NO_CREATE);
//My pending intent exists, no need to reset the alarm
if (sender!=null)
return;
sender = PendingIntent.getService(cx, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager) cx.getSystemService(cx.ALARM_SERVICE);
//Cancel any previous alarms????
am.cancel(sender);
am.setRepeating(AlarmManager.RTC_WAKEUP, firstRun, interval, sender);
}catch (Exception e){
}
}
This is called by a BroadcastReceiver that listens for the following events
ACTION_SCREEN_ON
ACTION_BOOT_COMPLETED
CONNECTIVITY_ACTION
via
setAlarm(context.getApplicationContext());
It seems to work however I start seeing multiple calls to my webservice per second on random devices.
I have tried getting it to happen whilst debugging with no success.
What am I doing wrong here?
Update
I ran
adb shell dumpsys alarm > dump.txt
to check the alarm lock and I see the number of wakeups/alarms increases every time the alarm manager executes my PendingIntent:
com.x
279ms running, 22 wakeups
22 alarms: flg=0x4 cmp=com.x/.service.ResultService
Does it mean anything?
UPDATE 2
I tracked one of the offending devices.
It calls the web service as it should for most of the day, then suddenly at 19:53 last evening I get 330 extra calls from the device in 6 seconds.
Afterwards it runs fine until 06:50 this morning when I get 282 extra calls and on 06:55 I get another 130 extra calls.
int RQS=1;
//RQS- request id for starting the alram ,get the same request and cancel
// starting the alarm
intent = new Intent(getBaseContext(), Receiver.class);
pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, targetCal.getTimeInMillis(),
pendingIntent);
//cancelling the request
alarmManager.cancel(PendingIntent.getBroadcast(getBaseContext(), RQS, intent,
PendingIntent.FLAG_UPDATE_CURRENT));
I am currently trying to write alarm manager that will make an alarm go off within a specified period of time, daily. First I check to see if the user has had an alarm set for that for that day:
if ((User.getReminderTime(Home.this) > 0)
&& (dt.getDate() != today.getDate() || dt.getDay() != today
.getDay())) {
AppointmentManager.setFutureAppointmentCheck(this
.getApplicationContext());
User.setLongSetting(this, "futureappts", today.getTime());
}
Then I go and set the actual alarm to go off between 12 and 12:10 of the next day:
public static void setFutureAppointmentCheck(Context con) {
AlarmManager am = (AlarmManager) con
.getSystemService(Context.ALARM_SERVICE);
Date futureDate = new Date(new Date().getTime() + 86400000);
Random generator = new Random();
futureDate.setHours(0);
futureDate.setMinutes(generator.nextInt(10));
futureDate.setSeconds(0);
Intent intent = new Intent(con, FutureAppointmentReciever.class);
PendingIntent sender = PendingIntent.getBroadcast(con, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
am.set(AlarmManager.RTC_WAKEUP, futureDate.getTime(), sender);
}
Now I setup a test environment for this to go off every two minutes and it seems to be working fine, however when I deploy to an actual device, the reciever does not seem to be recieving the alarms. I thought it might be an issue with the device being asleep, so I added the power manager. But it still does not work:
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "keepAlive");
wl.acquire();
setFutureAppointments(context.getApplicationContext());
AppointmentManager.setFutureAppointmentCheck(context
.getApplicationContext());
User.setLongSetting(context.getApplicationContext(), "futureappts",
new Date().getTime());
wl.release();
Anyone see anything I am doing blatantly wrong or am I going about this incorrectly? thanks for any and all help.
I usually do something more along the lines of:
Intent i = new Intent(this, MyService.class);
PendingIntent pi = PendingIntent.getService(this, 0, i, 0);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.cancel(pi); // cancel any existing alarms
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + AlarmManager.INTERVAL_DAY,
AlarmManager.INTERVAL_DAY, pi);
This way, you don't have to worry about re-setting the AlarmManager in your Service.
I usually run this bit of code when my app starts (onResume in my main activity) and in a BroadcastReceiver that is set up to receive BOOT_COMPLETED.
I've written a guide on creating Services and using the AlarmManager, which is based on my own experience and a few tips & tricks I picked off from watching a Google I/O talk. If you're interested, you can read it here.
To answer your question below, all I can do is quote the docs:
public void setInexactRepeating (int type, long triggerAtTime, long interval, PendingIntent operation)
Schedule a repeating alarm that has inexact trigger time requirements; for example, an alarm that repeats every hour, but not necessarily at the top of every hour. These alarms are more power-efficient than the strict recurrences supplied by setRepeating(int, long, long, PendingIntent), since the system can adjust alarms' phase to cause them to fire simultaneously, avoiding waking the device from sleep more than necessary.
Your alarm's first trigger will not be before the requested time, but it might not occur for almost a full interval after that time. In addition, while the overall period of the repeating alarm will be as requested, the time between any two successive firings of the alarm may vary. If your application demands very low jitter, use setRepeating(int, long, long, PendingIntent) instead.
In conclusion, it's not very clear. The docs only say that the alarm "may vary". However, it should be important for you to know that the first trigger might not occur for almost a full interval after that time.
This is working, this will shoot alarm after every 5 seconds
private void setRecurringAlarm() {
Logger.d(IConstants.DEBUGTAG, "Setting Recurring Alarm");
Calendar updateTime = Calendar.getInstance();
updateTime.set(Calendar.SECOND, 5);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
PendingIntent recurringDownload = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.cancel(recurringDownload);
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), 1000 * 5, recurringDownload); //will run it after every 5 seconds.
}