WorkManager之SystemJobScheduler、SystemAlarmScheduler、GreedyScheduler三大调度器,源码流程详细解读

SystemJobScheduler的工作过程

先看其构造方法,它会将我们传递进来的WorkManagerImpl 与context进行保存,并在它的重载方法里获取了系统服务jobScheduler,要了解jobScheduler,推荐一篇博文可以看看JobScheduler的使用,

而另外一个参数SystemJobInfoConverter 则是用来将我们的workSpecs转化为JobInfo,这个JobInfo是jobScheduler需要的参数 ,用来描述任务的执行时间,条件,策略等一系列的行为。因为我们的workSpecs中包含worker执行的一些配置和条件,从而可以通过SystemJobInfoConverter将workSpecs转化为JobInfo

public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
    this(context,
            workManager,
            (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE),  //获取到了系统服务JobScheduler进行重载
            new SystemJobInfoConverter(context));   //用来将我们传递的数据转换为JobInfo类型
}

@VisibleForTesting
public SystemJobScheduler(
        Context context,
        WorkManagerImpl workManager,
        JobScheduler jobScheduler,
        SystemJobInfoConverter systemJobInfoConverter) {
    mWorkManager = workManager;
    mJobScheduler = jobScheduler;
    mIdGenerator = new IdGenerator(context);
    mSystemJobInfoConverter = systemJobInfoConverter;
}

它的schedule方法,主要做了这么几件事,根据传递进来的WorkSpec的ID,从数据库获取对应的WorkSpec,并根据WorkSpec.id从SystemIdInfo这张数据表中获取对应的系统编号,该编号的一大作用是,在 调度器执行该worker时,消除重复的任务调用。

  public void schedule(WorkSpec... workSpecs) {
        WorkDatabase workDatabase = mWorkManager.getWorkDatabase();

        for (WorkSpec workSpec : workSpecs) {   //循环遍历workSpecs
            workDatabase.beginTransaction();
            try {

                WorkSpec currentDbWorkSpec = workDatabase.workSpecDao().getWorkSpec(workSpec.id); //从数据库中根据id获取对应的WorkSpec 
                if (currentDbWorkSpec == null) {
                    Logger.get().warning(
                            TAG,
                            "Skipping scheduling " + workSpec.id
                                    + " because it's no longer in the DB");
                    continue;
                } else if (currentDbWorkSpec.state != WorkInfo.State.ENQUEUED) {
                    Logger.get().warning(
                            TAG,
                            "Skipping scheduling " + workSpec.id
                                    + " because it is no longer enqueued");
                    continue;
                }

                SystemIdInfo info = workDatabase.systemIdInfoDao()
                        .getSystemIdInfo(workSpec.id);     //获取对应的系统编号

                if (info != null) {
                    JobInfo jobInfo = getPendingJobInfo(mJobScheduler, workSpec.id);  //如果该workSpec 已经被转化为JobInfo 且已经放入到了jobScheduler,这直接跳过本次循环
                    if (jobInfo != null) {
                        Logger.get().debug(TAG, String.format(
                                "Skipping scheduling %s because JobScheduler is aware of it "
                                        + "already.",
                                workSpec.id));
                        continue;
                    }
                }

                int jobId = info != null ? info.systemId : mIdGenerator.nextJobSchedulerIdWithRange(
                        mWorkManager.getConfiguration().getMinJobSchedulerId(),
                        mWorkManager.getConfiguration().getMaxJobSchedulerId());  //将info.systemId作为jobId 如果为空,则根据Configuration中指定的条件去生成jobId 

                if (info == null) {
                    SystemIdInfo newSystemIdInfo = new SystemIdInfo(workSpec.id, jobId);
                    mWorkManager.getWorkDatabase()
                            .systemIdInfoDao()
                            .insertSystemIdInfo(newSystemIdInfo);
                }   //如果info为空,通过jobId与workSpec.id生成一条心的数据插入SystemIdInfo 

                scheduleInternal(workSpec, jobId);    


                if (Build.VERSION.SDK_INT == 23) {
                    int nextJobId = mIdGenerator.nextJobSchedulerIdWithRange(
                            mWorkManager.getConfiguration().getMinJobSchedulerId(),
                            mWorkManager.getConfiguration().getMaxJobSchedulerId());

                    scheduleInternal(workSpec, nextJobId);   //根据条件调用scheduleInternal方法
                }

                workDatabase.setTransactionSuccessful();
            } finally {
                workDatabase.endTransaction();
            }
        }
    }

查看scheduleInternal,在其内部,就是通过给定的参数,将workspec转化为jobinfo,并放入到JobScheduler中。JobScheduler是系统服务,由android程序启动的时候自动拉起无需手动去操作,而与JobScheduler对应的jobservice由系统自动生成,继续反编译查看apk

public void scheduleInternal(WorkSpec workSpec, int jobId) {
    JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
    Logger.get().debug(
            TAG,
            String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));
    mJobScheduler.schedule(jobInfo);
}

会发现系统为我们自动实现了一个SystemJobService,也就是当JobScheduler中的条件满足时,SystemJobService就会被调用到
​​
在这里插入图片描述

在 SystemJobService中的onStartJob中,重点是最后一个方法,该方法调用了WorkManagerImpl.startWork,并将workSpecId,与runtimeExtras作为参数传入。

  @Override
    public boolean onStartJob(JobParameters params) {
           //屏蔽了无关代码
        mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
        return true;
    }
 在startWork中,调用了mWorkTaskExecutor去执行StartWorkRunnable

 public void startWork(String workSpecId, WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkTaskExecutor
                .executeOnBackgroundThread(
                        new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }

进入StartWorkRunnable的run方法中,调用了Processor的startWork方法(Processor之前有讲解)

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class StartWorkRunnable implements Runnable {

    private WorkManagerImpl mWorkManagerImpl;
    private String mWorkSpecId;
    private WorkerParameters.RuntimeExtras mRuntimeExtras;

    public StartWorkRunnable(
            WorkManagerImpl workManagerImpl,
            String workSpecId,
            WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkManagerImpl = workManagerImpl;
        mWorkSpecId = workSpecId;
        mRuntimeExtras = runtimeExtras;
    }

    @Override
    public void run() {
        mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
    }
}

这里最终调用了mWorkManagerImpl.getProcessor().startWork方法

mWorkManagerImpl.getProcessor().startWork方法流程分析该方法过程的调用会在后面多次使用到

Processor会在我们实例化WorkManagerImpl时构造,在Processor的startWork方法内首先会通过之前传递进来的对象锁进行加锁,然后在内部构件了一个workWrapper,它是一个runable,最后放到了mWorkTaskExecutor在后台去执行

 public boolean startWork(String id, WorkerParameters.RuntimeExtras runtimeExtras) {
        WorkerWrapper workWrapper;
        synchronized (mLock) {
            // Work may get triggered multiple times if they have passing constraints
            // and new work with those constraints are added.
            if (mEnqueuedWorkMap.containsKey(id)) {
                Logger.get().debug(
                        TAG,
                        String.format("Work %s is already enqueued for processing", id));
                return false;
            }

            workWrapper =
                    new WorkerWrapper.Builder(
                            mAppContext,
                            mConfiguration,
                            mWorkTaskExecutor,
                            mWorkDatabase,
                            id)
                            .withSchedulers(mSchedulers)
                            .withRuntimeExtras(runtimeExtras)
                            .build();
            ListenableFuture<Boolean> future = workWrapper.getFuture();
            future.addListener(
                    new FutureListener(this, id, future),
                    mWorkTaskExecutor.getMainThreadExecutor());
            mEnqueuedWorkMap.put(id, workWrapper);
        }
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
        Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
        return true;
    }

它的run方法内部比较复杂,主要是调用mWorker.startWork(),以及在最后结尾调用了onWorkFinished,接下来一个个分析这2步都做了什么动作

public void run() {
    mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);   //从WorkTag中获取到WorkSpecId对应的Tag
    mWorkDescription = createWorkDescription(mTags);    //创建work说明,内部通过StringBuilder进行拼接
    runWorker(); 
}

private void runWorker() {
    if (tryCheckForInterruptionAndResolve()) {   //检查程序是否中断,如果中断直接返回
        return;
    }

    mWorkDatabase.beginTransaction();    //开启数据库事务
    try {
        mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);   //通过传递进来的WorkSpecId查询符合条件的WorkSpec并返回
        if (mWorkSpec == null) {    //如果没有找到打印错误直接返回
            Logger.get().error(
                    TAG,
                    String.format("Didn't find WorkSpec for id %s", mWorkSpecId));
            resolve(false);
            return;
        }

        // Do a quick check to make sure we don't need to bail out in case this work is already
        // running, finished, or is blocked.
        if (mWorkSpec.state != ENQUEUED) {    //如果该WorkSpec的状态不为ENQUEUED,表示对应的worker没有入队,无事可做,直接返回
            resolveIncorrectStatus();    //继续检查WorkSpec的状态是否为running,如果是,则重新安排
            mWorkDatabase.setTransactionSuccessful();   //设置数据库中对应状态为successful
            Logger.get().debug(TAG,
                    String.format("%s is not in ENQUEUED state. Nothing more to do.",
                            mWorkSpec.workerClassName));
            return;
        }



        if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
            long now = System.currentTimeMillis();
            // Allow first run of a PeriodicWorkRequest when flex is applicable
            // (when using AlarmManager) to go through. This is because when periodStartTime=0;
            // calculateNextRunTime() always > now. We are being overly cautious with the
            // SDK_INT check and the intervalDuration != flexDuration check.
            // For more information refer to b/124274584
            boolean isFirstRunWhenFlexApplicable =
                    Build.VERSION.SDK_INT < WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL
                            && mWorkSpec.intervalDuration != mWorkSpec.flexDuration
                            && mWorkSpec.periodStartTime == 0;

            if (!isFirstRunWhenFlexApplicable && now < mWorkSpec.calculateNextRunTime()) {
                Logger.get().debug(TAG,
                        String.format(
                                "Delaying execution for %s because it is being executed "
                                        + "before schedule.",
                                mWorkSpec.workerClassName));
                // For AlarmManager implementation we need to reschedule this kind  of Work.
                // This is not a problem for JobScheduler because we will only reschedule
                // work if JobScheduler is unaware of a jobId.
                resolve(true);
                return;
            }
        }

        // Needed for nested transactions, such as when we're in a dependent work request when
        // using a SynchronousExecutor.
        mWorkDatabase.setTransactionSuccessful();
    } finally {
        mWorkDatabase.endTransaction();
    }

    // Merge inputs.  This can be potentially expensive code, so this should not be done inside
    // a database transaction.
    Data input;
    if (mWorkSpec.isPeriodic()) {     //判断是否是定期任务
        input = mWorkSpec.input;      //将我们通过workRequest传入的参数保存
    } else {
        InputMerger inputMerger = InputMerger.fromClassName(mWorkSpec.inputMergerClassName);   //通过WorkSpec中存储的worker的类名反射构造该worker的实例
        if (inputMerger == null) {
            Logger.get().error(TAG, String.format("Could not create Input Merger %s",
                    mWorkSpec.inputMergerClassName));
            setFailedAndResolve();
            return;
        }
        List<Data> inputs = new ArrayList<>();
        inputs.add(mWorkSpec.input);
        inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
        input = inputMerger.merge(inputs);    //将我们执行worker的入参添加到input中去
    }

    WorkerParameters params = new WorkerParameters(
            UUID.fromString(mWorkSpecId),
            input,
            mTags,
            mRuntimeExtras,
            mWorkSpec.runAttemptCount,
            mConfiguration.getExecutor(),
            mWorkTaskExecutor,
            mConfiguration.getWorkerFactory());

    // Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
    // in test mode.
    if (mWorker == null) {    //如果mWorker 为空,则根据WorkSpec.workerClassName创建一个worker实例
        mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
                mAppContext,
                mWorkSpec.workerClassName,
                params);
    }

    if (mWorker == null) {     
        Logger.get().error(TAG,
                String.format("Could not create Worker %s", mWorkSpec.workerClassName));
        setFailedAndResolve();
        return;
    }

    if (mWorker.isUsed()) {   //如果该mWorker已经被调用过
        Logger.get().error(TAG,
                String.format("Received an already-used Worker %s; WorkerFactory should return "
                        + "new instances",
                        mWorkSpec.workerClassName));
        setFailedAndResolve();
        return;
    }
    mWorker.setUsed();    //将mWorker设置为已被使用

    // Try to set the work to the running state.  Note that this may fail because another thread
    // may have modified the DB since we checked last at the top of this function.
    if (trySetRunning()) {   //尝试运行
        if (tryCheckForInterruptionAndResolve()) {   //检查任务是否中断并尝试解决
            return;
        }

        final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();    //构造一个future 实例
        // Call mWorker.startWork() on the main thread.
        mWorkTaskExecutor.getMainThreadExecutor()   //切换到主线程执行任务
                .execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Logger.get().debug(TAG, String.format("Starting work for %s",
                                    mWorkSpec.workerClassName));
                            mInnerFuture = mWorker.startWork();   //调用了worker实例的startwork方法并将执行结果返回
                            future.setFuture(mInnerFuture);  //将结果放入future
                        } catch (Throwable e) {
                            future.setException(e);
                        }

                    }
                });

        // Avoid synthetic accessors.
        final String workDescription = mWorkDescription;
        future.addListener(new Runnable() {   //future监听通知,
            @Override
            @SuppressLint("SyntheticAccessor")
            public void run() {
                try {
                    // If the ListenableWorker returns a null result treat it as a failure.
                    ListenableWorker.Result result = future.get();   //将上面通过setFuture获得的结果返回
                    if (result == null) {
                        Logger.get().error(TAG, String.format(
                                "%s returned a null result. Treating it as a failure.",
                                mWorkSpec.workerClassName));
                    } else {
                        Logger.get().debug(TAG, String.format("%s returned a %s result.",
                                mWorkSpec.workerClassName, result));
                        mResult = result;   //将结果赋值给mResult 
                    }
                } catch (CancellationException exception) {
                    // Cancellations need to be treated with care here because innerFuture
                    // cancellations will bubble up, and we need to gracefully handle that.
                    Logger.get().info(TAG, String.format("%s was cancelled", workDescription),
                            exception);
                } catch (InterruptedException | ExecutionException exception) {
                    Logger.get().error(TAG,
                            String.format("%s failed because it threw an exception/error",
                                    workDescription), exception);
                } finally {
                    onWorkFinished();   //
                }
            }
        }, mWorkTaskExecutor.getBackgroundExecutor());
    } else {
        resolveIncorrectStatus();
    }
}

首先看startWork方法,它是ListenableWorker中的方法,而ListenableWorker是Worker的父类,查看Worker,在它的startWork

中开启了一个线程池去执行dowork方法,到这一步,终于走完了整个调用过程,也就是我们创建的继承Worker类的testworker中的dowork方法会被调用,完成了后台任务的执行。当我们的dowork执行完毕后,会返回该结果,而这个结果在上面说了,是通过result进行了接收。而该状态会在onWorkFinished方法中进行更新。SystemJobScheduler的执行流程分析完毕。

public final @NonNull ListenableFuture<Result> startWork() {
    mFuture = SettableFuture.create();
    getBackgroundExecutor().execute(new Runnable() {
        @Override
        public void run() {
            try {
                Result result = doWork();
                mFuture.set(result);
            } catch (Throwable throwable) {
                mFuture.setException(throwable);
            }

        }
    });
    return mFuture;
}

**

SystemAlarmScheduler工作流程

**

进入SystemAlarmScheduler的schedule方法,它会对传入的workSpecs进行遍历,并调用scheduleWorkSpec方法

   public void schedule(WorkSpec... workSpecs) {
        for (WorkSpec workSpec : workSpecs) {
            scheduleWorkSpec(workSpec);
        }
    }

而这个方法会调用createScheduleWorkIntent创建SystemAlarmService服务并将我们的workspecid进行传入,并开启该服务

private void scheduleWorkSpec(@NonNull WorkSpec workSpec) {
    Logger.get().debug(TAG, String.format("Scheduling work with workSpecId %s", workSpec.id));
    Intent scheduleIntent = CommandHandler.createScheduleWorkIntent(mContext, workSpec.id);
    mContext.startService(scheduleIntent);
}


static Intent createScheduleWorkIntent(@NonNull Context context, @NonNull String workSpecId) {
    Intent intent = new Intent(context, SystemAlarmService.class);
    intent.setAction(ACTION_SCHEDULE_WORK);
    intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
    return intent;
}

除此之外,还可以通过反编译apk看出,系统自动为我们静态注册了很多系统广播,
在这里插入图片描述
当我们接收到这些系统广播时,就会调用到createConstraintsChangedIntent,拉起SystemAlarmService,也就是当接收到这些广播时,SystemAlarmService会被拉起,然后根据传入的intent进行一系列操作,从而执行符合条件的worker

abstract class ConstraintProxy extends BroadcastReceiver {
    private static final String TAG = Logger.tagWithPrefix("ConstraintProxy");

    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.get().debug(TAG, String.format("onReceive : %s", intent));
        Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
        context.startService(constraintChangedIntent);
    }

    /**
     * Proxy for Battery Not Low constraint
     */
    public static class BatteryNotLowProxy extends ConstraintProxy {
    }

    /**
     * Proxy for Battery Charging constraint
     */
    public static class BatteryChargingProxy extends ConstraintProxy {
    }

    /**
     * Proxy for Storage Not Low constraint
     */
    public static class StorageNotLowProxy extends ConstraintProxy {
    }

    /**
     * Proxy for Network State constraints
     */
    public static class NetworkStateProxy extends ConstraintProxy {
    }

查看该服务做了些什么,发现其内部创建了SystemAlarmDispatcher对象,并在onStartCommand中调用了其add方法

public class SystemAlarmService extends LifecycleService
        implements SystemAlarmDispatcher.CommandsCompletedListener {

    private static final String TAG = Logger.tagWithPrefix("SystemAlarmService");

    private SystemAlarmDispatcher mDispatcher;

    @Override
    public void onCreate() {
        super.onCreate();
        mDispatcher = new SystemAlarmDispatcher(this);
        mDispatcher.setCompletedListener(this);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mDispatcher.onDestroy();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        if (intent != null) {
            mDispatcher.add(intent, startId);     //在这里进行了调用
        }
        // If the service were to crash, we want all unacknowledged Intents to get redelivered.
        return Service.START_REDELIVER_INTENT;
    }

    @MainThread
    @Override
    public void onAllCommandsCompleted() {
        Logger.get().debug(TAG, "All commands completed in dispatcher");
        // Check to see if we hold any more wake locks.
        WakeLocks.checkWakeLocks();
        // No need to pass in startId; stopSelf() translates to stopSelf(-1) which is a hard stop
        // of all startCommands. This is the behavior we want.
        stopSelf();
    }
}

在该add方法中,会将我们传递进来的intent放入到mIntents中去,该mIntents是一个intent的list集合,并在最后调用了processCommand方法

public boolean add(@NonNull final Intent intent, final int startId) {
    Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
    assertMainThread();   //必须在主线程调用
    String action = intent.getAction();
    if (TextUtils.isEmpty(action)) {
        Logger.get().warning(TAG, "Unknown command. Ignoring");
        return false;
    }

    // If we have a constraints changed intent in the queue don't add a second one. We are
    // treating this intent as special because every time a worker with constraints is complete
    // it kicks off an update for constraint proxies.
    if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
            && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
        return false;
    }

    intent.putExtra(KEY_START_ID, startId);
    synchronized (mIntents) {
        boolean hasCommands = !mIntents.isEmpty();
        mIntents.add(intent);    //将intent放入到集合中去
        if (!hasCommands) {
            // Only call processCommand if this is the first command.
            // The call to dequeueAndCheckForCompletion will process the remaining commands
            // in the order that they were added.
            processCommand();   
        }
    }
    return true;
}

该方法内会将我们的intent传递给mCommandHandler.onHandleIntent去处理

 private void processCommand() {
        assertMainThread();  //确保在主线程执行
        PowerManager.WakeLock processCommandLock =   //
                WakeLocks.newWakeLock(mContext, PROCESS_COMMAND_TAG);
        try {
            processCommandLock.acquire();    //获取锁
            // Process commands on the background thread.
            mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {  //将任务切换到WorkManager中的WorkTaskExecutor去执行
                @Override
                public void run() {
                    synchronized (mIntents) {   //对mIntents加锁
                        mCurrentIntent = mIntents.get(0);   //从mIntents中获取intent
                    }

                    if (mCurrentIntent != null) {
                        final String action = mCurrentIntent.getAction();
                        final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
                                DEFAULT_START_ID);
                        Logger.get().debug(TAG,
                                String.format("Processing command %s, %s", mCurrentIntent,
                                        startId));
                        final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                                mContext,
                                String.format("%s (%s)", action, startId));
                        try {
                            Logger.get().debug(TAG, String.format(
                                    "Acquiring operation wake lock (%s) %s",
                                    action,
                                    wakeLock));

                            wakeLock.acquire();
                            mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                    SystemAlarmDispatcher.this);   //调用mCommandHandler.onHandleIntent方法
                        } catch (Throwable throwable) {
                            Logger.get().error(
                                    TAG,
                                    "Unexpected error in onHandleIntent",
                                    throwable);
                        }  finally {
                            Logger.get().debug(
                                    TAG,
                                    String.format(
                                            "Releasing operation wake lock (%s) %s",
                                            action,
                                            wakeLock));
                            wakeLock.release();   //释放锁
                            // Check if we have processed all commands
                            postOnMainThread(
                                    new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));   // 当执行完毕后在主线程将本次任务意图从intents中进行移除
                        }
                    }
                }
            });
        } finally {
            processCommandLock.release();
        }
    }

这个方法内使用到了状态模式,会对intent的action进行判断,并做不同的处理,当我们第一次进入时会进入handleScheduleWorkIntent

 void onHandleIntent(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {

        String action = intent.getAction();

        if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            handleConstraintsChanged(intent, startId, dispatcher);   //如果约束发生了改变啊,则更新数据库中workspec的状态,并重新构建intent重新添加到intents集合中去
        } else if (ACTION_RESCHEDULE.equals(action)) {   //如果执行的时间发生变化
            handleReschedule(intent, startId, dispatcher);
        } else {
            Bundle extras = intent.getExtras();
            if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
                Logger.get().error(TAG,
                        String.format("Invalid request for %s, requires %s.",
                                action,
                                 KEY_WORKSPEC_ID));
            } else {   //对intent的action进行判断,并做不同的处理,当我们第一次开启service时,action为ACTION_SCHEDULE_WORK
                if (ACTION_SCHEDULE_WORK.equals(action)) {   
                    handleScheduleWorkIntent(intent, startId, dispatcher);  //处理任务
                } else if (ACTION_DELAY_MET.equals(action)) {
                    handleDelayMet(intent, startId, dispatcher);   //处理延迟
                } else if (ACTION_STOP_WORK.equals(action)) {
                    handleStopWork(intent, startId, dispatcher);   //处理任务停止
                } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                    handleExecutionCompleted(intent, startId, dispatcher);  //处理任务完成
                } else {
                    Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
                }
            }
        }
    }

查看handleReschedule做了什么,该方法会调用WorkManagerImpl的rescheduleEligibleWork

  private void handleReschedule(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {

        Logger.get().debug(TAG, String.format("Handling reschedule %s, %s", intent, startId));
        dispatcher.getWorkManager().rescheduleEligibleWork();
    }

在这个方法内会将我们的任务状态进行更新,并重新调用Schedulers.schedule

  public void rescheduleEligibleWork() {
        // TODO (rahulrav@) Make every scheduler do its own cancelAll().
        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
            SystemJobScheduler.jobSchedulerCancelAll(getApplicationContext());
        }

        // Reset scheduled state.
        getWorkDatabase().workSpecDao().resetScheduledState();

        // Delegate to the WorkManager's schedulers.
        // Using getters here so we can use from a mocked instance
        // of WorkManagerImpl.
        Schedulers.schedule(getConfiguration(), getWorkDatabase(), getSchedulers());
    }

查看handleScheduleWorkIntent,这里会判断workSpec是否存在约束,最终会走到Alarms.setAlarm中去,区别在于如果存在约束,则会调用完Alarms.setAlarm之后通过CommandHandler.createConstraintsChangedIntent构建一个intent意图发送给Dispatcher,然后重新进行一次回调

private void handleScheduleWorkIntent(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {



        if (!workSpec.hasConstraints()) {   //该workspec是否存在约束
            Logger.get().debug(TAG,
                    String.format("Setting up Alarms for %s at %s", workSpecId, triggerAt));
            Alarms.setAlarm(mContext, dispatcher.getWorkManager(), workSpecId, triggerAt);
        } else {
            // Schedule an alarm irrespective of whether all constraints matched.
            Logger.get().debug(TAG,
                    String.format("Opportunistically setting an alarm for %s at %s", workSpecId,
                            triggerAt));
            Alarms.setAlarm(
                    mContext,
                    dispatcher.getWorkManager(),
                    workSpecId,
                    triggerAt);

    
            Intent constraintsUpdate = CommandHandler.createConstraintsChangedIntent(mContext);
            dispatcher.postOnMainThread(
                    new SystemAlarmDispatcher.AddRunnable(   //在主线程调用了AddRunnable
                            dispatcher,
                            constraintsUpdate,
                            startId));
        }

        workDatabase.setTransactionSuccessful();
    } finally {
        workDatabase.endTransaction();
    }
}

static Intent createConstraintsChangedIntent(@NonNull Context context) {
    Intent intent = new Intent(context, SystemAlarmService.class);
    intent.setAction(ACTION_CONSTRAINTS_CHANGED);   //约束发生了变更
    return intent;
}

查看 该AddRunnable 的run方法,会重新调用dispatcher的add方法,此时的intent的action已经发生了变化,所以当再次走到onHandleIntent方法时,就会走不同的调用渠道。(状态模式)

static class AddRunnable implements Runnable {
    private final SystemAlarmDispatcher mDispatcher;
    private final Intent mIntent;
    private final int mStartId;

    AddRunnable(@NonNull SystemAlarmDispatcher dispatcher,
            @NonNull Intent intent,
            int startId) {
        mDispatcher = dispatcher;
        mIntent = intent;
        mStartId = startId;
    }

    @Override
    public void run() {
        mDispatcher.add(mIntent, mStartId);   //在这里将该intent作为参数,执行其add方法
    }
}

继续查看setAlarm, 这个方法内,会通过数据库中根据workSpecId去获取对应的系统编号,如果没有则会获取一个系统编号,并注册到数据库中去,他们最终都调用了setExactAlarm方法

public static void setAlarm(
        @NonNull Context context,
        @NonNull WorkManagerImpl workManager,
        @NonNull String workSpecId,
        long triggerAtMillis) {

    WorkDatabase workDatabase = workManager.getWorkDatabase();
    SystemIdInfoDao systemIdInfoDao = workDatabase.systemIdInfoDao();
    SystemIdInfo systemIdInfo = systemIdInfoDao.getSystemIdInfo(workSpecId);    //获取workSpecId对应的系统编号
    if (systemIdInfo != null) {        //对systemIdInfo 进行判断并做不同处理
        cancelExactAlarm(context, workSpecId, systemIdInfo.systemId);    
        setExactAlarm(context, workSpecId, systemIdInfo.systemId, triggerAtMillis);
    } else {
        IdGenerator idGenerator = new IdGenerator(context);
        int alarmId = idGenerator.nextAlarmManagerId();
        SystemIdInfo newSystemIdInfo = new SystemIdInfo(workSpecId, alarmId);  //重新构建systemIdInfo 并放入数据库
        systemIdInfoDao.insertSystemIdInfo(newSystemIdInfo);
        setExactAlarm(context, workSpecId, alarmId, triggerAtMillis);
    }
}

在这里会获取AlarmManager,它属于系统服务之一的定时服务,会根据指定的意图,在合适的时间去调用所对应的SystemAlarmService,在这里会通过CommandHandler.createDelayMetIntent去创建一个inent,并放入到了AlarmManager

该createDelayMetIntent创造的意图的action为ACTION_DELAY_MET

  static Intent createDelayMetIntent(@NonNull Context context, @NonNull String workSpecId) {
        Intent intent = new Intent(context, SystemAlarmService.class);
        intent.setAction(ACTION_DELAY_MET);   //表明为延迟任务
        intent.putExtra(KEY_WORKSPEC_ID, workSpecId);
        return intent;
    }

该定时服务会根据我们指定的PendingIntent,在合适的时间开启其对应的SystemAlarmService,并将对应的intent传递到SystemAlarmService中去,上面说过了在该SystemAlarmService的onStartCommand方法中调用了Dispatcher.add方法且将intent作为参数,这个方法最终又执行到了onHandleIntent方法,且此时的intent.action发生了变化,所以会走不同的渠道(状态模式)

private static void setExactAlarm(
        @NonNull Context context,
        @NonNull String workSpecId,
        int alarmId,
        long triggerAtMillis) {

    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); //获取系统服务alarmManager 
    Intent delayMet = CommandHandler.createDelayMetIntent(context, workSpecId);
    PendingIntent pendingIntent = PendingIntent.getService(
            context, alarmId, delayMet, PendingIntent.FLAG_ONE_SHOT);    //根据传递进来的intent构造pendingIntent 
    if (alarmManager != null) {
        if (Build.VERSION.SDK_INT >= 19) {
            alarmManager.setExact(RTC_WAKEUP, triggerAtMillis, pendingIntent);
        } else {
            alarmManager.set(RTC_WAKEUP, triggerAtMillis, pendingIntent);
        }
    }
}

通过反编译apk看出,该service由程序自动生成,无需手动实现

上面分析了,执行了setAlarm方法后,会重新构建一个intent并放入到了SystemAlarmService中,而该intent的action为ACTION_DELAY_MET,所以当再次执行到onHandleIntent时,就调用到了handleDelayMet方法

该方法主要执行了delayMetCommandHandler.handleProcessWork

   private void handleDelayMet(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {

        Bundle extras = intent.getExtras();
        synchronized (mLock) {
            String workSpecId = extras.getString(KEY_WORKSPEC_ID);   //通过KEY_WORKSPEC_ID获取workSpecId 
            Logger.get().debug(TAG, String.format("Handing delay met for %s", workSpecId));

            // Check to see if we are already handling an ACTION_DELAY_MET for the WorkSpec.
            // If we are, then there is nothing for us to do.
            if (!mPendingDelayMet.containsKey(workSpecId)) {   //如果不是延迟任务,则打印错误
                DelayMetCommandHandler delayMetCommandHandler =
                        new DelayMetCommandHandler(mContext, startId, workSpecId,  
dispatcher);   //由DelayMetCommandHandler 进行一次封装
                mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
                delayMetCommandHandler.handleProcessWork();   //执行handleProcessWork
            } else {
                Logger.get().debug(TAG,
                        String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
                                workSpecId));
            }
        }
    }

在handleProcessWork中,会根据是否有约束选择调用onAllConstraintsMet与mWorkConstraintsTracker.replace

void handleProcessWork() {
    mWakeLock = WakeLocks.newWakeLock(
            mContext,
            String.format("%s (%s)", mWorkSpecId, mStartId));
    Logger.get().debug(TAG,
            String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
    mWakeLock.acquire();    //加锁

    WorkSpec workSpec = mDispatcher.getWorkManager()
            .getWorkDatabase()
            .workSpecDao()
            .getWorkSpec(mWorkSpecId);    //根据mWorkSpecId获取workSpec 

    // This should typically never happen. Cancelling work should remove alarms, but if an
    // alarm has already fired, then fire a stop work request to remove the pending delay met
    // command handler.
    if (workSpec == null) {  
        stopWork();
        return;
    }

    // Keep track of whether the WorkSpec had constraints. This is useful for updating the
    // state of constraint proxies when onExecuted().
    mHasConstraints = workSpec.hasConstraints();

    if (!mHasConstraints) {   //判断是否有约束
        Logger.get().debug(TAG, String.format("No constraints for %s", mWorkSpecId));
        onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
    } else {
        // Allow tracker to report constraint changes
        mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
    }
}

如果没有约束则在这个方法内,调用了Dispatcher.getProcessor().startWork(mWorkSpecId);最终完成了worker的执行 (上面有讲该执行的过程)

  public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
        // WorkConstraintsTracker will call onAllConstraintsMet with list of workSpecs whose
        // constraints are met. Ensure the workSpecId we are interested is part of the list
        // before we call Processor#startWork().
        if (!workSpecIds.contains(mWorkSpecId)) {
            return;
        }

        Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
        // Constraints met, schedule execution

        // Not using WorkManagerImpl#startWork() here because we need to know if the processor
        // actually enqueued the work here.
        // TODO(rahulrav@) Once WorkManagerImpl provides a callback for acknowledging if
        // work was enqueued, call WorkManagerImpl#startWork().
        boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);    //在这里调用

        if (isEnqueued) {
            // setup timers to enforce quotas on workers that have
            // been enqueued
            mDispatcher.getWorkTimer()
                    .startTimer(mWorkSpecId, CommandHandler.WORK_PROCESSING_TIME_IN_MS, this);  
        } else {
            // if we did not actually enqueue the work, it was enqueued before
            // cleanUp and pretend this never happened.
            cleanUp();
        }
    }

再查看其有约束的情况下怎么处理,

WorkConstraintsTracker.replace执行过程

查看WorkConstraintsTracker.replace方法,在这里调用了controller.replace(workSpecs);

public void replace(@NonNull List<WorkSpec> workSpecs) {
    synchronized (mLock) {
        for (ConstraintController controller : mConstraintControllers) {
            controller.setCallback(null);
        }

        for (ConstraintController controller : mConstraintControllers) {
            controller.replace(workSpecs);
        }

        for (ConstraintController controller : mConstraintControllers) {
            controller.setCallback(this);
        }
    }
}

replace主要就是遍历 workSpecs,并将有约束的workSpec添加到mMatchingWorkSpecIds,并在最后调用了updateCallback

public void replace(@NonNull List<WorkSpec> workSpecs) {
    mMatchingWorkSpecIds.clear();

    for (WorkSpec workSpec : workSpecs) {   //循环遍历workSpecs,判断其是否存在约束
        if (hasConstraint(workSpec)) {
            mMatchingWorkSpecIds.add(workSpec.id);  //如果有则添加到mMatchingWorkSpecIds
        }
    }

    if (mMatchingWorkSpecIds.isEmpty()) {
        mTracker.removeListener(this);
    } else {
        mTracker.addListener(this);
    }
    updateCallback();
}

这里updateCallback会根据约束情况来选择调用,而看源码可以发现这里是个回调方法,由WorkConstraintsTracker进行了实现

   private void updateCallback() {
        if (mMatchingWorkSpecIds.isEmpty() || mCallback == null) {
            return;
        }
 
        if (mCurrentValue == null || isConstrained(mCurrentValue)) {   //此处通过mCurrentValue 进行判断
            mCallback.onConstraintNotMet(mMatchingWorkSpecIds);   //如果不存在约束,或者约束条件满足
        } else {
            mCallback.onConstraintMet(mMatchingWorkSpecIds);   //存在约束的情况
        }
    }

    @Override
    public void onConstraintChanged(@Nullable T newValue) {   //当约束发生改变时会调用这个方法,并对mCurrentValue 赋值
        mCurrentValue = newValue;
        updateCallback();
    }

WorkConstraintsTracker的onConstraintMet主要是判断有约束条件的workSpecId的条件是否已经全部满足,如果满足则和不受约束的workSpecId做相同的处理,这里继续发生了回调

public void onConstraintMet(@NonNull List<String> workSpecIds) {
    synchronized (mLock) {
        List<String> unconstrainedWorkSpecIds = new ArrayList<>();
        for (String workSpecId : workSpecIds) {   //遍历查看workSpec是否所有约束都满足
            if (areAllConstraintsMet(workSpecId)) {
                Logger.get().debug(TAG, String.format("Constraints met for %s", workSpecId));
                unconstrainedWorkSpecIds.add(workSpecId);    //将满足所有条件的workSpecId 放入到该list中去
            }
        }
        if (mCallback != null) {
            mCallback.onAllConstraintsMet(unconstrainedWorkSpecIds);   //在这里调用onAllConstraintsMet,与不受约束条件的workSpecId 走了相同的渠道
        }
    }
}

@Override
public void onConstraintNotMet(@NonNull List<String> workSpecIds) {
    synchronized (mLock) {
        if (mCallback != null) {
            mCallback.onAllConstraintsNotMet(workSpecIds);
        }
    }
}

具体的方法调用是在DelayMetCommandHandler中,最终又走到了onAllConstraintsMet方法,完成了整体调用

 public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
        // WorkConstraintsTracker will call onAllConstraintsMet with list of workSpecs whose
        // constraints are met. Ensure the workSpecId we are interested is part of the list
        // before we call Processor#startWork().
        if (!workSpecIds.contains(mWorkSpecId)) {
            return;
        }

        Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
        // Constraints met, schedule execution

        // Not using WorkManagerImpl#startWork() here because we need to know if the processor
        // actually enqueued the work here.
        // TODO(rahulrav@) Once WorkManagerImpl provides a callback for acknowledging if
        // work was enqueued, call WorkManagerImpl#startWork().
        boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);

        if (isEnqueued) {
            // setup timers to enforce quotas on workers that have
            // been enqueued
            mDispatcher.getWorkTimer()
                    .startTimer(mWorkSpecId, CommandHandler.WORK_PROCESSING_TIME_IN_MS, this);
        } else {
            // if we did not actually enqueue the work, it was enqueued before
            // cleanUp and pretend this never happened.
            cleanUp();
        }
    }

到这里为止SystemAlarmScheduler的执行流程分析完毕

GreedyScheduler的工作流程

它的执行过程相比之下比较简单,如果worker(这里使用的是workspec,但其描述的是对应的worker,与该worker有关联关系,这里对workspec的判断,其实还是对worker执行条件的判断)不受约束,则直接调用WorkManagerImpl.startWork去执行worker,否则调用WorkConstraintsTracker.replace。

public void schedule(WorkSpec... workSpecs) {
    registerExecutionListenerIfNeeded();

    // Keep track of the list of new WorkSpecs whose constraints need to be tracked.
    // Add them to the known list of constrained WorkSpecs and call replace() on
    // WorkConstraintsTracker. That way we only need to synchronize on the part where we
    // are updating mConstrainedWorkSpecs.
    List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
    List<String> constrainedWorkSpecIds = new ArrayList<>();
    for (WorkSpec workSpec: workSpecs) {    //循环遍历workSpecs
        if (workSpec.state == WorkInfo.State.ENQUEUED   //是否入队
                && !workSpec.isPeriodic()    //是否定期任务
                && workSpec.initialDelay == 0L   //初始延迟是否为0
                && !workSpec.isBackedOff()) {    //是否为回退状态
            if (workSpec.hasConstraints()) {     //是否存在约束
                // Exclude content URI triggers - we don't know how to handle them here so the
                // background scheduler should take care of them.
                if (Build.VERSION.SDK_INT < 24
                        || !workSpec.constraints.hasContentUriTriggers()) {
                    constrainedWorkSpecs.add(workSpec);
                    constrainedWorkSpecIds.add(workSpec.id);     将有约束的workSpec和workSpec.id分别进行保存
                }
            } else {
                Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                mWorkManagerImpl.startWork(workSpec.id);    //否则调用WorkManagerImpl.startWork进行执行
            }
        }
    }

    // onExecuted() which is called on the main thread also modifies the list of mConstrained
    // WorkSpecs. Therefore we need to lock here.
    synchronized (mLock) {
        if (!constrainedWorkSpecs.isEmpty()) {   //如果constrainedWorkSpecs不为空,也就是还有受限的WorkSpec存在时进入
            Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
                    TextUtils.join(",", constrainedWorkSpecIds)));
            mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
            mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
        }
    }
}

WorkConstraintsTracker.replace,这个方法之前已经分析过了,不过在GreedyScheduler中,我们最后一步步回调会调用到GreedyScheduler

public void replace(@NonNull List<WorkSpec> workSpecs) {
    synchronized (mLock) {
        for (ConstraintController controller : mConstraintControllers) {
            controller.setCallback(null);
        }

        for (ConstraintController controller : mConstraintControllers) {
            controller.replace(workSpecs);
        }

        for (ConstraintController controller : mConstraintControllers) {
            controller.setCallback(this);
        }
    }
}

可以发现在这2个方法中,对于符合条件和不符合条件的worker,循环遍历去执行或者做停止操作

   @Override
    public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
        for (String workSpecId : workSpecIds) {
            Logger.get().debug(
                    TAG,
                    String.format("Constraints met: Scheduling work ID %s", workSpecId));
            mWorkManagerImpl.startWork(workSpecId);    //调用了   mWorkManagerImpl.startWork方法
        }
    }

    @Override
    public void onAllConstraintsNotMet(@NonNull List<String> workSpecIds) {
        for (String workSpecId : workSpecIds) {
            Logger.get().debug(TAG,
                    String.format("Constraints not met: Cancelling work ID %s", workSpecId));
            mWorkManagerImpl.stopWork(workSpecId);   //调用 mWorkManagerImpl.stopWork方法
        }
    }

查看WorkManagerImpl.startWork方法

   public void startWork(String workSpecId, WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkTaskExecutor
                .executeOnBackgroundThread(
                        new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }

发现最终也是调用了Processor().startWork

public class StartWorkRunnable implements Runnable {

    private WorkManagerImpl mWorkManagerImpl;
    private String mWorkSpecId;
    private WorkerParameters.RuntimeExtras mRuntimeExtras;

    public StartWorkRunnable(
            WorkManagerImpl workManagerImpl,
            String workSpecId,
            WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkManagerImpl = workManagerImpl;
        mWorkSpecId = workSpecId;
        mRuntimeExtras = runtimeExtras;
    }

    @Override
    public void run() {
        mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);   //在这里执行
    }
}

到此为止,3大调度器的工作流程和原理全部分析完毕

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值