如何在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);

Answers:


61

我认为startForeground()再次使用相同的唯一ID和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请@CommonsWare考虑删除此答案,因为您的高声誉得分使该答案成为“正确的答案”休闲浏览器。谢谢。
HYS

2
不适用于我(尽管我记得在上一个项目中使用了相同的方法)。使用NotificationManager按预期工作。
user149408

224

当您想通过startForeground()更新通知集时,只需构建一个新的通知,然后使用NotificationManager来通知它。

关键是要使用相同的通知ID。

我没有测试重复调用startForeground()来更新Notification的场景,但是我认为使用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);
}

文档的状态

要设置通知以便可以更新,请致电发出通知ID NotificationManager.notify()。要在发出通知后更新此通知,请更新或创建一个 NotificationCompat.Builder对象,Notification从中构建一个对象,然后使用Notification与您以前使用的ID相同的ID 发出。如果先前的通知仍然可见,则系统会根据Notification对象的内容对其进行更新。如果先前的通知已被取消,则会创建一个新的通知。


35
这是正确的答案!上面的答案是非常错误和误导的。您不需要重新启动服务,只需更新一个愚蠢的通知即可。
Radu

7
@Radu虽然我同意这是最佳答案(它避免了Commonsware的答案所采用的代码路径稍长),但您误认为Commonsware的答案是什么-start / stopForegound不会启动/停止服务,它们只会影响其前景。
Stevie 2014年

@Stevie感谢Stevie,您可能是对的。还是我也不会搞砸!
Radu 2014年

呼叫notify()startForeground()两者都导致呼叫onStartCommand()
M. Reza Nasirloo

10
NotificationManager用于更新显示的通知的问题startForeground在于,调用stopForeground将不再删除该通知。通过另一个调用来更新它startForeground可以避免该问题。
塔德2016年

21

更新通知时,在android 8.0+中改进Luca Manzo的答案,它会发出声音并显示为抬头。
以防止您需要添加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);
}

你救了我的日子。谢谢
鲁本Viguera

1
缺少关键字,应该有new NotificationChannel
7hny

5

这是在您的服务中执行此操作的代码。创建一个新通知,但是要求通知管理器通知您在startForeground中使用的相同通知ID。

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的前台状态。
Martin Marconcini 2013年

@Daniel Kao您的解决方案未启动前台服务
IgorGanapolsky

4
如果我错了,请纠正我,但是人们可以否决这个答案,请对它有什么问题进行更多描述。这个问题不是问如何启动前台服务,而是问如何更新前台服务的通知。这实际上与人们同意的Luca相同,并且可以维持前台地位。
TheIT

@TheIT它不起作用。通知的状态成为not foregroundforeground created消息。
Vyacheslav'1

1
这将导致重复的通知,因为已经调用了startForeground()
IgorGanapolsky

2

似乎所有现有的答案都没有显示如何处理全部情况-如果startForeground是第一个调用,但要更新通知以供后续调用使用。

您可以使用以下模式来检测正确的情况:

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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.