【Android】【应用保活】保证应用在后台继续运行

网上关于Service保活和进程保活的文章有很多,但其实大多作用并不大
这些文章大多是只适合很久以前的旧系统,或者是原生的安卓系统,更多的是以讹传讹,未经实践,实际并不可用

实际Service的运行,不完全是由代码控制的,很大程度上取决于操作系统的进程管理策略,手机系统、手机性能息息相关

这里根据我的经验总结出的一些可行的方案,在中高端机型上,基本都能保证Service在后台运行

锁定后台应用

这个功能允许应用在后台运行,不被清理,可以点击任务键,再点击带锁图标的菜单开启

这个不是安卓系统原生的功能,不同的操作系统有不同的打开方法,一般都要点击任务键

在这里插入图片描述

在系统设置-启动管理里面,允许应用后台运行

这个设置允许应用进程在切换到后台时继续运行,但是每个手机的设置方法都有所不同

这里以华为手机为例,有的手机上叫应用启动管理,有的叫后台运行管理,有的叫电池优化

安卓系统定制版本太多了,大家需要自己动手多研究下,把系统设置里面权限应用电池相关的选项都过一遍就能找到

在这里插入图片描述
允许应用后台获取位置信息

这对于有定位功能的应用来说很重要,即便应用进程存活,系统也可能限制定位来达到省电目的
安卓从10.0开始,增加了一个后台定位权限管理功能(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
需要在Manifest清单中配置此权限,并通过代码动态申请此权限,否则息屏后就会立刻停止定位

在这里插入图片描述
关闭电源优化功能

系统对后台进程和定位进行限制,无非为了两个目的,一个是节省CPU和内存,另一个是续航
随着手机性能越来越好,很多高端手机已经不存在性能问题,但是电量消耗问题仍然是存在的

尤其对于定位,视频,屏幕亮度这些功能,大家如果有留意就会知道,它们会明显地大幅提示电量消耗速度
对于有电源优化设置的手机,大家一定要关闭电源优化
也有一些比较简单的系统,会把后台运行等设置都归到电源优化功能里面来

在这里插入图片描述

将服务设置为前台服务

这个功能将后台服务转为前台服务,从而避免被系统清理,这个功能对服务保活效果显著,同样也能提升整个进程的存活率
但相应地,必须将服务与一个前台通知绑定,服务存活期间通知会一直显示,以便告之用户后台应用在运行


	service.startForeground(id, notification);

监听系统广播重新启动

我们可以通过静态注册一个广播接收器,去监听一些比较广播频次比较高系统广播,比如电量更新广播
当系统发出这些广播时,广播接收器就会被唤起,然后我们在Receiver里面重新启动Service即可

不过可惜,从安卓8.0开始,静态广播已经受到了严格限制,这个方法现在已经行不通了
如果所有应用都按这种方法做,系统只要一发广播,就会启动一批应用,非常消耗性能和电量
所以从安卓8.0开始,发送静态广播必须指定包名和接收器的类名,只有特定应用可以收到,而不再是群发
我之所以还把它写出来,是因为它代表了一种通用的解决思路,这个思路在其它操作系统中也许是可行的

多进程守护

即开启两个或更多个进程,彼此之间定时相互启动,除非两个进程同时被杀,否则就可以达到一直存活的目的

不过很遗憾,安卓系统从6.0之后,在杀死进程时,会按照包名,将同一个应用下的所有子进程同时杀死
所以这个方法对于进程复活来说,效果并不理想,但是对于复活Service来说,还是可行的


	//定义一个保活服务基类,服务之间定时相互启动
	//当服务被启动时,服务所在的进程,自然也随之被唤起
	public class KeepAliveService extends Service {
	
	    public Class<? extends Service>[] services() {
	        return new Class[]{SA.class, SB.class};
	    }
	
	    @Override
	    public void onCreate() {
	        super.onCreate();
	        WorkThread.postByLoop(() -> {
	            for (Class<? extends Service> service : services())
	                startService(new Intent(this, service));
	            Threads.sleep(5000);
	        });
	        Notification notification = Notifications.buildForegroundNotification("应用正在后台运行");
	        startForeground(Codes.CODE_BACKGROUND_RUNNING, notification);
	    }
	
	    @Override
	    public int onStartCommand(Intent intent, int flags, int startId) {
	        Console.info(getClass().getSimpleName(), Applications.currentProcessName(), Applications.currentProcessId(), hashCode());
	        return Service.START_STICKY;
	    }
	
	    @Nullable
	    @Override
	    public IBinder onBind(Intent intent) {
	        return null;
	    }
	}


	public class SA extends KeepAliveService {
	
	    @Override
	    public int onStartCommand(Intent intent, int flags, int startId) {
	        Console.info("SA execute its work");
	        return super.onStartCommand(intent, flags, startId);
	    }
	}


	public class SB extends KeepAliveService {
	
	    @Override
	    public int onStartCommand(Intent intent, int flags, int startId) {
	        Console.info("SB execute its work");
	        return super.onStartCommand(intent, flags, startId);
	    }
	}


	<service
	    android:name=".SA"
	    android:exported="true"
	    android:process=":aaa" />
	
	<service
	    android:name=".SB"
	    android:exported="true"
	    android:process=":bbb" />


	//在主进程启动所有服务
	startService(new Intent(this, SA.class));
	startService(new Intent(this, SB.class));

通过JobScheduler拉活进程

JobScheduler用于在系统中注册一个定时任务,可以定时启动一个JobService
所以只要我们的服务继承JobService,就可以定时被JobScheduler唤起

当然,和其它的功能一样,安卓系统肯定是不可能允许应用通过JobScheduler来作弊,在后台一直运行的
实际上,JobScheduler最快也得15分钟才能唤醒一次任务,JobService最多只能工作10分钟就会被杀死
JobScheduler设计出来并不是让用户肆意滥用的,而是给那些需要定时工作,但是工作频次小,工作时间短的功能设计的

虽然JobScheduler不能保证进程一直存活,但却可以起到定时拉活进程的目的,我们再配合其它技术一起使用,就可以达到一个比较好的效果
毕竟,服务和进程并不会一启动就会被系统杀死,我们将进程拉活和进程守护功能结合起来,就可以保证进程在大多时间都在工作


	//在触发JobScheduler任务时,启动指定Service来拉活进程
	public class SJ extends JobService {
	
	    public Class<? extends Service>[] services() {
	        return new Class[]{SA.class, SB.class};
	    }
	
	    @Override
	    public boolean onStartJob(JobParameters params) {
	        for (Class<? extends Service> service : services())
	            startService(new Intent(this, service));
	        return false;
	    }
	
	    @Override
	    public boolean onStopJob(JobParameters params) {
	        return true;
	    }
	}


	//在主进程启动所有服务
	startService(new Intent(this, SA.class));
	startService(new Intent(this, SB.class));
	
	//启动JobScheduler任务来定时拉活Service进程
	startKeepAliveJob(1001, SJ.class);
	
	//开启一个用于拉活的JobScheduler任务
	public void startKeepAliveJob(int jobId, Class<? extends JobService> service) {
		ComponentName componentName = new ComponentName(getPackageName(), service.getName());
		JobInfo.Builder builder = new JobInfo.Builder(jobId, componentName);
		builder.setPeriodic(15 * 60 * 1000);
		builder.setPersisted(true);
		JobInfo jobInfo = builder.build();
		JobScheduler scheduler = getSystemService(JobScheduler.class);
		scheduler.schedule(jobInfo);
	}


	<service
	    android:name=".SA"
	    android:exported="true"
	    android:process=":aaa" />
	
	<service
	    android:name=".SB"
	    android:exported="true"
	    android:process=":bbb" />
	
	<service
	    android:name=".SJ"
	    android:exported="true"
	    android:permission="android.permission.BIND_JOB_SERVICE"
	    android:process=":job" />

设置服务以粘性模式启动

STICKY模式的服务,会在进程意外中断时,自动重新启动


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

综合各种方法技巧

进程保活并不是一个100%稳定的技术,但是以上的措施都会起到一些作用,有的作用其实非常明显
将它们结合起来,基本可以保证应用在后台常驻,千万不要偷懒,奢望只用一个方法就能达到目的

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值