147

I have a foreground service setup in Android. I would like to update the notification text. I am creating the service as shown below.

How can I update the notification text that is setup within this foreground service? What is the best practise for updating the notification? Any sample code would be appreciated.

public class NotificationService extends Service {

    private static final int ONGOING_NOTIFICATION = 1;

    private Notification notification;

    @Override
    public void onCreate() {
        super.onCreate();

        this.notification = new Notification(R.drawable.statusbar, getText(R.string.app_name), System.currentTimeMillis());
        Intent notificationIntent = new Intent(this, AbList.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        this.notification.setLatestEventInfo(this, getText(R.string.app_name), "Update This Text", pendingIntent);

        startForeground(ONGOING_NOTIFICATION, this.notification);

    }

I am creating the service in my main activity as shown below:

    // Start Notification Service
    Intent serviceIntent = new Intent(this, NotificationService.class);
    startService(serviceIntent);
Luke
  • 5,895
  • 11
  • 54
  • 84

5 Answers5

243

When you want to update a Notification set by startForeground(), simply build a new notication and then use NotificationManager to notify it.

The key point is to use the same notification id.

I didn't test the scenario of repeatedly calling startForeground() to update the Notification, but I think that using NotificationManager.notify would be better.

Updating the Notification will NOT remove the Service from the foreground status (this can be done only by calling stopForground );

Example:

private static final int NOTIF_ID=1;

@Override
public void onCreate (){
    this.startForeground();
}

private void startForeground() {
    startForeground(NOTIF_ID, getMyActivityNotification(""));
}

private Notification getMyActivityNotification(String text){
    // The PendingIntent to launch our activity if the user selects
    // this notification
    CharSequence title = getText(R.string.title_activity);
    PendingIntent contentIntent = PendingIntent.getActivity(this,
            0, new Intent(this, MyActivity.class), 0);

    return new Notification.Builder(this)
            .setContentTitle(title)
            .setContentText(text)
            .setSmallIcon(R.drawable.ic_launcher_b3)
            .setContentIntent(contentIntent).getNotification();     
}

/**
 * This is the method that can be called to update the Notification
 */
private void updateNotification() {
    String text = "Some text that will update the notification";

    Notification notification = getMyActivityNotification(text);

    NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(NOTIF_ID, notification);
}

The documentation states

To set up a notification so it can be updated, issue it with a notification ID by calling NotificationManager.notify(). To update this notification after you've issued it, update or create a NotificationCompat.Builder object, build a Notification object from it, and issue the Notification with the same ID you used previously. If the previous notification is still visible, the system updates it from the contents of the Notification object. If the previous notification has been dismissed, a new notification is created instead.

Yan Foto
  • 8,951
  • 4
  • 45
  • 79
Luca Manzo
  • 2,771
  • 2
  • 13
  • 9
  • 36
    THIS IS THE CORRECT ANSWER! The answer above is very wrong and misleading. You don't need to restart your service just so you update a silly notification. – Radu Nov 25 '13 at 12:33
  • 7
    @Radu While I agree that this is the optimal answer (it avoids the slightly longer code-path taken by Commonsware's answer) you are mistaken about what Commonsware's answer does - start/stopForegound do not start/stop the service, they just affect its foregroundness. – Stevie Jul 12 '14 at 12:36
  • @Stevie Thanks for that Stevie you are probably right. Still I would not mess with that either! – Radu Jul 15 '14 at 10:55
  • Calling the `notify()` or`startForeground()` both lead to call `onStartCommand()`. – M. Reza Nasirloo Jul 07 '15 at 14:22
  • 10
    The problem with using `NotificationManager` to update a notification shown with `startForeground` is that calling `stopForeground` will no longer remove the notification. Updating it with another call to `startForeground` obviates that problem. – Tad Apr 14 '16 at 13:07
  • @Tad it worked for me. Created a notification with `startForeground()`, updated it a few times as described in the answer, then called `stopForeground()`—notification gone. – user149408 Aug 05 '17 at 21:17
  • This works. Tested with emulator running API 27. No need to re-create the service. – Wrichik Basu Apr 01 '20 at 08:06
  • This is not working for me on target version 29 and with notification channels. Did anyone get the same problem? – Bolling Jun 30 '20 at 09:02
  • @Bolling Try using `NotificationManager`. It will not be working if you are using `NotificationManagerCompat`. – Sri Harsha Chilakapati Jul 19 '20 at 18:59
70

I would think that calling startForeground() again with the same unique ID and a Notification with the new information would work, though I have not tried this scenario.

Update: Based on the comments, you should use NotifcationManager to update the notification and your service continues to stay in the foreground mode. Take a look at the answer below.

Ali Nadalizadeh
  • 2,478
  • 3
  • 20
  • 24
CommonsWare
  • 910,778
  • 176
  • 2,215
  • 2,253
  • 1
    Could you please give me an example of how I would call that from my Activity though? I haven't been able to find a good sample on how to call methods in my foreground service. – Luke Apr 03 '11 at 12:51
  • @Luke: Oh, you can't call it from an activity. You would need to tell your service what to update and have it update the `Notification`. You can try to use `NotificationManager` and update the `Notification` directly from the activity, using the same unique ID. However, I worry that this would "undo" your foreground status. – CommonsWare Apr 03 '11 at 12:53
  • Yes, I was going to add a method to the service for updating the notification but I can't find any samples or examples on how to access my service instance and call my updateNotification method in my foreground service. I tried making my service a singleton class but that doesn't seem to work. – Luke Apr 03 '11 at 12:59
  • 1
    @Luke: There are any number of patterns for using a service, and I have no idea what yours is following. If you are calling `startService()` to pass a command to the service, then just call `startService()` again to tell it to update its text. Or, if you are calling `bindService()`, add a method to your API to have the service update its text. Or, consider whether the service itself should be the one making the decision whether or not to update the text. Or, perhaps the text is a `SharedPeference` that the service has a listener on. It's impossible to give you accurate advice in the abstract. – CommonsWare Apr 03 '11 at 13:15
  • @CommonsWare Have you ever discovered whether using `NotificationManager` would undo the foreground status? – yydl Nov 27 '11 at 07:36
  • 1
    @yydl In Google's Random Music Player sample app, they use NotificationManager to update the notification of a foreground service, so chances are pretty good that it retains the foreground status. – Lorne Laliberte Dec 06 '11 at 22:09
  • @LorneLaliberte Looks like a good answer for http://stackoverflow.com/questions/8289927/does-updating-a-notification-remove-a-services-foreground-status – yydl Dec 07 '11 at 21:30
  • @CommonsWare in my experience that does work (at least on 2.3.x and below), with one notable exception being that you can't make the ticker text appear more than once for a given notification, even if you change the ticker text (at least not if you're using Notification.FLAG_ONGOING_EVENT). To show ticker text for an existing notification, you need to cancel the notification and then restart it (and do so using startForeground if you want your service to have foreground status). – Lorne Laliberte Dec 07 '11 at 23:08
  • 9
    to clarify further: you can't `cancel()` a Notification set by `startForeground()`. You have to remove the foreground status of the service itself (using `stopForeground()` if you want to make ticker text appear again. I lost hours because these answers led me to believe it was in fact possible. – slinden77 Jul 09 '12 at 09:51
  • 4
    I have downvoted this answer as it is clearly plain wrong: https://developer.android.com/training/notify-user/managing.html Please @CommonsWare consider removing this answer, as your high reputation score makes this answer the "holy truth" for the casual browser. Thanks. – HYS Jun 15 '16 at 08:49
  • 2
    Didn’t work for me (though I recall using this same method in a previous project). Using `NotificationManager` worked as I expected. – user149408 Aug 05 '17 at 21:21
  • @CommonsWare would you please have a look of this relevant question too => https://stackoverflow.com/q/53132403/2304819 – sAm Nov 05 '18 at 11:34
  • Calling startForeground() again was the only thing working for me actually. – Bolling Jun 30 '20 at 08:58
26

Improving on Luca Manzo answer in android 8.0+ when updating the notification it will make sound and show as Heads-up.
to prevent that you need to add setOnlyAlertOnce(true)

so the code is:

private static final int NOTIF_ID=1;

@Override
public void onCreate(){
        this.startForeground();
}

private void startForeground(){
        startForeground(NOTIF_ID,getMyActivityNotification(""));
}

private Notification getMyActivityNotification(String text){
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
        ((NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(
        NotificationChannel("timer_notification","Timer Notification",NotificationManager.IMPORTANCE_HIGH))
}

        // The PendingIntent to launch our activity if the user selects
        // this notification
        PendingIntent contentIntent=PendingIntent.getActivity(this,
        0,new Intent(this,MyActivity.class),0);

        return new NotificationCompat.Builder(this,"my_channel_01")
        .setContentTitle("some title")
        .setContentText(text)
        .setOnlyAlertOnce(true) // so when data is updated don't make sound and alert in android 8.0+
        .setOngoing(true)
        .setSmallIcon(R.drawable.ic_launcher_b3)
        .setContentIntent(contentIntent)
        .build();
}

/**
 * This is the method that can be called to update the Notification
 */
private void updateNotification(){
        String text="Some text that will update the notification";

        Notification notification=getMyActivityNotification(text);

        NotificationManager mNotificationManager=(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
        mNotificationManager.notify(NOTIF_ID,notification);
}
humazed
  • 66,202
  • 30
  • 86
  • 124
6

here's the code to do so in your service. Create a new notification, but ask notification manager to notify the same notification id you used in startForeground.

Notification notify = createNotification();
final NotificationManager notificationManager = (NotificationManager) getApplicationContext()
    .getSystemService(getApplicationContext().NOTIFICATION_SERVICE);

notificationManager.notify(ONGOING_NOTIFICATION, notify);

for full sample codes, you can check here:

https://github.com/plateaukao/AutoScreenOnOff/blob/master/src/com/danielkao/autoscreenonoff/SensorMonitorService.java

klefevre
  • 8,134
  • 7
  • 37
  • 68
Daniel Kao
  • 348
  • 2
  • 6
  • I'm not sure this will maintain the foreground status of startService. – Martin Marconcini Jun 18 '13 at 19:45
  • @Daniel Kao Your solution does not start a foreground service – IgorGanapolsky Aug 30 '13 at 16:41
  • 4
    Correct me if I'm wrong but could the people down voting this answer please be more descriptive as to what's wrong with it? The question doesn't ask how to start a Foreground service, but how to update a notification of a foreground service. This is effectively the same answer as Luca which people agree works and maintains the foreground status. – TheIT Dec 17 '13 at 22:19
  • @TheIT It Does not work. The notification's status become `not foreground` for `foreground created` message. – Vyacheslav Jan 04 '16 at 13:15
  • 1
    This would result in a duplicate notification, because **startForeground()** was already called. – IgorGanapolsky Aug 29 '17 at 20:04
  • what if I had two? – Saik Caskey Sep 27 '17 at 09:41
3

It seems none of the existing answers show how to handle the full case - to startForeground if it's the first call but update the notification for subsequent calls.

You can use the following pattern to detect the right case:

private void notify(@NonNull String action) {
    boolean isForegroundNotificationVisible = false;
    NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    StatusBarNotification[] notifications = notificationManager.getActiveNotifications();
    for (StatusBarNotification notification : notifications) {
        if (notification.getId() == FOREGROUND_NOTE_ID) {
            isForegroundNotificationVisible = true;
            break;
        }
    }
    Log.v(getClass().getSimpleName(), "Is foreground visible: " + isForegroundNotificationVisible);
    if (isForegroundNotificationVisible){
        notificationManager.notify(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    } else {
        startForeground(FOREGROUND_NOTE_ID, buildForegroundNotification(action));
    }
}

Additionally you need to build the notification and channel as in other answers:

private Notification buildForegroundNotification(@NonNull String action) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        createNotificationChannel();
    }
    //Do any customization you want here
    String title;
    if (ACTION_STOP.equals(action)) {
        title = getString(R.string.fg_notitifcation_title_stopping);
    } else {
        title = getString(R.string.fg_notitifcation_title_starting);
    }
    //then build the notification
    return new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentTitle(title)
            .setOngoing(true)
            .build();
}

@RequiresApi(Build.VERSION_CODES.O)
private void createNotificationChannel(){
    NotificationChannel chan = new NotificationChannel(CHANNEL_ID, getString(R.string.fg_notification_channel), NotificationManager.IMPORTANCE_DEFAULT);
    chan.setLightColor(Color.RED);
    chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    assert manager != null;
    manager.createNotificationChannel(chan);
}
Nick Cardoso
  • 18,430
  • 9
  • 62
  • 110