Android 如何在关闭的情况下打开notification

0.前言

众所周知,通过消息推送下发消息,目前移动端APP产品运营最重要的运营手段之一。而如何解决这方面的问题,优化这部分的体验,是我们必须去解决的。

1.发现问题

最开始,运营同学反馈了一个问题:通知栏消息点击后无响应,无法打开应用,跳转到活动页面。通过查询日志信息,很快技术同学就回复说,这个问题是因为接收到消息后,kill掉应用导致的。

2.Why?

通过测试了几款不同的机型,发现部分机型,在清理应用时,不会清理这个应用对应的消息弹窗。继续查看相关代码,发现应用是通过接收广播来处理点击弹窗的。而注册广播时是采用的动态注册的方式,导致kill掉app后不能接收广播,自然就处理不了点击事件。

事件

表现

应用在前台,推送消息

app内展示消息弹窗

应用在后台,推送消息

通知栏展示消息,能正常点击

应用在后台,推送消息,kill掉app

部分机型不会清理通知栏中对应app的消息,点击无反应

3.解决问题

为了解决这个问题,我们试着给出了两个思路:

  1. 在app被kill的时候,我们主动去清理存在的弹窗。
  2. 支持kill掉应用后,点击弹窗打开应用。
3.1 思路一

在app被kill的时候,我们主动去清理存在的弹窗。

这个按理说是可行的,我们只需要去找出kill app时调用的方法,然后在方法里手动清理消息弹窗即可。

经过查找相关资料,我们了解到,Service的onTaskRemoved()方法在用户移除应用的一个Task栈时被调用。也就是当用户在最近任务界面把该应用的一个task划掉时,或者在最近任务界面进行清理时,这两种情况下onTaskRemoved()都会被调用
那好,我们就写一个RemoveNotificationService,并重写一下其onTaskRemove()方法,在这里我们主动清理消息弹窗。

public class RemoveNotificationService extends Service {

...

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        MLog.d(TAG, "RemoveNotificationService onTaskRemoved.");
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        if (notificationManager != null) {
            notificationManager.cancelAll();
        }
        super.onTaskRemoved(rootIntent);
    }
    
...

}

我们运行代码,发现kill app时,通知栏确实被清理了,这代码是有效果的。
但我们再多测试几次就会发现,有时会出现这样的Log日志

ActivityManager: Stopping service due to app idle: u0a552 -1m22s259ms ....

如果出现了这行日志后,再kill app,这时onTaskRemoved没被调用,通知信息也没被清理掉。
通过查询这行日志我们了解到,应用在后台超过1min,IDLE_UIDS_MSG就会执行,也就会将服务销毁。1

这是因为Google对Android8.0的系统后台可执行的操作有了更加严格对限制。2

为了解决这一问题,我们可以将这个Service的优先级提高,比如采用前台服务的方式运行服务。如果使用前台服务的方式运行,那么通知栏必定会显示出一行通知,xxx服务正在运行。很显然,我们不希望用户看到这一通知(当然仍然有一些方法可以关闭这通知来欺骗用户),而且对于这个功能点,也没必要使用优先级如此高的前台服务,所以这个方案先放一边。

3.2 思路二

支持kill掉应用后,点击弹窗打开应用。
为了支持这样一个功能,首先我们需要了解之前为啥不支持。前文提到,处理消息的点击是通过广播的方式,而app被关闭时,动态广播失效,所以导致了无法处理点击。
我们首先想到,既然动态广播不行,那么咋不试一试静态广播呢。我们知道静态广播在app关闭后也可以接收广播信息。我们在AndroidManifest.xml中注册好了广播,再来试一试,发现不行,接收不了广播!为啥啊?
原来还是前文提到的,Google在Android8.0对广播也做了限制:Android 8.0 或更高版本的应用无法继续在其清单中为隐式广播注册广播接收器。3

emm,既然不能用隐式的,那么,我们再改用一下显式的试一试?嗯,这次好像OK了。kill app 后,消息能正常打开了。


  1. startService启动服务,应用置于后台超过1min,服务被销毁 ↩︎
  2. Google文档:后台 Service 限制 ↩︎
  3. Google文档:广播限制 ↩︎