美文网首页
WorkManager

WorkManager

作者: 兜兜里面没有钱 | 来源:发表于2021-08-29 16:28 被阅读0次

    一 使用入门
    我们先了解WorkManager工作的流程:

    1. 谁来做? 定义一个负责工作的Worker
    public class SimpleWorker extends Worker {
    @NonNull
        @Override
        public Result doWork() {
            // return Result.failure(); // 执行失败
            // return Result.retry();   // 重试 
            // return Result.success(); // 执行成功
            return null;                // 执行结束?
        }
    }
    

    Worker是WorkManager最终实现任务的工人,它不用管会在什么实际执行任务,被安排怎样执行任务,只管处理任务doWork并根据任务执行情况反馈任务结果return Result。
    Worker执行结果Result:表示任务状态,他一般有failure、retry、success三个状态,且failure、success可以携带数据Data。

    2.怎么做 ?定义制定工作方案的WorkRequest
    这里就有两种request,一种是执行一次的,一种是重复执行的。
    非重复性工作: 工人应该一次性把工作做掉
    OneTimeWorkRequest workRequest1 = OneTimeWorkRequest.from(SimpleWorker.class);
    周期任务: 工人应该每隔多久做一次工作
    PeriodicWorkRequest request = new PeriodicWorkRequest
    .Builder(SimpleWorker.class, 16, TimeUnit.MINUTES).build();
    注意: 关于周期任务有一个霸王条款:
    注意:可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同)。

    2.1 WorkRequest 目前有两个子类,分别为单次执行任务 OneTimeWorkRequest和周期执行任务 PeriodicWorkRequest. 他们的主要区别是任务执行方案不同导致的状态变化不同。

    [简单单次执行任务 OneTimeWorkRequest 工作状态图]

    avatar

    对于 one-time 工作请求,工作的初始状态为 ENQUEUED。
    在 ENQUEUED 状态下,您的工作会在满足其 Constraints 和初始延迟计时要求后立即运行。接下来,该工作会转为 RUNNING 状态,然后可能会根据工作的结果转为 SUCCEEDED、FAILED 状态;或者,如果结果是 retry,它可能会回到 ENQUEUED 状态。在此过程中,随时都可以取消工作,取消后工作将进入 CANCELLED 状态。

    周期执行任务 PeriodicWorkRequest 工作状态图


    avatar

    周期性工作的初始状态为 ENQUEUED,正常执行情况下,周期性工作的状态在ENQUEUED ↔RUNNING之间交替,知道任务被取消时,状态为CANCELLED。

    成功和失败状态仅适用于一次性工作和链式工作。定期工作只有一个终止状态 CANCELLED。这是因为定期工作永远不会结束。每次运行后,无论结果如何,系统都会重新对其进行调度。

    上面讲了一些简单工作执行。下来看看他还有什么可以操作的地方。
    二 任务监听
    务进度)

    上面介绍了工作的状态,我们该如何监听这些状态呢?

    在将工作加入队列后,您可以随时按其 name、id 或与其关联的 tag 在 WorkManager 中进行查询,以检查其状态

    // by id
    workManager.getWorkInfoById(syncWorker.id); // ListenableFuture<WorkInfo>
    
    // by name
    workManager.getWorkInfosForUniqueWork("sync"); // ListenableFuture<List<WorkInfo>>
    
    // by tag
    workManager.getWorkInfosByTag("syncTag"); // ListenableFuture<List<WorkInfo>>
    
    

    该查询会返回 WorkInfo对象的 ListenableFuture,该值包含工作的 id、其标记、其当前的 State 以及通过 Result.success(outputData)设置的任何输出数据。

    利用每个方法的 LiveData变种,您可以通过注册监听器来观察 WorkInfo 的变化。例如,如果您想要在某项工作成功完成后向用户显示消息,您可以进行如下设置:

    
    workManager.getWorkInfoByIdLiveData(syncWorker.id)
            .observe(getViewLifecycleOwner(), workInfo -> {
        if (workInfo.getState() != null &&
                workInfo.getState() == WorkInfo.State.SUCCEEDED) {
            Snackbar.make(requireView(),
                        R.string.work_completed, Snackbar.LENGTH_SHORT)
                    .show();
       }
    });
    

    复杂的工作查询

    WorkManager 2.4.0 及更高版本支持使用 WorkQuery对象对已加入队列的作业进行复杂查询。WorkQuery 支持按工作的标记、状态和唯一工作名称的组合进行查询。

    以下示例说明了如何查找带有“syncTag”标记、处于 FAILEDCANCELLED 状态,且唯一工作名称为“preProcess”或“sync”的所有工作。

    
    WorkQuery workQuery = WorkQuery.Builder
           .fromTags(Arrays.asList("syncTag"))
           .addStates(Arrays.asList(WorkInfo.State.FAILED, WorkInfo.State.CANCELLED))
           .addUniqueWorkNames(Arrays.asList("preProcess", "sync")
         )
        .build();
    
    ListenableFuture<List<WorkInfo>> workInfos = workManager.getWorkInfos(workQuery);
    
    

    取消和停止工作

    如果您不再需要运行先前加入队列的工作,则可以要求将其取消。您可以按工作的 name、id 或与其关联的 tag 取消工作。

    // by id
    workManager.cancelWorkById(syncWorker.id);
    
    // by name
    workManager.cancelUniqueWork("sync");
    
    // by tag
    workManager.cancelAllWorkByTag("syncTag");
    
    

    传递数据

    在一些业务场景下,我们需要向任务中传递数据,可以使用androidx.work.Data向WorkRequest中添加数据。

    // 创建需传递的数据
    Data data = new Data.Builder().putString("message", "MainActivity").build();
    // 单次执行任务
    OneTimeWorkRequest request1 = new OneTimeWorkRequest.Builder(Task4DataWorker.class)
            // 向WorkRequest中添加数据
            .setInputData(data).build();
    // 将任务加入队列
    WorkManager.getInstance(this).enqueue(request1);
    

    需要注意的是,Data源码可知,此处传递数据仅支持基础数据类型及其封装、String以及上述类型的数组

    有传递数据自然有获取数据,我们可以Worker中通过getInputData()获取
    传入的Data对象,通过Data获取传入的值,也可以通过Data和Result将值传递给观察者。

    public class DataWorker extends Worker {
    
        public DataWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
            super(context, workerParams);
        }
    
        @SuppressLint("RestrictedApi")
        @NonNull
        @Override
        public Result doWork() {
            String message = getInputData().getString("message");
            Data myMsg = new Data.Builder().putString("message", "message from Worker").build();
            return new Result.Success(myMsg);
        }
    }
    

    而从Worker中传出的Data则可在WorkRequest的工作状态WorkInfo中取得:

    WorkManager.getInstance(context)
                   .getWorkInfoByIdLiveData(workRequest.getId())
                   .observe(context, workInfo -> {
                       if (workInfo!=null){
                           if (workInfo.getState().isFinished()) {
                               String message = workInfo.getOutputData().getString("message");
                           }
                       }
                   });
    

    多任务串联

    除一般的单一任务的场景外,我们面对的业务往往也要处理多个任务的场景,此时我们可以串联多个任务,也可以将多个任务编组成任务链去使用:

    WorkManager.getInstance(this).beginWith(workRequest2)
                    .then(workRequest3)
                    .then(workRequest4)
                    .enqueue();
    // or
    WorkContinuation continuation = WorkManager.getInstance(this)
                    .beginWith(workRequest2)
                    .then(workRequest3)
                    .then(workRequest4);
    
    continuation.then(workRequest).enqueue();
    

    唯一任务

    唯一工作是一个很实用的概念,可确保同一时刻只有一个具有特定名称的工作实例。与 ID 不同的是,唯一名称是人类可读的,由开发者指定,而不是由 WorkManager 自动生成。与标记不同,唯一名称仅与一个工作实例相关联。

    唯一工作既可用于一次性工作,也可用于定期工作。您可以通过调用以下方法之一创建唯一工作序列,具体取决于您是调度重复工作还是一次性工作。WorkManager.enqueueUniqueWork()(用于一次性工作)
    WorkManager.enqueueUniquePeriodicWork()(用于定期工作)

    这两种方法都接受 3 个参数:

    uniqueWorkName - 用于唯一标识工作请求的 String。
    existingWorkPolicy - 此 enum 可告知 WorkManager:如果已有使用该名称且尚未完成的唯一工作链,应执行什么操作。

    OneTimeWorkRequest workRequest2 = new OneTimeWorkRequest.Builder(SimpleWorker2.class).build();
    
    WorkManager.getInstance(this).beginUniqueWork("SimpleWorker",
            ExistingWorkPolicy.REPLACE, workRequest)
    //      ExistingWorkPolicy.APPEND, workRequest)
    //      ExistingWorkPolicy.KEEP, workRequest)
            .then(workRequest2)
    //      .then(workRequest)
            .enqueue();
    

    此处定义的唯一,仅在任务正在执行且出现相同uniqueWorkName名称时,existingWorkPolicy才生效,无法影响已结束的同名任务(此同名仅与uniqueWorkName有关)。
    以定义两个相同uniqueWorkName的WorkRequest为例,来观察existingWorkPolicy值的作用及影响

    任务约束

    Constraints可确保将工作延迟到满足最佳条件时运行。以下约束适用于 WorkManager。

    NetworkType 约束运行工作所需的网络类型。例如 Wi-Fi (UNMETERED)。
    BatteryNotLow 如果设置为 true,那么当设备处于“电量不足模式”时,工作不会运行。
    RequiresCharging 如果设置为 true,那么工作只能在设备充电时运行。
    DeviceIdle 如果设置为 true,则要求用户的设备必须处于空闲状态,才能运行工作。如果您要运行批量操作,否则可能会降低用户设备上正在积极运行的其他应用的性能,建议您使用此约束。
    StorageNotLow 如果设置为 true,那么当用户设备上的存储空间不足时,工作不会运行

    任务延时

    如果工作没有约束,或者当工作加入队列时所有约束都得到了满足,那么系统可能会选择立即运行该工作。如果您不希望工作立即运行,可以将工作指定为在经过一段最短初始延迟时间后再启动。

    重试和退避政策

    若Worker返回结果Result.retry()时,触发重试退避政策,即下次调度Worker应在多长时间以后,支持设置退避时间基数和基数递增方式,递增方式目前支持线性LINEAR和指数EXPONENTIAL

    OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(RetryWorker.class)
                    .setBackoffCriteria(BackoffPolicy.LINEAR,
                            OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                            TimeUnit.MILLISECONDS)
                    .build();
    

    注意:退避延迟时间不精确,在两次重试之间可能会有几秒钟的差异,但绝不会低于配置中指定的初始退避延迟时间。

    最短退避延迟时间设置为允许的最小值,即 10 秒。由于政策为 LINEAR,每次尝试重试时,重试间隔都会增加约 10 秒。例如,第一次运行以 Result.retry() 结束并在 10 秒后重试;然后,如果工作在后续尝试后继续返回 Result.retry(),那么接下来会在 20 秒、30 秒、40 秒后重试,以此类推。如果退避政策设置为 EXPONENTIAL,那么重试时长序列将接近 20、40、80 秒,以此类推。
    你可以为WorkRequest添加tag,从而使得你可以通过WorkManager.getWorkInfosByTag(String)获取WorkRequest的工作状态WorkInfo,你也可以直接通过WorkManager.cancelAllWorkByTag(String)取消对应标记的所有WorkRequest.

    标记工作

    由WorkRequest中关于tag的定义Set mTags可知,你可以为WorkRequest定义多个标记,当然的,你也可以为多个WorkRequest定义同一个标记用以统一管理。

    WorkRequest request =
           new OneTimeWorkRequest.Builder(SimpleWorker.class)
           .addTag("TAG")
           .build();
    // 根据tag取消任务
    WorkManager.getInstance(this).cancelAllWorkByTag("TAG");
    // 根据tag查找任务状态
    WorkManager.getInstance(this).getWorkInfosByTag("TAG");
    

    取消工作

    // by id
    workManager.cancelWorkById(syncWorker.id);
    // by name
    workManager.cancelUniqueWork("sync");
    // by tag
    workManager.cancelAllWorkByTag("syncTag");
    

    注意事项

    这里我们总结一下使用WorkManager的一些小Tips。

    PeriodicWorkRequest周期任务可以定义的最短重复间隔是 15 分钟(与 JobScheduler API 相同)
    延迟工作:执行工作器的确切时间还取决于 WorkRequest 中使用的约束和系统优化方式。WorkManager 经过设计,能够在满足这些约束的情况下提供可能的最佳行为。
    退避延迟时间不精确,在两次重试之间可能会有几秒钟的差异,但绝不会低于配置中指定的初始退避延迟时间。
    cancelAllWorkByTag(String) 会取消具有给定标记的所有工作。
    WorkRequest保证一定执行,但不保证一定在什么时间执行。

    WorkManager中Service组件之一的SystemAlarmService。

    SystemAlarmService 主要逻辑:

    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (mIsShutdown) {
                Logger.get().info(TAG,
                        "Re-initializing SystemAlarmDispatcher after a request to shut-down.");
    
                // Destroy the old dispatcher to complete it's lifecycle.
                mDispatcher.onDestroy();
                // Create a new dispatcher to setup a new lifecycle.
                initializeDispatcher();
                // Set mIsShutdown to false, to correctly accept new commands.
                mIsShutdown = false;
            }
    
            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;
        }
    

    我们看到, 此服务在创建时初始化并绑定闹钟事件调度器,当服务被start时,若有intent事件,则将intent传递给调度器. 调度器收到intent后执行以下操作:

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

    当闹钟调度器收到新命令,会将新命令放入命令集合,若新命令为集合中第一个命令则直接进入执行命令逻辑中,否则不处理,前一命令执行完毕后再次调用执行命令:

    @MainThread
    private void processCommand() {
        // do something
        mWorkManager.getWorkTaskExecutor().executeOnBackgroundThread(new Runnable() {
            // do something
         try {   
            // 若当前命令不为空,则将命令交给状态机执行
            mCommandHandler.onHandleIntent(mCurrentIntent, startId, SystemAlarmDispatcher.this);
         } catch (Throwable throwable) {}
         finally {
            // 当前命令执行完毕,回调SystemAlarmDispatcher处理命令集以及后续操作
            postOnMainThread( new 
    (SystemAlarmDispatcher.this));
         }
        }
    }
    

    当processCommand被调用,将开启新的线程处理命令,主要干了两件事:

    将命令交给CommandHandler分发[规划任务、重新规划任务、约束条件改变、执行完毕…]事件
    将事件处理完毕回调到闹钟事件调度器,由调度器处理后续事宜。
    我们来依次跟进这两件事:

    @WorkerThread
    void onHandleIntent(@NonNull Intent intent,int startId, @NonNull SystemAlarmDispatcher dispatcher) {
    
        String action = intent.getAction();
    
        if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            // 约束条件改变
            handleConstraintsChanged(intent, startId, dispatcher);
        } else if (ACTION_RESCHEDULE.equals(action)) {
            // 异常中断,需重新规划
            handleReschedule(intent, startId, dispatcher);
        } else {
            Bundle extras = intent.getExtras();
            if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
                // log error
            } else {
                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, dispatcher);
                } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                    // 完成任务
                    handleExecutionCompleted(intent, startId);
                } else {
                   // log error
                }
            }
        }
    }
    

    我们看到这里主要依据命令(intent)的action执行命令分发操作,此处我们主要关注handleDelayMet 时延结束后,处理任务的主线:

    private void handleDelayMet(
            @NonNull Intent intent,
            int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
    
        Bundle extras = intent.getExtras();
        // 保证任务处理线程安全
        synchronized (mLock) {
            String workSpecId = extras.getString(KEY_WORKSPEC_ID);
    
            // 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.
            // 检查该任务是否已经在即将执行队伍(map)中,如果是则不再重复操作
            if (!mPendingDelayMet.containsKey(workSpecId)) {
                DelayMetCommandHandler delayMetCommandHandler = new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
                // 将任务放入即将执行队伍(map)中
                mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
                // 处理任务
                delayMetCommandHandler.handleProcessWork();
            } else {
                // log error
            }
        }
    }
    

    我们可以看到,CommandHandler中维护了一个 mPendingDelayMet的Map来保证单次任务不被多次处理,若命令未被处理过,则将其加入到队伍中,然后调用delayMetCommandHandler.handleProcessWork()去处理任务:

    @WorkerThread
    void handleProcessWork() {
        // 创建WakeLocks
        mWakeLock = WakeLocks.newWakeLock(...);
        // 获取WorkSpec
        WorkSpec workSpec = mDispatcher.getWorkManager().getWorkDatabase().workSpecDao().getWorkSpec(mWorkSpecId);
        // 处理通常不会发生的情况 - 未获取到WorkSpec。
        // 取消工作应该删除alarm,但是如果alarm已经触发,那么就触发一个stop work请求来删除挂起的delay met命令处理程序。
        if (workSpec == null) {
            stopWork();
            return;
        }
        // 跟踪工作任务是否有约束
        mHasConstraints = workSpec.hasConstraints();
    
        if (!mHasConstraints) {
            // 若无约束
            onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
        } else {
            // 允许跟踪器报告约束更改
            // 此处可见,我们可以将时延视作一种特殊的约束
            mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
        }
    }
    
    

    这里判断了任务状态及约束状态,依据约束状态执行任务或更新任务状态,
    执行任务时:

    @Override
    public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
        // 保证任务正确    
        if (!workSpecIds.contains(mWorkSpecId)) {
            return;
        }
        // 保证执行安全
        synchronized (mLock) {
            if (mCurrentState == STATE_INITIAL) {
                // 更改状态
                mCurrentState = STATE_START_REQUESTED;
                // startWork => doWork
                //这里没有使用WorkManagerImpl#startWork(),因为我们需要知道处理器是否在这里将工作排队
                boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);
                // 若任务状态是轮询
                if (isEnqueued) {
                   // 设置计时器以强制已进入队列的任务配额
                    mDispatcher.getWorkTimer()
                            .startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
                } else {
                    // 执行完毕或取消,则清除状态
                    cleanUp();
                }
            } else {
                Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
            }
        }
    }
    

    这里最终改变任务状态并调用Processor.startWork()执行任务,我们将关注Processor.startWork(),这个方法最终执行到我们定义的Worker中doWork()方法,并且前面说到的WorkManager中负责任务执行的Service们最终都会调用到Processor.startWork()。

    首先我们简单过一下 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)的代码:

    public boolean startWork(@NonNull String id,  @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
        WorkerWrapper workWrapper;
        // 保证任务队列(MAP)只进或只出
        synchronized (mLock) {
            // doing 验证id不在队列中
            // 初始化WorkerWrapper
            workWrapper = new WorkerWrapper.Builder(...).build();
            ListenableFuture<Boolean> future = workWrapper.getFuture();
            // 为执行结果添加监听,注意这里监听回调何时调用后面会说明
            // 特别注意:future.addListener中 FutureListener 是一个Runnable对象
            // 特别注意: FutureListener 是一个Runnable对象
            future.addListener(
                    new FutureListener(this, id, future),
                    mWorkTaskExecutor.getMainThreadExecutor());
            // 将此任务加入队列
            mEnqueuedWorkMap.put(id, workWrapper);
        }
        // 使用工作线程执行任务
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
    }
    

    mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)主要做了三个操作:

    • 将任务要素封装为WorkerWrapper并为其执行期望添加监听
    • 任务要素入队
    • 工作线程执行处理任务
      相对主要的,我们继续跟进工作线程处理任务流程execute(Runnable), excute将WorkerWrapper继续封装为Task并加入任务队列,若当前无活跃Task则安排任务执行,任务执行完毕后继续检查任务队列,直到任务队列空置为止:
    @Override
    public void execute(@NonNull Runnable command) {
        // 保证任务队列只进或只出
        synchronized (mLock) {
            // 封装任务并入队
            mTasks.add(new Task(this, command));
            // 若无活跃任务,则尝试安排任务
            if (mActive == null) {
                scheduleNext();
            }
        }
    }
    
    // Synthetic access
    void scheduleNext() {
        // 保证任务队列只进或只出
        synchronized (mLock) {
            // 有任务且任务出队
            if ((mActive = mTasks.poll()) != null) {
                // 执行任务
                mExecutor.execute(mActive);
            }
        }
    }
    

    mExecutor.execute(mActive)会通过Task执行execute(Runnable)传入的Runnable,即执行WorkerWrapper,Task不论WorkerWrapper执行结果最终会继续轮询任务队列。此时,我们将目光移到WorkerWrapper.run():

    @WorkerThread
    @Override
    public void run() {
        mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
        mWorkDescription = createWorkDescription(mTags);
        runWorker();
    }
    private void runWorker() {
    // 各个状态判断最终调用到resolve(false);
    resolve(true); // OR resolve(false);
    
    // 尝试将工作设置为运行状态。注意,这可能会失败,因为自从上次在这个函数的顶部检查之后,另一个线程可能已经修改了数据库。
    if (trySetRunning()) {
        if (tryCheckForInterruptionAndResolve()) {
            return;
        }
    
        final SettableFuture<ListenableWorker.Result> future = SettableFuture.create();
        // Call mWorker.startWork() on the main thread.
        mWorkTaskExecutor.getMainThreadExecutor()
                .execute(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // 调用Worker执行工作任务
                            mInnerFuture = mWorker.startWork();
                            // 将任务结果回调添加到期望回调中
                            future.setFuture(mInnerFuture);
                        } catch (Throwable e) {
                            future.setException(e);
                        }
    
                    }
                });
    
        // Avoid synthetic accessors.
        final String workDescription = mWorkDescription;
        future.addListener(new Runnable() {
            @Override
            @SuppressLint("SyntheticAccessor")
            public void run() {
               // 最终调用到resolve(false);
               resolve(false);
            }
        }, mWorkTaskExecutor.getBackgroundExecutor());
    } else {
         // 最终调用到resolve(false);
         resolve(boolean);
    }
    
    }
    
    private void resolve(final boolean needsReschedule) {
        // 数据库事务操作
        
        mFuture.set(needsReschedule);
    }
    

    runWorker() 中包含各种任务状态的判断,并尝试将任务置为运行状态,若设置运行状态成功,则调用Worker.startWork() 即最终会走向个人定义的Worker的doWork()方法:

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

    DequeueAndCheckForCompletion如何完成回调:
    DequeueAndCheckForCompletion 是一个Runnable对象,postOnMainThread时调用run:

    @Override
    public void run() {
        mDispatcher.dequeueAndCheckForCompletion();
    }
    

    最终回调到dequeueAndCheckForCompletion:

    void dequeueAndCheckForCompletion() {
       // 保证mIntents命令集只进或只出
        synchronized (mIntents) {
            if (mCurrentIntent != null) {
                // 从命令集中移除已完成(第一个)命令
                if (!mIntents.remove(0).equals(mCurrentIntent)) {
                    throw new IllegalStateException("something");
                }
                mCurrentIntent = null;
            }
            SerialExecutor serialExecutor = mTaskExecutor.getBackgroundExecutor();
            // 若无预计执行和待执行命令,则通知SystemAlarmService执行完毕
            if (!mCommandHandler.hasPendingCommands()
                    && mIntents.isEmpty()
                    && !serialExecutor.hasPendingTasks()) {
                if (mCompletedListener != null) {
                    mCompletedListener.onAllCommandsCompleted();
                }
            } else if (!mIntents.isEmpty()) {
               // 若命令集中还有未执行的命令,则继续执行
                processCommand();
            }
        }
    }
    

    此时,若当前命令存在,则从命令集中移除当前命令。若无预计执行和待执行命令,则调用mCompletedListener.onAllCommandsCompleted()通知SystemAlarmService执行完毕;若命令集合中还有未执行命令,则调用processCommand()继续执行。

    而SystemAlarmService.onAllCommandsCompleted()最终会执行stopSelf()停止服务

    SystemJobService

    Service invoked by {@link android.app.job.JobScheduler} to run work tasks.
    服务将被JobScheduler调用来执行工作任务。
    从描述可得SystemJobService与SystemAlarmService职责是一致的,都是“run work tasks”。

    类似地,我们可以猜测SystemJobService执行流程始于onStartJob终于onStopJob。

    SystemJobService.onCreate()获取了WorkManagerImpl实例并将执行监听绑定到(任务)处理器Processor:
    // 还记得WorkManagerImpl是哪里初始化的吗?
    mWorkManagerImpl = WorkManagerImpl.getInstance(getApplicationContext());
    // 记住此处addExecutionListener
    // 记住此处addExecutionListener
    // 记住此处addExecutionListener
    mWorkManagerImpl.getProcessor().addExecutionListener(this);

    当SystemJobService被调用时会执行SystemJobService.onStartJob(),此时会将JobParameters以workId为Key存入工作Map中,并初始化WorkerParameters.RuntimeExtras, 最终调用mWorkManagerImpl.startWork(workSpecId, runtimeExtras)执行任务:

    public boolean onStartJob(@NonNull JobParameters params) {
        // WorkManagerImpl NonNull
        String workSpecId = getWorkSpecIdFromJobParameters(params);
        // workSpecId NonNull
        // 保证mJobParameters 工作参数集只进或只出
        synchronized (mJobParameters) {
            if (mJobParameters.containsKey(workSpecId)) {
               // log
                return false;
            }
            // 存入工作参数
            mJobParameters.put(workSpecId, params);
        }
        // 初始化WorkerParameters.RuntimeExtras
        
        // WorkManagerImpl执行任务
        mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
    }
    

    WorkManagerImpl.startWork后续会执行 mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras)。
    由Processor.startWork()中代码可知,流程末尾onExecuted中遍历执行的executionListener.onExecuted(workSpecId, needsReschedule)会调用SystemJobService.onExecuted():

    @Override
    public void onExecuted(@NonNull String workSpecId, boolean needsReschedule) {
        Logger.get().debug(TAG, String.format("%s executed on JobScheduler", workSpecId));
        JobParameters parameters;
        synchronized (mJobParameters) {
            parameters = mJobParameters.remove(workSpecId);
        }
        if (parameters != null) {
            jobFinished(parameters, needsReschedule);
        }
    }
    

    当然,根据JobService的生命周期,我们可以认定,最后会调用SystemJobService.onStopJob()

    @Override
    public boolean onStopJob(@NonNull JobParameters params) {
        if (mWorkManagerImpl == null) {
            return true;
        }
    
        String workSpecId = getWorkSpecIdFromJobParameters(params);
        if (TextUtils.isEmpty(workSpecId)) {
            return false;
        }
        
        // mJobParameters数据安全
        synchronized (mJobParameters) {
            // 移出任务参数队列(Map)
            mJobParameters.remove(workSpecId);
        }
        // 结束此任务
        mWorkManagerImpl.stopWork(workSpecId);
        return !mWorkManagerImpl.getProcessor().isCancelled(workSpecId);
    }
    

    SystemForegroundService

    在看过前面SystemAlarmService & SystenJobService 后,我本以为
    SystemForegroundService也如前者一般是处理任务执行的,但简单阅读代码后发现并非如此,尽管结构上与前者类似,但其主要作用是给运行任务的服务提权,保证其在前台执行
    SystemAlarmService & SystenJobService相似,SystemForegroundService也是LifecycleService的子类,主要方法有:

    @Override
    public void onCreate() {
        super.onCreate();
        sForegroundService = this;
        initializeDispatcher();
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        // 若正在被关闭,则
        if (mIsShutdown) {
            // 结束旧    Dispatcher 的生命周期
            mDispatcher.onDestroy();
            // 创建新的Dispatcher并设置新的生命周期
            initializeDispatcher();
            mIsShutdown = false;
        }
        if (intent != null) {
            // 告诉Dispatcher 有活儿来了
            mDispatcher.onStartCommand(intent);
        }
        // 如果服务崩溃,我们希望所有未确认的意图都得到重新交付。
        return Service.START_REDELIVER_INTENT;
    }
    @MainThread
    private void initializeDispatcher() {
        mHandler = new Handler(Looper.getMainLooper());
        mNotificationManager = (NotificationManager)
                getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
        mDispatcher = new SystemForegroundDispatcher(getApplicationContext());
        mDispatcher.setCallback(this);
    }
    

    初始化时创建新的SystemForegroundDispatcher绑定并设置新的生命周期,当收到指令被启动时,调用SystemForegroundDispatcher.onStartCommand():

    // SystemForegroundDispatcher#onStartCommand
    void onStartCommand(@NonNull Intent intent) {
        String action = intent.getAction();
        if (ACTION_START_FOREGROUND.equals(action)) {
            handleStartForeground(intent);
            // 调用handleNotify(),它反过来调用start前台()作为处理这个命令的一部分。这对一些原始设备制造商来说很重要。
            handleNotify(intent);
        } else if (ACTION_NOTIFY.equals(action)) {
            handleNotify(intent);
        } else if (ACTION_CANCEL_WORK.equals(action)) {
            handleCancelWork(intent);
        }
    }
    

    当intent (此处intent可视作前面提到的SystemXXXService的意图) 状态处于启动、更新状态时会调用handleNotify(intent),取消时会调用handleCancelWork(intent),
    状态为取消时,则最终会调用mWorkManagerImpl.cancelWorkById(UUID.fromString(workSpecId))取消任务执行。

    我们再来看看handleNotify干了哪些事情吧:

    // SystemForegroundDispatcher#handleNotify
    @MainThread
    private void handleNotify(@NonNull Intent intent) {
        int notificationId = intent.getIntExtra(KEY_NOTIFICATION_ID, 0);
        int notificationType = intent.getIntExtra(KEY_FOREGROUND_SERVICE_TYPE, 0);
        String workSpecId = intent.getStringExtra(KEY_WORKSPEC_ID);
        Notification notification = intent.getParcelableExtra(KEY_NOTIFICATION);
        if (notification != null && mCallback != null) {
            ForegroundInfo info = new ForegroundInfo(
                    notificationId, notification, notificationType);
            // 缓存信息
            mForegroundInfoById.put(workSpecId, info);
            if (TextUtils.isEmpty(mCurrentForegroundWorkSpecId)) {
                // 这是拥有前台生命周期的当前workSpecId.
                mCurrentForegroundWorkSpecId = workSpecId;
                mCallback.startForeground(notificationId, notificationType, notification);
            } else {
                // 更新通知
                mCallback.notify(notificationId, notification);
                // 如有必要,更新前台的通知,使其成为当前所有前台服务类型的联合。
                if (notificationType != FOREGROUND_SERVICE_TYPE_NONE
                        && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    int foregroundServiceType = FOREGROUND_SERVICE_TYPE_NONE;
                    for (Map.Entry<String, ForegroundInfo> entry : mForegroundInfoById.entrySet()) {
                        ForegroundInfo foregroundInfo = entry.getValue();
                        foregroundServiceType |= foregroundInfo.getForegroundServiceType();
                    }
                    //缓存中取出信息
                    ForegroundInfo currentInfo = mForegroundInfoById.get(mCurrentForegroundWorkSpecId);
                    if (currentInfo != null) {
                        mCallback.startForeground(
                                currentInfo.getNotificationId(),
                                foregroundServiceType,
                                currentInfo.getNotification()
                        );
                    }
                }
            }
        }
    }
    

    若当前服务intent是首个,那么就启动SystemForegroundService.startForeground()做周期性递归,而SystemForegroundService.startForeground()实际上是一个空方法,类似于使用了Looper保证SystemForegroundService一直运行:

    // SystemForegroundService#startForeground
    @Override
    public void startForeground(
            final int notificationId,
            final int notificationType,
            @NonNull final Notification notification) {
    
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    startForeground(notificationId, notification, notificationType);
                } else {
                    startForeground(notificationId, notification);
                }
            }
        });
    }
    

    若当前服务intent不是首个,则更新一则通知,并合并前台服务类型,mCallback.notify(notificationId, notification):

    // SystemForegroundService#notify
    @Override
    public void notify(final int notificationId, @NonNull final Notification notification) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                mNotificationManager.notify(notificationId, notification);
            }
        });
    }
    

    SystemForegroundService会在任务启动时启动,且会创建一个类似于Looper的结构保持服务前台持续运行,后续有任务触发时,会发送notification以保证其执行任务的服务可被视作前台服务保持运行。

    广播接收者

    WorkManager使用的重要组件:广播接收者,主要涉及意外停止监听广播ForceStopRunnable.BroadcastReceiver、约束状态监听广播ConstraintProxy.*、启动重新规划服务的广播RescheduleReceiver、代理约束更新广播ConstraintProxyUpdateReceiver、测试诊断广播DiagnosticsReceiver。此类组件为WorkManager稳定运行、重新规划、约束更新提供了支持.

    ForceStopRunnable

    WorkManager is restarted after an app was force stopped. Alarms and Jobs get cancelled when an application is force-stopped. To reschedule, we create a pending alarm that will not survive force stops.
    强制停止应用程序后,WorkManager将重新启动。当应用被强制停止时,Alarms和Jobs将被取消。为了重新安排,我们要创建一个无法通过强制停止的Alarm。

    在介绍ForceStopRunnable.BroadcastReceiver前,我们先简单看一下它的外部类ForceStopRunnable。
    ForceStopRunnable是一个Runnable对象,核心方法是run, 其在WorkManager初始化时调用WorkManagerImpl.internalInit(), 构造并调用了ForceStopRunnable.run():

    @Override
    public void run() {
        // 如果需要,将数据库迁移到不备份的目录。
        WorkDatabasePathHelper.migrateDatabase(mContext);
        // 清除属于WorkManager的无效作业,以及由于应用程序崩溃(运行状态)而可能被中断的作业。
        try {
            boolean needsScheduling = cleanUp();
            if (shouldRescheduleWorkers()) {
                mWorkManager.rescheduleEligibleWork();
                // 将WorkManager标记为已迁移。
                mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
            } else if (isForceStopped()) {
                // 异常退出,重新规划任务
                mWorkManager.rescheduleEligibleWork();
            } else if (needsScheduling) {
               // 发现未完成任务,规划它们
                Schedulers.schedule(
                        mWorkManager.getConfiguration(),
                        mWorkManager.getWorkDatabase(),
                        mWorkManager.getSchedulers());
            }
            mWorkManager.onForceStopRunnableCompleted();
        } catch (SQLiteCantOpenDatabaseException
                | SQLiteDatabaseCorruptException
                | SQLiteAccessPermException exception) {
            // ForceStopRunnable通常是访问数据库(或应用程序的内部数据目录)的第一件事。
            // 这意味着奇怪的PackageManager错误被归咎于ForceStopRunnable,这是不幸的。
            // 这为开发人员提供了更好的错误消息。
            String message =
                    "The file system on the device is in a bad state. WorkManager cannot access "
                            + "the app's internal data store.";
            Logger.get().error(TAG, message, exception);
            throw new IllegalStateException(message, exception);
        }
    }
    

    我们发现ForceStopRunable主要职责是处理异常中断后对WorkManager中任务进行清理和重新规划,run()主要干了3件事:

    如果需要,将数据库迁移到不备份的目录
    取消无效的JobScheduler作业/重新调度以前正在运行的作业
    针对异常中断时WorkManager的状态进行不同的调度操作
    那么他是如何判断是否是强制中断的呢,我们可以看下强制中断判断代码isForceStopped():

    @VisibleForTesting
    public boolean isForceStopped() {
        // 当应用程序在Eclair MR1强制停止启动时,Alarm被取消。
        // 在N-MR1中引入了强制停止作业的取消(SDK 25)。
        // 尽管API 23、24可能是安全的,但oem可能会选择采用不同的方法。
        PendingIntent pendingIntent = getPendingIntent(mContext, FLAG_NO_CREATE);
        if (pendingIntent == null) {
            setAlarm(mContext);
            return true;
        } else {
            return false;
        }
    }
    

    通过判断闹钟广播是否存在来确定应用是否被强行停止,若不存在即闹钟Alarm被取消即是强制中断,此时将重新设置一个闹钟Alarm;

    让我们来看看上文中的ForceStopRunnable.BroadcastReceiver闹钟广播。
    BroadcastReceiver

    A {@link android.content.BroadcastReceiver} which takes care of recreating the long lived alarm which helps track force stops for an application. This is the target of the alarm set by ForceStopRunnable in {@link #setAlarm(Context)}. 一个{@link android.content.BroadcastReceiver}负责重新创建长寿命的Alarm,这有助于跟踪应用程序的强制停止。这是在{@link #setAlarm(Context)}中由forceoprunnable设置的Alarm目标。

    从描述可知,此广播的主要目的是负责创建长期存活的闹钟,用以追踪应用程序的强制停止。

    其代码相对简单,若收到action为ACTION_FORCE_STOP_RESCHEDULE的广播,则设置一个长达十年的可唤醒设备的闹钟,然后再来一次(发送action为ACTION_FORCE_STOP_RESCHEDULE的广播)。

    // ForceStopRunnable
    
    private static PendingIntent getPendingIntent(Context context, int flags) {
        Intent intent = getIntent(context);
        // 发送广播的PendingIntent
        return PendingIntent.getBroadcast(context, ALARM_ID, intent, flags);
    }
    
    @VisibleForTesting
    static Intent getIntent(Context context) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(context, ForceStopRunnable.BroadcastReceiver.class));
        // 广播intent
        intent.setAction(ACTION_FORCE_STOP_RESCHEDULE);
        return intent;
    }
    
    static void setAlarm(Context context) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        //使用FLAG_UPDATE_CURRENT,因为我们只需要这个Alarm的一次实例
        PendingIntent pendingIntent = getPendingIntent(context, FLAG_UPDATE_CURRENT);
        // 设置执行时间为十年
        long triggerAt = System.currentTimeMillis() + TEN_YEARS;
        if (alarmManager != null) {
            if (Build.VERSION.SDK_INT >= 19) {
                // RTC_WAKEUP:绝对时间可唤醒类型,十年后,干啥
                alarmManager.setExact(RTC_WAKEUP, triggerAt, pendingIntent);
            } else {
                alarmManager.set(RTC_WAKEUP, triggerAt, pendingIntent);
            }
        }
    }
    
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static class BroadcastReceiver extends android.content.BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // Our alarm somehow got triggered, so make sure we reschedule it.  This should really never happen because we set it so far in the future.
            // 我们的闹钟不知怎么被触发了,所以一定要重新安排时间。这是不应该发生的,因为我们将它设置在遥远的未来(嘿,十年后再见)。
            if (intent != null) {
                String action = intent.getAction();
                if (ACTION_FORCE_STOP_RESCHEDULE.equals(action)) {
                    Logger.get().verbose(
                            TAG,
                            "Rescheduling alarm that keeps track of force-stops.");
                    ForceStopRunnable.setAlarm(context);
                }
            }
        }
    }
    

    有前文可知,应用启动时会初始化WorkManager,WorkManager初始化则会执行ForceStopRunnable.run(), 此时一般会调用setAlarm(), 创建Alarm,极端情况下会存在应用存活十年的情况,此BroadcastReceiver即是用于处理这种情况的,当十年后又收到了这个广播,那么我们在创建一个十年期的闹钟Alarm.

    ConstraintProxy

    BatteryNotLowProxy
    BatteryChargingProxy
    StorageNotLowProxy
    NetworkStateProxy

    上述BatteryNotLowProxy、BatteryChargingProxy、StorageNotLowProxy、NetworkStateProxy均是ConstraintProxy的子类,实现均一致,仅有注册action不同,用以针对不同action的系统广播更新约束状态, 此处一并分析之。

    以NetworkStateProxy为例,当网络状态变化,应用收到"android.net.conn.CONNECTIVITY_CHANGE"广播,触发NetworkStateProxy.onReceive():

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

    调用CommandHandler.createConstraintsChangedIntent(context)调起SystemAlarmService, 其intent的action为"ACTION_CONSTRAINTS_CHANGED":

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

    最终会调用到CommandHandler.onHandleIntent():

    void onHandleIntent(@NonNull Intent intent, int startId, @NonNull SystemAlarmDispatcher dispatcher) {
            String action = intent.getAction();
    
            if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
                handleConstraintsChanged(intent, startId, dispatcher);
            } 
            // 其他
    }
    
    private void handleConstraintsChanged(
            @NonNull Intent intent, int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
        ConstraintsCommandHandler changedCommandHandler =
                new ConstraintsCommandHandler(mContext, startId, dispatcher);
        changedCommandHandler.handleConstraintsChanged();
    }
    

    此时intent的action为"ACTION_CONSTRAINTS_CHANGED",最终会调用到ConstraintsCommandHandler.handleConstraintsChanged():

    void handleConstraintsChanged() {
        List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
                .workSpecDao()
                .getScheduledWork();
    
        //更新约束代理以潜在地禁用先前完成的WorkSpecs的代理。
        ConstraintProxy.updateAll(mContext, candidates);
    
        // 这需要在每个约束控制器中填充匹配的WorkSpec id。标记正在更新这些工作的状态
        mWorkConstraintsTracker.replace(candidates);
    
        List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
        // 筛选候选人应该已经规划好了。
        long now = System.currentTimeMillis();
        for (WorkSpec workSpec : candidates) {
            String workSpecId = workSpec.id;
            long triggerAt = workSpec.calculateNextRunTime();
            // 时间条件符合且无约束或约束已符合,则加入符合条件集合
            if (now >= triggerAt && (!workSpec.hasConstraints()
                    || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
                eligibleWorkSpecs.add(workSpec);
            }
        }
    
        for (WorkSpec workSpec : eligibleWorkSpecs) {
            String workSpecId = workSpec.id;
            Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
            // 告诉SystemAlarmDispatcher这个工作已经符合条件了,请给安排上
            mDispatcher.postOnMainThread(
                    new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
        }
        // 重置正在更新这些工作状态的标记
        mWorkConstraintsTracker.reset();
    }
    

    此方法先获取了规划中的工作生成集合,调用了ConstraintProxy.updateAll()方法更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,遍历判断每个WorkSpec是否符合条件,遍历符合约束条件的列表,告诉SystemAlarmDispatcher添加任务,若SystemAlarmDispatcher中无任务则分发执行任务(还记得processCommand()是一个环状的轮询吗?)。

    SystemAlarmDispatcher.AddRunnable()后续调用SystemAlarmDispatcher.add()在SystemAlarmService中也有
    SystemAlarmDispatcher.AddRunnable() -> SystemAlarmDispatcher.add()

    // SystemAlarmDispatcher
    public boolean add(@NonNull final Intent intent, final int startId) {
    
        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            return false;
        }
    
        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) {
                processCommand();
            }
        }
        return true;
    }
    
    static class AddRunnable implements Runnable {
        @Override
        public void run() {
            mDispatcher.add(mIntent, mStartId);
        }
    }
    

    ConstraintProxyUpdateReceiver
    上面讲到ConstraintsCommandHandler.handleConstraintsChanged()第一步会调用ConstraintProxy.updateAll(mContext, candidates)最终会发送广播给ConstraintProxyUpdateReceiver更新约束代理以潜在地禁用先前完成的WorkSpecs的代理,我们简单过一下。

    static void updateAll(Context context, List<WorkSpec> workSpecs) {
        boolean batteryNotLowProxyEnabled = false;
        boolean batteryChargingProxyEnabled = false;
        boolean storageNotLowProxyEnabled = false;
        boolean networkStateProxyEnabled = false;
    // 查找每个WorkSpec(是否有)约束条件状态
        for (WorkSpec workSpec : workSpecs) {
            Constraints constraints = workSpec.constraints;
            batteryNotLowProxyEnabled |= constraints.requiresBatteryNotLow();
            batteryChargingProxyEnabled |= constraints.requiresCharging();
            storageNotLowProxyEnabled |= constraints.requiresStorageNotLow();
            networkStateProxyEnabled |= constraints.getRequiredNetworkType() != NOT_REQUIRED;
    // 知道需要全部广播接收者,就不用判断了
            if (batteryNotLowProxyEnabled && batteryChargingProxyEnabled
                    && storageNotLowProxyEnabled && networkStateProxyEnabled) {
                break;
            }
        }
    
        Intent updateProxyIntent =
                ConstraintProxyUpdateReceiver.newConstraintProxyUpdateIntent(
                        context,
                        batteryNotLowProxyEnabled,
                        batteryChargingProxyEnabled,
                        storageNotLowProxyEnabled,
                        networkStateProxyEnabled);
    
        // ConstraintProxies are being updated via a separate broadcast receiver.
        // For more information on why we do this look at b/73549299
        context.sendBroadcast(updateProxyIntent);
    }
    

    这里先是判断需要哪些广播接收者,会发送广播给ConstraintProxyUpdateReceiver,最终会调用:

    @Override
    public void onReceive(@NonNull final Context context, @Nullable final Intent intent) {
        String action = intent != null ? intent.getAction() : null;
        if (!ACTION.equals(action)) {
    
        } else {
            final PendingResult pendingResult = goAsync();
            WorkManagerImpl workManager = WorkManagerImpl.getInstance(context);
            TaskExecutor taskExecutor = workManager.getWorkTaskExecutor();
            taskExecutor.executeOnBackgroundThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // 在后台线程上执行此操作,因为使用PackageManager来启用或禁用代理涉及到对文件系统的写操作。
                        boolean batteryNotLowProxyEnabled = intent.getBooleanExtra(
                                KEY_BATTERY_NOT_LOW_PROXY_ENABLED, false);
                        boolean batteryChargingProxyEnabled = intent.getBooleanExtra(
                                KEY_BATTERY_CHARGING_PROXY_ENABLED, false);
                        boolean storageNotLowProxyEnabled = intent.getBooleanExtra(
                                KEY_STORAGE_NOT_LOW_PROXY_ENABLED, false);
                        boolean networkStateProxyEnabled = intent.getBooleanExtra(
                                KEY_NETWORK_STATE_PROXY_ENABLED, false);
    
                        Logger.get().debug(
                                TAG,
                                String.format("Updating proxies: BatteryNotLowProxy enabled (%s), "
                                                + "BatteryChargingProxy enabled (%s), "
                                                + "StorageNotLowProxy (%s), "
                                                + "NetworkStateProxy enabled (%s)",
                                        batteryNotLowProxyEnabled,
                                        batteryChargingProxyEnabled,
                                        storageNotLowProxyEnabled,
                                        networkStateProxyEnabled));
    
                        PackageManagerHelper.setComponentEnabled(context, BatteryNotLowProxy.class,
                                batteryNotLowProxyEnabled);
                        PackageManagerHelper.setComponentEnabled(context,
                                BatteryChargingProxy.class,
                                batteryChargingProxyEnabled);
                        PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class,
                                storageNotLowProxyEnabled);
                        PackageManagerHelper.setComponentEnabled(context, NetworkStateProxy.class,
                                networkStateProxyEnabled);
                    } finally {
                        pendingResult.finish();
                    }
                }
            });
        }
    }
    

    主要执行了获取前文提供的需要哪些约束广播接收器,调用PackageManagerHelper.setComponentEnabled(context, StorageNotLowProxy.class, storageNotLowProxyEnabled)更改广播接收者注册信息:

    /**
     * 使用{@link PackageManager} 去开/关manifest声明的组件
     *
     * @param context {@link Context}
     * @param klazz The class of component
     * @param enabled {@code true} 开关组件
     */
    public static void setComponentEnabled(
            @NonNull Context context,
            @NonNull Class<?> klazz,
            boolean enabled) {
        try {
            PackageManager packageManager = context.getPackageManager();
            ComponentName componentName = new ComponentName(context, klazz.getName());
            packageManager.setComponentEnabledSetting(componentName,
                    enabled
                            ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                            : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                    PackageManager.DONT_KILL_APP);
    
        } catch (Exception exception) {
        }
    }
    

    RescheduleReceiver

    Reschedules alarms on BOOT_COMPLETED and other similar scenarios.
    在BOOT_COMPLETED和其他类似场景下重新调度Alarm。

    DiagnosticsReceiver

    The {@link android.content.BroadcastReceiver} which dumps out useful diagnostics information. 输出有用的诊断信息

    相关文章

      网友评论

          本文标题:WorkManager

          本文链接:https://www.haomeiwen.com/subject/chwwiltx.html