一、概述1.1简介据说Android开源系统App数不胜数,百家争鸣,都想在这个“系统”中找到一席之地。天下大争”。但是,系统资源是有限的。如果全都分王,再强大的CPU,再庞大的内存也会被耗尽,再大的电池寿命也将是昙花一现。面对万物生灵,无穷无尽的变数,系统以同样的方式应对一切变化,一个绝杀魔法技能力停就此诞生。这里我们以adb命令为例说明其内部机制:force-stop命令杀掉该空间下的所有用户。包名pkgName相关的信息也可以通过--user来指定用户Id。当执行上面的am命令时,会调用Am.java的main()方法,启动main方法。1.2Am.main[->Am.java]1.3Am.run[->Am.java]1.4Am.onRun[->Am.java]1.***m.runForceStop[->Am.java]当不当指定userId时,它默认为UserHandle.USER_ALL。1.6AMP.forceStopPackage[->ActivityManagerNative.java::AMP]1.7AMN.onTransact[->ActivityManagerNative.java]MP.forceStopPackage运行adb时创建的进程,经过BinderDriver后,进入system_server的一个binder进程线程执行AMN.forceStopPackage,从这里开始的操作(包括当前操作)都会运行在system_server系统进程中。1.8进程loreforce-stop不能被任何应用程序直接调用,否则应用程序会互相停止,天下大乱。该方法的存在是为了由系统调度。一般来说,点击首页弹窗清理用户最近使用app的策略是强制停止。至于force-stop的触发方法,除了adb方法外,还可以获取ActivityManager,然后调用它的方法forceStopPackage(),不过这??个是@hide隐藏方法也需要有FORCE_STOP_PACKAGES权限。虽然不能直接调用普通的第三方APP,但是要想深入了解Android,还是需要知道系统是如何清理进程的。接下来进入AMS,深入挖掘强制停止的内部机制。2.force-stop的内部机制2.1AMS.forceStopPackage[->ActivityManagerService.java]这里有一个很重要的过程,就是setPackageStoppedState()设置包的状态为stopped,那么所有的广播都收不到,除非标记为FLAG_INCLUDE_STOPPED_PACKAGES的广播,系统默认的广播几乎没有这个标志,也就是说被强行停止的应用无法通过建立移动网络状态或开启关闭广播来启动进程。使用forcestop方式结束进程时,原因一般是“frompid”+callingPid。当然还有一个,就是AMS.clearApplicationUserData方法调用forceStopPackageLocked的原因是“清空数据”。2.2AMS.forceStopPackageLocked清理包名除了相关流程和四大组件外,还会发送广播ACTION_PACKAGE_RESTARTED清理注册的告警和通知信息。2.3AMS.forceStopPackageLockedfordidSomething只引用方法中的所有行为,返回true。比如killPackageProcessesLocked(),只要有一个进程被杀死,就说明didSomething为真。该方法的主要作用:Process:调用AMS.killPackageProcessesLocked()清理该包涉及的进程;Activity:调用ASS.finishDisabledPackageActivitiesLocked()清理参与打包的Activity;Service:调用AS.bringDownDisabledPackageServicesLocked()清理包中涉及的Service;Provider:调用AMS.removeDyingProviderLocked()清理包InvolvedProvider;BroadcastRecevier:调用BQ.cleanupDisabledPackageReceiversLocked()清理包中涉及的广播。下面我们就从这五个角度说一下强行停止的执行过程。3.Process3.1AMS.killPackageProcessesLocked一般force-stopstop会指定包名,该方法会遍历所有当前运行的进程mProcessNames,同时不满足以下条件的进程将成为目标进程killed:(也就是说满足以下任何一个条件,都可以避免死亡)persistentprocess:processsetAdjActivityStackSupervisor.java]4.2AS.finishDisabledPackageActivitiesLocked[->ActivityStack.java]4.3AS.finishActivityLocked[->ActivityStack.java]4.3.1AR.makeFinishingLocked[->ActivityRecord.java]4.3。2ASS.requestVisibleBehindLocked4.3.3TaskRecord.setFrontOfTask[->TaskRecord.java]从下往上查询Task,第一个非finishing状态的ActivityRecord设置为根Activity(即r.frontOfTask=true),其他都是假的;当所有的activity都处于finishing状态时,设置**部分中的activity与Activity.4.3.4AS.adjustFocusedActivityLocked[->ActivityStack.java]4.3.***S设置为满足以下条件之一.finishCurrentActivityLocked,将执行finish和destroyActivity。mode是FINISH_IMMEDIATELYmode是FINISH_AFTER_PAUSE,activity状态已经是PAUSED;Activity的状态为STOPPED或INITIALIZING。4.3.6AS.destroyActivityLocked5.Service5.1bringDownDisabledPackageServicesLocked[->ActiveServices.java]5.2collectPackageServicesLocked[->ActiveServices.java]该方法主要作用是收集满足的service条件放入mTmpCollectionResults.5.3bringDownServiceLocked[->ActiveServices.java]5.4unscheduleServiceRestartLocked6.Provider6.1PM.collectPackageProvidersLocked[->ProviderMap.java]当userId=UserHandle.USER_ALL时,会查询所有属于package的provider在mSingletonByClass和mProvidersByClassPerUser结构中。当userId=UserHandle.USER_OWNER时,会从mSingletonByClass和mProvidersByClassPerUser中userId相等的数据结构中查询属于该包的所有provider。当userId不属于以上两者之一时,会查询mProvidersByClassPerUser中所有与userId相等的providerpackage的providers。6.2PM.collectPackageProvidersLocked[->ProviderMap.java]6.3AMS.removeDyingProviderLocked当其他app使用provider和建立一个稳定的连接,那么非持久进程将被杀死,因为它依赖于提供者.7。Broadcast7.1BQ.cleanupDisabledPackageReceiversLocked[->BroadcastQueue.java]该方法主要作用:清理并行广播队列mParallelBroadcasts;清理有序广播队列mOrderedBroadcasts7.2BR.cleanupDisabledPackageReceiversLocked[->BroadcastRecord.java]八.就是发送广播ACTION_PACKAGE_RESTARTED,经过Broadcast广播分发,最后调用注册广播的receiver8.1AlarmCleanup[->AlarmManagerService.java]调用AlarmManagerService中的removeLocked()方法解除mAlarmBatches的包锁和mPendingWhileIdleAlarmsqueuesRelevantalarm.8.2Notificationcleanup[->NotificationManagerService.java]mNotificationListqueues清除相关包的通知。九。层叠屠杀在这里给大家分享一个经验。记得之前有BAT的某浏览器厂商(具体名字匿名),浏览器会因为另一个app被杀而无辜的杀自己,怀疑是ROM定制导致的bug,于是发邮件给我们厂问原因。遇到这个问题,先把这两个app安装到谷歌原生系统,结果还是会被级联杀掉。显然,可以排除厂商ROM定制的原因。按照常理,这个bug应该是app自己解决的。出于好奇,我帮他们进一步调查了这个问题,发现被害的不是无辜的人,而是强行停止造成的连环杀戮。简单来说,App1调用getClassLoader()加载App2,然后App1运行的进程会将App2的包名添加到自己的pkgDeps队列中。pkgDeps在【3.2节】中已经提到,kill进程会遍历队列,当App2被forceStop杀死时,App1被级联杀死。由于App1会调用App2的ClassLoader来加载它的方法,所以已经建立了一定的连接。这就是谷歌有意赋予forceStop这个强大的查杀功能。这个故事就是告诉大家在外挂或者反射的过程中要注意这种情况,以免造成不必要的误伤。接下来说说这个过程是如何建立依赖关系的。9.1CI.getClassLoader[->ContextImpl.java]9.2LA.getClassLoader[->LoadedApk.java]9.3AMS.addPackageDependency调用ClassLoader加载启动包名时,包名会添加到进程的pkgDeps中。10、forceStop的作用总结如下:Process:调用AMS.killPackageProcessesLocked()清理涉及包的进程;Activity:调用ASS.finishDisabledPackageActivitiesLocked()清理包中涉及的Activity;Service:调用AS.bringDownDisabledPackageServicesLocked()清理包中涉及的Service;Provider:调用AMS.removeDyingProviderLocked()清理参与打包的Provider;BroadcastRecevier:调用BQ.cleanupDisabledPackageReceiversLocked()清理包中涉及的广播发送广播ACTION_PACKAGE_RESTARTED停止注册的告警,通知。功能点总结:强行停止不会杀掉持久化进程;APP被强制停止后,无法接收到任何普通的广播,所以经常会听到手机网络状态的变化或者屏幕亮灭的广播。过程肯定不可行;应用被强制停止时,闹钟也会被清理,无法实现定时响铃功能;App被强制停止后,四大组件和相关进程都被一一切断清理,即使是多进程架构的App也无法自行拉起;级联查杀:当一个应用通过ClassLoader加载另一个应用时,会在强制停止过程中被级联查杀;生死相随:当一个app与另一个app共享uid时,会处于强制停止的过程中,任何一方被杀死,另一方也会被杀死,建立起生死攸关的关系。既然force-stop多次提到杀掉进程,那么关于保活简单说几句:正确的保活姿势应该是保证自己永远不会在用户需要的时候被杀掉,不要强行保活活在用户不需要的时候,一切从用户做起。进程是否需要存活,系统上层有AMS管理缓存进程和空进程,下层有LowMemoryKiller根据系统可用内存管理进程是否存活。这种策略是从系统完整性的角度考虑的,为了给用户提供更流畅的用户体验。不要在用户需要的时候被杀:谨慎使用插件和共享uid,除非你愿意接受级联屠杀和生死的场景;还需要提高自己的应用程序的稳定性,减少崩溃和anr的频率。这是正确的方法。不要在用户不需要的时候强行保活:为了保活,多进程架构和使用各种技巧来提高优先级是不可取的。一招force-stop足以干掉90%以上的keepalive策略,当然keepalive还有其他的方法和漏洞,在系统层面往往会采用一些特殊的方法来禁止keepalive。博主做过手机底层的性能和功耗优化,深知很多APP的流氓行为,严重影响了系统的流畅度和手机的续航。为了Android有更好的用户体验,为了不影响手机系统的性能,为了不降低手机的续航能力,建议大家多花点时间和精力在如何提高app的健壮性,如何优化app的性能,共同打造良好的安卓生态。.【本文为“小米开放平台”专栏原创文章,“小米开放平台”微信公众号xiaomideveloper]