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
相关文章
|
7月前
|
Java Android开发 C++
Kotlin vs Java:选择最佳语言进行安卓开发
【4月更文挑战第13天】Java曾是安卓开发的主流语言,但Kotlin的崛起改变了这一局面。Google在2017年支持Kotlin,引发两者优劣讨论。Java以其成熟稳定、强大生态和跨平台能力占优,但代码冗长、开发效率低和语言特性过时是短板。Kotlin则以简洁语法、空安全设计和高度兼容Java脱颖而出,但社区和生态系统仍在发展中,可能存在学习曲线和性能问题。选择语言应考虑项目需求、团队熟悉度、维护性、性能和生态系统。无论选择哪种,理解其差异并适应新技术至关重要。
374 4
|
7月前
|
存储 Shell 分布式数据库
二叉树详讲(一)---完全二叉树、满二叉树、堆
二叉树详讲(一)---完全二叉树、满二叉树、堆
|
7月前
|
前端开发 JavaScript 开发者
如何在React中监听键盘事件
如何在React中监听键盘事件
199 0
|
Shell API Android开发
JobService的使用介绍(2)
JobService的使用介绍(2)
|
设计模式 网络协议 Java
JAVA-stateless4j StateMachine从入门到实战
JAVA-stateless4j StateMachine从入门到实战
329 0
|
缓存 JSON Java
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
450 1
java 实现读取txt文件,反射创建对象,android 手机缓存文件目录
|
文字识别 Java 开发工具
Android 使用so库的遇到的坑
Android 使用so库的遇到的坑
594 0
Android 使用so库的遇到的坑
|
Java Android开发
安卓解决java.lang.RuntimeException: Unable to get provider com.blankj.utilcode.util.Utils$FileProvider
安卓解决java.lang.RuntimeException: Unable to get provider com.blankj.utilcode.util.Utils$FileProvider
1929 0
|
Android开发 Java 编解码
|
Android开发
Android 后台限制启动Service、Activity与Notification、PendingIntent浅析
Android 后台限制启动Service、Activity与Notification、PendingIntent浅析
2817 0
Android 后台限制启动Service、Activity与Notification、PendingIntent浅析