I had been tasked with developing an alarm app. Something simple which lets users set an alarm at a specific time and select a music file that should play when the alarm goes off. The app works great on phone like Samsung, Motorola, and Nexus but on Xiomi, Oppo, Vivo phones the alarm does not go off at the correct times and sometimes it does not fire at all. I have used Android's AlarmManager APIs. I am wondering if there is something else I need to do to get the alarm to work as expected on devices like xiomi, oppo, and vivo. Most of the solutions on StackOverflow have not worked out for me. If there are devs here in this subreddit who have worked with AlarmManager APIs before or worked on Apps with Alarm features, id really appreciates some more insight into AlarmManager APIs and why they don't work as expected across all phones and if there are any alternatives I should be using.
Bellow is the method which will help you, Which I tested with the different-different device it's working.
/**
* Method for start Alarm on Defined time
*
* #param context
*/
public static void startAlarm(Context context,int minutes) {
Logger.print("AlarmReceiver startAlarm called");
Intent alarmIntent = new Intent(context, WakefulBrodcastReceiverService.class);
alarmIntent.setAction("testAPP");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 123451, alarmIntent, 0);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
long alarmPeriodicTime = System.currentTimeMillis() + Utils.getTimeInMilliSec(Constant.TimeType.MINUTE, minutes);
if (Build.VERSION.SDK_INT >= 23) {
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
} else if (Build.VERSION.SDK_INT >= 19) {
manager.setExact(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
} else {
manager.set(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
}
}
Related
I have implemented AlaramManager with background service, It works fine on devices below Oreo. But doesn't work on android Oreo and above as Google stopped background services from working on Oreo and Above.
Is there any source code or any kind resource which will help me to set alarms on android Oreo or above devices?
After searching for a while I found out about JobsIntentService, but couldn't found enough information about it. And I don't know if it is the way to go. Basically, I want to show notifications on particular dates and times.
Thanks in Advance.
Please try bellow method it will work for you.
/**
* Method for start Alarm on defined minute
* #param minutes Minute when you want to start after current time
* #param context
*/
public static void startAlarm(Context context, int minutes) {
Logger.print("AlarmReceiver startAlarm called");
Intent alarmIntent = new Intent(context, WakefulBroadcastReceiverService.class);
alarmIntent.setAction("testAPP");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 123451, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
long alarmPeriodicTime = System.currentTimeMillis() + Utils.getTimeInMilliSec(Constant.TimeType.MINUTE, minutes);
if (Build.VERSION.SDK_INT >= 23) {
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
} else if (Build.VERSION.SDK_INT >= 19) {
manager.setExact(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
} else {
manager.set(AlarmManager.RTC_WAKEUP, alarmPeriodicTime, pendingIntent);
}
}
Since the introduction Doze Mode and App StandBy managing alarms have changed. The problem I'm facing is my alarm manager fires correctly on KitKat, Lolipop and Marshmellow devices but above API 23 it does not fire unless the app is in foreground or background. But if the app is killed, the alarms are stopped.
Checked out Google Keep Application on my Android 7, turns out it does the same.
But Google Calendar fires regardless of whether the app is killed or not.
Done some reading and found out setExactAndAllowWhileIdle method on the alarm manager ensures to break the doze mode and trigger your alarm.
But it does not work, is there anything I'm missing here?
Here's my code:
Intent alertIntent = new Intent(this, NotificationPublisher.class);
alertIntent.putExtra("Hello", "Meal Time");
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
int id = (int) System.currentTimeMillis();
if (Build.VERSION.SDK_INT < 23) {
if (Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, delay, PendingIntent.getBroadcast(this, id, alertIntent, PendingIntent.FLAG_UPDATE_CURRENT));
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, delay, PendingIntent.getBroadcast(this, id, alertIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
} else {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, delay, PendingIntent.getBroadcast(this, id, alertIntent, PendingIntent.FLAG_UPDATE_CURRENT));
}
Broadcast Receiver:
public class NotificationPublisher extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("Hello");
Log.d("Called", "Meal Time");
}
Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name=".NotificationPublisher" />
Because of battery optimisation it is not working,i turned off the battery optimisation of particular app, it is working fine in oneplus 3.
You wanna white-list your app from battery optimisation programatically, check this link stackoverflow.com/a/42651399/3752079
Try with adding below line for your alertIntent
myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0,myIntent, 0);
My alarm clock app uses AlarmManager to set alarms and I have a problem with PendingIntents being cancelled when I close the app from recent app list (it happens occasionally on a KitKat device and almost constantly on Marshmallow), even if the alarm was set a couple minutes ago (so device is not asleep).
Here's how I set an alarm:
Calendar alarmCalendar = getCalendarForDay(day);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
(id * 10000 + day.position), getIntent(context, day.position), FLAG_UPDATE_CURRENT);
long intervalMsec = alarmCalendar.getTimeInMillis();
if (android.os.Build.VERSION.SDK_INT >= 19) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, intervalMsec, pendingIntent);
} else alarmManager.set(AlarmManager.RTC_WAKEUP, intervalMsec, pendingIntent);
getIntent():
private Intent getIntent(Context context, int dayPosition) {
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra(MainActivity.PACKAGE_NAME + ".alarmId", id);
intent.putExtra(MainActivity.PACKAGE_NAME + ".dayPosition", dayPosition);
return intent;
}
I've spent quite a while browsing this site and found some explanations here and here (basically says that it's normal and some manufacturers just do that), but none of them have actually helped me to deal with the problem. Still, I'm pretty sure that canbe solved, since there are some apps that work on all devices (such as Timely and other popular alarm clocks).
So does anyone have any thoughts on how those alarms actually deal with AlarmManager? Why their intents are never get cancelled?
I need to plan sheduled task every 10 minutes.
As in Lollipop and higher version setRepeating() is inexact, I use setExact() and (on alarm firing) I set new exact alarm in 10 minutes.
private void setAlarm(long triggerTime, PendingIntent pendingIntent) {
int ALARM_TYPE = AlarmManager.ELAPSED_REALTIME_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(ALARM_TYPE, triggerTime, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, triggerTime, pendingIntent);
}
}
triggerTime is calculated SystemClock.elapsedRealtime() + 600_000;
When alarm fires, firstly I plan new one, only after that I run my sheduled task.
setAlarm();
mySheduledTask;
I do have WAKE_LOCK permission in my manifest.
When I test this on Android 4 - it works perfect (deviation might be 12-15 milliseconds).
But when I run app on Xiaomi Redmi Note 3 Pro (5.1.1) - deviation can be up to 15 seconds!
For example, I see in my log file: first run was at 1467119934477 (of RTC time), second - at 1467120541683. Difference is 607_206 milliseconds, not 600_000, as it was planned!
What am I missing? What is a way to simulate behaviour of system alarm (it's the most close usecase that can describe my tack)?
PS. I use IntentService for PendingIntent = PendingIntent.getService(context, 0, myIntent, 0);
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:
Accept the time delays (but maybe consider using JobScheduler which is more recommended and will save you battery).
Use setExactAndAllowWhileIdle which might cause you battery issues (use this carefully, too many alarms will be bad for your battery).
This method isn't repeating, so you have to declare the next job to be run at the service which the pendingIntent opens.
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);
You can call the method from support.v4:
AlarmManagerCompat.setExact(...);
The internal implementation contains checks by sdk version.
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.
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
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.
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(...);
}
So I am having an issue with AlarmManager. I am trying to run some code every 2 minutes, which works okay while the phone is awake, but not when the device goes to sleep - during sleep, the intervals are perfectly 5 minutes apart.
Since my desired interval is 2 minutes, this is roughly 250% off my target interval, which for my specific application is not acceptable.
I am aware of the changes in API 19, and have followed suggestions to re-schedule the alarm using setExact() within my BroadcastReceiver. Code is below:
The code used to trigger BroadcastReceiver:
Intent intent = new Intent(this, AlarmReceiver.class);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 3000, pendingIntent);
And this code is in my BroadcastReceiver which re-schedules the alarm:
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "AlarmService Triggered.", Toast.LENGTH_SHORT).show();
Log.d(this.getClass().getSimpleName(), "Service triggered");
Intent newIntent = new Intent(context, this.getClass());
mPendingIntent = PendingIntent.getBroadcast(context, 0, newIntent, 0);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 120000, mPendingIntent);
}
Does anyone have suggestions as to how I could fix this? It is very frustrating that AlarmManager is totally ignoring my wish to have an alarm fire at an exact time. Is there any alternative that will allow me to schedule code at 2 minute intervals like I want?
DEVICE: Samsung Galaxy S6, OS 5.1.1
As #Francesc suggested, it ended up being the Samsung device. Tried it on another phone, now it works flawlessly.
Let this be a lesson to you - don't buy Samsung, they do weird stuff, lol.
Replace ELAPSED_REALTIME_WAKEUP with RTC_WAKEUP.
This is how I schedule alarms:
mTargetTime = System.currentTimeMillis() + timeout * 1000L;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, mTargetTime, mPendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, mTargetTime, mPendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, mTargetTime, mPendingIntent);
}