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.
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"/>