Как обновить текст уведомления для службы переднего плана в Android?

133

У меня есть настройка службы переднего плана в Android. Я хочу обновить текст уведомления. Я создаю сервис, как показано ниже.

Как я могу обновить текст уведомления, настроенный в этой службе переднего плана? Как лучше всего обновить уведомление? Приветствуется любой образец кода.

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);

    }

Я создаю сервис в своей основной деятельности, как показано ниже:

    // Start Notification Service
    Intent serviceIntent = new Intent(this, NotificationService.class);
    startService(serviceIntent);
Люк
источник

Ответы:

61

Я думаю, что startForeground()повторный вызов с тем же уникальным идентификатором и Notificationновой информацией будет работать, хотя я не пробовал этот сценарий.

Обновление: на основании комментариев вы должны использовать NotifcationManager для обновления уведомления, и ваша служба продолжает оставаться в режиме переднего плана. Взгляните на ответ ниже.

CommonsWare
источник
1
Не могли бы вы дать мне пример того, как я бы назвал это из моей деятельности? Мне не удалось найти хороший пример того, как вызывать методы в моей службе переднего плана.
Люк
1
@Luke: Существует множество шаблонов для использования службы, и я понятия не имею, чему вы следуете. Если вы звоните, startService()чтобы передать команду службе, просто позвоните еще startService()раз, чтобы сообщить ей об обновлении текста. Или, если вы звоните bindService(), добавьте метод в свой API, чтобы служба обновила свой текст. Или подумайте, должна ли сама служба принимать решение, обновлять текст или нет. Или, возможно, текст является тем, SharedPeferenceчто у службы есть слушатель. Дать вам точный совет абстрактно невозможно.
CommonsWare
9
для дальнейшего уточнения: вы не можете cancel()установить уведомление startForeground(). Вы должны удалить приоритетный статус самой службы (используя, stopForeground()если вы хотите, чтобы текст тикера появлялся снова. Я потерял часы, потому что эти ответы привели меня к мысли, что это действительно возможно.
slinden77
4
Я отклонил этот ответ, поскольку он явно неверен: developer.android.com/training/notify-user/managing.html Пожалуйста, @CommonsWare подумайте об удалении этого ответа, поскольку ваш высокий рейтинг репутации делает этот ответ "святой истиной" для обычный браузер. Спасибо.
HYS
2
У меня не получилось (хотя я помню, как использовал тот же метод в предыдущем проекте). Использование NotificationManagerсработало, как я ожидал.
user149408 05
224

Если вы хотите обновить набор уведомлений с помощью startForeground (), просто создайте новое уведомление, а затем используйте NotificationManager, чтобы уведомить его.

Ключевым моментом является использование того же идентификатора уведомления.

Я не тестировал сценарий многократного вызова startForeground () для обновления уведомления, но думаю, что лучше использовать NotificationManager.notify.

Обновление Уведомления НЕ удаляет Службу из состояния переднего плана (это можно сделать только путем вызова stopForground);

Пример:

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);
}

В документации указано

Чтобы настроить уведомление, чтобы его можно было обновить, отправьте его с идентификатором уведомления, позвонив NotificationManager.notify(). Чтобы обновить это уведомление после того, как вы его выпустили, обновите или создайте NotificationCompat.Builderобъект, постройте Notificationиз него объект и выдайте Notificationтот же идентификатор, который вы использовали ранее. Если предыдущее уведомление все еще отображается, система обновляет его, исходя из содержимого Notificationобъекта. Если предыдущее уведомление было отклонено, вместо него создается новое уведомление.

Лука Манцо
источник
35
ЭТО ПРАВИЛЬНЫЙ ОТВЕТ! Ответ выше очень неправильный и вводит в заблуждение. Вам не нужно перезапускать свой сервис только для того, чтобы обновить глупое уведомление.
Раду
7
@Radu Хотя я согласен с тем, что это оптимальный ответ (он позволяет избежать немного более длинного пути кода, взятого из ответа Commonsware), вы ошибаетесь относительно того, что делает ответ Commonsware - start / stopForegound не запускает / останавливает службу, они просто влияют на ее передний план ,
Стиви
@Stevie Спасибо за это, Стиви, ты, наверное, прав. И все же я бы не стал с этим связываться!
Раду
Вызов одного notify()или startForeground()обоих приводит к вызову onStartCommand().
М. Реза Насирлоо 07
10
Проблема с использованием NotificationManagerдля обновления уведомления, отображаемого с помощью, startForegroundзаключается в том, что вызов stopForegroundбольше не удаляет уведомление. Обновление с помощью другого вызова startForegroundустраняет эту проблему.
Tad
21

Улучшение ответа Луки Манзо в Android 8.0+ при обновлении уведомления будет звучать и отображаться как предупреждение.
чтобы предотвратить это, вам нужно добавитьsetOnlyAlertOnce(true)

так что код:

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
источник
Ты спас мой день. Спасибо
Рубен Вигера
1
Отсутствует ключевое слово, оно должно бытьnew NotificationChannel
7hny
5

вот код для этого в вашем сервисе . Создайте новое уведомление, но попросите диспетчер уведомлений уведомить тот же идентификатор уведомления, который вы использовали в startForeground.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

полные образцы кодов вы можете проверить здесь:

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

Даниэль Као
источник
Я не уверен, что это сохранит статус переднего плана startService.
Мартин Маркончини
@Daniel Kao Ваше решение не запускает службу переднего плана
Игорь Ганапольский
4
Поправьте меня, если я ошибаюсь, но не могли бы люди, проголосовавшие за этот ответ, пожалуйста, более подробно объясните, что с ним не так? Вопрос не в том, как запустить службу переднего плана, а в том, как обновить уведомление службы переднего плана. Фактически это тот же ответ, что и у Луки, который, по мнению людей, работает и сохраняет статус переднего плана.
TheIT
@TheIT Это не работает. Статус уведомления станет not foregroundдля foreground createdсообщения.
Вячеслав
1
Это приведет к дублированию уведомления, потому что startForeground () уже был вызван.
Игорь Ганапольский
2

Кажется, что ни один из существующих ответов не показывает, как обрабатывать полный случай - startForeground, если это первый вызов, но обновлять уведомление для последующих вызовов.

Вы можете использовать следующий шаблон, чтобы определить правильный случай:

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));
    }
}

Кроме того, вам необходимо создать уведомление и канал, как в других ответах:

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);
}
Ник Кардосо
источник