当系统自动结束服务时,如何取消Android通知

6

我有一个应用程序,实现了MediaBrowserServiceCompat。播放音乐时,它在前台运行,并显示系统无法取消的媒体控制通知。暂停后,它退出前台,通知被保留。这是标准的操作。

当此服务被系统自动终止时,通知不会被移除。

您可以通过将应用程序置于非前台暂停状态,离开应用程序并发出此命令来模拟此过程:

adb shell am kill com.myapp.package

通知仍然存在。您会收到以下Logcat消息:
W/ActivityManager: Scheduling restart of crashed service com.myapp.package/com.myapp.package.playback.platform.AndroidMediaService

所有明显的钩子(例如onDestroyonTaskRemoved等)在服务上似乎都没有被调用 - 它似乎被直接终止了。由于服务是使用START_NOT_STICKY启动的,因此我们也不会在新实例上调用onCreate

这个问题的部分解决方案是使服务具有“粘性”,并在服务重新启动时取消通知。然而,这可能需要相当长的时间才能实现,这段时间内通知无法工作,因此并不理想。它还可能产生进一步的后果。

其他应用程序似乎没有遇到这个问题。

重复这种情况(暂停,离开应用程序)并执行命令,例如在Google Play音乐上:

adb shell am kill com.google.android.music

或者 Spotify:

adb shell am kill com.spotify.music

你发现他们的通知在被拆除时立即消失,好像它们被取消了。

他们可能做了什么来实现这一点?


更新:我目前正在查看adb dumpsys,我发现其他服务(例如Play Music)即使在后台也保留其foregroundNoti属性。我的没有。因此,我怀疑在暂停音频时对服务执行的操作有所不同。 - Rob Pridham
3个回答

3
我已经找到了答案,当然,这是我的错。其他应用程序没有做任何特殊的事情-这是平台行为,而我们是异常情况。
我迟来才发现,在从前台切换到后台时,我们调用了:
ServiceCompat.stopForeground(service, ServiceCompat.STOP_FOREGROUND_DETACH)

我通过dumpsys发现了这个问题,例如:

adb shell dumpsys activity services com.google.android.music

我发现了一些差异。

我的应用程序:

ServiceRecord{294309d u0 com.myapp.debug/com.myapp.playback.platform.AndroidMediaService}
    intent={cmp=com.myapp.debug/com.myapp.playback.platform.AndroidMediaService}
    packageName=com.myapp.debug
    processName=com.myapp.debug:main
    baseDir=/data/app/com.myapp.debug-GM-nBaeXA_e47EmJKnM27g==/base.apk
    dataDir=/data/user/0/com.myapp.debug
    app=ProcessRecord{83fb4ffd0 23567:com.myapp.debug:main/u0a381}
    createTime=-27s211ms startingBgTimeout=--
    lastActivity=-26s911ms restartTime=-27s211ms createdFromFg=true
    startRequested=true delayedStop=false stopIfKilled=true callStart=true lastStartId=2

播放音乐:

ServiceRecord{8693ccb u0 com.google.android.music/.playback2.MusicPlaybackService}
    intent={cmp=com.google.android.music/.playback2.MusicPlaybackService}
    packageName=com.google.android.music
    processName=com.google.android.music:main
    baseDir=/data/app/com.google.android.music-lrn1VQr_3_RDi5PMbqozdw==/base.apk
    dataDir=/data/user/0/com.google.android.music
    app=ProcessRecord{9a3c417d0 22921:com.google.android.music:main/u0a191}
    isForeground=false foregroundId=1 foregroundNoti=Notification(channel=playback_v1 pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x2 color=0xffff5722 category=transport actions=5 vis=PUBLIC semFlags=0x0 semPriority=0 semMissedCount=0)
    createTime=-9m33s792ms startingBgTimeout=--
    lastActivity=-20s825ms restartTime=-9m33s792ms createdFromFg=true
    startRequested=true delayedStop=false stopIfKilled=false callStart=true lastStartId=1

在我的应用程序日志中可以看到缺少的“foregroundNoti”属性。这是detach调用的产物。我们很久以前选择这样做是为了避免通知被系统收集。回想起来,我们应该像其他应用程序一样允许其被杀死。

0

尝试在清单文件中添加此标志,以便在应用程序被杀死时停止服务:

<service
    ...
    android:stopWithTask="true"/>

如果它没有移除通知,将其设置为false应该会在服务中触发回调,在那里您应该能够移除通知。
@Override 
public void onTaskRemoved(Intent rootIntent) {
    ...
}

此外,这个答案可能与此有关。

更新

由于上述方法没有奏效,我查看了原生音乐应用程序在清单中的声明方式,他们没有使用我建议的方法。 他们使用的是另一种方式,可能与此有关。

<service
   ...
   android:process=":main" />

在所有的服务中以及改变通知的接收器中都值得尝试。


1
不幸的是,即使将 stopWithTask 设置为 true,它似乎也无法实现目标,而当设置为 false 时,只有在用户从最近使用列表中滑动应用程序时才会调用 onTaskRemoved - Rob Pridham
我跟你一样的想法,我不知道他们为什么要给进程命名,但是将应用程序和服务进程对齐(到:main),似乎也没有改变行为。 - Rob Pridham

0

你应该将通知的优先级设置为"LOW",并在Service销毁方法中使用以下代码手动删除通知:

 notificationManager.cancelAll();

请纠正我,但我认为当服务被 Android 杀死时,onDestroy() 方法不会被调用。 - Adam.M

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