Persistent-持久应用的那些事儿
组内有一个项目,需求是开机就启动,初始化参数,并监听一些诸如BLE、车身信号、云端push等事件,trigger对应的business logics。
Review代码时,发现作者将所有的代码实现一股脑放到了Application的onCreate中,No Activity, No Servcie, Nothing but Application.关键是这些实现还是经过测试验证整个流程没问题的。询问了作者时候发现这一切都是因为在AndroidManifest清单文件的application 设置了:android:persistent="true"
这个看起来很牛逼的东西之前没有用到过,是因为之前做的都是第三方APP,用也白用。现在我们做系统级应用,就可以开心的使用了。使用之后的效果就是,在系统开机的时候此应用的进程会被自动唤醒,并执行到Application的onCreate中。所以以上代码实现和效果就解释得通了。
但是心中的疑惑远远没有释怀,直觉告诉我,这么做是不合理的,所有的业务在Application中就做了,那Android设计的所谓“四大组件”岂不是对于我们这种APP形同虚设?带着各种疑惑,去查看了一下Android官方的文档,并且,RTFSC。现在尝试把过程记录一下(但并不代表疑惑都解除了)
persistent
顾名思义,有persistent加持的应用是“永久的”、“持久的”,对我们APP的试验也证明了这一点,在开机很早的时候就启动了这个进程,并且此进程被kill掉之后会顽固地再次起来。官方对此标签解释如下:
android:persistent
Whether or not the application should remain running at all times — “true” if it should, and “false” if not. The default value is “false”. Applications should not normally set this flag; persistence mode is intended only for certain system applications.
可见,这个“特权”并不是所有APP都可以用的,要是都可以用,那相当于在一个没有法律,没有道德,没有约束的“王国”里,想干什么就干什么,那这个系统就运作不下去了。
persistent应用自启动的原理
先回顾一下系统启动的大致流程吧:
关于Android的启动流程今天不展开讨论,那将是很长的篇幅。想了解的墙裂推荐gityuan大神的系列blog http://gityuan.com/2016/02/01/android-booting/
我们就从Zygote start SystemServer之后说起吧:
SystemServer的main函数中调用了SystemServer().run():
在run函数的最后阶段,启动了各种Service:
|
|
在执行到startOtherServices时,系统的关键服务已经被启动(ActivityManagerService、PowerManagerService、PackageManagerService、WindowManagerService等等等等),在此处又告诉这些关键Service一件事情:systemReady:
我们今天的主角其实就在AMS的systemReady中:
AMS的systemReady()会去遍历查找所有注册了persistent为true的APP:
可以看出,获取persistent列表这件事情最终是由PackageManagerService去完成的。代码如下:
在这里,命中为PersistentApplication的条件就是,当前应用是系统级应用,或者设备关闭了安全模式。一旦关闭了安全模式,所有应用都可以申请persistent。
之后AMS的systemReady()拿到这个list,判断如果应用名不是”android”则执行addAppLocked():
在这里就会判断如果进程没有被启动,则调用startProcessLocked启动之。
persistent应用自动重启的原理
上文说到,我们的这个APP除了开机自动启动,还会在启动之后被kill掉的话又重启。这是怎么实现的呢?
在每个进程的ActivityThread中,都会和AMS建立通讯,持有一个binder实例:mAppThread。当AMS执行到attachApplicationLocked时,给ActivityThread这一端注册一个AppDeathRecipient(APP讣告监听器),当目标进程挂掉之后,AMS中的AppDeathRecipient会收到这个“讣告”,
|
|
appDiedLocked()中调用handleAppDiedLocked(),handleAppDiedLocked()中执行到cleanUpApplicationRecordLocked()时会判断是不是persistent应用的进程,如果是的话标记restart为true,后面会将它启动:
以上关于persistent APP可以自启和“保活”的原理就差不多清楚了,但是,我们得回到最初的问题:具有persistent特性的APP,把业务逻辑都放到Application中去做,不启动Service这样到底是否可行?潜意识里觉得这是最基本的常识,可是要拿出具体原因的时候却没办法讲事实摆道理。不能只靠直觉吧,虽然搞了这么多年Android还是学术不精啊。利用今天的空闲时间大概查阅了一下developer guide里说进程和生命周期的时候有提到过。
Android进程和生命周期(与组件的关系)
这部分内容来自https://developer.android.com/guide/components/activities/process-lifecycle
各位可自行查看,摘出几个关键点和大家分享一下:
It is important that application developers understand how different application components (in particular Activity, Service, and BroadcastReceiver) impact the lifetime of the application’s process. Not using these components correctly can result in the system killing the application’s process while it is doing important work.
一个应用的进程存活与否一定是与它的组件(尤其是Activity、Service、BroadcastReceiver)息息相关的,是这些组件影响着进程的优先级,以便告诉System在什么条件下是否可以杀掉这个进程。
A common example of a process life-cycle bug is a BroadcastReceiver that starts a thread when it receives an Intent in its BroadcastReceiver.onReceive() method, and then returns from the function. Once it returns, the system considers the BroadcastReceiver to be no longer active, and thus, its hosting process no longer needed (unless other application components are active in it). So, the system may kill the process at any time to reclaim memory, and in doing so, it terminates the spawned thread running in the process. The solution to this problem is typically to schedule a JobService from the BroadcastReceiver, so the system knows that there is still active work being done in the process.
这里举了一个例子,如果在BroadcastReceiver onReceive方法中启动一个线程做一件耗时操作,但是onReceive方法很快返回了,系统就认为BroadcastReceiver已经不是活跃状态了,假设这个进程其它组件也没有活跃,系统就很可能在需要时把你干掉,即便是那个线程还没有结束。
由此可见,不使用组件,而在Application里处理所有的业务逻辑是不靠谱的,特别是对于一个第三方APP。
但是上述规则不知是不是仅仅为第三方APP这种“合法公民”制定的,对于persistent的System APP 这类“皇亲国戚”是否就不适用了?所以这部分疑惑还没真正解开,等有时间再系统地RTFSC,验证一下“猜想”吧。大家有什么想法或者经验也希望能不吝赐教。
另外,今天看我这个进程的oom_adj时发现是-14,这个很诡异,Android7.0之前 PERSISTENT_PROC_ADJ 的值应该是-12,但是看Android O的源码 ProcessList.PERSISTENT_PROC_ADJ 的值是-800啊,这个-14是什么鬼??
还是得从源码中仔细去找答案。改天吧,先去吃饭了。