JetPack 之 WorkManager

Q: 什么是WorkManager?

WorkManager 是一个 Android Jetpack 库,当满足工作的约束条件时,用来运行可延迟、需要保障的后台工作,即使应用程序退出,系统也会运行它们。

Q:WorkManager的适用场景?

1、可延迟进行的任务

a.满足某些条件才执行的任务,如需要在充电时才执行的任务。 b.用户无感知或可延迟感知的任务,如同步配置信息,同步资源,同步通讯录等。

2、定期重复性任务,但时效性要求不高的,如定期 log 上传,数据备份等。
3、退出应用后还应继续执行的未完成任务。

Q:WorkManager的好处?

  • 处理不同系统版本的兼容性
  • 遵循系统健康最佳实践
  • 支持异步一次性和周期性任务
  • 支持带输入/输出的链式任务
  • 允许你设置在任务运行时的约束
  • 即使应用程序或设备重启,也可以保证任务执行

Q:WorkManager的特点?

保证任务一定会被执行

WorkManager 有自己的数据库,每一个任务的信息与任务状态,都会保存在本地数据库中。所以即使程序没有在运行,或者在设备重启等情况下,WorkManager 依然可以保证任务的执行,只是不保证任务立即被执行。

合理使用设备资源

在执行很多周期性或非立即执行的任务时,WorkManager 提供我们 API,帮助我们合理利用设备资源,避免不必要的内存,流量,电量等消耗。

Q:WorkManager的初始化?

1、WorkManager 的初始化是在 app 冷启动后,由 WorkManagerInitializer 这个 ContentProvider 执行的。

2、初始化过程包含了 Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor 等的初始化过程。
Schedulers 有两个。
(1) GreedyScheduler: 执行没有任何约束的非周期性的任务。
(2)SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler: 执行周期性或者有约束性的任务。优先返回 SystemJobScheduler,在 build version 小于 23 的情况下先尝试返回 GcmBasedScheduler,若返回为空再返回 SystemAlarmScheduler。

3、初始化的最后,会根据情况找到需要被执行的任务进行调度执行。

Q: 不带约束条件的任务执行流程?

1、 在 WorkManager 执行了 enqueue() 后,创建 WorkContinuationImpl 对象执行 enqueue() 方法。

2、WorkContinuationImpl 持有的 EnqueueRunnable 对象将任务添加到 db,并交给 Schedulers 去调度。

3、Schedulers 将任务交给每一个 Scheduler 去处理。假设是GreedyScheduler 会先处理这个任务。

4、GreedyScheduler 经过一系列判断后,调用 WorkManager 的 startWork() 方法执行这种一次性,非延迟,无约束的任务。

5、WorkManager 持有的 StartWorkRunnable 对象会将任务交给 Processor 去处理,执行 startWork() 方法。

6、Processor 创建一个 WorkerWrapper 对象,由它去调用 Worker 的 startWork() 方法,执行我们自定义 worker 的任务,并返回相应的 result。

7、任务完成后,WorkerWrapper 会根据 result 对任务状态,db 等进行更新,然后 schedule 下一个任务。

Q:带约束条件的任务执行过程?

在任务执行的过程中,由于增加了约束条件,常驻的 GreedyScheduler 的 schedule() 方法将不会 startWork(),而是根据 build version 交由 SystemJobScheduler 或 SystemAlarmScheduler 进行处理。

SystemJobScheduler (Build Version >=23)
合理使用设备资源
>> 在执行很多周期性或非立即执行的任务时,WorkManager 提供我们 API,帮助我们合理利用设备资源,避免不必要的内存,流量,电量等消耗。

Q:WorkManager的初始化?
> 1、WorkManager 的初始化是在 app 冷启动后,由 WorkManagerInitializer 这个 ContentProvider 执行的。

>2、初始化过程包含了 Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor 等的初始化过程。
Schedulers 有两个。
>(1) GreedyScheduler: 执行没有任何约束的非周期性的任务。
(2)SystemJobScheduler/GcmBasedScheduler/SystemAlarmScheduler: 执行周期性或者有约束性的任务。优先返回 SystemJobScheduler,在 build version 小于 23 的情况下先尝试返回 GcmBasedScheduler,若返回为空再返回 SystemAlarmScheduler。

>3、初始化的最后,会根据情况找到需要被执行的任务进行调度执行。

Q: 不带约束条件的任务执行流程?
>1、 在 WorkManager 执行了 enqueue() 后,创建 WorkContinuationImpl 对象执行 enqueue() 方法。

> 2、WorkContinuationImpl 持有的 EnqueueRunnable 对象将任务添加到 db,并交给 Schedulers 去调度。

>3、Schedulers 将任务交给每一个 Scheduler 去处理。假设是GreedyScheduler 会先处理这个任务。

>4、GreedyScheduler 经过一系列判断后,调用 WorkManager 的 startWork() 方法执行这种一次性,非延迟,无约束的任务。

5、WorkManager 持有的 StartWorkRunnable 对象会将任务交给 Processor 去处理,执行 startWork() 方法。

6、Processor 创建一个 WorkerWrapper 对象,由它去调用 Worker 的 startWork() 方法,执行我们自定义 worker 的任务,并返回相应的 result。

7、任务完成后,WorkerWrapper 会根据 result 对任务状态,db 等进行更新,然后 schedule 下一个任务。

Q:带约束条件的任务执行过程?
> 在任务执行的过程中,由于增加了约束条件,常驻的 GreedyScheduler 的 schedule() 方法将不会 startWork(),而是根据 build version 交由 SystemJobScheduler 或 SystemAlarmScheduler 进行处理。
>>SystemJobScheduler (Build Version >=23)
在SystemJobScheduler的构造函数中,会获取JobScheduler对象,并传入了一个SystemJobInfoConverter对象( 用来将我们的workSpecs转化为JobInfo,这个JobInfo是jobScheduler需要的参数 ,用来描述任务的执行时间,条件,策略等一系列的行为。),在SystemJobScheduler 的 schedule() 方法执行了 scheduleInternal();

JobScheduler是系统服务,由android程序启动的时候自动拉起无需手动去操作,而与JobScheduler对应的jobservice由系统自动生成,这里的JobService是SystemJobService,当JobScheduler中的条件满足时,SystemJobService就会被调用到。

在 SystemJobService中的onStartJob中,重点是最后一个方法,该方法调用了WorkManagerImpl.startWork。

SystemAlarmScheduler(Build Version < 23)
SystemAlarmScheduler 使用的是 AlarmManager 来调度执行任务。
在 AndroidManifest 里有如下 receiver 注册:



    
          
          
    

在电量变化时,收到 BATTERY_LOW 的广播。在 BatteryNotLowProxy 的 onReceive() 进行处理:


//ConstraintProxy
    public static class BatteryNotLowProxy extends 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);
    }

createConstraintsChangedIntent() 的执行如下:


//ConstraintProxy
   static Intent createConstraintsChangedIntent(@NonNull Context context) {
       Intent intent = new Intent(context, SystemAlarmService.class);
       intent.setAction(ACTION_CONSTRAINTS_CHANGED);
       return intent;
   }

SystemAlarmService 的 onStartCommand() 处理如下:

@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;
    }

调用了 SystemAlarmDispatcher.add() 方法。

//SystemAlarmDispatcher
@MainThread
    public boolean add(@NonNull final Intent intent, final int startId) {
        ... ...
        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);
            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;
    }

add() 方法中执行了 processCommand(),这段代码的核心执行语句是:


//SystemAlarmDispatcher
mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                    SystemAlarmDispatcher.this);

在 CommandHandler 的 onHandleIntent() 方法中,action 为 ACTION_CONSTRAINTS_CHANGED 的执行是:


//CommandHandler
 if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            handleConstraintsChanged(intent, startId, dispatcher);
        } 

//CommandHandler
    private void handleConstraintsChanged(
            @NonNull Intent intent, int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {

        Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));
        // Constraints changed command handler is synchronous. No cleanup
        // is necessary.
        ConstraintsCommandHandler changedCommandHandler =
                new ConstraintsCommandHandler(mContext, startId, dispatcher);
        changedCommandHandler.handleConstraintsChanged();
    }

在 handleConstraintsChanged() 方法的执行中,会创建一个 action 为 ACTION_DELAY_MET 的 Intent 然后由 SystemAlarmDispatcher 发送出去,实际上也是调用了 SystemAlarmDispatcher.add() 方法。回到 SystemAlarmDispatcher 的 add() 流程。


//ConstraintsCommandHandler
Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
            Logger.get().debug(TAG, String.format(
                    "Creating a delay_met command for workSpec with id (%s)", workSpecId));
            mDispatcher.postOnMainThread(
                    new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));

回到 onHandleIntent() 方法,在 CommandHandler 的 onHandleIntent() 方法中,action 为 ACTION_DELAY_MET 的执行是:


//CommandHandler
else if (ACTION_DELAY_MET.equals(action)) {
                    handleDelayMet(intent, startId, dispatcher);
                } 

handleDelayMet() 的执行过程,会调用 DelayMetCommandHandler 的 handleProcessWork() 方法,接着执行 onAllConstraintsMet():

@Override
    public void onAllConstraintsMet(@NonNull List workSpecIds) {
        ... ...
        synchronized (mLock) {
            if (mCurrentState == STATE_INITIAL) {
                ... ...
                boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
                ... ...
            } else {
                Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
            }
        }
    }

到这里终于看到由 SystemAlarmDispatcher 调用了 Processor 的 startWork() 方法,回到了之前章节分析的任务执行流程。到此为止,一个任务在不同条件下的创建,执行流程就分析完毕。

Q:WorkManager在设备重启后如何保证任务可以得到执行?

WorkManager在初始化时创建了Room数据库, 将任务列表序列化到本地,记录每一个任务的属性,执行条件,执行顺序及执行状态等。从而保证任务在冷启动或硬件重启后,可以根据条件继续执行。

WorkManager 流程分析和源码解析 | 开发者说·DTalk
](https://mp.weixin.qq.com/s?__biz=MzAwODY4OTk2Mg==&mid=2652069060&idx=2&sn=77398948c657a70aeae4db35f8042a35&chksm=808cfc81b7fb759727b3b96d41addb3d6bb5f8793c064fb295631f0be496a4567ba75214dd3d&mpshare=1&scene=23&srcid=1107uIVkYdQ2fu0qgbADCEBW&sharer_sharetime=1604719995685&sharer_shareid=8206e3713102e9ca474649d51eda190c%23rd)

使用 WorkManager 处理需要立刻执行的后台任务

你可能感兴趣的:(JetPack 之 WorkManager)