Android系统与其他操作系统有个很不一样的地方,就是其他操作系统尽可能移除不再活动的进程,从而尽可能保证多的内存空间,而Android系统却是反其道而行之,尽可能保留进程。Android这样设计有什么优势呢?又是通过怎样的方法来管理这些被保留的进程的呢?Android用户又该如何正确使用手机从而更好发挥Android系统所特有的优势呢?本文将一一为您解开这些谜团。
成都创新互联公司是专业的东宁网站建设公司,东宁接单;提供成都做网站、网站设计,网页设计,网站设计,建网站,PHP网站建设等专业做网站服务;采用PHP框架,可快速的进行东宁网站开发网页制作和功能扩展;专业做搜索引擎喜爱的网站,专业的做网站团队,希望更多企业前来合作!
本文的主要内容如下:
一、Android进程管理的特殊设计
Linux系统对进程的管理方式是一旦进程活动停止,系统就会结束该进程。尽管Android基于Linux Kernel,但在进程管理上,却采取了另外一种独特的设计:当进程活动停止时,系统并不会立刻结束它,而是会尽可能地将该进程保存在内存中,在以后的某个时间,一旦需要该进程,系统就会立即打开它,而不用再做一些初始化操作。只有当剩余内存不够用了,为了维持新开启的进程或者比较重要的进程的正常运行,系统才会选择性地杀掉一些不重要的内存,腾出内存空间来,所以Android系统永远不会有内存不足的提示。
二、Android独特进程管理设计的好处
Android这种独特的设计,也正是Android标榜的优势之一,这有两个好处:
1、最大限度地提高内存的使用率。
比如,你的内存是8G,如果每次使用完某个进程就杀掉,那么被使用的内存基本上会始终保持在某个值,比如4G以内,那么内存的使用率就总是保存在50%以内,剩余的4G内存形同虚设,发挥用处的机会非常少。而Android的这种设计,就可以做到有多少内存就用多少内存,尽可能大地提高内存使用率。同样比如有8G内存,使用完的进程仍保留在内存中,累积下来,被使用的内存就尽可能地会接近8G。
2、提高再次启动时的启动速度
被驻留在内存中不再活动的进程(后台进程或空进程,后面会再讲到),很多是经常需要使用的,当再次使用该进程的时候,系统立即打开它,而不需要再重新初始化。例如,我们常用的浏览器,当暂时不再使用时,按下Home键或Back键,浏览器进程就变成了不再活动的进程。如果下次又要使用了,点击多任务键,在最近使用应用列表中点击浏览器即可,浏览器界面仍然保持着退出前的界面。但如果退出时把该进程移除了,那么再次使用时,就需要重新初始化,然后进入该应用,这往往会花费不少的时间。
三、Android进程的五个等级
Android系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,以此类推,以回收系统资源。该“重要性层级结构”将进程分为了五个等级:
1、前台进程(foreground)
前台进程是指那些有组件正和用户进行交互的应用程序的进程,也称为Active进程。这些都是Android尝试通过回收其他应用程序来使其保持相应的进程。这些进程的数量非常少,只有等到最后关头才会终止这些进程,是用户最不希望终止的进程。例如:而当你运行浏览器这类应用时,它们的界面就会显示在前台,它们就属于前台进程,当你按home键回到主界面,他们就变成了后台程序。
如果一个进程满足以下任一条件,即视为前台进程:
(1)托管处于活动状态的Activity,也就是说,它们位于前台并对用户事件进行响应,此时的情形为响应了Activity中的onResume()生命周期方法,但没有响应onPause()。
(2)托管正在执行onReceive()方法处理事件程序的BroadcastReceiver。
(3)托管正在执行onStart()、onCreate()或onDestroy()事件处理程序的Service。
(4)托管正在运行且被标记为在前台运行的Service,即调用了该Service的startForeground()方法。
(5)托管某个Service,且该Service正绑定在用户正在交互的Activity的Service,即该Activity正处于活动状态。
2、可见进程(visible)
没有任何前台组件、但仍然会影响用户在屏幕上所见内容的进程。如果一个进程满足以下任一条件,即视为可见进程:
(1)托管不在前台、但仍对用户可见的Activity(已调用其onPause()方法)。例如:如果前台Acitivty启动了一个对话框,或者启动了一个非全屏,亦或是一个透明的Activity,允许在其后显示上一个Activity,则可能会发生这种情况,这类Activity不在前台运行,也不能对用户事件作出反应。
(2)托管绑定到可见Activity的Service。(官网上说是绑定到可见或前台Activity,但笔者有一点疑问,这个和“前台进程”中第(5)点相矛盾吗,绑定到前台Activity,那就是前台进程了)
可见进程被视为是极其重要的进程,这类进程的数量也很少,只有在资源极度匮乏的环境下,为保证前台进程继续执行时才会终止。
3、服务进程(Service)
正在运行已使用startService()方法启动的Serice且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
有些资料上面也称这种进程为次要服务(Secondary Service),而属于上述两个更高类别的进程则被称为主要服务,主要服务往往属于系统进程,如拨号进程等,不可能被进程管理轻易终止。这里我们以Android开发者官网的称呼为标准,称为服务进程。
4、后台进程(hidden)
包含目前对用户不可见的Activity,即该Activity调用了onStop()方法。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供上述三个更高级别的进程使用。通常会有很多后台进程在运行,它们会保存在LRU(Least Recently Used,最近最少使用)列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个Activity正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该Activity时,Activity会恢复其所有可见状态。
这里读者可以做个试验,先开启微信,进入到朋友圈界面, 然后点击手机屏幕下方的导航栏中的Home按键进入到后台,再点击最近使用应用列表显示按钮(不同的手机位置不一样,有的在Home键左边,有的则在Home键右边),在显示的最近使用应用的列表中清理掉微信应用,最后再点击桌面的微信图标启动微信,会发现显示的界面仍然是朋友圈界面。
后台进程,我们可以简单理解为,应用(只考虑只有Activity组件的情况)启动后按Home键后被切换到后台的进程。如浏览器、阅读器等,当程序显示在屏幕上时,它们所运行的进程即为前台进程(foreground),一旦按home键(注意不是back键)返回到桌面,程序就停留在后台,成为后台进程。
5、空进程(empty)
不含任何活动应用组件的进程。保留这种进程的唯一目的是用作缓存,以缩短下次再其中运行组件所需要的启动时间。一般来说,当应用按back按键退出后应用后,就变成了一个空进程。比如BTE,在程序退出后,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。当系统内存不够用时,无疑,该进程是应该最先终止的。在最近使用应用列表中,可以看到按back键退出的应用。
根据进程中当前活动组件的重要程度,Android会将进程评定为它可能达到的最高级别。通俗地说,就是如果一个进程同时拥有多个对应上述不同等级进程的组件时,会以最高的那个等级作为该进程的等级。例如,如果某进程托管着服务和可见Activity,则会将此进程评定为可见进程,而不是服务进程。
此外,一个进程的级别可能会因为其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。例如,如果进程A中的内容提供程序为进程B中的客户端提供服务,或者如果进程A中的服务绑定到进程B中的组件,则进程A始终被视为至少与进程B同样重要。
由于运行服务的进程其级别高于托管后台Activity的进程,因此启动长时间运行操作的Activity最好为该操作启动Service,而不是简单地创建工作线程,当操作有可能比Activity更加持久时更应该如此。例如,正在将图片上传到网站的Activity应该启动服务来执行上传,这样一来,即使用户退出Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论Activity发生什么情况,该操作至少具备“服务进程”优先级。如果某个Activity开启了线程执行耗时操作,当Activity退出时,该Activity的实例将不会释放内存资源,直到线程执行完,这样容易导致内存泄漏。同理,广播接收器也应该使用服务,而不是简单地将耗时冗长的操作放入线程中。
四、进程移除顺序的依据——阈(yu,第四声)值
前面讲到,内存不够用时,会根据进程的等级来决定优先回收哪类进程。那么系统是根据什么来判断需要移除这些进程的时机的呢?答案是阈值。
1、查看阈值
我们可以采用如下方法查看手机中各个等级进程的阈值(需要root权限),如第二排数据所示(其单位为页):
以第一个数据44032为例,计算方法为:
1page=4KB=4*1024B=4096B
44032page* 4048B/page = 180355072B
180355072B/1024/1024 = 172M
即第一个等级的进程的阈值为172M。依次类推,阈值依次为:172M,190M,208M,226M,316M,415M。
有必要说明一下,在Android开发者官方文档中,是将Android应用进程分为了5个等级,但很多资料却是分的6个等级,在后台进程和空进程之间还有一个“内容提供节点(content provider)进程”。内容提供节点,没有实体程序,仅提供内容供别的程序去用 ,比如日历供应节点,邮件供应节点等,在终止进程时,这类进程有比较高的优先权。手机中应该是采用的6个等级的方式,如上六个数据,正好对应着六个等级的进程,等级越高,阈值越低,即前台进程阈值为172M,空进程为415M。当系统的剩余内存只剩余不到415M的时候,系统首先会回收空进程,依次类推,只有剩余内存不到172M了,才会去回收前台进程,这样就起到了优化保护重要进程的作用。
五、Home键、Back键和多任务键
Home键、Back键和多任务键,在手机屏幕的下方,这三个按键一般称为导航栏,中间的按钮为Home键,多任务键和Back键分别在其左右,一般根据手机品牌不同,左右位置也有所差异。
在运行App的时候,如果按一下Home键或者Back键,都可以退到桌面,那么这两者有什么区别呢?
Home键。按Home键的时候,App如果没有Service开启,会从一个前台进程转变为一个后台进程;如果有前台service运行,就仍然是前台进程,比如QQ音乐播放器等;如果是只有普通service运行,那么就转变为服务进程(参照前文中讲的Android进程的5个级别)。
Back键。按Back键的时候,App如果没有Service开启,会从一个前台进程转变为一个空进程;对于有Service运行的情况,和按Home键一样。
后台进程和空进程,都是驻留在后台,处于暂停状态,也都是除了占用一部分内存外,不占用其他如cpu等资源的,那么问题来了,为什么要设计后台进程和空进程这两种空进程呢?它们的区别到底在哪里呢?我们在前文讲Android进程的5个等级的时候讲到过,当剩余内存不足的时候,系统会按照等级顺序,优先移除不太重要进程,以收回内存供更重要的进程运行。那么,它们的区别就是,在剩余内存不足时,会优先移除空进程,再不足,才会移除空进程。所以,如果确实要退出某个应用一段时间内不大使用了,如果这款应用有退出按钮,就用应用自带的退出功能;如果没有,则最好按系统的Back键,这样可以变成空进程,当系统要回收内存时,就会优先被回收,从而释放的所占的资源。如果只是暂时退出去做点别的,过一会还要切换回来,或者对这款应用使用比较频繁,那就使用Home键,因为相比于按Back键,这样可以尽可能保住后台进程,方便下次使用的时候快速启动。
当然,按Home键或Back键,对用户来说,其实感觉不到差异,使用起来没什么两样,但是,对于Android开发者来说,却有必要作为常识来了解其中的道理和差异。无论是按Home键还是按Back键,在按多任务键的时候,都可以看到这些进程,如下图所示。最下面的按键为清理按键,点击后可以清除掉这些进程,回收内存了,当然,前面也讲了很多遍了,不建议这样做。
2、修改阈值。
可以采用命令:echo "44032,48640,53248,57856,80896,106241" /sys/module/lowmemorykiller/parameters/minfree来修改阈值,如下所示:
重启后,会恢复为原来的值。至于如何永久性修改该阈值,这里不深入探讨,有兴趣的童鞋可以自行研究,一般来说,就按照系统给定的默认值使用就可以了,没特殊用途的话,没必要修改。
对于这一节阈值的内容,暂时先讲到这里,如果要更深入,可以自行多研究研究。笔者也没有看到比较好的更深入的文章,所以也不好推荐,如果读者看到比较好的,可以推荐给笔者,感激不尽。
六、开发者选项中的进程管理功能
Android手机都带有开发者选项,隐藏了很多功能,顾名思义,这些功能主要用于辅助开发者调试程序用的。其中有一些就是关于进程管理功能的,笔者这里简单介绍一下其中两款,如下图红框部分所示:
不保留活动。用户离开以后即销毁每个活动(Activity),这样做使得后台进程都被销毁了。笔者试验过几款app,比如微信,浏览器,开启/关闭“不保留活动”前后,按Home键后,再打开应用,有明显的差别。当然,也试用了短信,DD打车,就没看出起了什么作用。读者若是感兴趣可以深入研究研究,到时候在指导指导笔者!
后台进程限制。如下图所示,给出了后台进程个数限制的选项。
七、进程管理软件的使用
Windows操作系统用户往往总想着保留更多的内存,在使用Android手机的时候,喜欢经常清理后台进程或空进程,而且清理完后,心里有一种特别爽的感觉,就像给家里做了一次大扫除一样,笔者最初使用Android手机的时候也是这样的心态-_-!基于这样的心态,一些进程清理软件,很受普通用户的青睐。其实这样做却正好抹杀了Android系统所标榜的优势,如前文所讲到的。
那么进程管理软件有无必要呢?当然有的,只是需要注意使用场合。当需要运行大型程序的时候,可以手动关闭掉一些进程,腾出足够的空间供大型程序使用,这样就可以有效避免系统调用进程调度策略而引起的卡顿,这一点,第八大点第3小节中会有说明。而且由于开发者的原因,可能是程序写得太烂,或程序容易出错,或做不该做的动作,或是恶意程序,对于这类程序进程,手动移除也是有好处的。
但如果是运行一些小程序,就完全没有必要去预先杀进程了,完全可以交给系统自己管理。读者可能会疑惑,因为小程序启动的时候,也有可能会因为内存不足而导致需要移除部分进程的情况。笔者认为,即便是内存不足,小程序运行引起的调用进程调度策略测的次数非常少,要移除的进程也非常少,产生的影响不大。同时,我们也要意识到另外一点就是,无论是手动杀死进程还是自动杀进程,都需要cpu去执行这些任务,所以也会拖慢手机和消耗电量。所以从这一点看,频繁杀进程,也是一个不好的习惯。
八、答疑解惑
在以前没有专门去了解Android进程管理机制的时候,甚至是在研究的过程中,笔者心里都经常存在很多疑惑,以下整理了其中5个,不知道读者您是否有也类似的困惑呢?
1、这么多驻留在内存的进程,不会耗电吗?
大多数用惯了Windows操作系统的童鞋,看到Android系统尽可能保留不在活动的进程的设计,可能第一反应就是质疑,难道这样不会增加耗电量吗?其实,但一个程序按home键变成后台进程或者按back键退出变成空进程后,其实已经被暂停了,只保留了运行状态,不会消耗cpu,一个程序会耗电,是因为它需要调用cpu来运算,现在不消耗cpu了,当然就不会耗电了。当然,开了service的应用就另当别论了,比如QQ音乐播放器,当按home键或back键后,音乐仍然播放,是因为它开启了服务,而且是一个前台服务,在后面我们会继续讲到,此时它是一个前台进程,而不是后台进程或空进程。
2、为什么一个不太app,运行时会占用很大的内存呢?
我们经常会碰到这样一种现象,一个只有20M的App,运行起来的时候,却会耗掉100M以上的内存。一方面是,程序运行时为对象分配内存,另一方面,是Android虚拟机的原因。Android中的应用启动的时候,系统都会给它开启一个独立的虚拟机,这样做的好处是可以避免虚拟机崩溃导致整个系统崩溃,代价就是耗用更多的内存。
3、为什么内存少的时候,运行大型程序会卡顿呢?
当剩余内存不多时,打开大型程序,系统会触发自身的进程调度策略,去移除一些等级比较低的进程来回收内存,以供大型程序运行。而这个进程调度策略在决定哪些进程需要被移除的过程,是一个十分消耗资源的操作,特别是一个程序频繁像系统申内存的时候,这样就导致了系统的卡顿。
4、应用开得太多了,手机变慢,是因为内存被占用太多吗?
其实手机变慢的根本原因是cpu被耗用太多,而不是内存占用太多,因为真正执行程序所要完成的任务的最终执行者是CPU,而不是内存(RAM)。在内存足够的情况下,如果系统中占用cpu的进程太多,那无疑cpu总有忙不过来的时候,那肯定就会变慢了。这就好比,在一条道路上驾车,道路就像内存,车的引擎就像cpu,如果车的引擎的动力不够,或者承载的货物太多,车都跑不快,即便是道路上一路畅通无阻,也无济于事。所以,内存占用多少并不重要,只要道路提供给车辆前行的空间是足够的,手机变慢的责任,就和内存无关了。这个比喻用来解释第三点也很恰当,道路提供的车辆前进的空间无法满足车辆所必需的空间时,就需要交通机制花时间来调节交通,给这辆车提供足够的空间,而在此期间,这辆车只能乖乖候着。
5、Android手机越用越慢,是什么原因呢?
Android手机常常是越用越慢,即使是恢复出厂设置,也无法改变这个现象。手机越用越慢,主要由如下几个原因:(1)虚拟机机制问题。这一点在上一个问题中也提到了,在Android4.4以前的系统,使用的是Dalvik虚拟机,它的设计机制有缺陷,就是越用越慢;在Android4.4系统中有切换按钮,可以在Art虚拟机和Dalvik虚拟机之间切换;在Android4.4以后的系统就彻底抛弃了Dalvik而全面使用Art。(2)开启了太多的服务,导致耗用太多的CPU。随着手机开机使用时间的增长,应用使用越来越多,很多应用看似退出了,而其实后台可能开了不少的服务,而他们可能还没有关闭。这些服务正在执行一些操作,会消耗CPU,而CPU才是手机变慢的根本原因。 而且Android app比较开放的,有很多不良应用充斥其中,可能对服务处理不当,滥用服务等,增加系统中的服务。(3)系统频繁调用自身的进程调度算法。这一点在前面已经说明了,这里不再赘述。(4)手机硬件的自然老化
·有一段程序供其执行
·拥有专用的系统堆栈空间
·在内存中有对应的进程控制块
·拥有独立的用户存储空间
·进程之间不能进行自由的信息交互(别问我,问就是Android规定的)
·每个进程必须包含一个线程
·进程间的切换开销比较大,线程创建和终止比进程快,进程间无法进行自由的资源交换,同进程内的线程可以自由交换
我们可以将Android系统比做一个大公司,进程相当于某个部门的经理,他有自己要做到项目(有一段程序供其执行,在内存中有对应的进程控制块),拥有自己部门的办公场地(拥有独立的用户存储空间)。
线程就相当于每个部门的员工,员工得依赖于经理提供的工作环境才能够进行工作(线程必须依赖于进程),而经理手下必须得有一个员工(要不然分配的工作交给谁来做),我们都知道在一个大公司下每个部门和每个部门沟通是比较困难的,但是一个部门之间的员工沟通相对容易许多(同进程下的线程共享资源)。
参考:
每一个 Android 应用启动后至少对应一个进程,有的是多个进程,而且主流应用中多个
进程的应用比例较大
对于任何一个进程,我们都可以通过 adb shell ps|grep package_name的方式来查看
它的基本信息
Android 中的进程跟封建社会一样,分了三流九等,Android 系统把进程的划为了如下
几种(重要性从高到低),网上多位大神都详细总结过(备注:严格来说是划分了 6 种)。
场景:
1.某个进程持有一个正在与用户交互的 Activity 并且该 Activity 正处于 resume 的
状态。
2.某个进程持有一个 Service,并且该 Service 与用户正在交互的 Activity 绑定。
3.某个进程持有一个 Service,并且该 Service 调用 startForeground()方法使之位于前台运行。
4.某个进程持有一个 Service,并且该 Service 正在执行它的某个生命周期回调方法,比如 onCreate()、 onStart()或 onDestroy()。
5.某个进程持有一个 BroadcastReceiver,并且该 BroadcastReceiver 正在执行其onReceive()方法。用户正在使用的程序,一般系统是不会杀死前台进程的,除非用户强制停止应用或者系统内存不足等极端情况会杀死。
场景:
1.拥有不在前台、但仍对用户可见的 Activity(已调用 onPause())。
2.拥有绑定到可见(或前台)Activity 的 Service
用户正在使用,看得到,但是摸不着,没有覆盖到整个屏幕,只有屏幕的一部分可见进程
不包含任何前台组件,一般系统也是不会杀死可见进程的,除非要在资源吃紧的情况下,
要保持某个或多个前台进程存活
场景
1.某个进程中运行着一个 Service 且该 Service 是通过 startService()启动的,与用户看见的界面没有直接关联。
在内存不足以维持所有前台进程和可见进程同时运行的情况下,服务进程会被杀死
场景:
在用户按了"back"或者"home"后,程序本身看不到了,但是其实还在运行的程序,
比如 Activity 调用了 onPause 方法系统可能随时终止它们,回收内存
场景:
某个进程不包含任何活跃的组件时该进程就会被置为空进程,完全没用,杀了它只有好处没坏处,第一个干它!
上面是进程的分类,进程是怎么被杀的呢?系统出于体验和性能上的考虑,app 在退到
后台时系统并不会真正的 kill 掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制
来判断要 kill 掉哪些进程,以腾出内存来供给需要的 app, 这套杀进程回收内存的机制
就叫 Low Memory Killer。那这个不足怎么来规定呢,那就是内存阈值,我们可以使用
cat /sys/module/lowmemorykiller/parameters/minfree 来查看某个手机的内存阈值。
其实系统在进程回收跟内存回收类似也是有一套严格的策略,可以
自己去了解,大概是这个样子的,oom_adj 越大,占用物理内存越多会被最先 kill 掉,OK,那么现在对于进程如何保活这个问题就转化成,如何降低 oom_adj 的值,以及如
何使得我们应用占的内存最少。
据说这个是手 Q 的进程保活方案,基本思想,系统一般是不会杀死前台进程的。所以要
使得进程常驻,我们只需要在锁屏的时候在本进程开启一个 Activity,为了欺骗用户,
让这个 Activity 的大小是 1 像素,并且透明无切换动画,在开屏幕的时候,把这个 Activity
关闭掉,所以这个就需要监听系统锁屏广播,我试过了,的确好使,如下。
如果直接启动一个 Activity,当我们按下 back 键返回桌面的时候,oom_adj 的值是 8,
上面已经提到过,这个进程在资源不够的情况下是容易被回收的。现在造一个一个像素
的 Activity。
为了做的更隐藏,最好设置一下这个 Activity 的主题,当然也无所谓了
在屏幕关闭的时候把 LiveActivity 启动起来,在开屏的时候把 LiveActivity 关闭掉,所以
要监听系统锁屏广播,以接口的形式通知 MainActivity 启动或者关闭 LiveActivity。
现在 MainActivity 改成如下
按下 back 之后,进行锁屏,现在测试一下 oom_adj 的值
果然将进程的优先级提高了。
但是还有一个问题,内存也是一个考虑的因素,内存越多会被最先 kill 掉,所以把上面
的业务逻辑放到 Service 中,而 Service 是在另外一个 进程中,在 MainActivity 开启这
个服务就行了,这样这个进程就更加的轻量,
OK,通过上面的操作,我们的应用就始终和前台进程是一样的优先级了,为了省电,
系统检测到锁屏事件后一段时间内会杀死后台进程,如果采取这种方案,就可以避免了
这个问题。但是还是有被杀掉的可能,所以我们还需要做双进程守护,关于双进程守护,
比较适合的就是 aidl 的那种方式,但是这个不是完全的靠谱,原理是 A 进程死的时候,
B 还在活着,B 可以将 A 进程拉起来,反之,B 进程死的时候,A 还活着,A 可以将 B
拉起来。所以双进程守护的前提是,系统杀进程只能一个个的去杀,如果一次性杀两个,
这种方法也是不 OK 的。
事实上
那么我们先来看看 Android5.0 以下的源码,ActivityManagerService 是如何关闭在应用
退出后清理内存的
在应用退出后,ActivityManagerService 不仅把主进程给杀死,另外把主进程所属的进
程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也
就停止了。所以在 Android5.0 以后的手机应用在进程被杀死后,要采用其他方案。
这种大部分人都了解,据说这个微信也用过的进程保活方案,移步微信 Android 客户端
后台保活经验分享,这方案实际利用了 Android 前台 service 的漏洞。
原理如下
对于 API level 18 :调用 startForeground(ID, new Notification()),发送空的
Notification ,图标则不会显示。
对于 API level = 18:在需要提优先级的 service A 启动一个 InnerService,两个服务
同时 startForeground,且绑定同样的 ID。Stop 掉 InnerService ,这样通知栏图标即
被移除。
public class KeepLiveService extends Service{
public static final int NOTIFICATION_ID=0x11;
public KeepLiveService() {
}
@Override
public IBinder onBind(Intent intent){
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate(); //API 18 以下,直 接发 送 Notification 并 将 其 置 为 前 台
if(Build.VERSION.SDK_INTBuild.VERSION_CODES.JELLY_BEAN_MR2){
startForeground(NOTIFICATION_ID,new Notification());
} else { //API 18 以上,发送 Notification 并将其置为前台后,启动 InnerService
Notification.Builder builder=new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
startService(new Intent(this, InnerService.class));
}
}
public class InnerService extends Service{
@Override public IBinder onBind(Intent intent) {
return null;
}
@Override public void onCreate() {
super.onCreate(); //发送与 KeepLiveService中 ID 相同的 Notification,然后将其取消并取消自己的前台显示
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);startForeground(NOTIFICATION_ID,
builder.build());
new Handler().postDelayed(new Runnable() {
@Override public void run() {
stopForeground(true);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
},
100);
}
}
}
在没有采取前台服务之前,启动应用,oom_adj 值是 0,按下返回键之后,变成 9(不
同 ROM 可能不一样)
相互唤醒的意思就是,假如你手机里装了支付宝、淘宝、天猫、UC 等阿里系的 app,
那么你打开任意一个阿里系的 app 后,有可能就顺便把其他阿里系的 app 给唤醒了。
这个完全有可能的。此外,开机,网络切换、拍照、拍视频时候,利用系统产生的广播
也能唤醒 app,不过 Android N 已经将这三种广播取消了。
如果应用想保活,要是 QQ,微信愿意救你也行,有多少手机上没有 QQ,微信呢?或
者像友盟,信鸽这种推送 SDK,也存在唤醒 app 的功能。
拉活方法
JobSheduler是作为进程死后复活的一种手段,
native进程方式最大缺点是费电,Native
进程费电的原因是感知主进程是否存活有两种实现方式,在 Native 进程中通过死循环
或定时器,轮训判断主进程是否存活,当主进程不存活时进行拉活。其次 5.0 以上系统
不支持。 但是 JobSheduler 可以替代在 Android5.0 以上 native 进程方式,这种方式即
使用户强制关闭,也能被拉起来,亲测可行。
JobSheduler@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {
@Override
public void onCreate() {
super.onCreate();
startJobSheduler();
}
public void startJobSheduler() {
try {
JobInfo.Builder builder = new JobInfo.Builder(1,new ComponentName(getPackageName(), MyJobService.class.getName()));
builder.setPeriodic(5); builder.setPersisted(true); JobScheduler jobScheduler =(JobScheduler)
this.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(builder.build());
}
catch
(Exception ex)
{ ex.printStackTrace(); } }
@Override
public boolean onStartJob(JobParameters jobParameters) {
return false;
} @Override public boolean onStopJob(JobParameters jobParameters) {
return false;
}
}
这个是系统自带的,onStartCommand 方法必须具有一个整形的返回值,这个整形的返
回值用来告诉系统在服务启动完毕后,如果被 Kill,系统将如何操作,这种方案虽然可
以,但是在某些情况 or 某些定制 ROM 上可能失效,我认为可以多做一种保保守方案。
1.START_STICKY
如果系统在 onStartCommand 返回后被销毁,系统将会重新创建服务并依次调用
onCreate 和 onStartCommand(注意:根据测试 Android2.3.3 以下版本只会调用
onCreate 根本不会调用 onStartCommand,Android4.0 可以办到),这种相当于服务
又重新启动恢复到之前的状态了)。
2.START_NOT_STICKY
如果系统在 onStartCommand 返回后被销毁,如果返回该值,则在执行完
onStartCommand 方法后如果 Service 被杀掉系统将不会重启该服务
3.START_REDELIVER_INTENT
START_STICKY 的兼容版本,不同的是其不保证服务被杀后一定能重启。
4.相比与粘性服务与系统服务捆绑更厉害一点,这个来自爱哥的研究,这里说的系统服务
很好理解,比如 NotificationListenerService,NotificationListenerService 就是一个监听
通知的服务,只要手机收到了通知,NotificationListenerService 都能监听到,即时用户
把进程杀死,也能重启,所以说要是把这个服务放到我们的进程之中,那么就可以呵呵
了
所以你的应用要是有消息推送的话,那么可以用这种方式去欺骗用户。
接上篇cpuset,这篇来看看进程优先级与调度策略管理。
Linux中,优先级号一共有0-139,其中0-99的是RT(实时)进程,100-139的是非实时进程。
数字越低优先级越高。
SCHED_IDLE idle状态低优先级进程调度
先看Process中调度策略的划分,与上面介绍的一样。
首先在AMS中封装了FIFO和NORMAL的两个策略,NORMAL好说,看看FIFO在哪用到
这里Process.setThreadScheduler并没有太多的应用,我们直接来看优先级设置吧。else中将top app的UI线程与render线程都设置为TOP_APP_PRIORITY_BOOST优先级,nice值为-10,非常高。
这里主要调用androidSetThreadPriority方法
这里通过set_sched_policy来调整调度策略,并通过setpriority设置进程优先级。这里不特意区分进程与线程了,反正在linux中都是进程。
这里与前面的cpuset非常相似,依然是写节点,节点前面也提了就是:
那么这里又引入了一个schedtune子系统,简单介绍下:
schedtune是ARM/Linaro为了EAS新增的一个子系统,主要用来控制进程调度选择CPU以及boost触发。通过权重来分配CPU负载能力来实现快速运行。高权重意味着会享受到更好的cpu负载来处理对应的任务,换句话说你能享受相对更好的cpu运行性能。
简单梳理下schedtune和不同类型SchedPolicy之间的对应关系:
看下具体文件夹内容:
系统配置:
这里/dev/stune相关配置只做了这么一个
前言:本文将通过两种方式,在手机亮屏和屏锁状态下联合对进程进行保活。
一、手机亮屏时如何提高存活率
在点击home键使app长时间停留在后台时,内存不足时会被杀死。
处理这种情况时运用灰色保活,在service里通过Service.startForeground() 设置为前台服务,提高存活率。
GrayInnerService
二、手机锁屏时如何提高存活率
手机在进入锁屏状态一段时间,省电机制会杀死后台进程。
处理这种情况时,我们需注册广播监听锁屏和解锁事件, 锁屏后启动一个1像素的透明Activity, 解锁后销毁这个透明Activity。
注:这个广播要写到要保活的service中。
OnePiexlActivity.class
总结:以上就是在手机锁屏和亮屏时对进程进行保活的两种方法
源码地址:
打开设置--应用与服务(不同机型进入方式可能不同),你就会看到当前正在运行的进程和服务,也就是目前正在“后台运行”的任务。列表中有你刚刚使用过的 APP ,也有一两小时前打开过的 APP。还有一些软件你甚至不知道自己什么时候打开过(其实根本就不是自己打开的),或者记得自己已经“关闭”了,但它们也在列表中,消耗着你的手机资源。列表中有一些条目名字很奇怪,一般人看不懂,但还是觉得“它很重要”,不敢轻易强制关闭。这个列表展示的内容和普通的后台管理界面不太一样,感觉稍稍有些神秘,然而这又是我们日常使用所回避不了的一部分。
作为一名资深的手机用户(我相信人人都是),是时候该解决这类疑问了。这一切都要从人与宇宙的关系。。。咳咳。。手机进程的概念开始说起。
在开发文档中是这么说的:当一个应用程序启动时(仅仅只是“启动时”,并不一定有组件运行),就会产生一个进程。在这个进程中同时会创建一个主线程,使应用内的任务开始执行。Android系统总是尽可能地保留进程。举个例子,当你打开qq时,进程创建(同时创建主线程),随后各种内容加载(首先是活动,然后是各种控件什么的)。当你完成操作时,一般都会按后退键(back),直至退出程序。
这里需要注意,一般情况下我们一直按后退是希望应用程序关闭的。然而事实上这样做只是关闭了界面(活动),大多数app的“进程”仍会保留(少数良心app可以设置在退出时“完全关闭”),占用内存以进行后台任务。进程随应用启动而产生,但往往并不随着应用的“关闭”而关闭
所以很多时候我们看上去关闭了程序,但其实它仍在后台运行!(此处请自行回忆那些困扰你的流氓软件们)。不过不必担心,Android 系统自有一套进程管理机制来帮你管理后台任务。系统会根据应用的重要程度把所有进程划归为几个等级,最不重要的进程将会被优先关闭,相对重要的进程将获得资源来保留。
那么问题来了----到底如何分辨哪些进程重要而哪些不重要呢?
系统当然要保证用户体验,所以重要等级的划分原则就是要首先满足用户当前的需求:用户正在使用的当然不能关闭,而用户暂时不需要的,相对的就没那么重要了。
1.Foreground process 前台进程:也就是用户正在进行操作的进程。这样的进程优先级(优先保留)最高,最不容易销毁,因为它表现在屏幕上,直接同用户进行交互,所以只有当内存资源极度紧张等一些其他极端情况才会关闭,表现为“闪退”。我用的第一台 Android 手机运行内存(RAM)只有 290M,多任务时经常内存不足导致程序“闪退”。这手机我竟然用了两年,现在想想都佩服我自己hhhh。
不只是界面交互,如果应用程序中的服务(service)组件正在进行一些操作或者广播接收者(BroadcastReceiver)在执行接收广播的操作(onReceive)时,该进程仍被视为前台进程。
2.Visible process 可视进程:顾名思义,就是仍然在屏幕上有显示,但用户不再能直接与它交互的程序。比如当在应用中打开下滑菜单时(有些下滑菜单是透明的),用户能“看得到”,但是“摸不着”。优先级仅次于前台进程。
3.Service process 服务进程:该进程中开启了一个服务(通过startService方法)。注意这里强调的是服务的“开启”,区别于第一类中的“服务正在执行一些操作”。大多数音乐软件都是通过这种方法来保留其播放音乐的进程。
4.Background process 后台进程:当你按下 HOME 键或 BACK 键时,手机退回主界面,此时应用程序不再可见,转入后台运行。如果如果不满足前几类的条件,这个进程就会被判定为后台进程。
5.Empty process 空进程:A process that doesn't hold any active application components.没有任何组件在运行,包括活动界面(Activity)。事实上用户已经不再需要这个进程了,但出于 Android 系统“尽可能保留进程”的原则,这样的进程出现后不会被立即销毁。保留进程的唯一理由,就是为了下次开启这个应用时能快一些。其实现在的手机硬件性能足够好,这样的缓存对于用户体验的提升效果不怎么明显。这样的进程最不重要,将首先被销毁。
也许你已经注意到了,在屏幕上正在显示的或者正在服务于用户的进程的重要等级是比较高的,这是出于对用户体验的考虑-----谁会接受在自己打王者荣耀的时候游戏突然闪退呢?大多数情况下,一个应用程序的组件成分都会比较复杂,这个进程可能同时满足多个级别的划分条件。在这种情况下,它会被尽可能地划为能够达到的最重要等级。
如果你的手机上安装了好几个同一家公司推出的 APP(比如企鹅系、头条系等),那么当你启动其中之一时,剩下的几款 APP 大概率也会被唤醒(视软件的流氓程度而定)。联动开启的 APP 会大大占用内存,让手机变得卡顿。并且它们许多都需要联网服务,占用网速,有些还会在你不知情(因为你并没有直接开启或使用它们)的情况下监控你的数据并上传。
不过,你以为这就完了?
事实上,如果不手动清除,这样的进程很难被系统关闭,它们会一直长期运行。这些进程大多属于第三或第四等级,然而如果不同 APP 中的组件构成“相互依赖”的关系,它们所属进程的保留优先级就会提高,也就越不容易被关闭。(我等流氓软件可不是浪得虚名的ε=ε=ε=(  ̄▽ ̄) )
尽管 Android 想要尽可能的保存所有的进程,但是并非所有的内存都会被用于维持进程。比如系统运行会占用相当的内存,系统也需要留出一部分闲置内存用以处理新事件。Android 的管理让内存的分配处于一种“动态平衡”中,以保障各项任务都能尽可能的稳定、高效地执行。
好了,关于进程的管理就暂时说到这了。众所周知,Android 系统是一个复杂的机体,它管理着手机硬件和软件,让它们尽可能的配合,提供给用户最好的服务。这次只是简单介绍了进程管理机制,今后我也会尽量用通俗的语言从系统上去解释那些平常看上去似是而非的问题,期待你的关注!