1.概述
据前人验证,在没有白名单的情况下,安卓系统要做一个任何情况下都不被杀死的应用是基本不可能的,但是我们可以做到应用基本不被杀死,如果杀死可以立即复活.经过上网查询,进程常驻的方案众说纷纭,但是很多的方案都是不靠谱的或不是最好的,结合很多资料,今天总结一下Android进程保活的一些可行方法.
2.问题
系统为什么会杀掉进程,杀的为什么是我们的进程,这是根据什么规则来决定的,是一次性干掉多个进程,还是一个接着一个杀掉?保活套路一堆,如何进行进程保活才是比较恰当......
3.分析
3.1进程的划分
Android中的进程也是有着严格的等级,分了三流九等,Android系统把进程划为了如下几种(重要性从高到低):
3.1.1. 前台进程 —— Foreground process
用户当前操作所必需的进程。通常在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。
A. 拥有用户正在交互的 Activity(已调用onResume())
B. 拥有某个 Service,后者绑定到用户正在交互的 Activity
C. 拥有正在“前台”运行的 Service(服务已调用startForeground())
D. 拥有正执行一个生命周期回调的 Service(onCreate()、onStart()或onDestroy())
E. 拥有正执行其onReceive()方法的 BroadcastReceiver
3.1.2. 可见进程 —— Visible process
没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。
A. 拥有不在前台、但仍对用户可见的 Activity(已调用onPause())。
B. 拥有绑定到可见(或前台)Activity 的 Service
3.1.3. 服务进程 —— Service process
尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。
A. 正在运行startService()方法启动的服务,且不属于上述两个更高类别进程的进程。
3.1.4. 后台进程 —— Background process
后台进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU 列表中,以确保包含用户最近查看的Activity的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。
A. 对用户不可见的 Activity 的进程(已调用Activity的onStop()方法)
3.1.5. 空进程 —— Empty process
保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。
A. 不含任何活动应用组件的进程
具体地,活动进程指的就是用户正在操作的程序,是前台进程,可以看到且能够操作;可见进程就是看得见摸不着的,不能直接操作的进程;服务进程是没有界面的一直在后台工作的进程,优先级不高,当系统内存不足时会被杀死,再次充裕的时候会再次开启;后台进程就是用户按了"back"或者"home"后,程序本身看不到了,但是其实还在运行的程序,比如Activity调用了onPause方法系统可能随时终止它们,回收内存.空进程:某个进程不包含任何活跃的组件时该进程就会被置为空进程,完全没用,杀了它只有好处没坏处,第一个被处理!
3.2内存阈值
进程是怎么被杀的呢?系统出于体验和性能上的考虑,app在退到后台时系统并不会真正的kill掉这个进程,而是将其缓存起来。打开的应用越多,后台缓存的进程也越多。在系统内存不足的情况下,系统开始依据自身的一套进程回收机制来判断要kill掉哪些进程,以腾出内存来供给需要的app, 这套杀进程回收内存的机制就叫 Low Memory Killer。那这个不足怎么来规定呢,那就是内存阈值,我们可以使用cat /sys/module/lowmemorykiller/parameters/minfree来查看某个手机的内存阈值。
注意这些数字的单位是page. 1 page = 4 kb.上面的六个数字对应的就是(MB): 72,90,108,126,144,180,这些数字也就是对应的内存阀值,内存阈值在不同的手机上不一样,一旦低于该值,Android便开始按顺序关闭进程. 因此Android开始结束优先级最低的空进程,即当可用内存小于180MB(46080*4/1024)。
进程是有它的优先级的,这个优先级通过进程的adj值来反映,它是linux内核分配给每个系统进程的一个值,代表进程的优先级,进程回收机制就是根据这个优先级来决定是否进行回收,adj值定义在com.android.server.am.ProcessList类中,这个类路径是${android-sdk-path}\sources\android-23\com\android\server\am\ProcessList.java。oom_adj的值越小,进程的优先级越高,普通进程oom_adj值是大于等于0的,而系统进程oom_adj的值是小于0的,我们可以通过cat /proc/进程id/oom_adj可以看到当前进程的adj值。
也就是说,oom_adj越大,占用物理内存越多会被最先kill掉,OK,那么现在对于进程如何保活这个问题就转化成,如何降低oom_adj的值,以及如何使得我们应用占的内存最少。
开启一个像素的Activity
据说这个是手Q的进程保活方案,基本思想,系统一般是不会杀死前台进程的。所以要使得进程常驻,我们只需要在锁屏的时候在本进程开启一个Activity,为了欺骗用户,让这个Activity的大小是1像素,并且透明无切换动画,在开屏幕的时候,把这个Activity关闭掉,所以这个就需要监听系统锁屏广播.
publicclassSinglePixelActivityextendsActivity{
public staticfinalStringTAG=SinglePixelActivity.class.getSimpleName();
public static void actionToSinglePixelActivity(Context pContext) {
Intent intent =newIntent(pContext, SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
pContext.startActivity(intent);
}
@Override
protectedvoid onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG,"onCreate");
setContentView(R.layout.activity_singlepixel);
Windowwindow = getWindow();
//放在左上角
window.setGravity(Gravity.START|Gravity.TOP);
WindowManager.LayoutParamsattributes = window.getAttributes();
//宽高设计为1个像素
attributes.width =1;
attributes.height =1;
//起始坐标
attributes.x =0;
attributes.y =0;
window.setAttributes(attributes);
ScreenManager.getInstance(this).setActivity(this);
}
@Override
protectedvoid onDestroy() {
super.onDestroy();
}
}
在屏幕关闭的时候把SinglePixelActivity启动起来,在开屏的时候把SinglePixelActivity 关闭掉,所以要监听系统锁屏广播,以接口的形式通知MainActivity启动或者关闭SinglePixActivity。
publicclassScreenBroadcastListener{
privateContext mContext;
privateScreenBroadcastReceiver mScreenReceiver;
privateScreenStateListener mListener;
publicScreenBroadcastListener(Context context){
mContext = context.getApplicationContext();
mScreenReceiver =newScreenBroadcastReceiver();
}
interfaceScreenStateListener{
voidonScreenOn();
voidonScreenOff();
}
/**
* screen状态广播接收者
*/
privateclassScreenBroadcastReceiverextendsBroadcastReceiver{
privateString action =null;
@Override
publicvoidonReceive(Context context, Intent intent){
action = intent.getAction();
if(Intent.ACTION_SCREEN_ON.equals(action)) {// 开屏
mListener.onScreenOn();
}elseif(Intent.ACTION_SCREEN_OFF.equals(action)) {// 锁屏
mListener.onScreenOff();
}
}
}
publicvoidregisterListener(ScreenStateListener listener){
mListener = listener;
registerListener();
}
privatevoidregisterListener(){
IntentFilter filter =newIntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
mContext.registerReceiver(mScreenReceiver, filter);
}
}
publicclassScreenManager{
privateContext mContext;
privateWeakReference mActivityWref;
publicstaticScreenManager gDefualt;
publicstaticScreenManagergetInstance(Context pContext){
if(gDefualt ==null) {
gDefualt =newScreenManager(pContext.getApplicationContext());
}
returngDefualt;
}
privateScreenManager(Context pContext){
this.mContext = pContext;
}
publicvoidsetActivity(Activity pActivity){
mActivityWref =newWeakReference(pActivity);
}
publicvoidstartActivity(){
SinglePixelActivity.actionToSinglePixelActivity(mContext);
}
publicvoidfinishActivity(){
//结束掉SinglePixelActivity
if(mActivityWref !=null) {
Activity activity = mActivityWref.get();
if(activity !=null) {
activity.finish();
}
}
}
}
现在MainActivity改成如下
publicclassMainActivityextendsAppCompatActivity{
@Override
protectedvoid onCreate(BundlesavedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
finalScreenManagerscreenManager =ScreenManager.getInstance(MainActivity.this);
ScreenBroadcastListenerlistener =newScreenBroadcastListener(this);
listener.registerListener(newScreenBroadcastListener.ScreenStateListener() {
@Override
public void onScreenOn() {
screenManager.finishActivity();
}
@Override
public void onScreenOff() {
screenManager.startActivity();
}
});
}
}
按下back之后,进行锁屏,现在测试一下oom_adj的值
果然将进程的优先级提高了。
据说这个微信也用过的进程保活方案,该方案实际利用了Android前台service的漏洞。
原理如下
对于 API level < 18 :调用startForeground(ID, new Notification()),发送空的Notification ,图标则不会显示。
对于 API level >= 18:在需要提优先级的service A启动一个InnerService,两个服务同时startForeground,且绑定同样的 ID。Stop 掉InnerService ,这样通知栏图标即被移除。
publicclassKeepLiveServiceextendsService{
publicstaticfinalintNOTIFICATION_ID=0x11;
publicKeepLiveService(){
}
@Override
publicIBinderonBind(Intent intent){
thrownewUnsupportedOperationException("Not yet implemented");
}
@Override
publicvoidonCreate(){
super.onCreate();
//API 18以下,直接发送Notification并将其置为前台
if(Build.VERSION.SDK_INT
startForeground(NOTIFICATION_ID,newNotification());
}else{
//API 18以上,发送Notification并将其置为前台后,启动InnerService
Notification.Builder builder =newNotification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
startService(newIntent(this, InnerService.class));
}
}
publicstaticclass InnerService extends Service{
@Override
publicIBinderonBind(Intent intent){
returnnull;
}
@Override
publicvoidonCreate(){
super.onCreate();
//发送与KeepLiveService中ID相同的Notification,然后将其取消并取消自己的前台显示
Notification.Builder builder =newNotification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(NOTIFICATION_ID, builder.build());
newHandler().postDelayed(newRunnable() {
@Override
publicvoidrun(){
stopForeground(true);
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
},100);
}
}
}
在没有采取前台服务之前,启动应用,oom_adj值是0,按下返回键之后,变成9(不同ROM可能不一样)
在采取前台服务之后,启动应用,oom_adj值是0,按下返回键之后,变成2(不同ROM可能不一样),确实进程的优先级有所提高。
进程相互唤醒
顾名思义,就是指的不同进程,不同app之间互相唤醒,如你手机里装了支付宝、淘宝、天猫、UC等阿里系的app,那么你打开任意一个阿里系的app后,有可能就顺便把其他阿里系的app给唤醒了。
JobSheduler
JobSheduler是作为进程死后复活的一种手段,native进程方式最大缺点是费电, Native 进程费电的原因是感知主进程是否存活有两种实现方式,在 Native 进程中通过死循环或定时器,轮训判断主进程是否存活,当主进程不存活时进行拉活。其次5.0以上系统不支持。 但是JobSheduler可以替代在Android5.0以上native进程方式,这种方式即使用户强制关闭,也能被拉起来,亲测可行。
JobSheduler@TargetApi(Build.VERSION_CODES.LOLLIPOP)
publicclassMyJobServiceextendsJobService{
@Override
publicvoidonCreate(){
super.onCreate();
startJobSheduler();
}
publicvoidstartJobSheduler(){
try{
JobInfo.Builder builder =newJobInfo.Builder(1,newComponentName(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
publicbooleanonStartJob(JobParameters jobParameters){
returnfalse;
}
@Override
publicbooleanonStopJob(JobParameters jobParameters){
returnfalse;
}
}
这个是系统自带的,onStartCommand方法必须具有一个整形的返回值,这个整形的返回值用来告诉系统在服务启动完毕后,如果被Kill,系统将如何操作,这种方案虽然可以,但是在某些情况or某些定制ROM上可能失效,认为可以多做一种保保守方案。
@Override
publicintonStartCommand(Intent intent,intflags,intstartId){
returnSTART_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的兼容版本,不同的是其不保证服务被杀后一定能重启。
相比与粘性服务与系统服务捆绑更厉害一点,这里说的系统服务很好理解,比如NotificationListenerService,NotificationListenerService就是一个监听通知的服务,只要手机收到了通知,NotificationListenerService都能监听到,即时用户把进程杀死,也能重启,所以说要是把这个服务放到我们的进程之中,那么就得劲了
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class LiveService extends NotificationListenerService {
publicLiveService() {
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
}
}
但是这种方式需要权限
android:name=".LiveService"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
所以你的应用要是有消息推送的话,那么可以用这种方式去欺骗用户。
okhttp执行流出:
流程简述:
(1)、当我们通过OkhttpClient创立一个Call,并发起同步或者异步请求时;
(2)、okhttp会通过Dispatcher对我们所有的RealCall(Call的具体实现类)进行统一管理,并通过execute()及enqueue()方法对同步或者异步请求进行解决;
(3)、execute()及enqueue()这两个方法会最终调用RealCall中的getResponseWithInterceptorChain()方法,从阻拦器链中获取返回结果;
(4)、阻拦器链中,依次通过RetryAndFollowUpInterceptor(重定向阻拦器)、BridgeInterceptor(桥接阻拦器)、CacheInterceptor(缓存阻拦器)、ConnectInterceptor(连接阻拦器)、CallServerInterceptor(网络阻拦器)对请求依次解决,与服务的建立连接后,获取返回数据,再经过上述阻拦器依次解决后,最后将结果返回给调用方。
————————————————
版权声明:本文为CSDN博主「沐潼爱吃鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aiyoufang/article/details/103173087