JobService的使用介绍(1)

简介: JobService的使用介绍(1)

JobService是Android L时候官方新增的组件,适用于需要特定条件才执行后台任务的场景。

由系统统一管理和调度,在特定场景下使用JobService更加灵活和省心,相当于是Service的加强或者优化。

本篇文章,我们一起学习下如何使用JobService。

 

官方链接如下:https://developer.android.google.cn/reference/android/app/job/JobService.html

我们先看看JobService类的继承关系。

public abstract class JobService

extends Service

可以看到JobService是继承自Service的抽象类。

看来JobService的本质还是Service,只不过封装了些额外的方法和逻辑。

 

那到底JobService和Service在使用有什么区别?

如何使用JobService

首先我们来看下官方的对于JobService的解释。

Entry point for the callback from the JobScheduler.


This is the base class that handles asynchronous requests that were previously scheduled.

You are responsible for overriding onStartJob(JobParameters), which is where you will implement your job logic.


This service executes each incoming job on a Handler running on your application's main thread.

This means that you must offload your execution logic to another thread/handler/AsyncTask

of your choosing.

大概意思就是,JobService是JobScheduler的回调,是安排的Job请求的实际处理类。

需要我们覆写onStartJob(JobParameters)方法,并在里面实现实际的任务逻辑。

因为JobService的执行是在APP的主线程里响应的,所以必须提供额外的异步逻辑去执行这些任务。

JobService API

・onStartJob() Job开始时的回调,实现实际的工作逻辑。


注意,如果返回false的话,系统会自动结束本job。


・jobFinished() Job执行完毕后,由App端自己调用,以通知JobScheduler已经完成了任务。


注意,该方法调用导致的Job结束并不会回调onStopJob(),只会回调onDestroy()。


・onStopJob() Job中止的时候回调。当JobScheduler发觉该Job条件不满足的时候,或者Job被抢占的时候强制回调该方法。

注意,如果想让这种意外中止的Job重新开始,复写该函数返回true。


另外还有父类Service的基础方法,可以覆写来实现一些辅助作用。


・onCreate() Service被初始化后的回调,可以在这里设置BroadcastReceiver或者ContentObserver等处理。


・onDestroy() Service被销毁前的回调。可以在这里注销BroadcastReceiver或者ContentObserver。



上面可以看出,JobService只是Job执行和中止时机的回调入口。

那如何将这个入口告诉系统,就需要用到JobScheduler了。


官方链接如下:https://developer.android.google.cn/reference/android/app/job/JobScheduler.html我们先看看JobScheduler类的继承关系。

JobScheduler
public abstract class JobScheduler
extends Object

可以看到JobScheduler就是个抽象类,实际上它的实现逻辑在android.app.JobSchedulerImpl里。

但是我们暂时不管那么多,只要知道使用JobScheduler里面提供的API即可。

照例,看下官方的解释。

This is an API for scheduling various types of jobs against the framework that will be

executed in your application's own process.


See JobInfo for more description of the types of jobs that can be run and how to construct them.



You will construct these JobInfo objects and pass them to the JobScheduler with schedule(JobInfo).

When the criteria declared are met, the system will execute this job on your application's JobService.

描述的非常清晰,就是说JobScheduler是framework层里用来安排各种各样的将要执行在app自己进程里的任务的机制。

我们需要创建各种Job的描述类JobInfo。并且通过JobScheduler传递给系统。


当我们描述的条件或者标准满足了,系统将执行app的JobService。

JobScheduler API。

・schedule() 安排一个Job任务。


・enqueue() 安排一个Job任务,但是可以将一个任务排入队列。


・cancel() 取消一个执行ID的Job。


・cancelAll() 取消该app所有的注册到JobScheduler里的任务。


・getAllPendingJobs() 获取该app所有的注册到JobScheduler里未完成的任务列表。


・getPendingJob() 按照ID检索获得JobScheduler里未完成的该任务的JobInfo信息。


上面还提到需要创建JobInfo对象,实际要通过JobInfo.Builder类利用建造者模式创建出JobInfo对象。

JobInfo.Builder API

我们选取几个代表性的API看看。


・Builder() JobInfo.Builder的内部类构造函数

注意,参数之一ID必须是APP UID内唯一的,如果APP和别的APP共用了UID,那么要防止该ID和别的APP里有冲突


・setOverrideDeadline() 设置job被立即执行的最大延迟期限

注意:即便其他条件没满足此期限到了也要立即执行


・setRequiresDeviceIdle() 是否需要在IDLE状态下运行该Job


・setRequiredNetworkType() 设置需要何种网络类型条件



至此,JobService,JobScheduler以及JobInfo三大块的API我们都已经有些了解了,那我们先写个简单的JobService跑起来试试。

JobService DEMO

1. 首先创建JobService实现类

onCreate():简单打个Log

onStartJob():模拟业务逻辑,返回true

onStopJob():期待被再次执行,返回true

onDestroy():简单打个log

注意:manifest里JobService的声明里必须请求android:permission="android.permission.BIND_JOB_SERVICE"的权限

2. 在UI端安排JobInfo进任务队列

获取系统的JobSchedulerService的代理对象;

新建JobInfo.Builder对象,并传入JobService实现类的ComponentName和ID;

通过JobInfo.Builder对象设置Job执行的条件,这里设置条件为简单的1s deadline;

通过JobInfo.Builder对象创建出JobInfo对象;

通过JobSchedulerService代理对象将JobInfo发送到JobScheduler机制中去;

3. UI端模拟任务完成,通知JobService

对应的代码

● JobService端

public class EllisonsJobService extends JobService {
    public static final int ELLISONS_JOB_ID = 0;
    public static final int ELLISONS_JOB_OVERDIDE_DEADLINE = 1000;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.w(TAG, "EllisonsJobService onCreate()");
    }
    @Override
    public void onDestroy() {
      super.onDestroy();
      Log.w(TAG, "EllisonsJobService destroyed.");
    }
    @Override
    public boolean onStartJob(JobParameters params) {
        Log.w(TAG, "EllisonsJobService onStartJob()");
        Helpers.doHardWork(this, params);
        return true;
    }
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.w(TAG, "EllisonsJobService stopped & wait to restart params:" + params + " reason:" + params.getStopReason());
        return false;
    }

● UI端

添加四个按钮:用于schedule,cancel,finish和enqueue job。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.w(TAG, "MainActivity onCreate() PID:" + android.os.Process.myPid() + " TID:" + android.os.Process.myTid());
        setContentView(R.layout.activity_main);
    }
    public void onClick_Schedule(View view) {
        Log.w(TAG, "MainActivity onClick_Schedule()");
        Helpers.schedule(this);
    }
    public void onClick_Finished(View view) {
        Log.w(TAG, "MainActivity onClick_Finished()");
        Helpers.jobFinished();
    }
    public void onClick_Cancel(View view) {
        Log.w(TAG, "MainActivity onClick_Cancel()");
        Helpers.cancelJob(this);
    }
    public void onClick_Enqueue(View view) {
        Log.w(TAG, "MainActivity onClick_Enqueue()");
        Helpers.enqueueJob();
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.w(TAG, "MainActivity onDestroy()");
    }

● 控制端

public class Helpers {
    private static JobService mJob;
    private static JobParameters mJobParams;
    public static void schedule(Context context) {
        Log.w(TAG, "Helpers schedule()");
        final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
        final JobInfo.Builder builder = new JobInfo.Builder(EllisonsJobService.ELLISONS_JOB_ID,
                new ComponentName(context, EllisonsJobService.class));
        builder.setOverrideDeadline(EllisonsJobService.ELLISONS_JOB_OVERDIDE_DEADLINE);
        scheduler.schedule(builder.build());
    }
    public static void cancelJob(Context context) {
        Log.w(TAG, "Helpers cancelJob()");
        final JobScheduler scheduler = context.getSystemService(JobScheduler.class);
        scheduler.cancel(EllisonsJobService.ELLISONS_JOB_ID);
    }
    public static void jobFinished() {
        Log.w(TAG, "Helpers jobFinished()");
        mJob.jobFinished(mJobParams, false);
    }
    public static void enqueueJob() {
        Log.w(TAG, "Helpers enqueueJob()");
    }
    public static void doHardWork(JobService job, JobParameters params) {
        Log.w(TAG, "Helpers doHardWork()");
        mJob = job;
        mJobParams = params;
    }

DEMO的主界面

1832b220aa754cd18c504acc7686a560.png

点击schedule的button。通过log看到JobService启动了。

01-31 17:59:23.110 13813 13813 W Ellison : MainActivity onClick_Schedule()
01-31 17:59:23.111 13813 13813 W Ellison : Helpers schedule()
01-31 17:59:23.119 13813 13813 W Ellison : EllisonsJobService onCreate()
01-31 17:59:23.121 13813 13813 W Ellison : EllisonsJobService onStartJob()
01-31 17:59:23.121 13813 13813 W Ellison : Helpers doHardWork(

点击finish的button。通过log看到JobService结束了。

01-31 17:59:35.582 13813 13813 W Ellison : MainActivity onClick_Finished()
01-31 17:59:35.582 13813 13813 W Ellison : Helpers jobFinished()
01-31 17:59:35.595 13813 13813 W Ellison : EllisonsJobService destroyed.

点击schedule和cancel的button。通过log看到JobService执行后被停止了。

01-31 17:59:48.599 13813 13813 W Ellison : MainActivity onClick_Schedule()
01-31 17:59:48.599 13813 13813 W Ellison : Helpers schedule()
01-31 17:59:48.608 13813 13813 W Ellison : EllisonsJobService onCreate()
01-31 17:59:48.611 13813 13813 W Ellison : EllisonsJobService onStartJob()
01-31 17:59:48.611 13813 13813 W Ellison : Helpers doHardWork()
01-31 17:59:55.039 13813 13813 W Ellison : MainActivity onClick_Cancel()
01-31 17:59:55.039 13813 13813 W Ellison : Helpers cancelJob()
01-31 17:59:55.040 13813 13813 W Ellison : EllisonsJobService stopped & wait to restart params:android.app.job.JobParameters@ac7bfd7 reason:0★
01-31 17:59:55.049 13813 13813 W Ellison : EllisonsJobService destroyed

★处显示被停止的原因值为0。

而据JobParameters.java里关于stop reason的如下定义可以看到,确实是因为取消导致的Job中止。

/** @hide */
  public static final int REASON_CANCELED = 0;

*关于enqueue的逻辑,后续再添加。

查看JobService的运行

那除了自己加log的形式,有没有别的方式查看自己的JobService了呢?

可以借助adb shell service list命令得到相关信息。

47 jobscheduler: [android.app.job.IJobScheduler]

可以看到只有JobSchedulerService的简单信息,看不到Job Service的详细信息。

 

但是adb给我们提供了另外一种方式查看JobService。

 adb shell dumpsys jobscheduler | grep packagename


我们将packagename换成我们的包名"com.example.TimeApiDemo",得到以下信息。


注册到JobScheduler里的JobService的属性记录

JOB #u0a174/0: 1beb0fa com.example.timeapidemo/.EllisonsJobService
  u0a174 tag=*job*/com.example.timeapidemo/.EllisonsJobService
  Source: uid=u0a174 user=0 pkg=com.example.timeapidemo
  JobInfo:
  Service: com.example.timeapidemo/.EllisonsJobService
  Requires: charging=false batteryNotLow=false deviceIdle=false
  Max execution delay: +1s0ms
  Backoff: policy=1 initial=+30s0ms
  Has late constraint
  Required constraints: DEADLINE
  Satisfied constraints: DEADLINE APP_NOT_IDLE DEVICE_NOT_DOZING
  Unsatisfied constraints:
  Tracking: TIME
  Enqueue time: -36s216ms
  Run time: earliest=none, latest=-35s216ms
  Ready: false (job=true user=true !pending=true !active=false !backingup=true comp=true

正在运行的JobService的信息

Active jobs:
  ...
  Slot #3: 1beb0fa #u0a174/0 com.example.timeapidemo/.EllisonsJobService
  Running for: +36s215ms, timeout at: +9m23s798ms
  u0a174 tag=*job*/com.example.timeapidemo/.EllisonsJobService
  Source: uid=u0a174 user=0 pkg=com.example.timeapidemo
  Required constraints: DEADLINE
  Tracking: TIME
  Enqueue time: -36s216ms
  Run time: earliest=none, latest=-35s216ms
  Evaluated priority: 40
  Active at -36s213ms, pending for +2ms
相关文章
|
5月前
|
传感器 Java Android开发
Android HAL深入探索(1): 架构概述
Android HAL深入探索(1): 架构概述
553 1
|
Shell API Android开发
JobService的使用介绍(2)
JobService的使用介绍(2)
|
Web App开发 Java Linux
【性能优化】使用Perfetto定位应用启动性能的瓶颈
本篇文章将会结合我个人对Perfetto的实际使用经历,讲解车载应用的启动时间是如何测量得到的,测量出启动时间后,我们又该如何找出其中的性能瓶颈。
1649 1
【性能优化】使用Perfetto定位应用启动性能的瓶颈
|
Java C++
Java并发 --- CAS解析(对比synchronized )
Java并发 --- CAS解析(对比synchronized )
|
Java 开发工具 Android开发
Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8.
|
移动开发 前端开发 JavaScript
网页|利用progress实现进度条效果
网页|利用progress实现进度条效果
503 0
|
缓存 Java fastjson
Java开发都需要参考的一份命名规范
好的命名能体现出代码的特征,含义或者是用途,让阅读者可以根据名称的含义快速厘清程序的脉络。不同语言中采用的命名形式大相径庭,Java中常用到的命名形式共有三种,既首字母大写的UpperCamelCase,首字母小写的lowerCamelCase以及全部大写的并用下划线分割单词的UPPERCAMELUNSER_SCORE。通常约定,类一般采用大驼峰命名,方法和局部变量使用小驼峰命名,而大写下划线命名通常是常量和枚举中使用。
Java开发都需要参考的一份命名规范
|
XML Android开发 数据格式
android程序安装后在模拟器上不显示,并且控制台显示The launch will only sync the application package on the device!
android程序安装后在模拟器上不显示,并且控制台显示The launch will only sync the application package on the device!
156 3
|
Android开发 Python
airtest中的adb使用
airtest中的adb使用
|
存储 消息中间件 API
下沉式通知的一种实现 | Android悬浮窗Window应用
当你浏览公众号时来了一条新消息,通知在屏幕顶部会以自顶向下动画的形式入场,而且它是跨界面的全局浮窗(效果如下图)。虽然上一篇中抽象的浮窗工具类已经能实现这个需求。但本文在此基础上再封装一些更加友好的
369 0
下沉式通知的一种实现 | Android悬浮窗Window应用