Search code examples
androidbroadcastreceiveralarmmanager

AlarmManager for long term notifications - it works


skip below to my answer

I have seen similar questions but no suitable or correct answer, some are even contradictory.

The question is simple: can't alarmManager work for a long duration (more than a few hours)?

I have an app that has a message array. At first run, every msg gets a personal timing (Calendar obj), the app sets an alarmManager for each one and saves all at sharedPrefference. The code below (I copied only the relevant of course) works fine, even when I reboot the device (thanks to sharedPref) for few hours and then stops sending the notifications.

If I set the whole msg time to a short term (let's say: 10 msg, 1 every 5 minutes) it works great and they all sent. But if I schedule it for every hour, it works only for the first 2-3 hours.

I've seen some answers say that alarmManager doesn't fit for the long term while other answers say that it is the perfect service for it :( can someone give me a professional answer?

I also tried alarmManager.setAlarmClock () and setExactAndAllowWhileIdle () but same results.


Solution

  • Ok, after a long digging I think I figure it out. It works now, and after a reboot, the notifications are back.

    I post my code here, first for anybody who looks for a solution, and second - I'd like to hear your opinion - if I did it right etc. Tnx

    msg.activity

    private void addAlerts (MyMsg myMsg) {
            MyReceiver rc = new MyReceiver ();
    
            for (int i = 1; i < myMsg.totalMsgNum; i++)
                rc.setSingleNotifications (this, myMsg.msg[i]);
    }
    

    MyReciever.java

    public class MyReceiver extends BroadcastReceiver {
    
        final String CHANNEL_ID = "777";
        SharedPreferences pref;
        SharedPreferences.Editor editor;
    
    
        @Override
        public void onReceive (Context context, Intent intent) {
    
                // get the relevant content
                pref = context.getSharedPreferences ("SAVED_FILE", Context.MODE_PRIVATE);
    
                int atMsgNum = pref.getInt ("CURRENT", 1);
    
                String json = pref.getString ("USER_OBJ", "");
                Gson gson = new Gson ();
                User user = gson.fromJson (json, User.class);
    
                String titleStr = user.msg[atMsgNum].title;
                String contentStr = user.msg[atMsgNum].msg;
    
    
                // update current msg num
                editor = pref.edit ();
                editor.putInt ("CURRENT", atMsgNum + 1);
                editor.commit ();
    
    
                // intent for showing the msg activity
                intent.setClass (context, OnPressNotificationActivity.class);
                intent.putExtra ("ID", atMsgNum);
                intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    
    
                // notification
                createNotificationChannel (context);
    
                TaskStackBuilder stackBuilder = TaskStackBuilder.create (context);
                stackBuilder.addNextIntentWithParentStack (intent);
    
                PendingIntent pendingIntent = stackBuilder.getPendingIntent (atMsgNum, 0);
    
                NotificationCompat.Builder builder;
    
                builder = new NotificationCompat.Builder (context, CHANNEL_ID)
                        .setSmallIcon (R.drawable.single_pics_logo)
                        .setContentTitle (titleStr)
                        .setContentText (contentStr)
                        .setStyle (new NotificationCompat.BigTextStyle ()
                                .bigText (contentStr))
                        .setPriority (NotificationCompat.PRIORITY_HIGH)
                        .setContentIntent (pendingIntent)
                        .setAutoCancel (true)
                        .setBadgeIconType (NotificationCompat.BADGE_ICON_SMALL)
                        .setVisibility (NotificationCompat.VISIBILITY_PUBLIC); 
    
                NotificationManagerCompat notificationManager = NotificationManagerCompat.from (context);
                notificationManager.notify (atMsgNum, builder.build ());
            }
    
    
    
        public void setSingleNotifications (Context context, Msg msg) {
    
    
            Intent intent = new Intent (context, MyReceiver.class);
    
            PendingIntent alarmIntent = PendingIntent.getBroadcast (context, msg.num, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    
    
            Calendar calendar = Calendar.getInstance ();
            calendar.setTimeInMillis (System.currentTimeMillis ());
            calendar.set (Calendar.YEAR, msg.calendar.get (Calendar.YEAR));
            calendar.set (Calendar.MONTH, msg.calendar.get (Calendar.MONTH));
            calendar.set (Calendar.DAY_OF_MONTH, msg.calendar.get (Calendar.DAY_OF_MONTH));
            calendar.set (Calendar.HOUR_OF_DAY, msg.calendar.get (Calendar.HOUR_OF_DAY));
            calendar.set (Calendar.MINUTE, msg.calendar.get (Calendar.MINUTE));
            calendar.set (Calendar.SECOND, msg.calendar.get (Calendar.SECOND));
            calendar.add (Calendar.SECOND, 2);
    
            AlarmManager alarmManager = (AlarmManager) context.getSystemService (ALARM_SERVICE);
    
    
           AlarmManager.AlarmClockInfo almInfo = new AlarmManager.AlarmClockInfo (calendar.getTimeInMillis (), null);
    
            alarmManager.setAlarmClock (almInfo, alarmIntent);
        }
    
    
    
        private void createNotificationChannel (Context context) {
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
            {
                CharSequence name = "myMsgApp";
                String description = "daily alert";
                int importance = NotificationManager.IMPORTANCE_HIGH;
                NotificationChannel channel = new NotificationChannel (CHANNEL_ID, name, importance);
                channel.setDescription (description);
    
                NotificationManager notificationManager = context.getSystemService (NotificationManager.class);
                notificationManager.createNotificationChannel (channel);
            }
        }
    }
    

    RestartReceiver.java // for reboot

    public class RestartReceiver extends BroadcastReceiver {
    
    
        @Override
        public void onReceive (Context context, Intent intent) { // restarts alarms only when device restart
    
            if (intent.getAction ().equals ("android.intent.action.BOOT_COMPLETED")) {
    
                SharedPreferences sharedPreferences = context.getSharedPreferences ("SAVED_FILE", context.MODE_PRIVATE);
                String json = sharedPreferences.getString ("USER_OBJ", "");
    
                Intent in = new Intent (context, RestartService.class);
                in.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); // ??
                in.putExtra ("JSON_STRING", json);
                in.putExtra ("CURRENT", sharedPreferences.getInt ("CURRENT", 1));
    
    
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    context.startForegroundService (in);
                else
                    context.startService (in);
            }
        }
    }
    

    RestartService.java

    public class RestartService extends IntentService {
    
    
        public RestartService () {
            super ("RestartService");
        }
    
        public RestartService (String name) {
            super (name);
        }
    
        @Override
        protected void onHandleIntent (@Nullable Intent intent) {
    
            String json = intent.getExtras ().getString ("JSON_STRING", "");
            Gson gson = new Gson ();
            User user = gson.fromJson (json, User.class);
            user.atMsgNum = intent.getExtras ().getInt ("CURRENT", 1);
    
            MyReceiver rc = new MyReceiver ();
    
            for (int i = user.atMsgNum; i < user.totalMsgNum; i++)
                rc.setSingleNotifications (this, user.msg [i]);
        }
    }
    

    AndroidManifest includes:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    
    <receiver android:name=".MyReceiver">
                <intent-filter>
                    <action android:name="android.intent.action.INPUT_METHOD_CHANGED" />
                    <action android:name="android.media.action.DISPLAY_NOTIFICATION" />
                    <category android:name="android.intent.category.DEFAULT" />
                </intent-filter>
            </receiver>
    
            <receiver
                android:name=".RestartReceiver"
                android:enabled="true">
                <intent-filter>
                    <action android:name="android.intent.action.BOOT_COMPLETED" />
                </intent-filter>
            </receiver>
    
            <service android:name="myPackageName.RestartService"
                android:exported="false"/>