android-job 调度任务 框架简介

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MrJarvisDong/article/details/81086505

Android-job 源码分析

链接地址
整体框架

JobManger

策略式调用不同策略实现调度任务,单例类,通常在app中进行初始化;
JobManager.create(this).addJobCreator(new XXXJobCreator());
调度任务的入口点,依赖于不同的sdk版本号使用不同的api去调度任务;
当运行在21版本以上,JobScheduler是首选策略;其他的版本使用AlarmManger兼容;如果安装了GooglePlay Service,也支持GcmNetworkManager;

基本用法:

为了调度job ,必须建造一个继承Job 的类,用JobRequest.Builder 建造者创建一个JobRequest类,设置对应参数,调用schedule(JobRequest)即可. 如果想要更新penging request,调用Jobrequest.cancelAndEdit(),更新参数并重新schedule();

基本实现:

单例模式JobManager;

先获取当前支持的Api策略 (JobApi枚举),WorkManager优先,最低支持Api14.

初始化JobManager时,创建JobCreatorHolder, JobExecutor, JobStorage
根据JobConfig参数选择性开启重新调度的服务JobRescheduleService(extends SafeJobIntentService extends JobIntentService)

创建完毕后通过sendAddJobCreatorIntent(context)方法:
JobCreator中的广播接受者AddJobCreatorReceiver的子类都通过反射和addJobCreator()加入到JobCreatorHolder容器中,由JobManager统一管理;

schedule(JobRequest)方法:线程安全,调度一个请求,如果没有取消请求,调用方法多次是无效的;先清除孤儿Job,根据选择的JobApi枚举类构建一个代理策略JobProxy,根据条件调用plantOneOff,plantPeriodic,plantPeriodicFlexSupport,具体实现由对应的策略实现;

JobApi

枚举类,枚举所有异步调度的策略;

work_manager 使用WorkManager实现调度任务;

v_26 使用JobScheduler实现调度任务;

v_24 使用JobScheduler实现调度任务;

v_21 使用JobScheduler实现调度任务;

v_19 使用AlarmManager实现调度任务;

v_14 使用AlarmManager实现调度任务;

CGM 使用GcmNetworkManager实现调度任务;(不讨论)

主要判断当前的Sdk版本使用哪种JobProxy的策略,根据对应枚举创建 JobProxy 的代理类;

JobProxy

接口,定义各个调度策略的公共抽象方法;

planOneOff(request) 一次性job的执行方法

planPeriodic(request) 重复性job的执行方法

plantPeriodicFlexSupport(request) 重复性且灵活性支持的执行方法;


定义了一个Common.Class内部类,可理解为执行job的工具类;

上述策略异步执行job ,最后都调取了Common定义的方法 ,先使用
getPendingRequest(boolean 清除孤儿Job,boolean 标记开始执行)
获取 JobRequest; 在通过
executeJobRequest(JobRequest ,Bundle)执行Job;

//方法的具体实现:
public Job.Result executeJobRequest(@NonNull JobRequest request, @Nullable Bundle transientExtras) {
    //获取JobManager的JobExecutor 对象 ;
    //将JobManager的JobCreatorHolder中存储的**JobCreator**对象取出根据设置的Tag构建**Job**对象;
    //使用执行者**JobExecutor**执行Job对象
    Future<Job.Result> future = JobExecutor.execute(context,request,job,transientExtras);
    // wait until done
   //Job.Result result = future.get();

    //返回Job.Result结果;
}

JobCreatorHolder

一个容器类;

包含一个List 容器 ,及操作容器方法,使用JobCreator快速建Job的方法;

JobCreator

接口,定义抽象方法Job create(String); 根据tag 创建一个Job类;
需要通过JobRequest.Builder 构造器传入Tag;
tag和Job是一一对应的;
用于自定义实现类,使用tag找到对应的自定义Job;

内部类addJobCreatorReceiver抽象的广播监听者,监听 是否有JobCreator 需要被添加;
(注:JobManager已经写了添加所有基于addJobCreatorReceiver 的广播的方法sendAddJobCreatorIntent())

JobExecutor

SpareArray < Job > mJobs;//存内部中的Jobs;

LruCache < Integer, WeakReference< Job >>> mFinishedJobsCache; 存放已运行完毕的Job的弱引用;

SparseArray < Job.Result> mFinishedJobResults 存放已运行完毕的job的运行结果对象Result;

Set < JobRequest> mStartingRequests 标记运行的JobRequest 集合;

执行方法:

public synchronized Future

while (oldVersion < newVersion) {
                switch (oldVersion) {
                    case 1:
                        upgradeFrom1To2(db);
                        oldVersion++;
                        break;
                    case 2:
                        upgradeFrom2To3(db);
                        oldVersion++;
                        break;
                    default:
                        throw new IllegalStateException("not implemented");
                }
            }

JobRequest

持有penging Job的信息,提供构造类Builder构造JobRequest的对象;一旦建立了request 可调用schedule()方法开始调度或者JobManager的scheule()方法开始调度;

包含所有Job的信息和getter,setter方法,其中Job的标志Tag为必填;

Job

运行延时Job的基类,在后台线程job被执行;

提供Result 枚举类 SUCCESS,FAILURE,RESCHEDULE三种状态;提供job要求条件的检查等功能;持有Params对象,用来保存JobRequest,PersistableBundleCompat,Bundle(mTransientExtras)等对象;

定义抽象方法:abstract Result onRunJob(Params)

//JobExecutor 通过execute()方法会调用Job此方法回调;
/*package*/ final Result runJob() {
        try {
            // daily jobs check the requirements manually
            if (this instanceof DailyJob || meetsRequirements(true)) {
            //抽象方法;
                mResult = onRunJob(getParams());
            } else {
                mResult = getParams().isPeriodic() ? Result.FAILURE : Result.RESCHEDULE;
            }
            return mResult;
        } finally {
            mFinishedTimeStamp = System.currentTimeMillis();
        }
    }

子类继承Job重写抽象方法 onRunJob(Params) ,重写调度运行成功后的方法;


JobApi中的策略 (implement JobProxy)

plantOneOff(JobRequest): 用来一次性调用方法;
plantPeriodic(JobRequest): 用来周期性调用方法;
plantPeriodicFlexSupport(JobRequest): 周期性且灵活支持各种版本的异步调度;
cancel(int): 取消某一个Job的调度;
isPlatformJobScheduled(JobRequest):
  • 使用WorkManager 调度任务(重要)

简介:
使用WorkManager实现;
具体的XXXWorker (PlatformWorker extends Worker) 实现具体的doWork操作:

最终是通过 JobProxy的Common类方法 executeJobRequest(JobRequest,Bundle)
->JobExecutor$JobCallable
->Job.runJob();
->abstract Result onRunJob(@NonNull Params params);

任务调度由WorkRequest的子类 PeriodicWorkRequest 重复工作的请求和OneTimeWorkRequest 非重复工作的请求 进行控制;

plantOneOff :

OneTimeWorkRequest.Builder(PlatformWorker.class)
设置JobRequest的参数,由WorkManger执行;

plantPeriodic:

plantPeriodicFlexSupport

PeriodicWorkRequest.Builder(PlatformWorker.class)
设置JobRequest的参数,由WorkManger执行;

WorkManager的简介和使用:

api:23+: 
JobScheduler;
api:14-22: 
using Firebase JobDispatcher && Firebase dependency  ,使用Firebase JobDispatcher;
其他的使用自定义的AlarmManager + BroadcastReceiver 实现

所有的工作由相应的 `Worker` 去执行计算,执行在后台线程中;

WorkManger支持两种工作类型 即OneTimeWorkRequest 和PeriodicWorkRequest :

WorkManager workManager = WorkManager.getInstance();
workManager.enqueue(new OneTimeWorkRequest.Builder(FooWorker.class).build());}
 *

WorkRequest 可使用id 用来观察和查找:

WorkRequest request = new OneTimeWorkRequest.Builder(FooWorker.class).build();
workManager.enqueue(request);
LiveData<WorkStatus> status = workManager.getStatusById(request.getId());
status.observe(...);}

也可使用id 去取消某一个Work:

WorkRequest request = new OneTimeWorkRequest.Builder(FooWorker.class).build();
workManager.enqueue(request);
workManager.cancelWorkById(request.getId());}</pre>

 WorkRequest可以使用调用链:

WorkRequest request1 = new OneTimeWorkRequest.Builder(FooWorker.class).build();
WorkRequest request2 = new OneTimeWorkRequest.Builder(BarWorker.class).build();
WorkRequest request3 = new OneTimeWorkRequest.Builder(BazWorker.class).build();
workManager.beginWith(request1, request2).then(request3).enqueue();}
使用 beginWith(OneTimeWorkRequest...) or beginWith(List)
返回 WorkContinuation ;这允许创建复杂的构造者链;
EG:
A
|
+------+
|       |
B       C
|
+------+
|       |
D       E

WorkContinuation continuation=workManager.beginWith(A);
continuation.then(B).then(D, E).enqueue();
continuation.then(C).enqueue();}
A 是隐式的队列;

WorkRequest 能接受约束Constraints ,输入Data,返回 criteria;
工作链能给一个独一无二的标志名. 如:beginUniqueWork(String,
ExistingworkPolicy , OneTimeWorkRequest ...)

默认情况下,WorkManager 运行在后台线程中,需要同步调用WorkManager时,使用synchronous();

  • 使用JobProxy21调度任务
  • 子类 JobProxy24JobProxy26 相同实现

简介:
使用 JobSchedulerJobService 实现;
构建JobInfo.Builder创建JobInfo ,通过设置JobRequest的参数达到将任务分离;
真正的执行逻辑运行于JobService中;

最终是通过 JobProxy的Common类方法 executeJobRequest(JobRequest,Bundle)
->JobExecutor$JobCallable
->Job.runJob();
->abstract Result onRunJob(@NonNull Params params);

plantOneOff

plantPeriodicFlexSupport

JobInfo设置JobInfo.Builder.setMinimumLatency(startMs).setOverrideDeadline(endMs)

plantPeriodic

JobInfo设置
JobInfo.Builder.setPeriodic(intervalMs);

设置不同参数,表示不同约束,使用JobScheduler调度;

JobScheduler的介绍和使用

JobScheduler  用于调度多种类型的job,构建了JobInfo对象后通过JobSceduler 调度JobInfo,符合规则条件时,会在JobService中执行job,初始化时通过getSystemService(Context.JOB_SCHEDULER_SERVICE);
具体的操作逻辑位于 JobService (PlatformJobService extends JobService extends Service 进程间的通信Binder实现)  ,JobConfig线程池中通过JobProxy.Common .getPendingRequest()方法获取JobRequest,标记为start状态后,
也是通过`Job.Result executeJobRequest(request,transientBundle)`方法执行任务
->最终通过JobExecutor$JobCallable将执行结果回调给Job的子类;




JobInfo 的设置参数,构建一个基准的JobInfo,通过设置时间参数的不同达到不同的实现方式,即一次性实现和周期性实现;
JobInfo 是一个封装 JobScheduler 需要参数 的数据容器;必须至少指定一个排序约束;[约束即设置周期或者设置开始时间结束时间等]

//创建一个通用的JobInfo,设置不同参数可控制约束;
protected JobInfo.Builder createBaseBuilder(JobRequest request, boolean allowPersisting) {
        JobInfo.Builder builder = new JobInfo.Builder(request.getJobId(), new ComponentName(mContext, PlatformJobService.class))
                .setRequiresCharging(request.requiresCharging())
                .setRequiresDeviceIdle(request.requiresDeviceIdle())
                .setRequiredNetworkType(convertNetworkType(request.requiredNetworkType()))
                .setPersisted(allowPersisting && !request.isTransient() && JobUtil.hasBootPermission(mContext));

        return setTransientBundle(request, builder);
    }

JobService

JobScheduler 的回调的入口点,这是一个处理异步预先调度请求的抽象基类;
重写onStartJob(JobParameters) 可实现job的逻辑;
这个service会在你的主线程使用Handler执行每个传入的job,意味着你必须把你的执行逻辑装到其他的线程/hander/AsyncTask 中;

优点:只需要重写onStartJob,即可使用线程池实现自己的逻辑;

  • 使用JobProxy14调度任务
  • 子类JobProxy19

简介:
使用 AlarmManagerBroadcastReceiverService(JobIntentService & 需设置唤醒锁Service)实现;

最终是通过 JobProxy的Common类方法 executeJobRequest(JobRequest,Bundle)
->JobExecutor$JobCallable
->Job.runJob();
->abstract Result onRunJob(@NonNull Params params);

获取BroadcastReceiver的Intent ,构造PendingIntent;
Intent intent = PlatformAlarmReceiver.createIntent(mContext, jobId, exact, transientExtras);
return PendingIntent.getBroadcast(mContext, jobId, intent, flags);

在api19中(AlarmManager 时间不精确):
!request.isExact() 时重写 plantOneOffInexact 设置:
alarmManager.setWindow(AlarmManager.RTC, startMs, lengthMs, pendingIntent);
以替换原先的alarmManager.set(AlarmManager.RTC, triggerAtMs, pendingIntent);

plantOneOff

精确时间时:

需要立即运行->开启服务PlatformAlarmService (extends SafeJobIntentService extends JobIntentService extends Service)->执行 Common.executeJobRequest(request,transientExtras)方法…

不需立即运行->设置alarmManager的参数,PendingIntent执行PlatformAlarmReceiver 的onReceive(..)

->不是精确时间->
开启服务PlatformAlarmService(extends SafeJobIntentService extends JobIntentService extends Service) ->onHandleWork(Intent)运行runJob()静态方法->
Common.executeJobRequest(request,transientExtras)方法…

->是精确时间->开启服务PlatformAlarmServiceExact(extends Service) ->
因为广播接受者中开启服务 ,需要设置唤醒锁(PowerManager)
->onStartCommand JobConfig线程池执行 Common.executeJobRequest(request,transientExtras)方法…
->释放唤醒锁;

plantPeriodicFlexSupport

设置AlarmManager 的属性 alarmManager.set(AlarmManager.RTC, triggerAtMs, pendingIntent);
PendingIntent执行PlatformAlarmReceiver 的onReceive(..)-> 跟plantOneOff一致;

plantPeriodic

设置AlarmManager 的属性Repeating();
PendingIntent执行PlatformAlarmReceiver 的onReceive(..)->跟plantOneOff一致;

PendingIntent

延迟Intent,描述一个Intent和执行跳转目标action;
创建by getActivity getActivities ,getBroadcast,getService; 
其返回的对象 能够被传递到其他应用里,可以执行你指定延迟加载的action ;
构建PengingIntent ,必须提供一个显示的component name的Intent,PendingIntent 本身是一个简单的引用记号(token),
被用于检索原始数据的系统所维持;意味着 ,即使它本身的应用进程被杀死,pendingIntent 也会被其他已经联系的进程保持可用状态;
注意 一个常见的错误是建造了多个PendingIntent对象, 使用只是他们的extra不一样的Intent构建,这样得到相同的PendingIntent对象;

AlarmManager

mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
该类允许你调度你的应用在某一时刻运行;当alarm 定时已到,被系统广播注册的Intent,如果还没有运行的目标应用会自动开始运行;当设备是处于睡眠状态时(也可以在定时到了选择性的唤醒设备)注册alarms时可保留的,但是设备关机或重启时会被清除;
alarm 的receiver 方法执行的情况下,alarm manager会一直持有Cpu的唤醒锁;这可以保证手机不会睡眠直到你结束了广播接受者的处理;一旦onReceiver 返回了 ,alarm manager 释放唤醒锁;意味着手机在一些情况下 当你的onReceive()完成后会很快的进入睡眠状态;
 如果此时你的alarm receiver 调用 Context.startService().
很可能在你请求服务启动之前,手机将会睡眠; 因此,你的广播接受者和服务需要实现一个单独的唤醒锁策略保证手机能持续运行知道服务成为可获得的;
从api19开始,  alarm 传递是不精确的,OS 偏移alarms 时间为了最小化唤醒和电池的使用.new的api 支持需要严格保证传递的应用,setWindow(int ,long ,long PendingIntent) and 
setExact(int ,long,long ,PendingIntent);

JobIntentService

对在一个job/service的队列中且正在进行的work提供帮助的类 ;api>Version_codes_o work 将作为一个job通过JobScheduler传递;其他将使用startService();
使用enqueueWork(Context,CLass,int,Intent) 去将一个new work 加入队列,被你的service分发且处理,在onHandlerWork(Intent)中执行;
使用这个类时,不需要使用WakeFulBroadcastReceiver (带唤醒锁).
当运行在version_code_o时,JobScheduler 将关注唤醒锁(从你的work在队列中时持有唤醒锁直到job 传递已被传递且正在运行).其他版本中,这里唤醒锁的处理模拟了直接调用PowerManager的处理;意味着应用必须要求 permission.WAKE_LOCK权限;
运行在version_codes_o 上及以后,作为Job或pre-0 不同的几点:

当做 pre-0 service 运行: 
enqueueing work 的动作将立即开启服务,无视设备睡眠后其他条件下; 
正常的服务执行语义也适用:service能够无限期的运行,尽管它运行的时间越长越可能被彻底的杀死进程,并且在低内存压力下即使最近开启了service也应该希望进程被杀死;

当做 job 运行: 
它隶属于标准的JobScheduler 策略 .作为一个当setOverrideDeadline(long)为0 的情况下的job,当设备睡眠状态(dozing) 时可以不会运行; 可能会延时很久;
典型的执行时间限制(如:JobService)适用:当job被stopped(清除,但没有杀死进程) 并且重新调度稍后会继续执行的情况下,job一般情况下在低内存压力不会被杀死,因为并发的job数量基于设备的内存状态被调整了;

最大的优点是不需要设置唤醒锁,使用onHandlerWork方便;

猜你喜欢

转载自blog.csdn.net/MrJarvisDong/article/details/81086505