Como atualizo o texto da notificação para um serviço em primeiro plano no Android?


133

Eu tenho uma configuração de serviço em primeiro plano no Android. Gostaria de atualizar o texto da notificação. Estou criando o serviço, como mostrado abaixo.

Como posso atualizar o texto de notificação configurado neste serviço em primeiro plano? Qual é a melhor prática para atualizar a notificação? Qualquer código de exemplo seria apreciado.

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

    }

Estou criando o serviço na minha atividade principal, como mostrado abaixo:

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

Respostas:


61

Eu acho que chamar startForeground()novamente com o mesmo ID exclusivo e um Notificationcom as novas informações funcionaria, embora eu não tenha tentado esse cenário.

Atualização: com base nos comentários, você deve usar o NotifcationManager para atualizar a notificação e seu serviço continua no modo de primeiro plano. Dê uma olhada na resposta abaixo.


1
Você poderia me dar um exemplo de como eu chamaria isso da minha Atividade? Não consegui encontrar uma boa amostra de como chamar métodos no meu serviço em primeiro plano.
— Lucas

1
@ Lucas: Há vários padrões para usar um serviço, e eu não tenho ideia do que o seu está seguindo. Se você estiver ligando startService()para passar um comando para o serviço, basta ligar startService()novamente para solicitar a atualização do texto. Ou, se você estiver chamando bindService(), adicione um método à sua API para que o serviço atualize seu texto. Ou considere se o serviço em si deve ser quem decide se deve ou não atualizar o texto. Ou talvez o texto seja SharedPeferenceaquele em que o serviço tenha um ouvinte. É impossível fornecer conselhos precisos em resumo.
— CommonsWare

9
para esclarecer mais: você não pode cancel()definir uma Notificação por startForeground(). Você tem que remover o status de primeiro plano da própria (usando o serviço stopForeground()se você quiser fazer o texto ticker aparecer novamente eu perdi horas porque estas respostas me levou a acreditar que era na verdade possível..
— slinden77

4
Eu diminuí a votação desta resposta, pois ela está claramente errada: developer.android.com/training/notify-user/managing.html Por favor, @CommonsWare considere remover essa resposta, pois sua pontuação de alta reputação faz com que essa resposta seja a "verdade sagrada" para o navegador casual. Obrigado.
— HYS

2
Não funcionou para mim (embora eu me lembre de usar esse mesmo método em um projeto anterior). Usando NotificationManagerfuncionou como eu esperava.
— user149408

224

Quando você quiser atualizar um conjunto de Notificações por startForeground (), simplesmente crie uma nova observação e use o NotificationManager para notificá-la.

O ponto principal é usar o mesmo ID de notificação.

Não testei o cenário de chamar repetidamente startForeground () para atualizar a Notificação, mas acho que usar o NotificationManager.notify seria melhor.

A atualização da notificação NÃO removerá o serviço do status de primeiro plano (isso pode ser feito apenas chamando stopForground);

Exemplo:

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

A documentação declara

Para configurar uma notificação para que possa ser atualizada, emita-a com um ID de notificação ligando para NotificationManager.notify(). Para atualizar esta notificação após a sua emissão, atualize ou crie um NotificationCompat.Builderobjeto, crie um Notificationobjeto a partir dele e emita Notificationo mesmo com o mesmo ID usado anteriormente. Se a notificação anterior ainda estiver visível, o sistema a atualizará do conteúdo do Notificationobjeto. Se a notificação anterior foi descartada, uma nova notificação será criada.


35
ESSA É A RESPOSTA CORRETA! A resposta acima é muito errada e enganosa. Você não precisa reiniciar o serviço apenas para atualizar uma notificação boba.
— Radu

7
@Radu Embora eu concorde que essa é a resposta ideal (evita o caminho de código um pouco mais longo adotado pela resposta do Commons), você está enganado sobre o que a resposta do Commons faz - start / stopForegound não inicia / interrompe o serviço, apenas afeta seu primeiro plano .
— Stevie

@ Stevie Obrigado por Stevie, você provavelmente está certo. Ainda assim, eu não iria mexer com isso também!
— Radu

Chamar o notify()ou os startForeground()dois leva à chamada onStartCommand().
— M. Reza Nasirloo

10
O problema de usar NotificationManagerpara atualizar uma notificação mostrada startForegroundé que a chamada stopForegroundnão removerá mais a notificação. Atualizá-lo com outra chamada para startForegroundevitar esse problema.
— Tad

21

Ao melhorar a resposta de Luca Manzo no Android 8.0+, ao atualizar a notificação, ele emitirá som e será exibido como Heads-up.
para impedir que você precise adicionarsetOnlyAlertOnce(true)

então o código é:

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

Você salvou o meu dia. Obrigado
— Rubén Viguera

1
new NotificationChannel
— Palavra-

5

aqui está o código para fazê-lo em seu serviço . Crie uma nova notificação, mas solicite ao gerente de notificações para notificar o mesmo ID de notificação usado no startForeground.

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

notificationManager.notify(ONGOING_NOTIFICATION, notify);

para obter códigos de amostra completos, você pode conferir aqui:

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


Não tenho certeza se isso manterá o status de primeiro plano de startService.
— Martin Marconcini

@ Daniel Kao Sua solução não inicia um serviço de primeiro plano
— IgorGanapolsky

4
Corrija-me se eu estiver errado, mas as pessoas que votaram nessa resposta podem ser mais descritivas quanto ao que há de errado com ela? A pergunta não pergunta como iniciar um serviço em primeiro plano, mas como atualizar uma notificação de um serviço em primeiro plano. Esta é efetivamente a mesma resposta que Luca, que as pessoas concordam que funciona e mantém o status de primeiro plano.
— TheIT

@TheIT Não funciona. O status da notificação se torna not foregroundpara foreground createdmensagem.
— Vyacheslav

1
Isso resultaria em uma notificação duplicada, porque startForeground () já foi chamado.
— IgorGanapolsky

2

Parece que nenhuma das respostas existentes mostra como lidar com o caso completo - para iniciar o Foreground se for a primeira chamada, mas atualizar a notificação para as chamadas subseqüentes.

Você pode usar o seguinte padrão para detectar o caso correto:

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

Além disso, você precisa criar a notificação e o canal como em outras respostas:

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);
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.