Android应用互相唤醒的状况是中国特点,国外由于有 Google Play 的评价系统和基本的审核机制,不会有国内这么疯狂的流氓式设计。这样作的好处一个是方便收集用户信息,了解用户习惯,优化产品,再者给用户推送消息须要APP退出前台时可以保活,对于IM应用来说更是硬性需求。因此有些你没装过或者周围没人用的 APP ,随随便便都能日活上千万。html
当前业界的Android进程保活手段主要分为 黑、白、灰 三种,其大体的实现思路以下:java
不一样的app进程,用广播相互唤醒(包括利用系统提供的广播进行唤醒)linux
场景1:开机,网络切换、拍照、拍视频时候,利用系统产生的广播唤醒appandroid
场景2:接入第三方SDK也会唤醒相应的app进程,如微信sdk会唤醒微信,支付宝sdk会唤醒支付宝。由此发散开去,就会直接触发了下面的 场景3api
场景3:假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其余阿里系的app给唤醒了。(只是拿阿里打个比方,其实BAT系都差很少)缓存
没错,咱们的Android手机就是一步一步的被上面这些场景给拖卡机的。bash
针对场景1,估计Google已经开始意识到这些问题,因此在最新的Android N取消了 ACTION_NEW_PICTURE(拍照),ACTION_NEW_VIDEO(拍视频),CONNECTIVITY_ACTION(网络切换)等三种广播。微信
启动前台Service网络
白色保活手段很是简单,就是调用系统api启动一个前台的Service进程,这样会在系统的通知栏生成一个Notification,用来让用户知道有这样一个app在运行着,哪怕当前的app退到了后台。这样的目的也很容易理解,就是提升进程的优先级。让Linux在内存不足的时候不会优先被杀掉,从某种意义上来讲这算是最不流氓的一种保活方式了。
app
利用系统的漏洞启动前台Service
灰色保活,这种保活手段是应用范围最普遍。它是利用系统的漏洞来启动一个前台的Service进程,与普通的启动方式区别在于,它不会在系统通知栏处出现一个Notification,看起来就如同运行着一个后台Service进程同样。这样作带来的好处就是,用户没法察觉到你运行着一个前台进程(由于看不到Notification),但你的进程优先级又是高于普通后台进程的
在Android系统中,进程的优先级影响着如下三个因素:
系统对于进程的优先级有以下五个分类:
前台进程
指正在与用户进行交互的应用进程,该进程数量较少,是最高优先级进程,系统通常不会终止该进程,而判断为前台进程的因素有如下这些
进程中包含处于前台的正与用户交互的activity;
进程中包含与前台activity绑定的service;
进程中包含调用了startForeground()方法的service;
进程中包含正在执行onCreate(), onStart(), 或onDestroy()方法的service;
进程中包含正在执行onReceive()方法的BroadcastReceiver.
可视进程
能被用户看到,但不能根据根据用户的动做作出相应的反馈, 因素 进程中包含可见但不处于前台进程的activity(如:activity执行onPause()时处于可见状态,但并不处于前台进程中) 该进程有一个与可见/前台的activity绑定数据的service
服务进程
没有可见界面仍在不断的执行任务的进程,除非在可视进程和前台进程紧缺资源(如:内存资源)才会被终止 因素 包含除前台进程和可视进程的service外的service的进程
后台进程
一般系统中有大量的后台进程,终止后台进程不会影响用户体验,随时为优先级更高的进程腾出资源而被终止,优先回收长时间没用使用过的进程。 因素 包含不在前台或可视进程的activity的进程,也就是已经调用onStop()方法后的activity
空进程
为提升总体系统性能,系统会保存已经完成生命周期的应用程序 ,存在与内存当中,也就是缓存,为下次的启动更加迅速而设计。
上面是进程的分类,进程是怎么被杀的呢?系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的状况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给须要的app, 这套杀进程回收内存的机制就叫 Low Memory Killer。那这个不足怎么来规定呢,那就是内存阈值,咱们可使用cat /sys/module/lowmemorykiller/parameters/minfree来查看某个手机的内存阈值。
读到这里,你或许有一个疑问,假设如今内存不足,空进程都被杀光了,如今要杀后台进程,可是手机中后台进程不少,难道要一次性所有都清理掉?固然不是的,进程是有它的优先级的,这个优先级经过进程的adj值来反映,它是linux内核分配给每一个系统进程的一个值,表明进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收,adj值定义在com.android.server.am.ProcessList类中,这个类路径是${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java。
对于每个运行中的进程,Linux内核都经过proc文件系统暴露这样一个文件来容许其余程序修改指定进程的优先级:
/proc/[pid]/oom_score_adj。(修改这个文件须要root
权限)
这个文件容许的值的范围是:-1000 ~ +1000之间。值越小,表示进程越重要。
当内存很是紧张时,系统便会遍历全部进程,以肯定哪一个进程须要被杀死以回收内存,此时便会读取oom_score_adj
这个文件的值。关于这个值的使用,在后面讲解进程回收的的时候,咱们会详细讲解。
PS:在Linux 2.6.36以前的版本中,Linux 提供调整优先级的文件是/proc/[pid]/oom_adj
。这个文件容许的值的范围是-17 ~ +15
之间。数值越小表示进程越重要。 这个文件在新版的Linux中已经废弃。
但你仍然可使用这个文件,当你修改这个文件的时候,内核会直接进行换算,将结果反映到oom_score_adj
这个文件上。
Android早期版本的实现中也是依赖oom_adj
这个文件。可是在新版本中,已经切换到使用oom_score_adj
这个文件。oom_adj的值越小,进程的优先级越高,普通进程oom_adj值是大于等于0的,而系统进程oom_adj的值是小于0的,咱们能够经过cat /proc/进程id/oom_adj能够看到当前进程的adj值。
看到adj值是0,0就表明这个进程是属于前台进程,咱们按下Back键,将应用至于后台,再次查看
OOM全称Out Of Memory,是Linux当中,内存保护机制的一种。该机制会监控那些占用内存过大,尤为是瞬间很快消耗大量内存的进程,为了防止内存耗尽而内核将该进程杀掉。
当Kernel遇到OOM的时候,能够有2种选择:
1) 产生kernelpanic(死机)
2) 启动OOM killer,选择一个或多个“合适”的进程,干掉那些选择中的进程,从而释放内存。
在Linux中,系统就是经过算分去杀死进程的。至于分数的值,就是这3个参数的值。简单来讲,系统是这样进行算分的:算分主要分2部分,一部分是系统打分,主要根据该进程的内存使用状况(oom_score),另外一部分是用户大份额,就是oom_score_adj。每一个进程的实际得分是综合这2个参数的值的。而oom_adj只是一个旧的接口参数,在普通的linux系统中和oom_score_adj是差很少的(可是在android中是颇有用的)。
在Android中,及时用户退出当前应用程序后,应用程序仍是会存在于系统当中,这是为了方便程序的再次启动。可是这样的话,随着打开的程序的数量的增长,系统的内存就会不足,从而须要杀掉一些进程来释放内存空间。至因而否须要杀进程以及杀什么进程,这个就是由Android的内部机制LowMemoryKiller机制来进行的。
Andorid的Low Memory Killer是在标准的linux lernel的OOM基础上修改而来的一种内存管理机制。当系统内存不足时,杀死没必要要的进程释放其内存。没必要要的进程的选择根据有2个:oom_adj和占用的内存的大小。oom_adj表明进程的优先级,数值越高,优先级月低,越容易被杀死;对应每一个oom_adj均可以有一个空闲进程的阀值。Android Kernel每隔一段时间会检测当前空闲内存是否低于某个阀值。假如是,则杀死oom_adj最大的没必要要的进程,若是有多个,就根据oom_score_adj去杀死进程,,直到内存恢复低于阀值的状态。
LowMemoryKiller的值的设定,主要保存在2个文件之中,分别是/sys/module/lowmemorykiller/parameters/adj与/sys/module/lowmemorykiller/parameters/minfree。adj保存着当前系统杀进程的等级,minfree则是保存着对应的阀值。他们的对应关系以下:
举个例子说明一下上表,当当前系统内存少于55296×4K(即216MB)时,Android就会找出当前oom_adj≥9的进程,根据进程的等级,先把oom_adj数值最大的进程给杀掉,释放他的内存,当他们的oom_adj相等时,就对比他们的oom_score_adj,而后oom_score_adj越大,也越容易杀掉。
在这里,也许有人会问,为何采用LowMemoryKiller而不用OOM呢?咱们来对比一下二者,就能够得出答案了。
使用LowMemoryKiller可使系统内存较低时,调出进程管理器结束没必要要的人进程释放空间。在安卓中,若是等到真正的OOM时,也许进程管理器就已经无法启动了。
上面其实已经说过,LowMemoryKiller是对多个内存阀值的控制来选择杀进程的。可是,这些阀值是怎样联系在一块儿的呢?下面就个人理解,简单说一下其运行的原理。
首先,LowMemoryKiller是随着系统的启动而启动的。当前主要的LowMemoryKiller的代码主要在\system\core\lmkd的目录下,以前的代码\kernel\drivers\staging\android\lowmemorykiller.c已经再也不使用。
LowMemoryKiller在系统启动的时候就已经由init进程一并启动了。LowMemoryKiller启动就是,就会不断监测系统的运行状况和内存状况,当内存少于minfree限定的阀值的时候,lowMemoryKiller遍历当前进程的oom_score_adj,把大于对应阀值的进程进行kill操做。例如,在当前设置中,当系统内存少于315M时,系统就会自动把进程中oom_score_adj的值少于1000的杀掉,当系统内存少于216时,系统就会自动把进程中oom_score_adj的值少于529的杀掉,如此类推。
至于oom_adj和oom_score_adj是由谁去控制并写入的呢?
在系统当中,oom_adj和oom_score_adj是由ActivityManagerService去控制的,上层应用的启动都离不开AcitivityManagerService的调用与分配资源。有关oom_adj与oom_score_adj会在之后分析ActivityManagerService的时候加入相对详细的论述。在这里就不详细说明。
有关Minfree的值的写入,其实能够找到不少个地方,可是在最开始(还在用lowmemorykiller.c)的时候,是能够在lowmemorykiller.c中设置的。可是如今已经不用lowmemorykiller.c了,因此相对设置的地方也不同了。经查找验证,Minfree的阀值控制,是由ActivictyManagerService和lowmemorykiller一并控制写入的。
这种大部分人都了解,听说这个微信也用过的进程保活方案,这方案实际利用了Android前台service的漏洞。
原理以下
对于 API level < 18 :调用startForeground(ID, new Notification()),发送空的Notification ,图标则不会显示。
对于 API level >= 18:在须要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定一样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除
相互唤醒的意思就是,假如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其余阿里系的app给唤醒了。这个彻底有可能的。此外,开机,网络切换、拍照、拍视频时候,利用系统产生的广播也能唤醒app,不过Android N已经将这三种广播取消了。
这个是系统自带的,onStartCommand方法必须具备一个整形的返回值,这个整形的返回值用来告诉系统在服务启动完毕后,若是被Kill,系统将如何操做,这种方案虽然能够,可是在某些状况or某些定制ROM上可能失效
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_REDELIVER_INTENT;
}
复制代码
START_STICKY
若是系统在onStartCommand返回后被销毁,系统将会从新建立服务并依次调用onCreate和onStartCommand(注意:根据测试Android2.3.3如下版本只会调用onCreate根本不会调用onStartCommand,Android4.0能够办到),这种至关于服务又从新启动恢复到以前的状态了)。
START_NOT_STICKY
若是系统在onStartCommand返回后被销毁,若是返回该值,则在执行完onStartCommand方法后若是Service被杀掉系统将不会重启该服务。
START_REDELIVER_INTENT
START_STICKY的兼容版本,不一样的是其不保证服务被杀后必定能重启。
是做为进程死后复活的一种手段,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;
}
}复制代码
听说这个是手Q的进程保活方案,基本思想,系统通常是不会杀死前台进程的。因此要使得进程常驻,咱们只须要在锁屏的时候在本进程开启一个Activity,为了欺骗用户,让这个Activity的大小是1像素,而且透明无切换动画,在开屏幕的时候,把这个Activity关闭掉,因此这个就须要监听系统锁屏广播.