I have read about this AlarmManager, but it's still a bit confusing to me.
So I have a Service in my app, which I want to run all the time (its a medicinal app, so its supposed to notify the user the whole time its being used), but even though its a Service, Android kills it from time to time, so I want to schedule it to be recreated in like 30mins interval, and then scheduled again. How can I do it?
For started services, there are two additional major modes of operation they can decide to run in, depending on the value they return from onStartCommand(): START_STICKY is used for services that are explicitly started and stopped as needed, while START_NOT_STICKY or START_REDELIVER_INTENT are used for services that should only remain running while processing any commands sent to them. See the linked documentation for more detail on the semantics.
http://developer.android.com/reference/android/app/Service.html
Study this link... for implementing HOW TO CREATE ALARM MANAGER
CREATE ALARM MANAGER CLASS
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
// setRepeating() lets you specify a precise custom interval--in this case,
//20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 20, alarmIntent);
ADD THIS TO MANIFEST
<receiver android:name=".SampleBootReceiver"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
Hope this will help you.
Related
I am attempting to create a sample alarm application using AlarmManager. However, the alarm goes off at different times even though I have set a specific time for it to be triggered.
Setting the alarm:
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 18); // trigger at 6 PM
Intent notifyIntent = new Intent(context, CheckupAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast
(context, 100, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);
android.app.AlarmManager alarmManager = (android.app.AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
alarmManager.setInexactRepeating(android.app.AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
android.app.AlarmManager.INTERVAL_DAY, pendingIntent);
}
Manifest:
<receiver
android:name=".notifications.CheckupAlarmReceiver"
android:enabled="true"
android:exported="false" />
To re-iterate, the alarm works fine. It just fires at the wrong time. Is the Calendar instance not taking the time zone in to consideration? What am I doing wrong here?
Your are setting setInexactRepeating() which as its name says, it could not be fired at the exact time you specified. This is an optimization so the Operating System tries to get more than one alarm which should fire at similar times to fire all of them at the same time.
Instead, you can use void setExactAndAllowWhileIdle(int type, long triggerAtMillis, PendingIntent operation). See documentation.
Example:
alarmManager.setExactAndAllowWhileIdle(android.app.AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
As its recommended, you shouldn't use a repeating alarm with exact times. Instead, you can wait until the exact alarm fires and then schedule another alarm for the next day.
For API lower than 23, you can use void setExact(int type, long triggerAtMillis, PendingIntent operation). And for API lower than 19, you can use void setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) with the expected exact firing as always.
I know there are dozens of similar threads on SO about this topic but I just couldn't find one that really solves the problem / or identifies the root cause.
First of all, I'm targetting SDK 22 (Android 5.1) which means I could use the AlarmManager + WakefulBroadcastReceiver + IntentService even if this is not the latest way of doing things.
I'm not interested in the JobScheduler etc solutions, I just want to understand what is happening and why.
The phone I'm testing on has Android 8.0, but it shouldn't matter as I'm targeting Android 5.1.
So the code I'm dealing with sets the alarm for the next day, 06:00.
private fun setupAlarm() {
val calendar = Calendar.getInstance()
calendar.timeInMillis = System.currentTimeMillis()
calendar.add(Calendar.DAY_OF_YEAR, 1)
calendar.set(Calendar.HOUR_OF_DAY, 6)
calendar.set(Calendar.MINUTE, 0)
val alarmIntent = Intent(this, AlarmReceiver::class.java)
val alarmPendingIntent = PendingIntent.getBroadcast(this, 1221, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, alarmPendingIntent)
}
The AlarmReciever only starts a service:
class AlarmReceiver : WakefulBroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
startWakefulService(context, Intent(context, DownloadingIntentService::class.java));
}
}
This Service then tries to download a file, when finished it calls the completeWakefulIntent(intent) method letting know the system that it's done with its job.
I could not figure out when it is working and when it is not. One morning it did what it should have, on the other, it didn't.
I set up a remote LogCat feature to see whether the IntentService is started but so far I can't see any logs from it, so it means that the alarm is not triggered.
If I set up an alarm for the next minute, even repeating one whatever it works like it should. But when I set back the time for tomorrow morning then it's very unreliable.
Thanks for your help.
I've faced this exact issue myself. See what happens is that setRepeating method let's the android system adjust the time when the alarm should get fired. It will most likely try to batch different alarms in order to optimise battery usage. But in regular cases, if the phone isn't dozing... It generally fires the alarm at correct times.
However if the phone has been idle for a time, the phone goes into doze mode and due to this the alarm gets delayed. I have personally observed delays of upto 1 1:30 hours.
If you want it to fire exactly, you'll have to use the setExactAndAllowWhileIdle method or setAlarmClock method. In this case, you will have to handle the scheduling of your next alarm on your own. The methods work well with doze mode and do fire the alarms at exact times.
There are cons to these methods too. The setExactAndAllowWhileIdle method can only be used to schedule alarms Max once per nine minutes or so. The setAlarmClock method will mostly show a notification like a regular alarm to the user and will indicate the details of the alarm ( this behaviour varies with different os versions )
I used this code to trigger a backup every day. It is working for me, Give it a try.
AlarmManager alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, MyReceiver.class);
intent.setAction("CUSTOM_INTENT");
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 06);
calendar.set(Calendar.MINUTE, 00);
// setRepeating() lets you specify a precise custom interval--in this case,
// 1 day
alarmMgr.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis()/1000,
AlarmManager.INTERVAL_DAY, alarmIntent);
Try this:
Calendar now = Calendar.getInstance();
Calendar alarm = Calendar.getInstance();
alarm.set(Calendar.HOUR_OF_DAY, hourOfDay);
alarm.set(Calendar.MINUTE, minute);
long alarmMillis = alarm.getTimeInMillis();
if (alarm.before(now)) alarmMillis+= 86400000L; //It will add 1 day if your time selected before now
//set alarm method of yours\\
settingAlarmManager(requestCode, alarmMillis);
private void settingAlarmManager(String requestCode, Calendar calendar) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(AddTaskActivity.this, AlarmReceiver.class);
PendingIntent broadcast = PendingIntent.getBroadcast(AddTaskActivity.this,
Integer.valueOf(requestCode), notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, broadcast);
}
My Receiver class:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//do your stuff here\\
}
}
Manifest:
<receiver android:name=".utils.AlarmReceiver" />
I have tried above code for setting alarm and do my custom task. Maybe this will help you.
A few things:
The further in time an Alarm is scheduled, the less precise it will be.
Although you are targeting API Level 22, deprecated elements in higher Android version may not fully work, which is the case of the WakefulBroadcastReceiver
You are trying to run a background job in Android 8.0. It's worth exploring the Foreground Service:
It can be started from the background
Correctly notify users that you are indeed doing something while the phone should be idle.
Do not fear running tasks that might take a few seconds to complete.
You might have killed your application. When a user manually kills an app, in most devices all Alarm's and PendingIntent's are killed as well.
A scheduling strategy many developer use is not to set a repeating Alarm, yet have two single Alarms that reschedule each other continuously ( #Kushan mentioned something similar in his answer).
In short:
Have a Scheduler start as soon as possible during the day (even when a user opens your app, it can be fired multiple times). Check if the desired PendingIntents already exist (your background jobs). If they do not, just schedule them. As well, schedule another Alarm around 11.55.
All this midnight scheduler has to do, is to re-schedule the main AlarmManager in 5 minutes, which is then going to schedule the jobs and the midnight alarm for the next days.
This method allows you to:
schedule exact alarms, since repeating ones do not have the exact option.
reduce the time distance of your scheduled alarms, which will then generally be treated with more precision by the OS.
avoid alarms that trigger immediately because scheduled in the past
Also, try to get the most from the API version you are using:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
this.alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
this.alarmManager.setExact(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
} else {
this.alarmManager.set(
AlarmManager.RTC_WAKEUP,
rtcStartingTime.getTimeInMillis(),
pendingIntent
);
}
I'm new in android. I struggle with my application approximately 3 weeks. I need sent and receive packets in normal mode and sleep mode. My app must exchange data a 5 seconds. I tried using alarmmanager but on android 5 it's not works. On android 5 an interval changes it on 60 seconds. Such a solution makes the battery wears out quickly. When I use normal asynctask, not IntentService, then it works only when screen is ON and app is visible. When app is hidden or I click power OFF then exchange data stops working. What is the best solutions?
Even RTC_WAKEUP doesn't help most of the times.
Solution that worked for my app when device in deep sleep mode:
Use WakefulBroadcastReceiver combined with AlarmManager.
Service is started by startWakefulService() and when it is finished, it releases the wake lock by calling completeWakefulIntent(intent). So the device will be allowed to go back to sleep.
I'm not adding any code. Search for examples on how to use WakefulBroadcastReceiver with AlarmManager. Even WakefulBroadcastReceiver doc has some template code.
Also reduce the frequency of alarm so you can avoid draining so much battery.
You can use the AlarmManager class to wake up the device at a particular time, then fire off an operation at whatever interval you'd like. Code from the docs found here:
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
...
alarmMgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Set the alarm to start at 8:30 a.m.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 8);
calendar.set(Calendar.MINUTE, 30);
// setRepeating() lets you specify a precise custom interval--in this case,
// 20 minutes.
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
1000 * 60 * 20, alarmIntent);
Notice the last line of this block. You can use the method setRepeating() to set whatever interval you'd like.
I need the AlarmManager to push a notification at the same time everyday. When I first loaded the app, it worked as I expected. Unfortunately after I rebooted my phone, there's no notification at the time. If I open the app, the notification does show. What I need is to remind the user to use the app when they don't. If they have to enter the app for it to work, it's worthless.
Here is some of my code:
AlarmManager am = getAlarmManager(ctx);
Intent i = new Intent(ctx, UpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(ctx, 0, i, 0);
Calendar cal = Calendar.getInstance();
cal.setTime(new Date(System.currentTimeMillis()));
//If current time is later than 22, the alarm time should set 22 in the next day
if (cal.get(Calendar.HOUR_OF_DAY)>22)
{
cal.set(Calendar.DAY_OF_MONTH, cal.get(Calendar.DAY_OF_MONTH)+1);
}
cal.set(Calendar.HOUR_OF_DAY, 22);
cal.set(Calendar.MINUTE, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP ,cal.getTimeInMillis(), 24 * 60 * 60 * 1000, pendingIntent);
How can I get the app to always send the notifications correctly?
Unfortunately when you restart the device all of the AlarmManager stuff will be cleared.
...will be cleared if it is turned off and rebooted.
https://developer.android.com/reference/android/app/AlarmManager.html
You have to register a BroadcastReceiver for the boot like this:
<receiver android:name="com.your.app.BootCompletedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
From your receiver you have to schedule your notification again (just like you do in your post).
Obviously, I think my code is right. so I moved the app to AVD, and it could work. My test phone is not original Android OS, I guess the ROM has changed the way AlarmManager works.
My app needs to execute a specific task every hour. It does not matter if app is runing, suspended, or even closed.
When app is running or suspended, I can do it by just scheduling an AlarmManager broadcastreceiver. But when the application is closed, I have to call "unregisterReceiver" to not leak an intent, and app will never be wake up (or something) to process the task.
Then, the question is: how to schedule an alarmmanager task that I don't need to unregister, so it will be called even if my application is closed?
Use AlarmManager.setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) for this. Set the type to AlarmManager.RTC_WAKEUP to make sure that the device is woken up if it is sleeping (if that is your requirement).
Something like this:
Intent intent = new Intent("com.foo.android.MY_TIMER");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE);
long now = System.currentTimeMillis();
long interval = 60 * 60 * 1000; // 1 hour
manager.setRepeating(AlarmManager.RTC_WAKEUP, now + interval, interval,
pendingIntent); // Schedule timer for one hour from now and every hour after that
You pass a PendingIntent to this method. You don't need to worry about leaking Intents.
Remember to turn the alarm off by calling AlarmManager.cancel() when you don't need it anymore.
Don't register a Receiver in code for this. Just add an <intent-filter> tag to the manifest entry for your BroadcastReceiver, like this:
<receiver android:name=".MyReceiver">
<intent-filter>
<action
android:name="com.foo.android.MY_TIMER"/>
</intent-filter>
</receiver>
You need to user an Android Component Called Service for this. From the service code you can schedule your Task using the AlarmManager with PendingIntent Class for every hours. As your AlarmManger is declared in the Service Components it doesn't require any GUI and will get execute in background, till you have battery in your device.