在Android系统中,后台服务很容易由于各种原因被系统(手机厂商)杀掉,这就需要开发者对自己的应用进程进行保活,以满足一些功能需求(例如消息推送,音乐播放)。常见的保活方案很多,详情可以看Android进程保活招式大全,本文就新版本比较常用的方案设置前台服务与JobScheduler展开讨论。
一、设置前台服务
1.1如何设置前台服务
设置前台服务在代码中的设置很简单即在service中调用:
startForeground(1,getNotification());
第一个参数是通知id,第二个参数是Notification实例。这个通知也就是这个前台服务要绑定的通知。没错,前台服务必须要绑定一个通知,不然你以为google能给你无缘无故提升服务优先级吗?那么有没有方法去除这个通知栏,降低用户感知度呢?下一节介绍。
好了,设置前台服务是不是很简单呢?别急,我在调用的时候突然发现跟startForeground方法很像的一个方法startForegroundService(Intent service),而且这还是个android O中的新方法,于是google了一下官网,结果发现:
但是我调试了一下,在我的小米mix2(android8.0.0)和nexus6p(android8.0.0)直接使用startService启动服务都没有问题!当然使用startForegroundService启动服务,然后再使用startForeground设置前台进程也没有问题。难道是官网骗我???可能在比较新的android O中会有这个限制吧。。。
1.2如何去除前台服务的通知栏提醒
方案也是google找的,这里就复述一下,思路就是利用2个同进程的service,利用相同的notificationID,2个service分别startForeground,然后只在1个service里stopForeground,这样即可去掉通知栏的显示。
代码实现:
在你想保活的service的onCreate()方法中,绑定一个用来消除通知的服务:
if (mConnection == null) {
mConnection = new InnerServiceConnection();
}
this.bindService(new Intent(this, InnerService.class), mConnection, Service.BIND_AUTO_CREATE);
InnerService中的代码:
public class InnerService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new LocalBinder();
}
public class LocalBinder extends Binder {
public InnerService getService() {
return InnerService.this;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
InnerServiceConnection的实现:
private class InnerServiceConnection implements ServiceConnection {
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "MyService: onServiceDisconnected");
}
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "MyService: onServiceConnected");
// 的,会在通知栏显示service正在运行,这里不要让用户感知,所以这里的实现方式是利用2个同进程的service,利用相同的notificationID,
// 2个service分别startForeground,然后只在1个service里stopForeground,这样即可去掉通知栏的显示
Service innerService = ((InnerService.LocalBinder) binder)
.getService();
MainService.this.startForeground(1, getNotification());
innerService.startForeground(1, getNotification());
innerService.stopForeground(true);
MainService.this.unbindService(mConnection);
mConnection = null;
}
}
1.3设置前台服务总结
设置了前台服务后,服务一般就不会被系统自动清除了,但在miui这类专治流氓软件的系统中,用户手动清除后台,你的进程仍然会被杀掉(android原生系统中,亲测不会被杀掉)。那该怎么办呢?一般的方案是用一些手段重启服务。接下来介绍JobScheduler
二、JobScheduler
JobScheduler是android 5.0提供的一个API,可以指定一段时间循环执行特定的任务,这些任务都会被系统统一调度执行。因为以前大家的应用都是监听各种广播自启动,google终于忍不了了。
2.1JobScheduler的使用
先上调用代码:
//获取JobScheduler 他是一种系统服务
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.cancelAll();
JobInfo.Builder builder = new JobInfo.Builder(1024, new ComponentName(getPackageName(), JobProtectService.class.getName()));
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//android N之后时间必须在15分钟以上
// builder.setMinimumLatency(10 * 1000);
builder.setPeriodic(15 * 60 * 1000);
}else{
builder.setPeriodic(60 * 1000);
}
builder.setPersisted(true);
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
int schedule = jobScheduler.schedule(builder.build());
if (schedule <= 0) {
Log.w(TAG, "schedule error!");
}
这里JobInfo中包含了要执行的信息,JobInfo.Builder是他的构造器,其中:
JobInfo.Builder builder = new JobInfo.Builder(1024, new ComponentName(getPackageName(), JobProtectService.class.getName()));
是他的构造方法,指定了要跳转的JobService的信息
builder.setPeriodic(long intervalMillis)
表示间隔一定时间执行该任务,在version>=android N的系统版本中,这个值只能是15分钟以上
builder.setMinimumLatency(long minLatencyMillis);
表示延迟一定时间执行任务,改任务只会调用一次,这个值在android N中没有限制,所以如果你想突破setPeriodic中15分钟的限制,你可以使用setMinimumLatency方法,并在执行任务结束的时候再调用这个方法,等会介绍一下。
builder.setPersisted(true);
是否开机启动?这个方法需要权限
在国产rom中应用没开自启动你就别想了
builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
表示网络状态的限制
//不管是否有网络这个任务都会被执行
JobInfo.NETWORK_TYPE_NONE
//任意一种网络任务可以执行
JobInfo.NETWORK_TYPE_ANY
//它表示设备在WIFI连接时任务才会被执行
JobInfo.NETWORK_TYPE_UNMETERED
int schedule = jobScheduler.schedule(builder.build());
表示执行该任务,返回值>0表示执行成功
JobService代码:
public class JobProtectService extends JobService {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent,flags,startId);
}
@Override
public boolean onStartJob(JobParameters params) {
//TODO 写上你的任务逻辑代码
jobFinished(params, false);
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
2.2JobScheduler总结
JobScheduler用来检测你自己的服务是否被杀掉,如果被杀掉了,你重启自己的服务,经过测试,在miui等国产rom中也能够重启服务,前提是你的应用有自启动权限!