Comment mettre à jour le texte de notification pour un service de premier plan dans Android?

133

J'ai une configuration de service de premier plan dans Android. Je souhaite mettre à jour le texte de la notification. Je crée le service comme indiqué ci-dessous.

Comment puis-je mettre à jour le texte de notification configuré dans ce service de premier plan? Quelle est la meilleure pratique pour mettre à jour la notification? Tout exemple de code serait apprécié.

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

    }

Je crée le service dans mon activité principale comme indiqué ci-dessous:

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

Réponses:

61

Je pense qu'appeler à startForeground()nouveau avec le même identifiant unique et Notificationavec les nouvelles informations fonctionnerait, même si je n'ai pas essayé ce scénario.

Mise à jour: En fonction des commentaires, vous devez utiliser NotifcationManager pour mettre à jour la notification et votre service continue de rester en mode de premier plan. Jetez un œil à la réponse ci-dessous.

CommonsWare
la source
1
Pourriez-vous s'il vous plaît me donner un exemple de la façon dont j'appellerais cela à partir de mon activité? Je n'ai pas pu trouver un bon exemple sur la façon d'appeler des méthodes dans mon service de premier plan.
Luke
1
@Luke: Il existe un certain nombre de modèles d'utilisation d'un service, et je n'ai aucune idée de ce que le vôtre suit. Si vous appelez startService()pour passer une commande au service, rappelez-le simplement startService()pour lui dire de mettre à jour son texte. Ou, si vous appelez bindService(), ajoutez une méthode à votre API pour que le service mette à jour son texte. Ou, demandez-vous si le service lui-même devrait être celui qui prend la décision de mettre à jour ou non le texte. Ou peut-être que le texte est un sur SharedPeferencelequel le service a un auditeur. Il est impossible de vous donner des conseils précis dans l'abstrait.
CommonsWare
9
pour clarifier davantage: vous ne pouvez pas cancel()définir de notification par startForeground(). Vous devez supprimer le statut de premier plan du service lui-même (en utilisant stopForeground()si vous voulez faire réapparaître le texte du ticker. J'ai perdu des heures parce que ces réponses m'ont amené à croire que c'était en fait possible.
slinden77
4
J'ai décliné cette réponse car elle est clairement erronée: developer.android.com/training/notify-user/managing.html Veuillez @CommonsWare envisager de supprimer cette réponse, car votre score élevé de réputation fait de cette réponse la "sainte vérité" pour le navigateur occasionnel. Merci.
HYS
2
Cela n'a pas fonctionné pour moi (même si je me souviens avoir utilisé cette même méthode dans un projet précédent). L'utilisation a NotificationManagerfonctionné comme je m'y attendais.
user149408
224

Lorsque vous souhaitez mettre à jour une notification définie par startForeground (), créez simplement une nouvelle notification, puis utilisez NotificationManager pour la notifier.

Le point clé est d'utiliser le même identifiant de notification.

Je n'ai pas testé le scénario d'appels répétés à startForeground () pour mettre à jour la notification, mais je pense que l'utilisation de NotificationManager.notify serait mieux.

La mise à jour de la notification ne supprimera PAS le service du statut de premier plan (cela ne peut être fait qu'en appelant stopForground);

Exemple:

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 documentation déclare

Pour configurer une notification afin qu'elle puisse être mise à jour, émettez-la avec un identifiant de notification en appelant NotificationManager.notify(). Pour mettre à jour cette notification après l'avoir émise, mettez à jour ou créez un NotificationCompat.Builderobjet, créez un Notificationobjet à partir de celui-ci et émettez le Notificationavec le même ID que vous avez utilisé précédemment. Si la notification précédente est toujours visible, le système la met à jour à partir du contenu de l' Notificationobjet. Si la notification précédente a été rejetée, une nouvelle notification est créée à la place.

Luca Manzo
la source
35
CECI EST LA BONNE RÉPONSE! La réponse ci-dessus est très fausse et trompeuse. Vous n'avez pas besoin de redémarrer votre service juste pour mettre à jour une notification idiote.
Radu
7
@Radu Bien que je convienne que c'est la réponse optimale (cela évite le chemin de code légèrement plus long emprunté par la réponse de Commonsware), vous vous trompez sur ce que fait la réponse de Commonsware - start / stopForegound ne démarre / arrête pas le service, ils affectent juste son premier plan .
Stevie
@Stevie Merci pour cela Stevie, vous avez probablement raison. Pourtant, je ne voudrais pas jouer avec ça non plus!
Radu
Appeler le prospect notify()ou les startForeground()deux pour appeler onStartCommand().
M. Reza Nasirloo
10
Le problème avec l'utilisation NotificationManagerpour mettre à jour une notification affichée avec startForegroundest que l'appel stopForegroundne supprimera plus la notification. Le mettre à jour avec un autre appel pour startForegroundéviter ce problème.
Tad le
21

Amélioration de la réponse de Luca Manzo dans Android 8.0+ lors de la mise à jour de la notification, il émettra un son et s'affichera en tant que Heads-up.
pour éviter que vous deviez ajoutersetOnlyAlertOnce(true)

donc le code est:

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);
}
humilié
la source
Tu as sauvé ma journée. Merci
Rubén Viguera
1
Mot-clé manquant, il devrait y avoirnew NotificationChannel
7h le
5

voici le code pour le faire dans votre service . Créez une nouvelle notification, mais demandez au gestionnaire de notifications de notifier le même identifiant de notification que celui utilisé dans startForeground.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

pour des exemples de codes complets, vous pouvez vérifier ici:

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

Daniel Kao
la source
Je ne suis pas sûr que cela maintiendra le statut de premier plan de startService.
Martin Marconcini
@Daniel Kao Votre solution ne démarre pas un service de premier plan
IgorGanapolsky
4
Corrigez-moi si je me trompe, mais les gens qui votent pour cette réponse pourraient-ils être plus descriptifs quant à ce qui ne va pas? La question ne demande pas comment démarrer un service de premier plan, mais comment mettre à jour une notification d'un service de premier plan. C'est effectivement la même réponse que Luca qui, selon les gens, fonctionne et maintient le statut de premier plan.
TheIT
@TheIT Cela ne fonctionne pas. Le statut de la notification devient not foregroundpour foreground createdmessage.
Vyacheslav
1
Cela entraînerait une notification en double, car startForeground () a déjà été appelée.
IgorGanapolsky
2

Il semble qu'aucune des réponses existantes ne montre comment gérer le cas complet - pour démarrerForeground s'il s'agit du premier appel, mais mettre à jour la notification pour les appels suivants.

Vous pouvez utiliser le modèle suivant pour détecter le bon cas:

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

De plus, vous devez créer la notification et le canal comme dans les autres réponses:

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
la source