【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%稳定的技术,但是以上的措施都会起到一些作用,有的作用其实非常明显
将它们结合起来,基本可以保证应用在后台常驻,千万不要偷懒,奢望只用一个方法就能达到目的
更多推荐
所有评论(0)