Android8.0对系统资源的管控更加严格,添加了后台限制规则。
如果满足以下任意条件,应用
将被视为处于前台:
如果以上条件均不满足,应用将被视为处于后台。
系统不允许后台应用创建后台服务。 因此,Android 8.0 引入了一种全新的方法,即 Context.startForegroundService(),以在前台启动新服务。
在系统创建服务后应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。
如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR
。
Android强制开发者使用 JobScheduler 作业替换后台服务的意思,使用jobIntentService就可以比较方便的使用JobScheduler这个工具。
特殊情况是可以创建后台服务的:
JobIntentService实质为Service其继承关系如下所示。
java.lang.Object
↳ android.content.Context
↳ android.content.ContextWrapper
↳ android.app.Service
↳ android.support.v4.app.JobIntentService
Helper for processing work that has been enqueued for a job/service. When running on Android O or later, the work will be dispatched as a job via JobScheduler.enqueue. When running on older versions of the platform, it will use Context.startService.
官方文档解释为,用于处理被加入到job或service任务的一个辅助工具,8.0以下被当作普通的Intent使用startSerivce()启动service来执行。
8.0以上任务被作为job用jobScheduler.enqueue()方法来分发,
说到Jobscheduler,应该不陌生了,框架提供的用来APP调度任务的接口,根据APP要求构建JobInfo,系统会在适当的时间调用JobInfo指定的JobService来执行你的任务。
所以在Android8.0及以上JobIntentService和JobService做的事情是相同的,都是等着JobScheduler分配任务来执行。
不同点在于,JobService使用的handler使用的是主线程的Looper,因此需要在onStartJob()中手动创建AsyncTask去执行耗时任务,而JobIntentService则帮我们处理这一过程,使用它只需要写需要做的任务逻辑即可,不用关心卡住主线程的问题。另外,向jobScheduler传递任务操作也更简单了,不需要在指定JobInfo中的参数,直接enqueue(context,intent)就可以。
这有点像Service和IntentService的关系。
来看一段JobIntentService的源码
加入enqueue到onhandlework的过程。
//JobIntentService的入口方法
public static void enqueueWork(@NonNull Context context, @NonNull Class cls, int jobId,
@NonNull Intent work) {
if (work == null) {
throw new IllegalArgumentException("work must not be null");
}
synchronized (sLock) {
WorkEnqueuer we = getWorkEnqueuer(context, cls, true, jobId);//根据版本获取不同的WorkEnqueuer
we.ensureJobId(jobId);//每个jobIntentService 唯一对应一个JobId,所有给这个service的work都必须相同
we.enqueueWork(work);//调用WorkEnqueuer
}
}
static WorkEnqueuer getWorkEnqueuer(Context context, Class cls, boolean hasJobId, int jobId) {
WorkEnqueuer we = sClassWorkEnqueuer.get(cls);
if (we == null) {
if (BuildCompat.isAtLeastO()) {
if (!hasJobId) {
throw new IllegalArgumentException("Can't be here without a job id");
}
we = new JobWorkEnqueuer(context, cls, jobId);//8.0
} else {
we = new CompatWorkEnqueuer(context, cls);//8.0以前
}
sClassWorkEnqueuer.put(cls, we);
}
return we;
}
//8.0的WorkEnqueuer.enqueueWork()
@Override
void enqueueWork(Intent work) {
if (DEBUG) Log.d(TAG, "Enqueueing work: " + work);
mJobScheduler.enqueue(mJobInfo, new JobWorkItem(work));//调用JobScheduler.enqueue()
}
看到最终调用了JobScheduler来调度任务,那么是给哪个service执行呢,看mJobInfo怎么build的
JobWorkEnqueuer(Context context, Class cls, int jobId) {
super(context, cls);
ensureJobId(jobId);
JobInfo.Builder b = new JobInfo.Builder(jobId, mComponentName);//使用mComponentName
mJobInfo = b.setOverrideDeadline(0).build();
mJobScheduler = (JobScheduler) context.getApplicationContext().getSystemService(
Context.JOB_SCHEDULER_SERVICE);
}
使用mComponentName,那这个mComponentName在哪赋值
//JobWorkEnqueuer的父类
WorkEnqueuer(Context context, Class cls) {
mComponentName = new ComponentName(context, cls);
所以mComponentName就是在enqueueWork(@NonNull Context context, @NonNull Class cls, int jobId,@NonNull Intent work)中传入的service的类名,一般在调用JobIntentService时就会传入其子类的类名。
那么work在哪被处理的呢,按照jobService的逻辑找到onStartJob(),就是执行任务处理的地方
public boolean onStartJob(JobParameters params) {
if (DEBUG) Log.d(TAG, "onStartJob: " + params);
mParams = params;
// We can now start dequeuing work!
mService.ensureProcessorRunningLocked();
return true;
}
void ensureProcessorRunningLocked() {
if (mCurProcessor == null) {
mCurProcessor = new CommandProcessor();
if (DEBUG) Log.d(TAG, "Starting processor: " + mCurProcessor);
mCurProcessor.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);//mCurProcessor是线程池的AsyncTask
}
}
final class CommandProcessor extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
GenericWorkItem work;
if (DEBUG) Log.d(TAG, "Starting to dequeue work...");
while ((work = dequeueWork()) != null) {
if (DEBUG) Log.d(TAG, "Processing next work: " + work);
onHandleWork(work.getIntent()); //回调onHandleWork()
if (DEBUG) Log.d(TAG, "Completing work: " + work);
work.complete();
}
if (DEBUG) Log.d(TAG, "Done processing work!");
return null;
}
子类实现jobIntentService处理work就是实现onHandleWork,可以看到其使用线程池的AsyncTask来处理work的,所以不需要考虑主线程阻塞的问题。
public class MainActivity extends Activity {
Button btn ;
static int num =0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent workIntent = new Intent();
num++;
Log.d("houson", "onClick: "+num);
workIntent.putExtra("work","work num:"+num);
MyJobIntentService.enqueueWork(getApplicationContext(),workIntent);
}
});
}
}
public class MyJobIntentService extends JobIntentService {
/**
* 这个Service 唯一的id
*/
static final int JOB_ID = 10111;
/**
* Convenience method for enqueuing work in to this service.
*/
static void enqueueWork(Context context, Intent work) {
enqueueWork(context, MyJobIntentService.class, JOB_ID, work);
}
@Override
protected void onHandleWork( Intent intent) {
Log.d("houson", "onHandleWork: "+intent.getStringExtra("work").toString());
}
}
注意manifest!
<uses-permission android:name="android.permission.WAKE_LOCK">uses-permission>
<service android:name=".MyJobIntentService">service>
结果:
11-06 20:39:04.116 20653-20653/com.example.houson.jobintentservicedemo D/houson: onClick: 47
11-06 20:39:04.135 20653-20743/com.example.houson.jobintentservicedemo D/houson: onHandleWork: work num:47
11-06 20:39:04.216 20653-20653/com.example.houson.jobintentservicedemo D/houson: onClick: 48
11-06 20:39:04.234 20653-20745/com.example.houson.jobintentservicedemo D/houson: onHandleWork: work num:48
由于Android O的后台限制,创建后台服务需要使用JobScheduler来由系统进行调度任务的执行,而使用JobService的方式比较繁琐,8.0及以上提供了JobIntentService帮助开发者更方便的将任务交给JobScheduler调度,其本质是Service后台任务在他的OnhandleWork()中进行,子类重写该方法即可。使用较简单。
注意
:
1.需要添加android.permission.WAKE_LOCK权限,JobIntentService处理了亮屏/锁屏,因此要此权限。
2.注册JobintentService也是service