Come faccio ad aggiornare il testo della notifica per un servizio in primo piano in Android?


133

Ho una configurazione del servizio in primo piano in Android. Vorrei aggiornare il testo della notifica. Sto creando il servizio come mostrato di seguito.

Come posso aggiornare il testo della notifica impostato all'interno di questo servizio di primo piano? Qual è la migliore pratica per l'aggiornamento della notifica? Qualsiasi codice di esempio sarebbe apprezzato.

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

    }

Sto creando il servizio nella mia attività principale, come mostrato di seguito:

    // Start Notification Service
    Intent serviceIntent = new Intent(this, NotificationService.class);
    startService(serviceIntent);

Risposte:


61

Penserei che chiamare di startForeground()nuovo con lo stesso ID univoco e un Notificationcon le nuove informazioni funzionerebbe, anche se non ho provato questo scenario.

Aggiornamento: in base ai commenti, è necessario utilizzare NotifcationManager per aggiornare la notifica e il servizio continua a rimanere in modalità primo piano. Dai un'occhiata alla risposta qui sotto.


1
Potresti per favore darmi un esempio di come lo chiamerei dalla mia attività però? Non sono stato in grado di trovare un buon esempio su come chiamare i metodi nel mio servizio di primo piano.
Luca

1
@Luke: ci sono molti schemi per l'utilizzo di un servizio e non ho idea di cosa stia seguendo il tuo. Se stai chiamando startService()per passare un comando al servizio, chiama semplicemente di startService()nuovo per comunicargli di aggiornarne il testo. Oppure, se stai chiamando bindService(), aggiungi un metodo all'API per fare in modo che il servizio aggiorni il suo testo. Oppure, considera se il servizio stesso dovrebbe essere quello che prende la decisione se aggiornare o meno il testo. O, forse, il testo è a SharedPeferencecui il servizio ha un ascoltatore. È impossibile darti consigli accurati in astratto.
Commons War

9
per chiarire ulteriormente: non è possibile cancel()impostare una notifica startForeground(). Devi rimuovere lo stato di primo piano del servizio stesso (usando stopForeground()se vuoi che appaia di nuovo il testo del ticker. Ho perso ore perché queste risposte mi hanno portato a credere che fosse effettivamente possibile.
slinden77

4
Ho ridimensionato questa risposta in quanto è chiaramente sbagliato: developer.android.com/training/notify-user/managing.html Per favore @CommonsWare considera la rimozione di questa risposta, poiché il tuo punteggio di alta reputazione rende questa risposta la "santa verità" per la browser casuale. Grazie.
HYS,

2
Non ha funzionato per me (anche se ricordo di aver usato lo stesso metodo in un precedente progetto). L'utilizzo ha NotificationManagerfunzionato come mi aspettavo.
user149408

224

Quando si desidera aggiornare un set di notifiche tramite startForeground (), è sufficiente creare una nuova notifica e quindi utilizzare NotificationManager per notificarlo.

Il punto chiave è utilizzare lo stesso ID di notifica.

Non ho testato lo scenario di chiamare ripetutamente startForeground () per aggiornare la notifica, ma penso che usare NotificationManager.notify sarebbe meglio.

L'aggiornamento della notifica NON rimuoverà il servizio dallo stato di primo piano (ciò può essere fatto solo chiamando stopForground);

Esempio:

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

La documentazione afferma

Per impostare una notifica in modo che possa essere aggiornata, emettila con un ID di notifica chiamando NotificationManager.notify(). Per aggiornare questa notifica dopo averla emessa, aggiornare o creare un NotificationCompat.Builderoggetto, creare un Notificationoggetto da esso ed emettere Notificationlo stesso ID utilizzato in precedenza. Se la notifica precedente è ancora visibile, il sistema la aggiorna dal contenuto Notificationdell'oggetto. Se la precedente notifica è stata respinta, viene invece creata una nuova notifica.


35
QUESTA È LA RISPOSTA CORRETTA! La risposta sopra è molto sbagliata e fuorviante. Non è necessario riavviare il servizio solo per aggiornare una notifica sciocca.
Radu,

7
@Radu Anche se sono d'accordo che questa è la risposta ottimale (evita il percorso di codice leggermente più lungo seguito dalla risposta di Commonsware), ti sbagli su ciò che fa la risposta di Commonsware: start / stopForegound non avvia / arresta il servizio, ma ne influenza solo la priorità .
Stevie,

@Stevie Grazie Stevie, probabilmente hai ragione. Eppure non lo farei nemmeno con quello!
Radu,

Chiamare il notify()o startForeground()entrambi portano a chiamare onStartCommand().
M. Reza Nasirloo,

10
Il problema con l'utilizzo NotificationManagerper aggiornare una notifica mostrata con startForegroundè che la chiamata stopForegroundnon rimuoverà più la notifica. L'aggiornamento con un'altra chiamata per startForegroundovviare a questo problema.
Tad

21

Migliorando la risposta di Luca Manzo in Android 8.0+ durante l'aggiornamento della notifica, verrà emesso un suono e verrà visualizzato come Heads-up.
per evitare che sia necessario aggiungeresetOnlyAlertOnce(true)

quindi il codice è:

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

Mi hai salvato la giornata. Grazie
Rubén Viguera il

1
new NotificationChannel
Parola

5

ecco il codice per farlo al tuo servizio . Crea una nuova notifica, ma chiedi al gestore delle notifiche di notificare lo stesso ID di notifica che hai usato in startForeground.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

per i codici di esempio completi, puoi controllare qui:

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


Non sono sicuro che questo manterrà lo stato di primo piano di startService.
Martin Marconcini,

@Daniel Kao La tua soluzione non avvia un servizio di primo piano
IgorGanapolsky

4
Correggimi se sbaglio, ma le persone che votano questa risposta potrebbero essere più descrittive su cosa c'è che non va? La domanda non chiede come avviare un servizio in primo piano, ma come aggiornare una notifica di un servizio in primo piano. Questa è effettivamente la stessa risposta di Luca che le persone concordano che funziona e mantiene lo stato di primo piano.
TheIT il

@TheIT Non funziona. Lo stato della notifica diventa not foregroundper il foreground createdmessaggio.
Vyacheslav,

1
Ciò comporterebbe una notifica duplicata, poiché startForeground () era già stato chiamato.
IgorGanapolsky,

2

Sembra che nessuna delle risposte esistenti mostri come gestire il caso completo: avviare Foreground se è la prima chiamata ma aggiornare la notifica per le chiamate successive.

È possibile utilizzare il modello seguente per rilevare il caso corretto:

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

Inoltre, devi creare la notifica e il canale come in altre risposte:

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.