Solution 1 :
I can see one possible issue, and it’s possible to do this without the workmanager (assuming the device has a healthy connection at the time the notification runs).
Instead of doing your network in the receiver itself, I suggest starting a service (foreground service if above Android 8.0), and doing your work there. This is because android’s time limit for a receiver is much lower than a service/foreground service. So to me, it sounds plausible that your receiver is killed before the network request completes, and so no notification is shown.
You can show the notification in the service, and also schedule the next alarm since setExactAndAllowWhileIdle
isn’t a repetitive alarm on it’s own. So in your receiver, something like:
@Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, BirthdayNotifyService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(service);
} else {
context.startService(service);
}
}
Then a possible service class:
public class BirthdayNotifyService extends IntentService {
public BirthdayNotifyService() {
super("BirthdayNotifyService");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//Build a simple notification to show if a foreground service is neccesary
Notification noti = notiBuilder.build();
startForeground(1, noti);
}
//Do your network request work here. After completed, show your birthday notification and stop the foreground service.
}
}
To stop the service, right AFTER you display your notification for birthdays, use the following:
stopForeground(true);
stopSelf();
UPDATE: I used this method on my phone (Xiaomi Redmi Note 8 plus), it worked a few times but then stopped working. It works perfectly fine on stock android, but not on all devices. Therefore I would still say this is not a reliable solution to send notification on specific times.
Solution 2 :
It seems like you are doing everything right, and as you said in the comments that it’s working on nexus S, I assume that you also declared the receiver in the manifest.
So after week of having the same problem here is the result:
In some devices with custom OS like Xiaomi, when you swipe the app away from the list of recents, the OS considers it as a force stop and therefor all services and other stuff will be killed. Based on Issue Tracker there is no way around it at the moment to solve this issue. They responded that they are working with OEMs to resolve this issue.
But based on my own experience, if you setup your notification using work manager, it will be delayed, but you will eventually receive it (at least most of the time).
But if you want the timing to be exact, there is no way to proceed at the moment.
The only solution at the moment is to give some permission manully at the moment. Please visit dontkillmyapp for more information regarding this manual settings.
Problem :
I want to send offline notifications to users daily.Tried many things like: Alarm manager,Work manager but no luck.The notifications are not triggered or they just trigger at later.
Any way to get the job done?
Alarm function:
public void StartAlarm()
{
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY,23);
calendar.set(Calendar.MINUTE,59);
calendar.set(Calendar.SECOND,0);
AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
//alarmIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
Log.d("LOG", String.valueOf(calendar.getTimeInMillis()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 156, alarmIntent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 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);
}
Work manager code:
public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
@NonNull
@Override
public Result doWork() {
StartAlarm();
Log.d("In worker","yes");
return Result.success();
}
Work manager driver:
public void StartPeriodicWorker()
{
final PeriodicWorkRequest periodicWorkRequest = new
PeriodicWorkRequest.Builder(MyWorker.class,24,TimeUnit.HOURS)
.addTag("Birthday")
.build();
WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork("Birthday Notifier", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);
}
Alarm Receiver:
public class AlarmReceiver extends BroadcastReceiver {
private DatabaseReference myRef;
private ArrayList<String> allPeoples;
private int numberOfPeoples;
private Context ctx;
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"In alarm receiver",Toast.LENGTH_LONG).show();
ctx = context;
initPeoples();
}
public String getCurrentDate() {
Calendar calendar = Calendar.getInstance();
SimpleDateFormat mdformat = new SimpleDateFormat("d MMMM");
String strDate = mdformat.format(calendar.getTime());
return strDate;
}
private void initPeoples() {
FirebaseDatabase database = FirebaseDatabase.getInstance();
myRef = database.getReference("Users");
myRef.keepSynced(true);
myRef.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
allPeoples = new ArrayList<>();
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
if(snapshot.getKey().equals("Teacher") || snapshot.getKey().equals("Staff")){
for(DataSnapshot faculty : snapshot.child("peoples").getChildren()){
String birthday = (String) faculty.child("DOB").getValue();
if(birthday.equals(getCurrentDate())) {
String member = birthday;
allPeoples.add(member);
}
}
}else{
for(DataSnapshot student : snapshot.child("peoples").getChildren()){
String birthday = (String) student.child("DOB").getValue();
if(birthday.equals(getCurrentDate())) {
String member = birthday;
allPeoples.add(member);
}
}
}
}
numberOfPeoples = allPeoples.size();
ShowNotification();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
});
}
public void ShowNotification()
{
String CHANNEL_ID = "Channel_1453";
String CHANNEL_NAME = "Birthday Notification";
NotificationManagerCompat manager = NotificationManagerCompat.from(ctx);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
manager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(ctx, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_notify)
.setContentTitle("Birthday Reminder")
.setColor(Color.GREEN)
.setContentText("Click to see who has birthday today!")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(PendingIntent.getActivity(ctx, 0, new Intent(ctx, Birthday.class), PendingIntent.FLAG_UPDATE_CURRENT))
.build();
manager.notify(454, notification);
}
}
Comments
Comment posted by Keivan.k
are you having this problem on all devices? based on my experience it has to work well on emulator. correct?
Comment posted by null_override
Yeah all devices.My emulator is Google Pixel 3A xl .
Comment posted by Keivan.k
Yes, please try another another emulator like nexus s. Then please report back if it works fine or not. I will explain to you what might be your solution. If it also doesn’t work on nexus s then please add your code to the question.
Comment posted by null_override
Okay..One thing I forgot to say, I have used Nexus S API 19 on which works great.I am adding the codes here.
Comment posted by null_override
Just edited the question.I have also tried setRepeating on alarms (Didn’t used work manager then) but they don’t fire up
Comment posted by developer.android.com/reference/android/app/IntentService
Thanks for your answer Amin. But on Android R intentservice is depricated.Link:
Comment posted by stackoverflow.com/questions/63262762/…
There they say to use work manager or job schedular. I already tried to use a service, you can see my old post:
Comment posted by Amin
My example is designed to only start the service when the alarm fires. Any chance you’d be able to start the alarm in an activity instead and call the service from the receiver?
Comment posted by developer.android.com/preview/privacy/foreground-services#types
I haven’t tried JobIntentService, so I can’t speak whether that one will work in your case. But here’s the documentation for Android R and starting a foreground service – allowing you to make the network request in the background.
Comment posted by Amin
I see your reasoning for the constantly running service then – but I would recommend a single foreground service started from an alarm receiver, since background services are a pain to keep running on many android OEMs. Also if that’s your goal, I think you should move
Comment posted by stackoverflow.com/questions/63262762/…
I have been also tried everything(Almost) for a week and no luck.I wonder how does games do this(Offline notification about “You haven’t entered the game blah blah…” .Thanks for the info and if you have time can you look at my other question? This post and that one is related to each other.This is the link:
Comment posted by null_override
At first I went to the services approach and it kind of worked sometimes…Then came back to work manager and alarms
Comment posted by Keivan.k
I understand, games can accomplish it using work manager, if you try to send a notifcation by workmanager (and then set an initial delay) then it eventually sends the notification, but with some delay (like 8 or 10 hours delay)
Comment posted by Keivan.k
I checked your other questions, that was exactly the same for me, I also tried all these approaches with no luck. if you open the app it sends the notification. But if you wait longer, it will send the notification even if the app is not opened. the battery optimization will do this, the cluster some workrequests together and do them altogether, that is the reason behind the delay
Comment posted by Keivan.k
Could you please respond to my comment? Have you managed to make it work in a reliable way? I used the foreground service. It worked a few times, but the the Alarm stopped working when the app is closed.