1 生命周期

service:是一个后台服务,专门用来处理常驻后台的工作的组件。为了决定哪些进程留下,哪些进程被杀死,系统根据在进程中在运行的组件及组件的状态,为每一个进程分配了一个优先级等级,优先级最低的进程首先被杀死。

Android会尽可能高的估量一个进程的级别。比如,一个进程拥有一个可见状态的activity和一个service,这个进程会被认为是可见进程,而不是服务进程。

一个进程的级别可能会由于其它进程依赖于它而升高。一个为其它进程提供服务的进程级别永远高于使用它服务的进程。比如说,如果A进程中的内容提供者为进程B中的客户端提供服务,或进程A中的服务为进程B中的组件所绑定,则A进程高于或者等于进程B的等级。

2 进程的五个常用等级

2.1 前台进程:杀死前台进程需要用户交互,因为前台进程的优先级是最高的

(1)进程持有一个正在与用户交互的Activity
(2)进程持有一个Service,这个Service处于这几种状态:

①Service与用户正在交互的Activity绑定;
②Service调用了startForeground(),在前台运行;
③Service执行在用户正在交互的Activity的生命周期回调函数中(onCreate(), onStart(), onDestroy())。

(3)进程持有一个BroadcastReceiver,这个BroadcastReceiver正在执行它的 onReceive() 方法。

2.2 可见进程

如果一个进程不含有任何前台的组件,但仍可被用户在屏幕上所见。可见的进程也是很重要的,一般不会被销毁,除非是为了保证所有前台进程的运行而不得不杀死可见进程。

当满足如下任一条件时,进程被认为是可见的:
(1)进程持有一个Activity,这个Activity不在前台,但是仍然被用户可见(处于onPause()调用后又没有调用onStop()的状态,比如:前台的activity打开了一个对话框,这样activity就会在其后可见);
(2)进程持有一个Service,这个Service和一个可见的(或者前台的)Activity绑定。

2.3 服务进程

一个进程中运行着一个service,这个service是通过startService()开启的,比如后台播放音乐,后台下载数据等。

2.4 后台进程

通常会有很多个后台进程存在,它们会被保存在一个LRU (least recently used)列表中,这样就可以确保用户最近使用的activity最后被销毁,即最先销毁时间最远的activity。

(1)**进程持有一个用户不可见的Activity(activity的onStop()被调用,当此Activity退出到桌面),就认为进程是一个后台进程。**后台进程不直接影响用户体验,系统会为了优先级高的进程而任意杀死后台进程。

2.5 空进程:Empty process

**一个进程当中已经没有数据在运行了,但是内存当中还为这个应用驻留了一个进程空间。保存这种进程的唯一理由是为了缓存的需要,为了加快下次要启动这个进程中的组件时的启动时间。**系统为了平衡进程缓存和底层内核缓存的资源,经常会杀死空进程。

(1)一个进程不包含任何活跃的应用组件,则认为是空进程(android设计的,为了第二次启动更快,采取的一个权衡)

3 在Service中新开线程和在Activity直接开线程的区别

因为服务进程的优先级比后台进程的优先级高,所以对于一个需要启动一个长时间操作的activity来说,尤其是对于操作将超出activity的持续时间时,开启一个service比创建一个工作线程的方法更好。比如:要上传一个图片文件,应该开启一个service来进行上传工作,这样在用户离开activity时工作仍在进行。

(1)若直接在Activity中新开一条线程来做耗时操作,当该Activity退出到桌面或其他情况时将成为一个后台进程。
(2)若在Service中新启动线程,将会保证操作至少有服务进程的优先级。

4 保证Service不死常用技巧总结(要根据自己的需要来使用)

4.1 设置优先级

在AndroidManifest.xml文件中对于intent-filter可以通过android:priority = “1000”这个属性设置最高优先级,1000是最高值,如果数字越小则优先级越低,同时实用于广播。

<service  
     android:name="com.dbjtech.acbxt.waiqin.UploadService"  
     android:enabled="true" >  
     <intent-filter android:priority="1000" >  
         <action android:name="com.dbjtech.myservice" />  
     </intent-filter>  
</service> 

4.2 startForeground()方法

在onStartCommand里面调用startForeground()方法把Service提升为前台进程级别,然后再onDestroy里面要记得调用stopForeground ()方法。

4.3 onStartCommand方法,返回START_STICKY

在onStartCommand方法中手动返回START_STICKY,当service因内存不足被kill,当内存又存在的时候,service又被重新创建,但是不能保证任何情况下都被重建,比如:进程被干掉了。

public int onStartCommand(Intent intent, int flags, int startId) {  
     flags = START_STICKY;  
     return super.onStartCommand(intent, flags, startId);  
} 

补充说明:onStartCommand()方法,返回的是一个int整形。这个整形可以有以下四个取值:
(1)START_STICKY:“粘性的”。如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null;
(2)START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand(Intent,int,int)后,服务被异常kill掉,系统不会自动重启该服务;
(3)START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入;
(4)START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。

4.4 在onDestroy方法里发广播重启service

service+broadcast方式,就是当service走onDestory的时候,发送一个自定义的广播,当收到广播的时候,重新启动service。(第三方应用或是在setting里-应用-强制停止时,APP进程就直接被干掉了,onDestroy方法都进不来,所以无法保证会执行)。

<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="com.dbjtech.waiqin.destroy" />//这个就是自定义的action
            </intent-filter>
        </receiver>
// 在onDestroy时
@Override
    public void onDestroy() {
        stopForeground(true);
        Intent intent = new Intent("com.dbjtech.waiqin.destroy");
        sendBroadcast(intent);
        super.onDestroy();
    }
// 在BootReceiver里
public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {
            //在这里写重新启动service的相关操作
            startUploadService(context);
        }
    }
}

4.5 监听(系统)广播判断Service状态

通过监听系统、QQ,微信,系统应用,友盟推送等的一些广播,比如:手机重启、界面唤醒、应用状态改变等监听并捕获到,然后判断我们的Service是否还存活,并唤醒自己的App。这是大厂常用方法,例如:阿里收购友盟后,通过友盟的广播唤醒阿里的App,然后把自己启动了。

<receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="android.intent.action.PACKAGE_RESTARTED" />
                <action android:name="com.dbjtech.waiqin.destroy" />
            </intent-filter>
        </receiver>
// BroadcastReceiver中
@Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            System.out.println("手机开机了....");
            startUploadService(context);//开启服务
        }
        if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
            startUploadService(context);
        }
    }

4.6 Application加上Persistent属性

看Android的文档知道,当进程长期不活动,或系统需要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。但是如果某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),应该怎么做,才能使进程不被杀死。

// 加上以上属性相当于将该进程设置为常驻内存进程。
add android:persistent=”true” into the section in your AndroidManifest.xml

切记,这个不可滥用,一般只适用于放在/system/app下的app,系统中用这个的service,app一多,整个系统可能就会崩溃。比如系统phone中配置了android:persistent=”true”属性,并且Phone.apk是安装在/system/app/目录下的,所以在开机时会自动启动PhoneApp类。

4.7 类似监听锁屏方法

4.7.1 需求

QQ采取在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉(顺便同时把自己的核心服务再开启一次)。
故事:小米撕逼。
背景:当手机锁屏的时候什么都干死了,为了省电。
锁屏界面在上面盖住了。
监听锁屏广播,锁了---启动这个Activity。
监听锁屏的,开启---结束掉这个Activity。
要监听锁屏的广播---动态注册。
ScreenListener.begin(new xxxListener
	onScreenOff()
);

被系统无法杀死的进程。

4.7.2 参考源码

双进程守护+锁屏
这里写图片描述

4.8 双进程守护+JobScheduler调度

4.8.1 概念

双进程守护—可以防止单个进程杀死,同时可以防止第三方的360清理掉。
一个进程被杀死,另外一个进程又被他启动。相互监听启动。A<—>B,杀进程是一个一个杀的。本质是和杀进程时间赛跑。

JobScheduler源码分析:JobScheduler来执行一些需要满足特定条件但不紧急的后台任务,把任务加到系统调度队列中,当到达任务窗口期的时候就会执行,我们可以在这个任务里面启动我们的进程。这样可以做到将近杀不死的进程。

4.8.2 参考源码

这里写图片描述

4.8.3 结果截图

双进程守护+锁屏
这里写图片描述

4.9 其他

(1)App运营商与手机厂商合作,列入白名单。
(2)利用账号同步机制唤醒我们的进程。
(3)NDK来解决,Native进程来实现双进程守护。

5 参考链接

Android基础总结——进程优先级及提高优先级的方法(Service尽量不死之法)

【腾讯Bugly干货分享】Android 进程保活招式大全


版权声明:本文为chenliguan原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/chenliguan/article/details/54603611