Android O对后台Service限制 这篇文章说明了Android O版本对后台Service的限制,并且在文章的末尾提到了解决方案,那就是使用JobScheduler。
JobScheduler的作用
从Android O(8.0, API 26)开始,系统只允许前台app创建和使用后台Service。这样就可以限制后台Service无限运行,从而可以提升设备性能和用户体验。
JobScheduler是一种替代方案,它可以在某些条件满足的情况下执行任务。例如当网络连接上,并且处于充电状态,应用可以向云端同步数据。又例如可以监听ContentProvider的URI,当它发生改变时,执行一些任务。而且更牛逼的是,即使应用不处于前台甚至没有运行的情况下,当条件满足时,还是可以执行任务。
使用JobScheduler
本文将使用JobScheduler来完成下载图片的任务,并将图片显示到界面上。
创建JobService
JobScheduler是使用JobService来执行任务的,JobService是一个标准的Service,它是一个抽象类,我们需要创建一个它的子类
public class MyJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
当onStartJob()被调用时,表明任务开始执行,你需要在这个方法中实现执行任务的逻辑。但是呢,它是在主线程中执行的,所以你如果执行一些耗时的操作,你需要把任务放到工作线程中执行,可以考虑使用Thread/Handler/AsyncTask。
onStartJob()的返回值我们需要注意,如果返回true,表明任务还要继续执行,例如我们开了一个工作线程与云端服务器同步数据,这个任务非常耗时,所以通过让onStartJob()返回true,表明后台任务还在继续执行。
但是任务总有执行完成的时刻,此时我们需要调用jobFinished()方法来告诉系统任务已经完成。
如果onStartJob()返回false,那么表示任务已经执行完毕。
现在来谈下onStopJob()。这个方法只有在条件不满足时才触发,例如任务的触发条件是有网络,如果网络断开,那么就会调用onStopJob()。此时我们要注意,这也意味着要停止在onStartJob()中执行的任务。
onStopJob()如果返回true,表示任务将被再次调度执行,如果返回false表示任务完全结束。
在任务执行期间,系统会持有唤醒锁(wakelock),并且直到调用
jobFinished()或者onStopJob()后才会释放。
现在来创建一个JobService的子类,并实现异步线程的框架
public class MyJobService extends JobService {
private class JobServiceHandler extends Handler {
public JobServiceHandler(@NonNull Looper looper) {
super(looper);
}
@Override
public void handleMessage(@NonNull Message msg) {
// 执行任务...
// 任务执行完毕,通知系统
jobFinished(parameters, false);
}
}
private JobServiceHandler mHandler;
@Override
public void onCreate() {
super.onCreate();
// 启动一个工作线程
HandlerThread handlerThread = new HandlerThread("JobService_thread");
handlerThread.start();
// 创建一个工作线程相关的Handler
mHandler = new JobServiceHandler(handlerThread.getLooper());
}
@Override
public boolean onStartJob(JobParameters params) {
// 把任务交给工作线程处理
final Message message = mHandler.obtainMessage();
message.what = params.getJobId();
message.obj = params;
message.sendToTarget();
// 返回true表示任务还是继续执行
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
MyJobService是一个标准的Android Service,在它创建的时候,也就是在onCreate()中,利用HandlerThread创建一个工作线程,并创建了与之关联的Handler。
然后在任务启动时,也就是在onStartJob()中,通过向Handler发送消息,把任务交给了工作线程处理。
如此一来,就避免了任务在主线程中处理,从而可能导致的ANR。
onStartJob()返回的是true,表示任务在工作线程中继续执行。那么同时我们还需要在任务完成时,调用jobFinished()通知系统任务已经完成,这就是在Handler的handleMessage()中调用。
onStopJob()直接返回false,表明任务不再需要再次调度执行。注意,这个方法被调用是因为触发任务的条件被打破,此时需要停止正在执行任务,具体如何停止任务,要看实际情况决定。
注册JobService
由于JobService是一个标准的Android Service,因此需要在AndroidManifest.xml中注册,但是它的注册有点要求,必须要设置一个固定的权限。
<service
android:name=".MyJobService"
android:permission="android.permission.BIND_JOB_SERVICE" />
使用JobScheduler计划任务
JobScheduler是Job Scheduler Service的一个client代理 类,获取方式如下
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
这个命名是真的奇怪,为何不叫做JobSchedulerManager呢?
现在需要创建一个任务,它由JobInfo类表示。JobInfo是由Builder构造的,代码如下
JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_DOWNLOAD_MEIZI, new ComponentName(this, MyJobService.class))
.setExtras(createExtras()) // 传入参数
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY); // 设置需要网络
Builder构造函数需要传入一个Job ID和一个JobService的实现类。
通过setExtras()给任务设置的参数,通过setRequiredNetworkType()设置任务需要网络。
现在万事俱备,只欠东风。我们需要把这个任务发布给系统
jobScheduler.schedule(builder.build());
代码参考
这个Demo使用JobScheduler下载了一个妹子图片,并显示到Activity界面。在测试的过程中,首先不要联网,然后启动App,再通过Home键把App置于后台,最后联网,通过Log你可以发现任务执行了完毕,那么再次打开应用,就可以看到妹子图片显示到了Activity界面。这就说明JobScheduler解决了后台Service的限制。