Wie aktualisiere ich den Benachrichtigungstext für einen Vordergrunddienst in Android?

133

Ich habe ein Vordergrund-Service-Setup in Android. Ich möchte den Benachrichtigungstext aktualisieren. Ich erstelle den Dienst wie unten gezeigt.

Wie kann ich den Benachrichtigungstext aktualisieren, der in diesem Vordergrunddienst eingerichtet wurde? Was ist die beste Vorgehensweise zum Aktualisieren der Benachrichtigung? Jeder Beispielcode wäre willkommen.

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

    }

Ich erstelle den Dienst in meiner Hauptaktivität wie folgt:

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

Antworten:

61

Ich würde denken, dass ein startForeground()erneuter Anruf mit derselben eindeutigen ID und einer Notificationmit den neuen Informationen funktionieren würde, obwohl ich dieses Szenario nicht ausprobiert habe.

Aktualisieren: Basierend auf den Kommentaren sollten Sie NotifcationManager verwenden, um die Benachrichtigung zu aktualisieren, und Ihr Dienst bleibt weiterhin im Vordergrundmodus. Schauen Sie sich die Antwort unten an.

CommonsWare
quelle
1
Könnten Sie mir bitte ein Beispiel geben, wie ich das aus meiner Aktivität heraus nennen würde? Ich konnte in meinem Vordergrunddienst kein gutes Beispiel zum Aufrufen von Methoden finden.
Luke
1
@ Luke: Es gibt eine beliebige Anzahl von Mustern für die Nutzung eines Dienstes, und ich habe keine Ahnung, was Ihr folgt. Wenn Sie anrufen startService(), um einen Befehl an den Dienst zu übergeben, rufen Sie ihn startService()erneut an, um ihn anzuweisen, seinen Text zu aktualisieren. Wenn Sie aufrufen bindService(), fügen Sie Ihrer API eine Methode hinzu, damit der Dienst seinen Text aktualisiert. Oder überlegen Sie, ob der Dienst selbst die Entscheidung treffen soll, ob der Text aktualisiert werden soll oder nicht. Oder vielleicht ist der Text ein Text SharedPeference, auf dem der Dienst einen Listener hat. Es ist unmöglich, Ihnen abstrakte Ratschläge zu geben.
CommonsWare
9
Zur weiteren Verdeutlichung: Sie können keine cancel()Benachrichtigung von festlegen startForeground(). Sie müssen den Vordergrundstatus des Dienstes selbst entfernen (mit, stopForeground()wenn Sie möchten, dass der
Tickertext erneut angezeigt wird
4
Ich habe diese Antwort abgelehnt, da sie eindeutig falsch ist: developer.android.com/training/notify-user/managing.html Bitte @CommonsWare erwägen, diese Antwort zu entfernen, da Ihre hohe Reputationsbewertung diese Antwort zur "heiligen Wahrheit" für die macht Gelegenheitsbrowser. Vielen Dank.
HYS
2
Hat bei mir nicht funktioniert (obwohl ich mich erinnere, dass ich in einem früheren Projekt dieselbe Methode verwendet habe). Die Verwendung NotificationManagerhat wie erwartet funktioniert.
user149408
224

Wenn Sie eine von startForeground () festgelegte Benachrichtigung aktualisieren möchten, erstellen Sie einfach eine neue Benachrichtigung und benachrichtigen Sie sie dann mit NotificationManager.

Der entscheidende Punkt ist die Verwendung derselben Benachrichtigungs-ID.

Ich habe das Szenario des wiederholten Aufrufs von startForeground () zum Aktualisieren der Benachrichtigung nicht getestet, aber ich denke, dass die Verwendung von NotificationManager.notify besser wäre.

Durch das Aktualisieren der Benachrichtigung wird der Dienst NICHT aus dem Vordergrundstatus entfernt (dies kann nur durch Aufrufen von stopForground erfolgen).

Beispiel:

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

In der Dokumentation heißt es

Um eine Benachrichtigung so einzurichten, dass sie aktualisiert werden kann, geben Sie ihr eine Benachrichtigungs-ID, indem Sie anrufen NotificationManager.notify(). Um diese Benachrichtigung zu aktualisieren, nachdem Sie sie ausgestellt haben, aktualisieren oder erstellen Sie ein NotificationCompat.BuilderObjekt, erstellen Sie ein NotificationObjekt daraus und geben Sie Notificationdie gleiche ID aus, die Sie zuvor verwendet haben. Wenn die vorherige Benachrichtigung weiterhin sichtbar ist, aktualisiert das System sie vom Inhalt des NotificationObjekts. Wenn die vorherige Benachrichtigung verworfen wurde, wird stattdessen eine neue Benachrichtigung erstellt.

Luca Manzo
quelle
35
DAS IST DIE RICHTIGE ANTWORT! Die obige Antwort ist sehr falsch und irreführend. Sie müssen Ihren Dienst nicht neu starten, nur um eine dumme Benachrichtigung zu aktualisieren.
Radu
7
@Radu Obwohl ich der Meinung bin, dass dies die optimale Antwort ist (es vermeidet den etwas längeren Codepfad, den die Antwort von Commonsware einnimmt), irren Sie sich darüber, was die Antwort von Commonsware bewirkt - start / stopForegound startet / stoppt den Dienst nicht, sondern wirkt sich nur auf seinen Vordergrund aus .
Stevie
@Stevie Danke für diesen Stevie, du hast wahrscheinlich recht. Trotzdem würde ich mich auch nicht damit anlegen!
Radu
Das Anrufen des notify()oder startForeground()beider führen zum Anruf onStartCommand().
M. Reza Nasirloo
10
Das Problem bei NotificationManagerder Aktualisierung einer angezeigten Benachrichtigung startForegroundbesteht darin, dass durch einen Anruf stopForegrounddie Benachrichtigung nicht mehr entfernt wird. Durch Aktualisieren mit einem anderen Aufruf startForegroundwird dieses Problem vermieden.
Tad
21

Wenn Sie die Antwort von Luca Manzo in Android 8.0+ verbessern, wird beim Aktualisieren der Benachrichtigung ein Ton ausgegeben und als Heads-up angezeigt.
um zu verhindern, dass Sie hinzufügen müssensetOnlyAlertOnce(true)

Der Code lautet also:

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
quelle
Du hast meinen Tag gerettet. Vielen Dank
Rubén Viguera
1
Fehlendes Schlüsselwort sollte es seinnew NotificationChannel
7hny
5

Hier ist der Code, um dies in Ihrem Dienst zu tun . Erstellen Sie eine neue Benachrichtigung, aber bitten Sie den Benachrichtigungsmanager, dieselbe Benachrichtigungs-ID zu benachrichtigen, die Sie in startForeground verwendet haben.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

Die vollständigen Beispielcodes finden Sie hier:

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

Daniel Kao
quelle
Ich bin nicht sicher, ob dies den Vordergrundstatus von startService beibehält.
Martin Marconcini
@ Daniel Kao Ihre Lösung startet keinen Vordergrunddienst
IgorGanapolsky
4
Korrigieren Sie mich, wenn ich falsch liege, aber könnten die Leute, die diese Antwort ablehnen, bitte genauer beschreiben, was daran falsch ist? Die Frage lautet nicht, wie ein Vordergrunddienst gestartet werden soll, sondern wie eine Benachrichtigung über einen Vordergrunddienst aktualisiert werden soll. Dies ist praktisch die gleiche Antwort wie Luca, der die Leute zustimmen, dass sie funktioniert und den Vordergrundstatus beibehält.
TheIT
@ TheIT Es funktioniert nicht. Der Status der Benachrichtigung wird not foregroundfür foreground createdNachricht.
Vyacheslav
1
Dies würde zu einer doppelten Benachrichtigung führen, da startForeground () bereits aufgerufen wurde.
IgorGanapolsky
2

Es scheint, dass keine der vorhandenen Antworten zeigt, wie der gesamte Fall behandelt werden soll - StartForeground, wenn es der erste Anruf ist, aber die Benachrichtigung für nachfolgende Anrufe aktualisieren.

Sie können das folgende Muster verwenden, um den richtigen Fall zu erkennen:

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

Zusätzlich müssen Sie die Benachrichtigung und den Kanal wie in anderen Antworten erstellen:

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
quelle