AlarmManager is unstable - android

I used AlarmManager to set time and expect to do some work in the future. Almost case it work well without problem. But sometimes (just sometimes), the alarm not fired. It is difficult to reproduce the issue and I still do not know the reason.
I got this issue on several OS version : 4.4, 5.1, 6.0, 6.1, 7.0.
I already used WakefulBroadcastReceiver to start a service with Wakelock, but the issue still happen.
Below is my code to schedule a alarm.
Intent alarmIntent = new Intent(context, AlarmReceiver.class);
alarmIntent.putExtra("todo_id", myTodo.getId());
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, myTodo.getId(), alarmIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(calendar.getTimeInMillis(), pendingIntent);
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
LogUtil.debug("onReceive()");
if (null != intent.getExtras()) {
int id = intent.getIntExtra("todo_id", -1);
if (id != -1) {
Intent myIntent = new Intent(context, AlarmService.class);
myIntent.putExtra("todo_id", id);
LogUtil.debug("Receiver receive todo id: "+id );
startWakefulService(context, myIntent);
}
}
}
}
public class AlarmService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
LogUtil.debug("Service onStartCommand");
int id = intent.getIntExtra("todo_id", -1);
LogUtil.debug("Service receive todo id: " + id);
// do some stuff here
AlarmReceiver.completeWakefulIntent(intent);
return START_REDELIVER_INTENT;
}
Does anyone have the same issue like me ? And what is your solution ?
Maybe this issue come from Android SDK, they made AlarmManager work unstable.

I had the same issue , i solved this issue by doing all my logic in AlarmReceiver. I was also starting service in receiver which hold my alarm logic. But once i move my code into alarm receiver it works fine. Mine issue was i wasn't ending my service , it kept on running and causing some issue or might be other reasons but it solved my issue. You should try and let me know if this help.

Related

Oreo AlarmManager not working in Background

I want to start a Notification Service with an AlarmManager. However, it doesn't alyways work, when swiped the app in the recent tasks layer. I have also tried out JobIntentService and JobService with the propriate entries in the Manifest, everything resulted in the same problem: The Notification or also startActivity() don't get called (sometimes!, maybe when in doze mode?) after I closed the app.
Now, I thought to maybe go back to the AlarmManager, as it seems to work for other people here.
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
{
AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), AlertReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1, intent, 0);
if(Build.VERSION.SDK_INT <= 23)
{
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeForAlarm, pendingIntent);//Alarm with setExact and AlertReceiver should be better than the old Alarm Manager
}
else
{
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeForAlarm, pendingIntent);//Alarm with setExact and AlertReceiver should be better than the old Alarm Manager
}
}
else
{
final AlarmManager alarm = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), Service_Notification.class);
PendingIntent startPendingIntent = PendingIntent.getService(getActivity(), 0, intent, 0);
alarm.set(AlarmManager.RTC_WAKEUP, timeForAlarm, startPendingIntent);
}
This is my AlertReceiver for Versions above KitKat (I also tried extends BroadCastReceiver)
public class AlertReceiver extends WakefulBroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
context.startService(new Intent(context, Service_Notification.class));
}
}
So, what can I do now? Thank you in advance
I came across the same issue. It seems the policy for background task has been changed a lot since Oreo. You could check the WorkManager in Jetpack. It simplifies a lot the use cases across different OS version.
OneTimeWorkRequest.Builder ob = new OneTimeWorkRequest.Builder(YourWorker.class)
.setConstraints(constraints)
.setInitialDelay(delayed, TimeUnit.MILLISECONDS)
.addTag(yourWorkerTag);
WorkManager.getInstance().beginUniqueWork(orderTag, ExistingWorkPolicy.REPLACE, ob.build()).enqueue();
And then start your Notification Service in YourWorker. It's done!

Service is not triggered or set by AlarmManager. Same codes work differently on different devices and different projects

I'm sorry for asking a common question,
but I did a lot of study and still couldn't find out the solution.
I also did a android test that I set my service to be triggered soon, but the codes below work totally differently on my two phones.
One of them with 7.0 android version never works, even the service was never triggered.
Another one with 5.0 version successfully worked if I set the triggered time to System.currentTimeMillis(), but it didn't work with any times after the current time (such as System.currentTimeMillis() + 2000). As well as if the notification is sent successfully, then followed by it disappears in a very short time strangely.
Here's my android test method.
#Test
public void setAlarm(){
Context context = InstrumentationRegistry.getTargetContext();
Intent intent = new Intent(context, MedicationNotificationService.class);
PendingIntent sender = PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() , sender);
}
Then here comes my detailed codes of service.
MedicationNotification is just a POJO which contains the triggered time.
The service sends the notification and set the next alarm.
public class MedicationNotificationService extends Service {
private static final String TAG = "MedicationNfService";
public MedicationNotificationService() {}
#Override
public IBinder onBind(Intent intent) {return null;}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
MedicationNotification notification = (MedicationNotification) intent.getSerializableExtra(NOTIFICATION);
Log.d(TAG, "Service started. notification: " + notification);
NotificationUtils.showNotification(this, getString(R.string.itsTimeToMedicate),
notification.getMedication().getName(), notification.getId());
if (notification.getMedication().getEndDate().after(new Date())) //set next notification if not expired
{
AlarmManagerUtils.arrangeMedicationNotification(this, notification);
Log.d(TAG, "Medication does not expire, set the next alarm.");
}
else
Log.d(TAG, "Medication expired, delete it.");
return START_STICKY;
}
}
I've also made sure that I did not misspell the services' name.
<service
android:name=".android.MedicationNotificationService">
</service>
Finally, here's the AlarmManager codes. It's as same as I did in the test method.
The id of PendingIndent's depends on MedicationNotification's id.
And set the exact time.
public class AlarmManagerUtils {
public static void arrangeMedicationNotification(Context context, MedicationNotification notification){
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null)
{
Intent alarmIntent = new Intent(context, MedicationNotificationService.class);
alarmIntent.putExtra(NOTIFICATION, notification);
PendingIntent pendingIntent = PendingIntent.getService(context, 50000 + notification.getId(),
alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, notification.getNotifyTime().getTimeInMillis(), pendingIntent);
}
}
}
Thanks for your patience, tell me if you need more information, I really spent hours solving this. It's really strange, it used to work on my another project. Thanks.
For setting alarm use below code
if (SDK_INT < Build.VERSION_CODES.KITKAT) {
alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent);
}
else if (Build.VERSION_CODES.KITKAT <= SDK_INT && SDK_INT < Build.VERSION_CODES.M) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent);
}
else if (SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pendingIntent);
}
Add below line after notification notify method
PowerManager pm = (PowerManager) context
.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn ;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
isScreenOn = pm.isInteractive();
}else {
isScreenOn=pm.isScreenOn();
}
if (!isScreenOn) {
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
| PowerManager.ACQUIRE_CAUSES_WAKEUP
| PowerManager.ON_AFTER_RELEASE, "MyLock");
wl.acquire(10000);
PowerManager.WakeLock wl_cpu = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"MyCpuLock");
wl_cpu.acquire(10000);
}
and also add this permission on manifest
<uses-permission android:name="android.permission.WAKE_LOCK" />

Modify static BlockingQueue from PendingIntent triggered by AlarmManager

In my android application I have a "global" application class and inside it, among others, I have a static BlockingQueue in which I store information. When the queue is full I flush it to file. I have multiple producers, Services, BroadcastReceivers and everything works fine. Now I have to add an alarm that every 30 minutes triggers a PendingIntent from which I have to write to this BlockingQueue and this doesn't work! I see that the appropriate functions are called but the data is not written. If I Log.d() it before blockingQueue.put() I can see the data and then it get lost. I do the same procedure from everywhere in my app and it only does not work from the BroadcastReceiver of the PendingIntent of the Alarm. For sure I'm missing something. What can I do?
Here's how I trigger the Alarm from a Service (and it works):
alarmNotificationIntent = PendingIntent.getBroadcast(this, 0, new Intent(this, QuestionnaireNotificationReceiver.class), 0);
if(alarmNotificationIntent!=null) {
if (alarmManager != null) {
if (iLogApplication.isAtLeastMarshmallow()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
}
}
}
And this is the BroadcastReceiver:
public class QuestionnaireNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(context.toString(), "Questionnaire notification");
//this method does blockingQueue.put(answer.toString());
iLogApplication.persistInMemoryAnswerQuestionnaireEvent(new Answer(new Question(666)));
System.out.println(iLogApplication.questionnaire.toString());
SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PACKAGE_NAME, Context.MODE_PRIVATE);
Intent notificationIntent = new Intent(context, NotificationActivity.class);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), notificationIntent, 0);
iLogApplication.questionnaireBuilder.setContentTitle("Nuova domanda disponibile")
.setContentIntent(pendingIntent)
.setSmallIcon(R.drawable.ic_notification_bar)
.setAutoCancel(false)
.setOngoing(true);
String notificationText = "Hai %d domand%c a cui rispondere";
if (iLogApplication.questionnaireBuilder != null) {
iLogApplication.questionnaireBuilder.setWhen(System.currentTimeMillis());
if(iLogApplication.questionnaire.getNumberOfQuestions()>2) {
iLogApplication.questionnaireBuilder.setContentText(String.format(notificationText, iLogApplication.questionnaire.getNumberOfQuestions(), 'e'));
}
else {
iLogApplication.questionnaireBuilder.setContentText(String.format(notificationText, iLogApplication.questionnaire.getNumberOfQuestions(), 'a'));
}
}
if (iLogApplication.notificationManager != null) {
iLogApplication.notificationManager.notify(Constants.QUESTIONNAIRENOTIFICATIONID, iLogApplication.questionnaireBuilder.build());
}
PendingIntent alarmNotificationIntent = PendingIntent.getBroadcast(context, 0, new Intent(context, QuestionnaireNotificationReceiver.class), 0);
if(alarmNotificationIntent!=null) {
if(iLogApplication.alarmManager!=null) {
if (iLogApplication.isAtLeastMarshmallow()) {
iLogApplication.alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+ Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
} else {
iLogApplication.alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+ Constants.NOTIFICATION_INTERVAL, alarmNotificationIntent);
}
}
}
}
}
Since I want my alarm to work also in Marshmallow Doze mode I have to use setExactAndAllowWhileIdle() and recall the method every time the Broadcast is triggered.
The problem was the PendingIntent. In the manifest I declared it like this:
<receiver android:process=":remote" android:name=".broadcastreceivers.QuestionnaireNotificationRunnable"></receiver>
the flag android:process=":remote" forces it to run on a different process. By removing it everything started working perfectly.

Alarm manager for background services

I have implemented the alarm manager to wake up the background services every 15 mins periodically. It is working fine, but since the inclusion of DOZE mode Android 6.0, the seems like behaving strange and not waking up in every 15 mins. Although, I am using the method alarm.setExactAndAllowWhileIdle(), but still not working in Idle state
here is my method for implementing Alarm Manager
private void serviceRunningBackground()
{
final Intent restartIntent = new Intent(this, service.class);
restartIntent.putExtra("ALARM_RESTART_SERVICE_DIED", true);
alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Handler restartServiceHandler;
restartServiceHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
pintent = PendingIntent.getService(getApplicationContext(), 0, restartIntent, 0);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
Log.d(TAG, " Marshmellow "+ TIMER_START_TIME);
alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, 900000, pintent);
} else {
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 900000, pintent);
}
sendEmptyMessageDelayed(0, TIMER_START_TIME);
}
};
restartServiceHandler.sendEmptyMessageDelayed(0, 0);
}
Any help would be appreciated..thanks
Try this:
public class PollReceiver extends WakefulBroadcastReceiver{
static final String PERIOD = "period";
#Override
public void onReceive(Context context, Intent intent){
startWakefulService(context,new Intent(context,MyService.class));
long period = intent.getLongExtra(PERIOD,-1);
if(period>0){
scheduleExactAlarm(context,(AlarmManager)context.getSystemService(Context.ALARM_SERVICE),period)
}
}
static void scheduleExactAlarm(Context context,AlarmManager alarms, long period){
Intent i = new Intent(context,PollReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context,0,i,0);
if(Build.VERSION.SDK_INT>Build.VERSION_CODES.LOLLIPOP_MR1){
alarms.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + period,pi);
}
}
Ive tested scheduling alarms in this way in Doze and it works. They go off every 15 minutes. Check out https://commonsware.com , thats where I found this method of scheduling a repeating alarm.

Android getExtra() always gets default value in service

I am making an alarm, and service is started by the alarm. I want services to receive Extra data so with googling I set the flag as Intent.FILL_IN_DATA. Below is my code.(Only important part is shown)
(I have several alarm mode, and different services are called)
public static void setAlarm(Context mContext, int mode){
if(mode==A){
intent = new Intent(mContext, AService.class);
}
else{
intent = new Intent(mContext, BService.class);
intent.putExtra(NOTI_MODE, mode);
}
PendingIntent pIntent = PendingIntent.getService(mContext, 0, intent,Intent.FILL_IN_DATA);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
Log.d("set alarm","alarm 4.4");
alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
} else {
Log.d("set alarm","alarm 4.4 under");
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
}
}
public static void cancelAlarm(Context mContext){
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
PendingIntent pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, AService.class),Intent.FILL_IN_DATA);
alarmManager.cancel(pIntent);
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class),Intent.FILL_IN_DATA);
alarmManager.cancel(pIntent);
}
service side code(edit: this code is in BService class. AService does not need to receive Extras.)
public int onStartCommand(Intent intent, int flags, int startId) {
int notiMode=intent.getIntExtra(AlarmManagerHelper.NOTI_MODE, -1);
}
However, the result is always -1, which means no Extras is being received.
What is the problem with my code? I'm googling for hours and no hope.
Please help if you have any ideas.
Thanks in advance.
EDIT: setAlarm method is in
public class AlarmManagerHelper extends BroadcastReceiver{} class.
and below is from AndroidManifest
<receiver android:name="com.example.test.AlarmManagerHelper" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
When I use
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class), 0);
instead of
pIntent = PendingIntent.getService(mContext, 0, new Intent(mContext, BService.class),Intent.FILL_IN_DATA);
at cancelAlarm(), service gets Extras all right (but the alarm does not cancel of course)
I don't get why setAlarm() does not work when I change cancelAlarm()..
I found the solution. Quite embarrassing.. I should have searched more for myself before posting the question...
Changed Intent.FILL_IN_DATA to PendingIntent.FLAG_UPDATE_CURRENT and now it works like charm.
Does not know why, but if you are suffering from same problem, please try this solution :)
According to the code you posted, you have two services (AService and BService).
I am guessing that the code snippet you are sharing is for your AService?
I think the top part of your setAlarm method should be:
public static void setAlarm(Context mContext, int mode)
{
if(mode==A)
{
intent = new Intent(mContext, AService.class);
// NOTE: Add this line!
intent.putExtra(NOTI_MODE, mode);
}
else
{
intent = new Intent(mContext, BService.class);
intent.putExtra(NOTI_MODE, mode);
}
// snipped rest of code in this method
}

Categories

Resources