Як оновити текст сповіщення для служби переднього плану в 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 для оновлення сповіщення, і ваша служба продовжує залишатися в режимі переднього плану. Подивіться на відповідь нижче.


1
Чи можете ви, будь ласка, надати мені приклад того, як я б назвав це зі своєї діяльності? Я не зміг знайти хороший зразок того, як викликати методи в службі переднього плану.
Лука

1
@Luke: Існує будь-яка кількість моделей використання сервісу, і я не маю уявлення про те, що слід за вами. Якщо ви телефонуєте, startService()щоб передати команду службі, просто просто зателефонуйте startService()ще раз, щоб сказати їй, щоб оновити текст. Або, якщо ви телефонуєте bindService(), додайте метод у свій API, щоб служба оновила текст. Або подумайте, чи саме сервіс повинен приймати рішення щодо оновлення тексту чи ні. Або, можливо, текст - це SharedPeferenceте, що служба має слухача. Надати точну пораду в рефераті неможливо.
CommonsWare

9
щоб уточнити далі: Ви не cancel()можете встановити сповіщення від startForeground(). Ви повинні видалити статус переднього плану самої служби (використовуючи, stopForeground()якщо ви хочете, щоб текст з тикером з’явився знову. Я втратив години, тому що ці відповіді змусили мене повірити, що це насправді можливо.
slinden77

4
Я спростував цю відповідь, оскільки це явно неправильно: developer.android.com/training/notify-user/managing.html Будь ласка, @CommonsПодумайте видалити цю відповідь, оскільки ваш високий показник репутації робить цю відповідь "святою правдою" для випадковий браузер. Дякую.
HYS

2
Мені не вдалося (хоча я пам'ятаю, що цей самий метод використовувався в попередньому проекті). Використання NotificationManagerпрацювало так, як я очікував.
користувач149408

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().
М. Реза Насірлу

10
Проблема використання NotificationManagerоновлення сповіщення, показаного в startForegroundтому, що виклик stopForegroundбільше не видалятиме сповіщення. Оновлення його за допомогою іншого дзвінка, щоб startForegroundусунути цю проблему.
Тад

21

Покращення відповіді Лука Манцо в android 8.0+ під час оновлення сповіщення звучить і відображатиметься як Heads-up.
щоб запобігти тому, що вам потрібно додати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);
}

Ти врятував мені день. Дякую
Рубен Вігера

1
Відсутнє ключове слово, має бутиnew NotificationChannel
7

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 Ваше рішення не запускає послугу переднього плану
IgorGanapolsky

4
Виправте мене, якщо я помиляюся, але чи можуть люди, які голосують за цю відповідь, бути більш описовими щодо того, що з цим не так? Питання не задає питання про те, як запустити послугу Foreground, а як оновити сповіщення про службу переднього плану. Це фактично та сама відповідь, що і Лука, яку люди згодні, працює і підтримує статус переднього плану.
THEIT

@TheIT Це не працює. Статус повідомлення стає not foregroundдля foreground createdповідомлення.
В’ячеслав

1
Це призведе до повторення сповіщення, тому що startForeground () вже викликався.
ІгорГанапольський

2

Здається, жодна з існуючих відповідей не показує, як впоратися з повною справою - запуститиForeground, якщо це перший дзвінок, але оновити сповіщення про наступні дзвінки.

Ви можете використовувати наступну схему для виявлення правильного випадку:

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.