Android 应用被杀后Notification不取消问题及应用深杀和浅杀时Service生命周期情况

项目中有如下需求:后台service进行导入操作,要更新Notification。当运行系统清理使应用被杀时,Notification无法取消,仍然在通知栏显示。为解决这个问题进行了如下探索:

首先想到利用service的startForeground()来更新通知栏,这样当应用被杀掉时候Notification可以一起被去掉。但针对项目的需求:service可以同时导入多个文件,并且会对应显示多个通知。这种情况下用service.startForeground()更新通知栏时候,当应用被杀时候之后cancel掉最后一次调用startForeground对应id的Notification,而其他通知仍然不能被取消。

继续探索用其他方式取消通知栏:在进程被杀掉的时候,会调用service的哪些生命周期函数呢?service的onDestroy()方法只有在调用Context的stopService()或Service的stopSelf()后才会被调用,在应用被杀时候Service的onDestroy()不会被执行。

我们发现service的 onTaskRemoved()方法,该方法何时被调用呢?方法的注释说明是这么写的:

/**
* This is called if the service is currently running and the user has
* removed a task that comes from the service's application.  If you have
* set {@linkandroid.content.pm.ServiceInfo#FLAG_STOP_WITH_TASK ServiceInfo.FLAG_STOP_WITH_TASK}
* then you will not receive this callback; instead, the service will simply
* be stopped.
*
*@paramrootIntentThe original root Intent that was used to launch
* the task that is being removed.
*/

public voidonTaskRemoved(Intent rootIntent) {
}

注释表明onTaskRemoved()方法在当用户移除应用的一个Task栈时被调用。也就是当用户在最近任务界面把该应用的一个task划掉时,或者在最近任务界面进行清理时。这两种情况下onTaskRemoved()都会被调用,但在大多Android机型上,这两种情况有所不同:第一种情况即应用被浅杀(用户只划掉这一个Task),该Task栈会被清理,但如果有后台service在运行,该应用的进程不会被杀掉,后台service仍然在运行。第二种即应用被深杀(用户在最近任务界面直接按清理按钮),该应用的进程会被直接杀掉,后台的service当然也停止了。对于不同的手机品牌和机型在最近任务进行各种清理时过程可能不太一样,但应用浅杀和深杀对于所有Android手机都是有普遍意义的。

下面我们分析在应用被浅杀和被深杀以及先浅杀再深杀后的生命周期:

浅杀:

04-21 17:55:13.733 8264-8264/com.qintong.test D/qintong: vCardService onTaskRemoved.

深杀:
会出现两种情况:
(a).

04-26 16:20:00.349 32674-32674/? D/qintong: Service onTaskRemoved.
04-26 16:21:01.621 2936-2936/? D/qintong: Service is being created.
04-26 16:21:01.628 2936-2936/? D/qintong: Service onStartCommand.

(b).

04-21 17:59:58.397 8264-8264/com.qintong.test D/qintong: Service onCreate.
04-21 17:59:58.404 8264-8264/com.qintong.test D/qintong: Service onTaskRemoved.

浅杀+深杀 (service 的 onStartCommand 返回 STICKY):

04-21 18:05:12.717 8264-8264/com.qintong.test D/qintong: Service onTaskRemoved.
04-21 18:05:29.214 9207-9207/com.qintong.test D/qintong: Service onCreate.
04-21 18:05:29.223 9207-9207/com.qintong.test D/qintong: Service onStartCommand.

我们来分析这几种情况:
(1).浅杀时:应用进程没被杀掉,service仍然在执行,service的onTaskRemoved()立即被调用。

(2).深杀时:有两种情况:第一种情况是深杀后直接调用onTaskRemoved()且service停止,过段时间后service重启调用其onCreate()和onStartCommand()。第二种是应用的进程被杀掉,过一会后service的onCreate()方法被调用,紧接着onTaskRemoved()被调用。由于被深杀后应用的进程立刻停止了,所以service的onTaskRemoved()无法被立即调用。而过若干秒后,service重启,onCreate()被调用,紧接着onTaskRemoved()被调用。而这里service的其他方法并没有被调用,即使onStartCommand()返回STICKY,service重启后onStartCommand()方法也没有被调用。

(3).浅杀+深杀时(service 的 onStartCommand 返回 STICKY):onTaskRemoved()立刻被调用(浅杀后),深杀后过段时间onCreate()和onStartCommand()相继被调用。执行浅杀Task被清理,应用的进程还在,onTaskRemoved()被调用,过程与(1)一样。再执行深杀:由于该应用的Task栈已经没有了,所有再深杀onTaskRemoved()不会再被调用,深杀后service停止。而由于实验时候onStartCommand()返回STICKY,所有service过段时间会被再次启动,执行了onCreate()方法和onStartCommand()方法。

所以综上所述,service的onTaskRemoved()在应用浅杀后会被立即调用而在service被深杀后,会直接调用onTaskRemoved或service会被重启并调用onTaskRemoved()。

回到我们的问题:应用被杀后,如何取消Notification:
我们先看最后的解决方案,在来分析为何能work。
service的代码如下:

@Override
public void onCreate() {
  super.onCreate();
  mBinder=newMyBinder();
  if(DEBUG) Log.d(LOG_TAG,"vCardService is being created.");
  mNotificationManager= ((NotificationManager)getSystemService(NOTIFICATION_SERVICE));
  initExporterParams();
}

@Override
public int onStartCommand(Intent intent, intflags, intid) {
  if(DEBUG) Log.d(LOG_TAG,"vCardService onStartCommand.");
  mNotificationManager.cancelAll();
  returnSTART_STICKY;
}

@Override
public void onTaskRemoved(Intent rootIntent) {
  if(DEBUG) Log.d(LOG_TAG,"vCardService onTaskRemoved.");
  mNotificationManager.cancelAll();
  super.onTaskRemoved(rootIntent);
}

如上代码,在浅杀时候:只执行onTaskRemoved(),通知被取消,但service仍然在运行,所以还会继续发通知,正常运行。
深杀时:第一种情况直接调用onTaskRemoved()且service停止,通知被取消。第二种情况,进程被杀掉,几秒后service重启,onCreate() -> onTaskRemoved(),运行结果就是深杀后过几秒后Notification被取消。
浅杀+深杀时:浅杀后onTaskRemoved()被调用,service仍在运行,通知仍然在更新。深杀时,onCreate() -> onStartCommand(),在onStartCommand()时候取消通知。
另外,mNotificationManager.cancelAll()会清除应用的所有通知,如果应用想保留和该service无关其他通知,可以调用mNotificationManager.cancel(String tag, int id)或cancel(int id)清除指定通知。
当然,还可以有另一种方式:浅杀时后就把service后台执行的任务停止,并清理notification,我们可以根据需求来选择。

补充:
疑问:1.为啥有时候深杀不立即调用onTaskRemoved(),而是在重启之后调用的呢?
stackoverflow上的答复:https://stackoverflow.com/questions/32224233/ontaskremoved-called-after-oncreate-in-started-service-on-swipe-out-from-recent/41506752
大意是service执行较重UI操作时候service不会立即停止,而新的service会启动。不太确定这个解释的正确性......
2.为何servive.startForeground()添加的Notification可以在service被杀死后去掉呢?我们分析源码:ActiveServices中killServicesLocked()->scheduleServiceRestartLocked()中调用了r.cancelNotification(),清除了notification:

    public void cancelNotification() {
        if (foregroundId != 0) {
            // Do asynchronous communication with notification manager to
            // avoid deadlocks.
            final String localPackageName = packageName;
            final int localForegroundId = foregroundId;
            ams.mHandler.post(new Runnable() {
                public void run() {
                    INotificationManager inm = NotificationManager.getService();
                    if (inm == null) {
                        return;
                    }
                    try {
                        inm.cancelNotificationWithTag(localPackageName, null,
                                localForegroundId, userId);
                    } catch (RuntimeException e) {
                        Slog.w(TAG, "Error canceling notification for service", e);
                    } catch (RemoteException e) {
                    }
                }
            });
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容