I'm developing an Android App which collects data from inertial sensors using a service that I call with AlarmManager each five minutes. This service has a timer to collect data only one minute. So I want to collect one minute of data and sleep four minutes (loop process). The problem is that the AlarmManager with setRepeating() is not exact (one minute delay or more) on API>19. There are a lot of post but not a clear solution... In the MainActivity I have:
public void startservice(View view){
AlarmManager scheduler = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), SensorLogger.class);
PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, 0);
scheduler.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60000 * 5, scheduledIntent);
}
And in the service something like:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
timer.scheduleAtFixedRate(new stoptask(), 60000, 60000);
...
return START_NOT_STICKY;
}
private class stoptask extends TimerTask
{
public void run()
{
...
timer.cancel();
stopSelf();
}
}
Thanks!
You can't use the repeating api exactly anymore. You need to set a single exact alarm, then set a new alarm inside that alarm for the repeat.
I posted a library that takes care of multiple timer implementations on android- take a look at http://gabesechansoftware.com/timers-in-android/ it might help.
Related
here is my code in application class inside oncreate method: But I can't see any message from my app. Can anyone help me to do this?
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 0, alarmIntent, 0);
public void startAlarm() {
manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
int interval = 5000;
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
Toast.makeText(this, "Alarm Set", Toast.LENGTH_SHORT).show();
}
And on the broadcast receiver class I have the following code
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// For our recurring task, we'll just display a message
Toast.makeText(arg0, "I'm running", Toast.LENGTH_SHORT).show();
}
}
Edited answer:
Use setInexactRepeating() instead of setRepeating(). setRepeating only takes set intervals with the shortest being INTERVAL_FIFTEEN_MINUTES. setInexactRepeating() is the only way to set a repeating interval as short as 1000ms, or 5000ms in your case.
Change:
manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
to
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
If you are not getting the exact 5 second delay that you need, you will need to use a Handler. Any type of alarm with a 5 second delay will not work properly because as of Android 5.x basically all repeating alarms are inexact to save battery life.
I have modified your code to use a Handler:
startAlarm();
public void startAlarm() {
final Handler h = new Handler();
final int delay = 5000; //milliseconds
h.postDelayed(new Runnable(){
public void run(){
//do something
Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
sendBroadcast(alarmIntent);
h.postDelayed(this, delay);
}
}, delay);
}
That alarm method will work with your current BroadcastReceiver and do an actual 5 second delay.
I am trying to create a SMS-app which sends out SMS at one set time of day. For this I have created two services SendMessagePeriodicService and SendMessageService.
This is my SendMesagePeriodicService, which is supposed to trigger SendMessageService at a set time of day. The problem is that it triggers every time the onStartCommand-method is called...
public class SendMessagePeriodicService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) { //FIXME
Calendar cal = Calendar.getInstance();
Intent i = new Intent(this, SendMessageService.class);
PendingIntent pintent = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
cal.set(Calendar.MINUTE, PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("minute", -1));
cal.set(Calendar.HOUR_OF_DAY, PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("hour", -1));
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pintent);
return Service.START_NOT_STICKY;
}
}
I have also checked that the values in the sharedPreferences are correct...
Does anyone have any idea why this is happening?
I would do that by setting the alarmmanager in the activity and than use BroadcastReceiver to send the sms when the alarm fires.
I use the same technique to start a notification on a specific time.
I think I have read somewhere the you use services nowadays only for fileoperations. But I´m not 100%.
It seems that the reason this happens was that the time I set for trigger time was in the past. This means that AlarmManager triggers immediately and not the next time this set time occurs.
I have an alarm that I am wanting to repeat around every 5 minutes. For testing purposes, I have it set to repeat once every 5 seconds instead, as such:
AlarmManager alarmManager = (AlarmManager)getActivity().getSystemService(getActivity().ALARM_SERVICE);
Intent intent = new Intent(getActivity(), CoordinateAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getService(getActivity(), 1, intent, 0);
int repeatSeconds = 5;
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
repeatSeconds * 1000, pendingIntent);
And the receiving IntentService prints a log statement when it receives the alarm. However, it fires around once every minute and a half instead of once every 5 seconds, where is it set incorrectly? I have also tried using setRepeating() instead of setInexactRepeating() but I get the same results.
Here is my alarm receiver:
public class CoordinateAlarmReceiver extends IntentService {
public CoordinateAlarmReceiver(){
super("CoordinateAlarmReceiver");
}
/*
Alarm received, get new contacts which then shows notification
*/
#Override
protected void onHandleIntent(Intent intent) {
MyLog.i("coordinate alarm received");
//new GetNewContactsTask(this).execute();
}
}
I assume you are on api 19 or above. The alarmmanager documentations says:
Note: 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.
You tried using setRepeating() but on api 19 and above this calls setInexactRepeating(). On 19 and above you
setInexactRepeating(): 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.
This explains your weird result.
If you want to set is at a excat time, you should use setExact. Unfortunalety there is no setExactRepating so you have to create this yourself. Schedule a new alarm after one executes or something like that.
Note in the alarmmanager documentation:
Note: The Alarm Manager is intended for cases where you want to have your application code run at a specific time, even if your application is not currently running. For normal timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler.
Maybe you should take a look at this.
I had a similar problem in which I needed a Service fired every 15 seconds... I did the following.
I have a class that extends Application called MyApplication. This class holds an instance of an alarm manager.
public class MyApplication extends Application {
private MyAlarmManager alarmMgr;
#Override
public void onCreate() {
Log.d(TAG, "MyApplication onCreate");
super.onCreate();
Log.d(TAG, "initing alarmMgr ...");
alarmMgr = new MyAlarmManager(this);
}
public MyAlarmManager getAlarmManager(){
return alarmMgr;
}
}
An AlarmManager called MyAlarmManager creates, starts & stops the alarms AND NOW sets the next alarm for this one service.
public class MyAlarmManager {
private MyApplication mApp;
private Intent intent;
private PendingIntent pendingIntent;
private AlarmManager alarmMgr;
private static final long FREQUENCY = 15000;
public MyAlarmManager(Context context) {
mApp = (MyApplication) context;
// Android alarm service
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// service to be fired every 15 seconds
intent = new Intent(context, MyService.class);
pendingIntent = PendingIntent.getService(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
setNextAlarm();
}
public void setNextAlarm(){
Log.d(TAG, "setting next alarm...");
alarmMgr.set(AlarmManager.RTC_WAKEUP, (System.currentTimeMillis() + FREQUENCY), pendingIntent);
}
private void stopAlarms(){
Log.d(TAG, "stopping Alarms");
alarmMgr.cancel(pendingIntent);
}
}
When the Service is fired I get an instance of MyApplication, get the AlarmManager and schedule the next alarm.
public class MyService extends Service {
MyApplication mApp;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "in onStartCommand");
// init the app reference if needed
if (mApp == null) {
Log.d(TAG, "app was null. Creating now...");
mApp = (MyApplication) getApplicationContext();
}
// schedule the next zone check
mApp.getAlarmMgr().setNextAlarm();
// custom functionality ...
}
I want to start a service every day from 8am to 6pm. I am using 3 alarms.
For starting alarm2 everyday at 8am.
For starting service every 1 min.
To stop alarm2.
Is it the proper way? Also I am unable to start alarm2 from alarm1.
This is code for alarm 1:
public class AlarmReceiver1 extends BroadcastReceiver {
int interval;
#Override
public void onReceive(Context context, Intent intent) {
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent in=new Intent(this,AlarmReceiver2);
PendingIntent startingIntent = PendingIntent.getBroadcast(context, 0,in, 0);
long duration = interval * 60 * 1000;
manager.setRepeating(AlarmManager.RTC_WAKEUP,timeOn.getTimeInMillis(), 86400000,startingIntent);
}
}
Far from enough code to tell if it has been set up properly. There is a good example here on how you should set up the AlarmManager. I'd recommend looking it through if there is something you've missed.
Also as you can see in the example you gotta reset the Alarms every time the phone is rebooted. If you don't the services will not run.
I have this Service that do other stuff in the background and must always be running. On its onCreate i've set an Alarm that sends an email once a day. The server is not running in foreground, so sometimes its get killed by the system, and then created again, and when that happens its creates the alarm again, and sends another email every time it happens. So my question is, is there a way to prevent it from triggering the alarm if its already set for repeating in the system? Or is there another "place" in the Service where i can create the alarm once and prevent it for being recreated with the it?
public class ObserverService extends Service {
private String BABAS = "babas";
#Override
public void onCreate() {
setRecurringAlarm(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private void setRecurringAlarm(Context context) {
Log.i(BABAS, "entrou no set alarme");
Calendar updateTime = Calendar.getInstance();
updateTime.setTimeZone(TimeZone.getDefault());//getTimeZone("GMT"));
updateTime.set(Calendar.HOUR_OF_DAY, 00);
updateTime.set(Calendar.MINUTE, 00);
Intent downloader = new Intent("ALARME");//context, AlarmReceiver.class);
PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
0, downloader, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, recurringDownload);
}
}
If that code is everything your Service does you can completely get rid of the Service.
The only thing the service does is schedule a Broadcast once a day on the alarm manager, why not do this schedule directly from the Activity?
edit:
Given your comment, my original answer remains:
The Alarm set on the AlarmManager is permanent until the device is turned off or rebooted.
So you can set the first alarm via Activity and mark it as active on the SharedPreferences. Create a BroadcastReceiver to receive onBoot events, this receiver will check the SharedPreferences for if the alarm is active and in case it is, re-register it on the AlarmManager.
Your service still does the same ContentObserver stuff, but have no association with the recurring AlarmManager event.
I reckon that would be cleanest solution.
Alternatively (but I don't think it's a good/clean solution), you can use the AlarmManager.cancel() to remove the existing PendinIntent, and then re-trigger it
http://developer.android.com/reference/android/app/AlarmManager.html#cancel(android.app.PendingIntent)