JobIntentService详解及使用

来源:互联网 发布:网络股票龙头股票 编辑:程序博客网 时间:2024/06/05 18:45

Android o新特性–后台限制

Android8.0对系统资源的管控更加严格,添加了后台限制规则。

如果满足以下任意条件,应用将被视为处于前台:

  1. 具有可见 Activity(不管该 Activity 已启动还是已暂停)。
  2. 具有前台服务。
  3. 另一个前台应用已关联到该应用(不管是通过绑定到其中一个服务,还是通过使用其中一个内容提供程序)。 例如,如果另一个应用绑定到该应用的服务,那么该应用处于前台:
  4. IME
  5. 壁纸服务
  6. 通知侦听器
  7. 语音或文本服务

如果以上条件均不满足,应用将被视为处于后台。

系统不允许后台应用创建后台服务。 因此,Android 8.0 引入了一种全新的方法,即 Context.startForegroundService(),以在前台启动新服务。

在系统创建服务后应用有五秒的时间来调用该服务的 startForeground() 方法以显示新服务的用户可见通知。

如果应用在此时间限制内未调用 startForeground(),则系统将停止服务并声明此应用为 ANR

Android强制开发者使用 JobScheduler 作业替换后台服务的意思,使用jobIntentService就可以比较方便的使用JobScheduler这个工具。

特殊情况是可以创建后台服务的:

  1. 处理对用户可见的任务时,应用将被置于白名单中,后台应用将被置于一个临时白名单中并持续数分钟。 位于白名单中时,应用可以无限制地启动服务,并且其后台服务也可以运行。
    1. 处理一条高优先级 Firebase 云消息传递 (FCM) 消息。
    2. 接收广播,例如短信/彩信消息。
    3. 从通知执行 PendingIntent。
  2. bindService不受后台限制

JobIntentService

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: 4711-06 20:39:04.135 20653-20743/com.example.houson.jobintentservicedemo D/houson: onHandleWork: work num:4711-06 20:39:04.216 20653-20653/com.example.houson.jobintentservicedemo D/houson: onClick: 4811-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

原创粉丝点击