调用stopForeground(false)后允许取消通知

21
我有一个媒体服务,使用startForeground()在播放开始时显示通知。当播放时,它有暂停/停止按钮;当暂停时,它有播放/停止按钮。

我有一个媒体服务,使用 startForeground() 在播放开始时显示通知。当播放时,它有暂停 / 停止按钮,当暂停时,它有播放 / 停止按钮。

NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
// setup...

Notification n = mBuilder.build();

if (state == State.Playing) {
    startForeground(mId, n);
}
else {
    stopForeground(false);
    mNotificationManager.notify(mId, n);        
}

这里的问题是,在通知处于暂停状态时,如果要显示/更新通知,您应该允许将其删除。 mBuilder.setOngoing(false)似乎没有效果,因为之前的startForeground会覆盖它。

使用相同的代码调用stopForeground(true);可以正常工作,但是通知会闪烁,因为它被销毁并重新创建。有没有办法“更新”从startForeground创建的通知,以便在调用stop后可以将其删除?

编辑:根据要求,这里是创建通知的完整代码。每当服务播放或暂停时都会调用createNotification。

private void createNotification() {
    NotificationCompat.Builder mBuilder = 
            new NotificationCompat.Builder(this)
    .setSmallIcon(R.drawable.ic_launcher)
    .setContentTitle("No Agenda")
    .setContentText("Live stream");

    if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
    {
        if (state == State.Playing) {
            Intent pauseIntent = new Intent(this, MusicService.class);
            pauseIntent.setAction(ACTION_PAUSE);
            PendingIntent pausePendingIntent =     PendingIntent.getService(MusicService.this, 0, pauseIntent, 0);              

            mBuilder.addAction(R.drawable.pause, "Pause", pausePendingIntent);
            //mBuilder.setOngoing(true);
        }
        else if (state == State.Paused) {
            Intent pauseIntent = new Intent(this, MusicService.class);
            pauseIntent.setAction(ACTION_PAUSE);
            PendingIntent pausePendingIntent =  PendingIntent.getService(MusicService.this, 0, pauseIntent, 0);

            mBuilder.addAction(R.drawable.play, "Play", pausePendingIntent);
            mBuilder.setOngoing(false);
        }

        Intent stopIntent = new Intent(this, MusicService.class);
        stopIntent.setAction(ACTION_STOP);
        PendingIntent stopPendingIntent = PendingIntent.getService(MusicService.this, 0, stopIntent, 0);

        setNotificationPendingIntent(mBuilder);
        mBuilder.addAction(R.drawable.stop, "Stop", stopPendingIntent);
    }
    else
    {
        Intent resultIntent = new Intent(this, MainActivity.class);
        PendingIntent intent = PendingIntent.getActivity(this, 0, resultIntent, 0);

        mBuilder.setContentIntent(intent);
    }

    Notification n = mBuilder.build();

    if (state == State.Playing) {
        startForeground(mId, n);
    }
    else {
        stopForeground(true);
        mNotificationManager.notify(mId, n);
    }
}

@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setNotificationPendingIntent(NotificationCompat.Builder mBuilder) {
    Intent resultIntent = new Intent(this, MainActivity.class);

    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
    stackBuilder.addParentStack(MainActivity.class);
    stackBuilder.addNextIntent(resultIntent);

    PendingIntent resultPendingIntent =
            stackBuilder.getPendingIntent(
                0,
                PendingIntent.FLAG_UPDATE_CURRENT
            );

    mBuilder.setContentIntent(resultPendingIntent);
}

后续编辑:

以下评论中提到答案可能是“脆弱的”,截至Android 4.3发布时,startForeground的行为已经发生了变化。调用startForeground将强制您的应用程序在前台显示通知,并且只需使用要显示的通知来调用该方法即可。我没有测试,但是被接受的答案可能不再按预期工作。

至于在调用stopForeground时停止闪烁,我认为不值得与框架抗争。

这里有关于Android 4.3通知更改的一些额外信息


你能在尝试调用 setOngoing(false) 时展示你的代码吗?我怀疑通知没有被重建以获取更改,但从这个片段很难判断。 - dsandler
@dsandler 我已经添加了createNotification()的完整代码。 - Will Eddins
我还想指出目前我只在Jellybean上进行测试,即4.2.2版本(模拟器和手机),这也是我设置setOngoing(false)的地方。此外,当通知不是通过startForeground启动时(并且取消setOngoing(true)的注释)setOngoing(false)确实起作用。 - Will Eddins
6个回答

6

与其调用 stopService(true),调用 stopService(false) 将保留通知(没有持续状态),除非用户手动关闭或者服务停止。因此,只需调用 stopService(false) 并更新通知以显示暂停状态,现在用户可以关闭通知了。这也可以防止闪烁,因为我们不会重新创建通知。


6
您可以考虑使用不同的方法。
由于您应该为这样的任务(媒体播放)使用前台服务,我建议您继续执行startForeground(),但是不要向其传递通知,只需设置id为0,通知为null,如下:startForeground(0, null);
这样前台服务将不显示任何通知。
现在,为了您的目的,您可以使用常规通知并更新它们的状态(正在进行、布局、文本等),这样您就不会依赖于前台服务的通知行为。
希望这可以帮助到您。

1
这听起来像是正确的路径,但传递一个空的通知在startForeground()上会引发IllegalArgumentException。以下问题似乎表明了同样的情况:https://dev59.com/lGgu5IYBdhLWcg3w8bds - Will Eddins
2
看起来相当脆弱。我认为Google希望在前台服务中有一个通知,所以我不会指望当前的行为在未来的Android版本中能够正常工作...我建议使用stopForeground(true)并接受通知闪烁。 - jonasb
@WillEddins,您能告诉我如何在正在进行的通知中使用切换按钮吗?实际上我无法弄清楚。我已经卡住了两天了。如果能帮忙解决问题将不胜感激。 - Sanghati Mukherjee
@SanghatiMukherjee,您每次都在创建一个全新的通知,只是带有不同的操作。对最终用户来说,它看起来像是一个切换开关。 - Will Eddins
1
如@jonasb和@inistel所述,这个答案可能是“脆弱的”,因为在Android 4.3中,由于startForeground现在会创建自己的通知(如果没有传递通知),以尝试阻止恶意应用程序滥用前台状态,因此它可能不再起作用。有关更多信息,请参见我的问题底部的编辑。 - Will Eddins
显示剩余3条评论

3
根据文档,在Lollipop之前,这种行为是经过设计的。
应用程序定向到此版本或更高版本将获得以下新行为更改: ... 使用removeNotification false调用Service.stopForeground将修改仍然发布的通知,以使其不再强制进行。

1
在N版本之前,当stopForeground和startForeground按顺序调用时,通知必须重新出现。您需要再次显示通知,并在暂停后不将其变为前台通知。我建议您不要添加解决此问题的变通方法或黑客方法(虽然有变通方法,但可能会引入其他错误)。
在N+版本中,增加了一个标志ServiceCompat.STOP_FOREGROUND_DETACH。这将传递给stopForeground,并允许您保留当前通知在通知栏中。
另外,请不要忘记杀死服务(使用stopSelf()或其他方式)。因为如果用户划掉应用程序,您的应用主进程可能会保持运行。

0
我在寻找解决方案时遇到了这个问题,即使调用了“cancel”和“cancelAll”方法,持续的通知仍然没有被取消。
从这里的答案中我得到的帮助是停止前台服务。
因此,在取消通知之前,我确保广播一个意图来停止前台服务,然后通知被取消了!

0

Id 不能为0,所以我使用 stopForeground(false)。现在我能够移除通知了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接