JetPack之WorkManager学习记录


WorkManager主要是用来处理后台任务的,并且保证这些任务可以在App退出时在适当时机执行,实现这一目标主要是依靠:

  1. 使用Room将任务以WorkSec为单元存储在本地;
  2. 使用反射将Work类实例化,并执行其中定义的任务,保证了App退出时任务依然能得到执行。

其中后台任务分为两类:

  1. 立即执行的任务;
  2. 限制的任务。

WorkManager使用及介绍参考Exploring Jetpack: Scheduling tasks with Work Manager,WorkManager的原理参考Android Jetpack之WorkManager源码分析,下面主要记录几点细节。

1. 任务的启动

开始任务是通过调用Processor类的startWork()方法实现:

//Processor.java
public boolean startWork(
         @NonNull String id,
         @Nullable 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)) {
             return false;
         }

         workWrapper =
                 new WorkerWrapper.Builder(
                         mAppContext,
                         mConfiguration,
                         mWorkTaskExecutor,
                         this,
                         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);
     return true;
 }

这段代码构造了一个WorkerWrapper对象,然后获取WorkerWrapper任务执行完的结果的引用future,并对该future添加监听:

//AbstractFuture.java
@Override
    public final void addListener(Runnable listener, Executor executor) {
        checkNotNull(listener);
        checkNotNull(executor);
        Listener oldHead = listeners;
        if (oldHead != Listener.TOMBSTONE) {
            Listener newNode = new Listener(listener, executor);
            do {
                newNode.next = oldHead;
                if (ATOMIC_HELPER.casListeners(this, oldHead, newNode)) {
                    return;
                }
                oldHead = listeners; // re-read
            } while (oldHead != Listener.TOMBSTONE);
        }
        // If we get here then the Listener TOMBSTONE was set, which means the future is done, call
        // the listener.
        executeListener(listener, executor);
    }

将一个listener构造程一个Listener对象newNode,并添加到AbstractFuture中的listeners成员变量指向的链表头部,并且将listeners指向新的newNode。在添加过程中有可能该WorkerWrapper已经执行完毕(重复添加唯一型任务),原来的监听对象oldHead会被设置为Listener.TOMBSTONE,此时while循环结束,并立即执行新添加的listener。Processor类的startWork()方法接下来就会执行该WorkerWrapper对象,其run()方法会执行:

@Override
public void run() {
 mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
 mWorkDescription = createWorkDescription(mTags);
 runWorker();
}

private void runWorker() {
    if (tryCheckForInterruptionAndResolve()) {
        return;
    }

    mWorkDatabase.beginTransaction();
    //1. 执行任务条件判决
    try {
    	//1.1 数据库中没有该mWorkSpecId则不执行
        mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
        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.
        //1.2 数据库中包含该任务,但是该任务的状态不是ENQUEUED也不执行
        if (mWorkSpec.state != ENQUEUED) {
            resolveIncorrectStatus();
            mWorkDatabase.setTransactionSuccessful();
            Logger.get().debug(TAG,
                    String.format("%s is not in ENQUEUED state. Nothing more to do.",
                            mWorkSpec.workerClassName));
            return;
        }

        // Case 1:
        // Ensure that Workers that are backed off are only executed when they are supposed to.
        // GreedyScheduler can schedule WorkSpecs that have already been backed off because
        // it is holding on to snapshots of WorkSpecs. So WorkerWrapper needs to determine
        // if the ListenableWorker is actually eligible to execute at this point in time.

        // Case 2:
        // On API 23, we double scheduler Workers because JobScheduler prefers batching.
        // So is the Work is periodic, we only need to execute it once per interval.
        // Also potential bugs in the platform may cause a Job to run more than once.
		//1.3 对于定期任务和多次执行任务,除第一次外未到执行时间的也不执行
        if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
            long now = System.currentTimeMillis();
            // Allow first run of a PeriodicWorkRequest
            // to go through. This is because when periodStartTime=0;
            // calculateNextRunTime() always > now.
            // For more information refer to b/124274584
            boolean isFirstRun = mWorkSpec.periodStartTime == 0;
            if (!isFirstRun && 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()) {
    	//2.1 对于定期任务,不需要合并
        input = mWorkSpec.input;
    } else {
    	//2.2. 对于链式任务,需要对父任务的outpute进行合并,并做为当前任务的inpute
        InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
        String inputMergerClassName = mWorkSpec.inputMergerClassName;
        InputMerger inputMerger =
                inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
        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);
    }

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

    // Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override
    // in test mode.
    //3 用workerClassName反射创建一个Work实例
    if (mWorker == null) {
        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()) {
        Logger.get().error(TAG,
                String.format("Received an already-used Worker %s; WorkerFactory should return "
                        + "new instances",
                        mWorkSpec.workerClassName));
        setFailedAndResolve();
        return;
    }
    mWorker.setUsed();

    // 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();
        // Call mWorker.startWork() on the main thread.
        //4.1 在主线程开启该mWorker任务
        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();
                            future.setFuture(mInnerFuture);
                        } catch (Throwable e) {
                            future.setException(e);
                        }

                    }
                });

        // Avoid synthetic accessors.
        //4.2 为该mWorker的执行加过添加监听
        final String workDescription = mWorkDescription;
        future.addListener(new Runnable() {
            @Override
            @SuppressLint("SyntheticAccessor")
            public void run() {
                try {
                    // If the ListenableWorker returns a null result treat it as a failure.
                    ListenableWorker.Result result = future.get();
                    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;
                    }
                } 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();
    }
}		

runWorker()的逻辑比较复杂,具体分析见代码注释,这里看一下"3 用workerClassName反射创建一个Work实例"的过程:

//WorkerFactory.java
public final @Nullable ListenableWorker createWorkerWithDefaultFallback(
         @NonNull Context appContext,
         @NonNull String workerClassName,
         @NonNull WorkerParameters workerParameters) {

     ListenableWorker worker = createWorker(appContext, workerClassName, workerParameters);
     if (worker == null) {
         // Fallback to reflection
         Class<? extends ListenableWorker> clazz = null;
         try {
             clazz = Class.forName(workerClassName).asSubclass(ListenableWorker.class);
         } catch (ClassNotFoundException e) {
             Logger.get().error(TAG, "Class not found: " + workerClassName);
         }
         if (clazz != null) {
             try {
                 Constructor<? extends ListenableWorker> constructor =
                         clazz.getDeclaredConstructor(Context.class, WorkerParameters.class);
                 worker = constructor.newInstance(
                         appContext,
                         workerParameters);
             } catch (Exception e) {}
         }
     }
     return worker;
 }

对于“2.2 链式任务input的合并”中,初始化时指定的InputMergerFactory是默认的,其createInputMerger()方法返回null,因此这里会采用反射方式将inputMergerClassName实例化,inputMergerClassName对应的类为OverwritingInputMerger,其合并方法为:

//OverwritingInputMerger.java
@Override
public @NonNull Data merge(@NonNull List<Data> inputs) {
    Data.Builder output = new Data.Builder();
    Map<String, Object> mergedValues = new HashMap<>();

    for (Data input : inputs) {
        mergedValues.putAll(input.getKeyValueMap());
    }

    output.putAll(mergedValues);
    return output.build();
}

在WorkWrapper类中调用mConfiguration的getWorkerFactory()方法来获取用来创建Work实例的工厂对象WorkerFactory,在初始化的时候mConfiguration中的mWorkerFactory通过 WorkerFactory.getDefaultWorkerFactory()方法初始化。回到WorkerFactory类的createWorkerWithDefaultFallback()方法中,该默认工厂类的createWorker()方法返回null,因此上面代码worker == null为true。接下来通过反射workerClassName指定的ListenableWorker子类来实例化一个Work对象,所以当该App退出是依旧能获得Work实例,也就能保证Work中的任务得到执行。

2. OneTimeWorkRequest的链式组合

WorkRequest是一个虚类,用于指定Work执行所需的参数,其有两个子类:OneTimeWorkRequest和PeriodicWorkRequest。OneTimeWorkRequest是为单次任务指定参数,OneTimeWorkRequest可以将其所指代的工作进行链式组合。PeriodicWorkRequest是为重复任务指定参数的,不可以组合,在上一节启动任务时对于重复型任务是没有对inpute参数进行合并的。看一下例子:

 * <pre>
 *     A       C
 *     |       |
 *     B       D
 *     |       |
 *     +-------+
 *         |
 *         E    </pre>
 * 
* <pre>
     * {@code
     *  WorkContinuation left = workManager.beginWith(A).then(B);
     *  WorkContinuation right = workManager.beginWith(C).then(D);
     *  WorkContinuation final = WorkContinuation.combine(Arrays.asList(left, right)).then(E);
     *  final.enqueue();}</pre>

WorkManager的beginWith()方法和WorkContinuationImpl的then()方法都创建了一个WorkContinuationImpl对象,不同之处是前者的paraent参数为null,看一下then()方法:

//WorkContinuationImpl.java
@Override
public @NonNull WorkContinuation then(@NonNull List<OneTimeWorkRequest> work) {
    if (work.isEmpty()) {
        return this;
    } else {
        return new WorkContinuationImpl(mWorkManagerImpl,
                mName,
                ExistingWorkPolicy.KEEP,
                work,
                Collections.singletonList(this));
    }
}
//WorkContinuationImpl的构造方法
WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
            String name,
            ExistingWorkPolicy existingWorkPolicy,
            @NonNull List<? extends WorkRequest> work,
            @Nullable List<WorkContinuationImpl> parents) {
        mWorkManagerImpl = workManagerImpl;
        mName = name;
        mExistingWorkPolicy = existingWorkPolicy;
        mWork = work;
        mParents = parents;
        mIds = new ArrayList<>(mWork.size());
        mAllIds = new ArrayList<>();
        if (parents != null) {
            for (WorkContinuationImpl parent : parents) {
                mAllIds.addAll(parent.mAllIds);
            }
        }
        for (int i = 0; i < work.size(); i++) {
            String id = work.get(i).getStringId();
            mIds.add(id);
            mAllIds.add(id);
        }
    }

通过then()方法创建一个WorkContinuationImpl对象,并先将父节点列表parents中所有父亲WorkContinuationImpl中的mAllIds保存在自己的mAllIds链表中,再将work链表中所有工作的id保存在mAllIds链表中。WorkContinuation的combine()方法最终是调用的WorkContinuationImpl的combineInternal()方法,最终也是重现创建一个WorkContinuationImpl对象:

    @Override
    protected @NonNull WorkContinuation combineInternal(
            @NonNull List<WorkContinuation> continuations) {
        OneTimeWorkRequest combinedWork =
                new OneTimeWorkRequest.Builder(CombineContinuationsWorker.class)
                        .setInputMerger(ArrayCreatingInputMerger.class)
                        .build();

        List<WorkContinuationImpl> parents = new ArrayList<>(continuations.size());
        for (WorkContinuation continuation : continuations) {
            parents.add((WorkContinuationImpl) continuation);
        }

        return new WorkContinuationImpl(mWorkManagerImpl,
                null,
                ExistingWorkPolicy.KEEP,
                Collections.singletonList(combinedWork),
                parents);
    }

这里构造了一个combinedWork做为所有父任务的子任务,该子任务用于合并父任务执行的结果。结合1.1节中立即任务的启动过程可知,没有指定InputMergerFactory的WorkRequest是使用OverwritingInputMerger类来实现合并的,而通过cobine()方法创建的CombineContinuationsWorker的WorkRequest是使用ArrayCreatingInputMerger来实现合并的,看一下其合并实现:

    public @NonNull Data merge(@NonNull List<Data> inputs) {
        Data.Builder output = new Data.Builder();
        Map<String, Object> mergedValues = new HashMap<>();

        for (Data input : inputs) {
            for (Map.Entry<String, Object> entry : input.getKeyValueMap().entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                Class<?> valueClass = value.getClass();
                Object mergedValue;

                Object existingValue = mergedValues.get(key);
                if (existingValue == null) {
                    // First time encountering this key.
                    if (valueClass.isArray()) {
                        // Arrays carry over as-is.
                        mergedValue = value;
                    } else {
                        // Primitives get turned into size 1 arrays.
                        mergedValue = createArrayFor(value);
                    }
                } else {
                    // We've encountered this key before.
                    Class<?> existingValueClass = existingValue.getClass();

                    if (existingValueClass.equals(valueClass)) {
                        // The classes match; we can merge.
                        if (existingValueClass.isArray()) {
                            mergedValue = concatenateArrays(existingValue, value);
                        } else {
                            mergedValue = concatenateNonArrays(existingValue, value);
                        }
                    } else if (existingValueClass.isArray()
                            && existingValueClass.getComponentType().equals(valueClass)) {
                        // We have an existing array of the same type.
                        mergedValue = concatenateArrayAndNonArray(existingValue, value);
                    } else if (valueClass.isArray()
                            && valueClass.getComponentType().equals(existingValueClass)) {
                        // We have an existing array of the same type.
                        mergedValue = concatenateArrayAndNonArray(value, existingValue);
                    } else {
                        throw new IllegalArgumentException();
                    }
                }

                mergedValues.put(key, mergedValue);
            }
        }

        output.putAll(mergedValues);
        return output.build();
    }

因为涉及许多边界条件,合并的逻辑很复杂,既要考虑到去重,又要考虑到数据类型。

3. 工作调度

首先看一下调度器的创建:

//WorkManagerImpl.java
public @NonNull List<Scheduler> createSchedulers(Context context, TaskExecutor taskExecutor) {
   return Arrays.asList(
           Schedulers.createBestAvailableBackgroundScheduler(context, this),
           // Specify the task executor directly here as this happens before internalInit.
           // GreedyScheduler creates ConstraintTrackers and controllers eagerly.
           new GreedyScheduler(context, taskExecutor, this));
}

//Schedulers.java
static Scheduler createBestAvailableBackgroundScheduler(
            @NonNull Context context,
            @NonNull WorkManagerImpl workManager) {

        Scheduler scheduler;

        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
            scheduler = new SystemJobScheduler(context, workManager);
            setComponentEnabled(context, SystemJobService.class, true);
            Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
        } else {
            scheduler = tryCreateGcmBasedScheduler(context);
            if (scheduler == null) {
                scheduler = new SystemAlarmScheduler(context);
                setComponentEnabled(context, SystemAlarmService.class, true);
                Logger.get().debug(TAG, "Created SystemAlarmScheduler");
            }
        }
        return scheduler;
    }

这里忽略掉GcmBasedScheduler,因为必须要安装GooglePlay才能成功创建该对象。所以这里会创建三种调度器:SystemJobScheduler、SystemAlarmScheduler和GreedyScheduler。对这三种调度器的源码分析,WorkManager之SystemJobScheduler、SystemAlarmScheduler、GreedyScheduler三大调度器,源码流程详细解读这篇文章分析得很详细。

3.1. SystemAlarmScheduler调度过程

其调度过程如图所示:
在这里插入图片描述
从上图可以看到最核心的类是SystemAlarmService,该类是链接SystemAlarmScheduler与AlarmManager的桥梁:SystemAlarmScheduler调度任务是通过开启一个SystemAlarmService服务来实现的,调度过程是通过SystemAlarmDispatcher来控制的,调度的实现者是AlarmManager,AlarmManager执行任务时依然是开启一个SystemAlarmService。下面简要看一下具体过程:
先看一下SystemAlarmScheduler调度开始函数:

//SystemAlarmScheduler.java
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);
}

//CommandHandler.java
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;
}

开启了一个SystemAlarmService服务,看一下onStartCommand()方法:

public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    if (mIsShutdown) {
        // 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;
}

主要是调用类型为SystemAlarmDispatcher的mDispatcher实例的add()方法:

public boolean add(@NonNull final Intent intent, final int startId) {
    assertMainThread();
    String action = intent.getAction();
    // 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;
}

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() {
            @Override
            public void run() {
                synchronized (mIntents) {
                    mCurrentIntent = mIntents.get(0);
                }

                if (mCurrentIntent != null) {
                    final String action = mCurrentIntent.getAction();
                    final int startId = mCurrentIntent.getIntExtra(KEY_START_ID,
                            DEFAULT_START_ID);
                    final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                            mContext,
                            String.format("%s (%s)", action, startId));
                    try {
                        wakeLock.acquire();
                        mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                SystemAlarmDispatcher.this);
                    } catch (Throwable throwable) {
                    }  finally {
                        wakeLock.release();
                        // Check if we have processed all commands
                        postOnMainThread(
                                new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
                    }
                }
            }
        });
    } finally {
        processCommandLock.release();
    }
}

这里主要是将参数intent添加到类型为List<Intent>的成员mIntents中后然后执行函数processCommand()。在processCommand()函数中先拿到mIntents的第一个元素mCurrentIntent,然后用wakeLock同步加锁执行mCommandHandler对象的onHandleIntent()方法,最后在异步执行一个父类为Runnable的DequeueAndCheckForCompletion对象。看一下onHandleIntent()方法:

//CommandHandler.java
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)) {
            
        } 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 {
                Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
            }
        }
    }
}

这里即是根据intent中存储在action中的命令来决定执行对应的方法。这里要确定该action,回忆一下SystemAlarmScheduler的scheduleWorkSpec()方法中是调用CommandHandler的createScheduleWorkIntent()方法来生成一个Intent对象的,其action正好是ACTION_SCHEDULE_WORK。因此这里会调用handleScheduleWorkIntent()方法:

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

    Bundle extras = intent.getExtras();
    String workSpecId = extras.getString(KEY_WORKSPEC_ID);
    Logger.get().debug(TAG, String.format("Handling schedule work for %s", workSpecId));

    WorkManagerImpl workManager = dispatcher.getWorkManager();
    WorkDatabase workDatabase = workManager.getWorkDatabase();
    workDatabase.beginTransaction();

    try {
        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
        WorkSpec workSpec = workSpecDao.getWorkSpec(workSpecId);
        if (workSpec == null) {
            return;
        } else if (workSpec.state.isFinished()) {
            // We need to schedule the Alarms, even when the Worker is RUNNING. This is because
            // if the process gets killed, the Alarm is necessary to pick up the execution of
            // Work.
            return;
        }

        // Note: The first instance of PeriodicWorker getting scheduled will set an alarm in the
        // past. This is because periodStartTime = 0.
        long triggerAt = workSpec.calculateNextRunTime();

        if (!workSpec.hasConstraints()) {
            Alarms.setAlarm(mContext, dispatcher.getWorkManager(), workSpecId, triggerAt);
        } else {
            Alarms.setAlarm(
                    mContext,
                    dispatcher.getWorkManager(),
                    workSpecId,
                    triggerAt);

            // Schedule an update for constraint proxies
            // This in turn sets enables us to track changes in constraints
            Intent constraintsUpdate = CommandHandler.createConstraintsChangedIntent(mContext);
            dispatcher.postOnMainThread(
                    new SystemAlarmDispatcher.AddRunnable(
                            dispatcher,
                            constraintsUpdate,
                            startId));
        }

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

这里主要是通过调用Alarms类的静态方法setAlarm(),进而调用其静态方法setExactAlarm()获取AlarmManager来执行定时事物:

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

    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent delayMet = CommandHandler.createDelayMetIntent(context, workSpecId);
    PendingIntent pendingIntent = PendingIntent.getService(
            context, alarmId, delayMet, PendingIntent.FLAG_UPDATE_CURRENT);
    if (alarmManager != null) {
        if (Build.VERSION.SDK_INT >= 19) {
            alarmManager.setExact(RTC_WAKEUP, triggerAtMillis, pendingIntent);
        } else {
            alarmManager.set(RTC_WAKEUP, triggerAtMillis, pendingIntent);
        }
    }
}

这里通过调用CommandHandler的静态方法createDelayMetIntent()创建一个Intent类型的delayMet对象,进而封装成一个PendingIntent对象传递给AlarmManager,在时间到达设置时间时会执行该delayMet,也是通过其action来决定执行哪一个方法的,看一个该action:

//CommandHandler.java
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;
}

可以看到依然是开启SystemAlarmService服务来处理命令。回到onHandleIntent()方法中,这里action为ACTION_DELAY_MET,因此会执行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);
        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);
            mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
            delayMetCommandHandler.handleProcessWork();
        } else {
            Logger.get().debug(TAG,
                    String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
                            workSpecId));
        }
    }
}

这里主要实例化了一个类型为DelayMetCommandHandler的对象delayMetCommandHandler,并调用其handleProcessWork()来处理工作:

void handleProcessWork() {
    mWakeLock = WakeLocks.newWakeLock(
            mContext,
            String.format("%s (%s)", mWorkSpecId, mStartId));
    mWakeLock.acquire();

    WorkSpec workSpec = mDispatcher.getWorkManager()
            .getWorkDatabase()
            .workSpecDao()
            .getWorkSpec(mWorkSpecId);

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

该函数先通过mWorkSpecId从数据库中拿到对应的workSpec,然后看其是否有约束,如果没有则调用
onAllConstraintsMet()方法执行任务,如果有则让mWorkConstraintsTracker来对该workSpec对象进行跟踪,当约束条件得到满足时会再调用onAllConstraintsMet()方法执行任务。下面分别看一下两个过程。

3.1.1 无约束时启动任务过程

当满足约束条件时,调用onAllConstraintsMet()函数来处理:

//DelayMetCommandHandler.java
@Override
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;
    }

    synchronized (mLock) {
        if (mCurrentState == STATE_INITIAL) {
            mCurrentState = STATE_START_REQUESTED;
            // Constraints met, schedule execution
            // Not using WorkManagerImpl#startWork() here because we need to know if the
            // processor actually enqueued the work here.
            boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);

            if (isEnqueued) {
                // setup timers to enforce quotas on workers that have
                // been enqueued
                mDispatcher.getWorkTimer()
                        .startTimer(mWorkSpecId, 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();
            }
        } else {
        }
    }
}

这里先是获取SystemAlarmDispatcher的一个局部对象Processor的startWork()方法来启动任务,然后开启一个十分钟的定时器,当时间超出后如该任务还未结束则会强制结束该任务,若该任务已经添加到执行队列中了则会清除任务。

public boolean startWork(
        @NonNull String id,
        @Nullable 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 (isEnqueued(id)) {
            return false;
        }

        workWrapper =
                new WorkerWrapper.Builder(
                        mAppContext,
                        mConfiguration,
                        mWorkTaskExecutor,
                        this,
                        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);
    return true;
}

这里通过id构建了Runnable的子类WorkerWrapper对象workWrapper,然后获取后台线程来执行该任务。然后该WorkerWrapper的run()方法就会执行,会进一步调用runWorker()方法:

private void runWorker() {
        if (tryCheckForInterruptionAndResolve()) {
            return;
        }

        mWorkDatabase.beginTransaction();
        try {
            mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
            //...

            if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
                long now = System.currentTimeMillis();
                // Allow first run of a PeriodicWorkRequest
                // to go through. This is because when periodStartTime=0;
                // calculateNextRunTime() always > now.
                // For more information refer to b/124274584
                boolean isFirstRun = mWorkSpec.periodStartTime == 0;
                if (!isFirstRun && now < mWorkSpec.calculateNextRunTime()) {
                    resolve(true);
                    mWorkDatabase.setTransactionSuccessful();
                    return;
                }
            }
            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;
        } else {
            InputMergerFactory inputMergerFactory = mConfiguration.getInputMergerFactory();
            String inputMergerClassName = mWorkSpec.inputMergerClassName;
            InputMerger inputMerger =
                    inputMergerFactory.createInputMergerWithDefaultFallback(inputMergerClassName);
            if (inputMerger == null) {
                setFailedAndResolve();
                return;
            }
            List<Data> inputs = new ArrayList<>();
            inputs.add(mWorkSpec.input);
            inputs.addAll(mWorkSpecDao.getInputsFromPrerequisites(mWorkSpecId));
            input = inputMerger.merge(inputs);
        }

        WorkerParameters params = new WorkerParameters(
                UUID.fromString(mWorkSpecId),
                input,
                mTags,
                mRuntimeExtras,
                mWorkSpec.runAttemptCount,
                mConfiguration.getExecutor(),
                mWorkTaskExecutor,
                mConfiguration.getWorkerFactory(),
                new WorkProgressUpdater(mWorkDatabase, mWorkTaskExecutor),
                new WorkForegroundUpdater(mWorkDatabase, mForegroundProcessor, mWorkTaskExecutor));
        //Builder中的mWorker对象为空,这里会通过反射生成一个ListenableWorker对象
        if (mWorker == null) {
            mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
                    mAppContext,
                    mWorkSpec.workerClassName,
                    params);
        }

        // 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();
            // Call mWorker.startWork() on the main thread.
            mWorkTaskExecutor.getMainThreadExecutor()
                    .execute(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                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() {
                    try {
                        // If the ListenableWorker returns a null result treat it as a failure.
                        ListenableWorker.Result result = future.get();
                        if (result == null) {
                        } else {
                            mResult = result;
                        }
                    } catch (CancellationException exception) {
                    } catch (InterruptedException | ExecutionException exception) {
                    } finally {
                        onWorkFinished();
                    }
                }
            }, mWorkTaskExecutor.getBackgroundExecutor());
        } else {
            resolveIncorrectStatus();
        }
    }

该函数比较长,删减部分后结构也很清晰,在执行任务之前会判断该任务是否是定期任务和退避任务,若是则会进一步判断是否满足要求。然后会合并输入,并构造WorkerParameters对象。之后就是开启任务,调用ListenableWorker的startWork()方法启动任务。

3.1.2 约束的更新过程

在这里插入图片描述

追踪workSpec的约束是依靠WorkConstraintsTracker实现的,在其构造函数中会初始化一个包含上图七个Controller的数组mConstraintControllers,各个Controller又是通过Tarckers提供的四个BroadcastReceiverConstraintTracker的子类实例来追踪依赖条件的,其监控对应的系统参数的变化,通过接收广播来更新依赖条件是否得到满足。通过replace()函数可以修改追踪对象:

public void replace(@NonNull Iterable<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);
        }
    }
}

先是将所有的Controller中的callback置为空,然后将所有Controller中的workSpecs替换掉,然后重新设置新的callback。因此当约束满足或者不满足时均会回调WorkConstraintsTracker实现的OnConstraintUpdatedCallback接口方法:

//WorkConstraintsTracker
@Override
public void onConstraintMet(@NonNull List<String> workSpecIds) {
    synchronized (mLock) {
        List<String> unconstrainedWorkSpecIds = new ArrayList<>();
        for (String workSpecId : workSpecIds) {
            if (areAllConstraintsMet(workSpecId)) {
                Logger.get().debug(TAG, String.format("Constraints met for %s", workSpecId));
                unconstrainedWorkSpecIds.add(workSpecId);
            }
        }
        if (mCallback != null) {
            mCallback.onAllConstraintsMet(unconstrainedWorkSpecIds);
        }
    }
}

可以看到当约束条件满足时这里会进一步调用了上一节分析过的areAllConstraintsMet()方法启动任务。下面看一下系统参数变化是如何更新约束条件的,以BatteryChargingTracker为例,其会注册一个广播,广播参数通过getIntentFilter()方法获,当接收到广播后会调用onBroadcastReceive()方法,如下:

//BatteryChargingTracker.java
@Override
public IntentFilter getIntentFilter() {
    IntentFilter intentFilter = new IntentFilter();
    if (Build.VERSION.SDK_INT >= 23) {
        intentFilter.addAction(BatteryManager.ACTION_CHARGING);
        intentFilter.addAction(BatteryManager.ACTION_DISCHARGING);
    } else {
        intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
        intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
    }
    return intentFilter;
}

@Override
public void onBroadcastReceive(Context context, @NonNull Intent intent) {
    String action = intent.getAction();
    if (action == null) {
        return;
    }

    Logger.get().debug(TAG, String.format("Received %s", action));
    switch (action) {
        case BatteryManager.ACTION_CHARGING:
            setState(true);
            break;

        case BatteryManager.ACTION_DISCHARGING:
            setState(false);
            break;

        case Intent.ACTION_POWER_CONNECTED:
            setState(true);
            break;

        case Intent.ACTION_POWER_DISCONNECTED:
            setState(false);
            break;
    }
}

当状态发生变化会调用其父类ConstraintTracker的setState()来更新状态,其逻辑是通过注册是监听器来通知外部:

public void setState(T newState) {
        synchronized (mLock) {
            if (mCurrentState == newState
                    || (mCurrentState != null && mCurrentState.equals(newState))) {
                return;
            }
            mCurrentState = newState;

            // onConstraintChanged may lead to calls to addListener or removeListener.
            // This can potentially result in a modification to the set while it is being
            // iterated over, so we handle this by creating a copy and using that for
            // iteration.
            final List<ConstraintListener<T>> listenersList = new ArrayList<>(mListeners);
            mTaskExecutor.getMainThreadExecutor().execute(new Runnable() {
                @Override
                public void run() {
                    for (ConstraintListener<T> listener : listenersList) {
                        listener.onConstraintChanged(mCurrentState);
                    }
                }
            });
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值