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);
}
}
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.
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.
*At first integrate *
'$' dependencies { implementation "androidx.core:core:$core_version" }
in your gradle file.
then follow the Code : https://github.com/NafimAhmed/Offline_Notification
Related
How can I send scheduled notifications in Android Studio?
I got this task: I want to get notifications at the some chosen time of the day every day. I can easily get them when app is alive, but when its closed notifications don't come up.
I've already tried JobScheduler, AlarmManager and WorkManager and none of these didn't work well.
My project runs at minimum SDK 26 (Android Oreo). Target SDK is 30. Last code version looks like that:
AlertReceiver.java
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
intent = new Intent(context, NotificationService.class);
context.startService(intent);
}
}
NotificationService.java
public class NotificationService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
showNotifications();
return Service.START_STICKY;
}
private void showNotifications(){
NotificationHelper notificationHelper = new NotificationHelper(getApplicationContext());
NotificationCompat.Builder nb;
nb = notificationHelper.getChannelNotification("Title", "Description");
notificationHelper.getManager().notify(123, nb.build());
}
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
I schedule the alert like so:
...
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, hours);
calendar.set(Calendar.MINUTE, minutes);
AlarmManager alarm_manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlertReceiver.class);
PendingIntent pending_intent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarm_manager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pending_intent);
```
The problem is that from Android 8 calling startService from the background is not allowed:
In this code:
public class AlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
intent = new Intent(context, NotificationService.class);
context.startService(intent);
}
}
You can change from:
context.startService(intent);
To:
ContextCompat.startForegroundService(getApplicationContext(), intent)
And put this in your Manifest:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
This will at least allow your service to run from the background in your current implementation.
You then have 5 seconds to call startForeground() in your Service and post a Notification that lets the user know that your service is running or it will be terminated.
Also for what you are trying to do, I think you will get a better result with:
setExactAndAllowWhileIdle
Or
setAlarmClock
setExact does not work as the name implies. All the AlarmManager documentation needs to be read carefully and I would suggest studying "doze" in detail before attempting any Service implementations that rely on timing.
This is a notification that I used in a project (sorry for Kotlin language):
private fun createNotification() {
val notification = getServiceNotification("")
notificationManager.notify(NOTIFICATION_ID, notification.build())
}
private fun getServiceNotification(contentText: String): Notification.Builder {
var notification = Notification.Builder(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel()
notification = Notification.Builder(this, NOTIFICATION_SERVICE_CHANNEL_ID)
}
val openAppPendingIntent = PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
return notification
.setContentTitle("Notification Title")
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_main)
.setContentIntent(openAppPendingIntent)
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createNotificationChannel() {
val notificationChannel = NotificationChannel(
NOTIFICATION_SERVICE_CHANNEL_ID,
NOTIFICATION_SERVICE_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH
).apply {
lightColor = Color.GREEN
lockscreenVisibility = Notification.VISIBILITY_PRIVATE
}
notificationManager.createNotificationChannel(notificationChannel)
}
I have functionality in my app where a user will select the time when he wants to get notifications from the application. In this case, I am taking the input from user in this format 12:00 AM. I wrote a method for creating notifications in my app which is working fine but I want to call that method every day at a user-selected time. Also, if app is completely destroyed not even running in the background will this method be called at the user-selected time daily?
String user_time=tinyDB.getString("app_check_time"); // This is user selected time e.g, 12:00 PM
Calendar calendar = Calendar.getInstance();
Date date1 = null;
try {
date1=new SimpleDateFormat("h:m a").parse(user_time);
} catch (ParseException e) {
e.printStackTrace();
}
calendar.set(Calendar.HOUR_OF_DAY, date1.getHours());
calendar.set(Calendar.MINUTE, date1.getMinutes());
Date time = calendar.getTime();
System.out.println("hourr "+time);
timer = new Timer();
timer.schedule(new createNotification(), time);
You can use WorkManager for this. And yes, it'll run even if your app is completely dead.
From Android Docs:
WorkManager is a library used to enqueue deferrable work that is guaranteed to execute sometime after its Constraints are met.
You can see the guide here https://developer.android.com/topic/libraries/architecture/workmanager
The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.
Hope it helps!
Have you considered using something like Quartz Scheduler? This library has a mechanism for scheduling tasks to run at a set period of time every day using a cron like expression (take a look at CronScheduleBuilder).
Some example code (not tested):
public class GetDatabaseJob implements InterruptableJob
{
public void execute(JobExecutionContext arg0) throws JobExecutionException
{
getFromDatabase();
}
}
public class Example
{
public static void main(String[] args)
{
JobDetails job = JobBuilder.newJob(GetDatabaseJob.class);
// Schedule to run at 5 AM every day
ScheduleBuilder scheduleBuilder =
CronScheduleBuilder.cronSchedule("0 0 5 * * ?");
Trigger trigger = TriggerBuilder.newTrigger().
withSchedule(scheduleBuilder).build();
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.scheduleJob(job, trigger);
scheduler.start();
}
}
There's a bit more work upfront, and you may need to rewrite your job execution code, but it should give you more control over how you want you job to run. Also it would be easier to change the schedule should you need to
Creating a long running service for this task
Intent intent = new Intent(this, ReminderService.class);
startService(intent);
Creating a reminderservice which extends service
public class ReminderService extends Service {
#Override
public void onCreate() {
Calendar calendar = Calendar.getInstance();
calendar.set(calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH),
6,
0,
0);
setAlarm(calendar.getTimeInMillis());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// If we get killed, after returning from here, restart
return START_STICKY;
}
public void setAlarm(long timeInMillis) {
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, ReminderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this,
0, intent, 0);
if (alarmManager != null) {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, timeInMillis,
2 * 60 * 1000 , pendingIntent); //AlarmManager.INTERVAL_DAY //2 * 60 * 1000 (2 minutes)
}
}
}
creating ReminderReceiver which extends BroadcastReceiver
public class ReminderReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
//do task to show notification
ShowNotification showNotification = new ShowNotification(context);
showNotification.showNotification("Method called");
}
}
create ShowNotification class
public class ShowNotification {
private static final int NOTIFICATION = 0;
private static final String NOTIFICATION_CHANNEL_ID = "100";
private Context context;
private NotificationManager notificationManager;
private ConnectivityManager conManager;
public ShowNotification(Context context) {
this.context = context;
if (notificationManager == null) {
notificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (conManager == null) {
conManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
}
}
/**
* Show a notification while this service is running.
*
* #param key
*/
void showNotification(String key) {
String NOTIFICATION_CHANNEL_NAME = "NOTIFICATION_CHANNEL_NAME";
// In this sample, we'll use the same text for the ticker and the expanded notification
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME, importance);
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
notificationManager.createNotificationChannel(notificationChannel);
}
final Intent notificationIntent = new Intent(context, SplashActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_dialog_alert)
.setWhen(System.currentTimeMillis())
.setContentTitle("Method Called")
.setContentText(key)
.setContentIntent(contentIntent)
.setSound(Settings.System.DEFAULT_NOTIFICATION_URI)
.build();
// Send the notification.
notificationManager.cancel(NOTIFICATION);
notificationManager.notify(NOTIFICATION, notification);
}
}
In manifest file
<service
android:name=".ReminderService"
android:enabled="true"
android:exported="false" />
<receiver android:name=".ReminderReceiver" />
My application targets KITKAT and up android version and i am trying to create repetitive notification daily via Alarm Manager below is my code :
public class AlarmReceiver extends BroadcastReceiver {
public static final String MY_ACTION = "mymasterpeice.foreverservice.myaction";
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MY_ACTION)) {
// Do something here
// For our recurring task, we'll just display a message
Toast.makeText(context, "I'm running", Toast.LENGTH_SHORT).show();
// displayNotification(context, "Checkout New Articles");
sendNotification(context, "Checkout New Articles - Team");
}
}
/**
* Issues a notification to inform the user that server has sent a message.
*/
private static void sendNotification(Context context, String message) {
if (message != null && !TextUtils.isEmpty(message)) {
int icon = R.mipmap.ic_launcher;
long when = System.currentTimeMillis();
NotificationManager notificationManager = (NotificationManager)
context.getSystemService(Context.NOTIFICATION_SERVICE);
String title = context.getString(R.string.app_name);
Intent notificationIntent = new Intent(context, MainActivity.class);
// set intent so it does not start a new activity
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent intent =
PendingIntent.getActivity(context, 0, notificationIntent, 0);
Notification.Builder builder = new Notification.Builder(context);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
Intent.FLAG_ACTIVITY_SINGLE_TOP);
builder.setSmallIcon(icon)
.setContentTitle(title)
.setContentText(message)
.setContentIntent(intent);
Notification notification = builder.getNotification();
notificationManager.notify(icon, notification);
}
}
}
Main Activity :
public class MainActivity extends AppCompatActivity {
private String TAG=MainActivity.class.getSimpleName();
public static final String MY_ACTION = "mymasterpeice.foreverservice.myaction";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRecusrringTimer();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
private void setRecusrringTimer() {
Intent myIntent = new Intent(MY_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Calendar firingCal = Calendar.getInstance();
Calendar currentCal = Calendar.getInstance();
firingCal.set(Calendar.HOUR, 7); // At the hour you wanna fire
firingCal.set(Calendar.MINUTE, 0); // Particular minute
firingCal.set(Calendar.SECOND, 0); // particular second
long intendedTime = firingCal.getTimeInMillis();
long currentTime = currentCal.getTimeInMillis();
if (intendedTime >= currentTime) // you can add buffer time too here to ignore some small differences in milliseconds
{
alarmManager.setRepeating(AlarmManager.RTC,
intendedTime, AlarmManager.INTERVAL_DAY,
pendingIntent);
Log.d(TAG, "setRecusrringTimer ");
} else {
intendedTime = firingCal.getTimeInMillis();
Log.d(TAG, "setRecusrringTimer ");
alarmManager.setRepeating(AlarmManager.RTC,
intendedTime, AlarmManager.INTERVAL_DAY,
pendingIntent);
}
}
}
Manifest :
<receiver android:name=".AlarmReceiver">
<intent-filter>
<action android:name="mymasterpeice.foreverservice.myaction"/>
</intent-filter>
</receiver>
Issue is This code runs properly on Kitkat device until app is in memory , there are some delays like as in doc specified :
Note: 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.
But When app is not in memory the alarm manager does not show notification . I am specifically targeting Kitkat and above version of android.
I lost 1 month of my time for the same issue. Finally I found the solution. For the recent Android versions(exact version I am not sure ), an option called 'Auto Launch' have been introduced, which means the user can actually configure whether any app can be automatically started or not. So please check whether your app has permission for auto launch. This setting location may vay based on phone manufacturer. So you need to search for this 'Auto Lanuch' option in your phone settings. Even if it doesn't work, try removing your app from the 'Optimizations List' as well, which you can find in the settings.
What i want: To run a background service that would communicate with our server via RESTFUL webservice after regular interval of 1 minute (Its a strict project requirement. So cannot increase it.)
What i have: I tried various approaches using simple BroadcastReceivers with a simple Service AND WakefulBroadcastReceivers using WakefulIntentService etc..
Main Problem: The main issue is when device screen is ON, everything is working fine on regular/fixed intervals but when the screen goes off OR devices gets locked, then the alarm Manager triggers service with a minimum interval of 5 Minutes.. Thats exactly what i dont want. I want the same 1 minute interval while device is locked/screen off.
Below is my code:
Manifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
<receiver android:name=".MyScheduledReceiver" />
<service android:name=".BackgroundService" />
Activity.java
MyScheduledReceiver.scheduleAlarms(MainActivity.this);
gradle
// Background long running process
compile 'com.commonsware.cwac:wakeful:1.0.+'
repositories {
maven {
url "https://s3.amazonaws.com/repo.commonsware.com"
}
}
BroadcastReceiver
public class MyScheduledReceiver extends WakefulBroadcastReceiver {
private static final int PERIOD = 60 * 1000;
private static final int INITIAL_DELAY = 2000; // 5 seconds
#Override
public void onReceive(Context context, Intent i) {
try {
if (i.getAction() == null) {
WakefulIntentService.sendWakefulWork(context, BackgroundService.class);
} else {
scheduleAlarms(context);
}
} catch (Exception e) {
e.printStackTrace();
}
}
static void scheduleAlarms(Context ctxt) {
AlarmManager mgr = (AlarmManager) ctxt.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctxt, MyScheduledReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(ctxt, 0, i, 0);
mgr.setRepeating(AlarmManager.RTC_WAKEUP, INITIAL_DELAY, PERIOD, pi);
}
}
BackgroundService
public class BackgroundService extends WakefulIntentService {
public BackgroundService() {
super("BackgroundService");
}
#Override
protected void doWakefulWork(Intent intent) {
sendNotification("HELLOO");
stopSelf();
}
#Override
public void onTaskRemoved(Intent rootIntent) {
MyScheduledReceiver.scheduleAlarms(BackgroundService.this);
super.onTaskRemoved(rootIntent);
}
private void sendNotification(String message) {
try {
Intent intent = intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Notification notification;
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("test")
.setContentText(message)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
notification = notificationBuilder.build();
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Currently i am simply showing a notification with a sound in the notification bar from my BackgroundService.
I would really appreciate your help. Thanks !
What you want is not supported on Android 6.0 anyway. Android 6.0 will not issue alarms every minute, to any application, thanks to Doze mode.
The closest thing that will work is:
Have a service that uses a ScheduledExecutorService to get control every minute to do your work
Have that service acquire a WakeLock and keep the CPU on all the time
Have that service use startForeground() and START_STICKY to minimize the amount of time that it is not around and therefore incapable of doing this work
Make sure that application is added to the "ignore battery optimizations" whitelist in Settings
Ignore the cries of anguish from users, complaining that their battery life is atrocious
Try using like this
public static void scheduleAlarms(Context ctxt) {
AlarmManager mgr = (AlarmManager)ctxt.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctxt, MyScheduledReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(ctxt,REQUEST_CODE, i, 0);
// We want the alarm to go off 3 seconds from now.
long firstTime = SystemClock.elapsedRealtime();
firstTime += 3 * 1000;//start 3 seconds after first register.
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, firstTime,
600000, sender);//10min interval
}
Have you tried using a Handler to obtain this goal? AlarmManager will typically work on a battery friendly rhythm when possible. Whereas Handlers are much more exact.
For example, you could run this code in your Service:
// Store this as a member variable
private Handler mHandler = new Handler();
// This can be a member variable or local
private Runnable runnable = new Runnable() {
#Override
public void run() {
// Do stuff
// Tell the Handler to call itself again
mHandler.postDelayed(this, 60000);
}
};
// Put this at the start of `Service`
mHandler.postDelayed(runnable, 60000);
As Commonsware points out, having the Handler declared as it is above would cause the Handler to run on the Main Thread. Here is some documentation on how to get a Handler to run in the background:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
this is my code for menage a single notification:
myActivity.java
public class myActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
cal = Calendar.getInstance();
// it is set to 10.30
cal.set(Calendar.HOUR, 10);
cal.set(Calendar.MINUTE, 30);
cal.set(Calendar.SECOND, 0);
long start = cal.getTimeInMillis();
if(cal.before(Calendar.getInstance())) {
start += AlarmManager.INTERVAL_FIFTEEN_MINUTES;
}
Intent mainIntent = new Intent(this, myReceiver.class);
pIntent = PendingIntent.getBroadcast(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager myAlarm = (AlarmManager)getSystemService(ALARM_SERVICE);
myAlarm.setRepeating(AlarmManager.RTC_WAKEUP, start, AlarmManager.INTERVAL_FIFTEEN_MINUTES, pIntent);
}
}
myReceiver.java
public class myReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context c, Intent i) {
Intent myService1 = new Intent(c, myAlarmService.class);
c.startService(myService1);
}
}
myAlarmService.java
public class myAlarmService extends Service {
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
}
#SuppressWarnings("deprecation")
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
displayNotification();
}
#Override
public void onDestroy() {
super.onDestroy();
}
public void displayNotification() {
Intent mainIntent = new Intent(this, myActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(this, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager nm = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = new Notification.Builder(this);
builder.setContentIntent(pIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_noti)
.setTicker(getString(R.string.notifmsg))
.setContentTitle(getString(R.string.app_name))
.setContentText(getString(R.string.notifmsg));
nm.notify(0, builder.build());
}
}
AndroidManifest.xml
<uses-permission android:name="android.permission.WAKE_LOCK" />
...
...
...
<service android:name=".myAlarmService" android:enabled="true" />
<receiver android:name=".myReceiver"/>
IF the time has NOT past yet everything works perfectly. The notification appears when it must appear.
BUT if the time HAS past (let's assume it is 10.31 AM) the notification fires every time... when I close and re-open the app, when I click on the notification... it has a really strange behavior.
I can't figure out what's wrong in it. Can you help me please (and explain why, if you find a solution), thanks in advance :)
Place display notification inside an if statement , such that compare the current time with the notification set time and if the current time is before the set time then display notification, else do nothing.
int temp = calTemp.getTime().compareTo(calendar.getTime());
if(temp > 0){
}else{
alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(),
pendingIntent1);
}
here calTemp gives current time and calender gives the time i want to fire the alarm. So according to above code if the time has already past then the notification will not fire for sure .
Hi I've had the same problem and found a solution in this SO post, basically the idea is to rely on AlarmManager, Receiver but avoid usage of Service.
Since you are using the Service just to build and display the notification you may find useful my approach.
Let me know.