04 .WorkManager原理

WorkManager 核心组件

WorkManager 工作原理解析

  1. 创建
    • WorkManager 的初始化
    • WorkRequest 的创建
  2. 非约束条件任务的执行
  3. 带约束条件任务的执行

测试源码:

val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()
WorkManager.getInstance(this).enqueue(work1Request)

class Worker1(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {
        Thread.sleep(5000)
        return Result.success()
    }
}

创建

WorkManager 的初始化

在默认的情况下,WorkManager 并不是在我们调用 WorkManager.getInstance() 时创建的。通过反编译一下 apk,会发现在 AndroidManifest.xml 文件中注册了名为 WorkManagerInitializerContentProvider。因此 WorkManager 在 app 冷启动的时候已经被创建。

<!--AndroidManifest.xml-->
<provider
	android:name="androidx.work.impl.WorkManagerInitializer"
	android:exported="false"
	android:multiprocess="true"
	android:authorities="com.jandroid.multivideo.workmanager-init"
	android:directBootAware="false" />

WorkManagerInitializer 类:

public final class WorkManagerInitializer implements Initializer<WorkManager> {
    private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
    @NonNull
    @Override
    public WorkManager create(@NonNull Context context) {
        // Initialize WorkManager with the default configuration.
        Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
        WorkManager.initialize(context, new Configuration.Builder().build());
        return WorkManager.getInstance(context);
    }
    @NonNull
    @Override
    public List<Class<? extends androidx.startup.Initializer<?>>> dependencies() {
        return Collections.emptyList();
    }
}
// class WorkManager
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {  
    WorkManagerImpl.initialize(context, configuration);  
}

由于 WorkManager 是个单例,在此时 WorkManager 就已经被初始化了。在 initialize() 之前,会创建一个默认的 ConfigurationConfiguration 设置了许多属性,用来管理和调度工作的方式。通常我们使用 WorkManager 默认创建的 Configuration 即可。如需使用自己的 Configuration,可参考官方文档,有明确的使用说明。

继续看 initialize() 的实现:

// class WorkManager
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {  
    WorkManagerImpl.initialize(context, configuration);  
}
// class WorkManagerImpl
private static WorkManagerImpl sDelegatedInstance = null;  
private static WorkManagerImpl sDefaultInstance = null;
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
	synchronized (sLock) {
		if (sDelegatedInstance != null && sDefaultInstance != null) {
			throw new IllegalStateException("WorkManager is already initialized.  Did you "
					+ "try to initialize it manually without disabling "
					+ "WorkManagerInitializer? See "
					+ "WorkManager#initialize(Context, Configuration) or the class level "
					+ "Javadoc for more information.");
		}

		if (sDelegatedInstance == null) {
			context = context.getApplicationContext();
			if (sDefaultInstance == null) {
				sDefaultInstance = new WorkManagerImpl(
						context,
						configuration,
						new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
			}
			sDelegatedInstance = sDefaultInstance;
		}
	}
}

此时 sDelegatedInstancenullWorkManager 会先创建一个默认的 WorkManagerTaskExecutor 对象,用来执行 WorkManager 的任务。之后创建一个 WorkManagerImpl 对象:

public WorkManagerImpl(
	@NonNull Context context,
	@NonNull Configuration configuration,
	@NonNull TaskExecutor workTaskExecutor) {
this(context,
	configuration,
	workTaskExecutor,
	context.getResources().getBoolean(R.bool.workmanager_test_configuration));
}
public WorkManagerImpl(
		@NonNull Context context,
		@NonNull Configuration configuration,
		@NonNull TaskExecutor workTaskExecutor,
		boolean useTestDatabase) {
	this(context,
			configuration,
			workTaskExecutor,
			WorkDatabase.create(
					context.getApplicationContext(),
					workTaskExecutor.getBackgroundExecutor(),
					useTestDatabase)
	);
}

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

接着看 this() 的实现:

public WorkManagerImpl(
		@NonNull Context context,
		@NonNull Configuration configuration,
		@NonNull TaskExecutor workTaskExecutor,
		@NonNull WorkDatabase database) {
	Context applicationContext = context.getApplicationContext();
	Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
	List<Scheduler> schedulers =
			createSchedulers(applicationContext, configuration, workTaskExecutor);
	Processor processor = new Processor(
			context,
			configuration,
			workTaskExecutor,
			database,
			schedulers);
	internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}

Scheduler 初始化

先看 WorkManagerImpl.createSchedulers(),返回一个 Scheduler 的 List

public List<Scheduler> createSchedulers(
		@NonNull Context context,
		@NonNull Configuration configuration,
		@NonNull 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, configuration, taskExecutor, this));
}

由此可见,WorkManager 创建了两个 Scheduler

GreedyScheduler

GreedyScheduler() 是常驻的,用来执行没有任何约束的非周期性的任务。

createBestAvailableBackgroundScheduler

接下来看 createBestAvailableBackgroundScheduler() 的实现。

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

	Scheduler scheduler;

	if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) { // API23
		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;
}
public static final String GCM_SCHEDULER = "androidx.work.impl.background.gcm.GcmScheduler";
@Nullable  
private static Scheduler tryCreateGcmBasedScheduler(@NonNull Context context) {  
    try {  
        Class<?> klass = Class.forName(GCM_SCHEDULER);  
        Scheduler scheduler =  
                (Scheduler) klass.getConstructor(Context.class).newInstance(context);  
        Logger.get().debug(TAG, String.format("Created %s", GCM_SCHEDULER));  
        return scheduler;  
    } catch (Throwable throwable) {  
        Logger.get().debug(TAG, "Unable to create GCM Scheduler", throwable);  
        return null;    }  
}

这段代码对 build version 进行了判断。

Processor

public WorkManagerImpl(
		@NonNull Context context,
		@NonNull Configuration configuration,
		@NonNull TaskExecutor workTaskExecutor,
		@NonNull WorkDatabase database) {
	Context applicationContext = context.getApplicationContext();
	Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
	List<Scheduler> schedulers =
			createSchedulers(applicationContext, configuration, workTaskExecutor);
	Processor processor = new Processor(
			context,
			configuration,
			workTaskExecutor,
			database,
			schedulers);
	internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
}

Processor 存储了 ConfigurationTaskExecutorWorkDatabaseschedulers 等,用来在适当的时机进行任务调度。再来看 internalInit():

private void internalInit(@NonNull Context context,
		@NonNull Configuration configuration,
		@NonNull TaskExecutor workTaskExecutor,
		@NonNull WorkDatabase workDatabase,
		@NonNull List<Scheduler> schedulers,
		@NonNull Processor processor) {

	context = context.getApplicationContext();
	mContext = context;
	mConfiguration = configuration;
	mWorkTaskExecutor = workTaskExecutor;
	mWorkDatabase = workDatabase;
	mSchedulers = schedulers;
	mProcessor = processor;
	mPreferenceUtils = new PreferenceUtils(workDatabase);
	mForceStopRunnableCompleted = false;

	// Check for direct boot mode
	if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && context.isDeviceProtectedStorage()) {
		throw new IllegalStateException("Cannot initialize WorkManager in direct boot mode");
	}

	// Checks for app force stops.
	mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
}

记录了 ConfigurationTaskExecutorWorkDatabaseschedulersProcessor 等。

然后我们看最后一行执行语句,启动了一个 ForceStopRunnable,这个 Runnable 是干什么用的呢?直接看 run() 的实现:

@Override
public void run() {
	try {
		if (!multiProcessChecks()) {
			return;
		}
		while (true) {
			// Migrate the database to the no-backup directory if necessary.
			WorkDatabasePathHelper.migrateDatabase(mContext);
			// Clean invalid jobs attributed to WorkManager, and Workers that might have been
			// interrupted because the application crashed (RUNNING state).
			Logger.get().debug(TAG, "Performing cleanup operations.");
			try {
				forceStopRunnable();
				break;
			} catch (SQLiteCantOpenDatabaseException
					| SQLiteDatabaseCorruptException
					| SQLiteDatabaseLockedException
					| SQLiteTableLockedException
					| SQLiteConstraintException
					| SQLiteAccessPermException exception) {
				mRetryCount++;
				if (mRetryCount >= MAX_ATTEMPTS) {
					// ForceStopRunnable is usually the first thing that accesses a database
					// (or an app's internal data directory). This means that weird
					// PackageManager bugs are attributed to ForceStopRunnable, which is
					// unfortunate. This gives the developer a better error
					// message.
					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);
					IllegalStateException throwable = new IllegalStateException(message,
							exception);
					InitializationExceptionHandler exceptionHandler =
							mWorkManager.getConfiguration().getExceptionHandler();
					if (exceptionHandler != null) {
						Logger.get().debug(TAG,
								"Routing exception to the specified exception handler",
								throwable);
						exceptionHandler.handleException(throwable);
						break;
					} else {
						throw throwable;
					}
				} else {
					long duration = mRetryCount * BACKOFF_DURATION_MS;
					Logger.get()
							.debug(TAG, String.format("Retrying after %s", duration),
									exception);
					sleep(mRetryCount * BACKOFF_DURATION_MS);
				}
			}
		}
	} finally {
		mWorkManager.onForceStopRunnableCompleted();
	}
}

这个 Runnable 的作用就是在 WorkManager 初始化过程中,发现了未完成的,需要重新执行的任务,或者 app 被强制 kill 的情况下,直接对 Scheduler 进行调度。到此,一个 WorkManager 的初始化流程就完成了。

初始化小结

  1. WorkManager 的初始化是在 app 冷启动后,由 WorkManagerInitializer 这个 ContentProvider 执行的。
  2. 初始化过程包含了 ConfigurationWorkManagerTaskExecutorWorkDatabaseSchedulersProcessor 等的初始化过程。
  3. Schedulers 有两个。
    • (1) GreedyScheduler: 执行没有任何约束的非周期性的任务。
    • (2) SystemJobScheduler / GcmBasedScheduler / SystemAlarmScheduler: 执行周期性或者有约束性的任务。优先返回 SystemJobScheduler,在 build version 小于 23 的情况下先尝试返回 GcmBasedScheduler,若返回为空再返回 SystemAlarmScheduler。
  4. 初始化的最后,会根据情况找到需要被执行的任务进行调度执行。
  5. WorkManager 初始化流程图

WorkRequest 的创建

OneTimeWorkRequest 为例:val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()

// OneTimeWorkRequest.Builder
public Builder(@NonNull Class<? extends ListenableWorker> workerClass) {
	super(workerClass);
	mWorkSpec.inputMergerClassName = OverwritingInputMerger.class.getName();
}
// WorkRequest.Builder
UUID mId;  
WorkSpec mWorkSpec;  
Set<String> mTags = new HashSet<>();
Builder(@NonNull Class<? extends ListenableWorker> workerClass) {  
    mId = UUID.randomUUID();  
    mWorkerClass = workerClass;  
    mWorkSpec = new WorkSpec(mId.toString(), workerClass.getName());  
    addTag(workerClass.getName());  
}

接着看 build() 方法:

public final @NonNull W build() {
	W returnValue = buildInternal();
	Constraints constraints = mWorkSpec.constraints;
	// Check for unsupported constraints.
	boolean hasUnsupportedConstraints =
			(Build.VERSION.SDK_INT >= 24 && constraints.hasContentUriTriggers())
					|| constraints.requiresBatteryNotLow()
					|| constraints.requiresCharging()
					|| (Build.VERSION.SDK_INT >= 23 && constraints.requiresDeviceIdle());
	if (mWorkSpec.expedited) {
		if (hasUnsupportedConstraints) {
			throw new IllegalArgumentException(
					"Expedited jobs only support network and storage constraints");
		}
		if (mWorkSpec.initialDelay > 0) {
			throw new IllegalArgumentException("Expedited jobs cannot be delayed");
		}
	}
	// Create a new id and WorkSpec so this WorkRequest.Builder can be used multiple times.
	mId = UUID.randomUUID();
	mWorkSpec = new WorkSpec(mWorkSpec);
	mWorkSpec.id = mId.toString();
	return returnValue;
}
abstract @NonNull W buildInternal();

在 buildInternal() 拿到 OneTimeWorkRequest 对象之后,为 Builder 创建了一个新的 WorkSpec 对象,并赋予了新的 UUID。虽然与原先的 WorkSpec 对象中每个属性的值是一致的,但指向了不同的内存地址。这么做的目的是为了这个 Builder 对象可被重复利用。好了,现在我们一个任务的 WorkRequest 创建就完成了。

buildInternal() 方法返回了一个 WorkRequest 对象,这是个抽象方法,在子类 OneTimeWorkRequest.Builder 中的实现如下:

// OneTimeWorkRequest
@Override
@NonNull OneTimeWorkRequest buildInternal() {
	if (mBackoffCriteriaSet && Build.VERSION.SDK_INT >= 23 
		&& mWorkSpec.constraints.requiresDeviceIdle()) {
		throw new IllegalArgumentException("Cannot set backoff criteria on an idle mode job");
	}
	return new OneTimeWorkRequest(this);
}
OneTimeWorkRequest(Builder builder) {  
    super(builder.mId, builder.mWorkSpec, builder.mTags);  
}

小结

总结:

WorkRequest 的创建是为了持有三个重要的成员变量。分别是:

  1. mId: 由 UUID 生成的任务 id。
  2. mWorkSpec: 每个任务的属性。
  3. mTags: 每个任务的标签。

流程图:

非约束条件任务的执行过程

OneTime 一次性任务

OneTimeWorkRequest 为例:

WorkManager.getInstance(this).enqueue(work1Request)

WorkManager.getInstance() 获取到的是 WorkManagerImpl

// WorkManagerImpl.java
@Override
@NonNull
public Operation enqueue(@NonNull List<? extends WorkRequest> requests) {
	// ...
	return new WorkContinuationImpl(this, requests).enqueue();
}

创建一个 WorkContinuationImpl() 对象,再执行 enqueue() 方法。WorkContinuationImplWorkContinuation 的子类。用来把多个 OneTimeWorkRequest 根据需求串行,并行或合并处理。我们熟悉的 then()combine()enqueue() 等都是这个类的方法。

// WorkContinuationImpl.java
public WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
		@Nullable String name,
		@NonNull 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);
	}
}

WorkContinuation 保存了任务相关的所有信息,如 WorkManagerWorkRequest,父 WorkContinuation 等。

继续看 WorkContinuationImplenqueue() 方法的实现:

// WorkContinuationImpl.java
@Override
public @NonNull Operation enqueue() {
	// Only enqueue if not already enqueued.
	if (!mEnqueued) {
		// The runnable walks the hierarchy of the continuations
		// and marks them enqueued using the markEnqueued() method, parent first.
		EnqueueRunnable runnable = new EnqueueRunnable(this);
		mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
		mOperation = runnable.getOperation();
	} else {
		Logger.get().warning(TAG,
				String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
	}
	return mOperation;
}

WorkManagerTaskExecutor 执行了 EnqueueRunnableEnqueueRunnablerun() 的实现:

// EnqueueRunnable.java
@Override
public void run() {
	try {
		// ...
		boolean needsScheduling = addToDatabase();
		if (needsScheduling) {
			// Enable RescheduleReceiver, only when there are Worker's that need scheduling.
			final Context context =
					mWorkContinuation.getWorkManagerImpl().getApplicationContext();
			PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
			scheduleWorkInBackground();
		}
		mOperation.setState(Operation.SUCCESS);
	} catch (Throwable exception) {
		mOperation.setState(new Operation.State.FAILURE(exception));
	}
}
<receiver
	android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
	android:directBootAware="false"
	android:enabled="false"
	android:exported="false"
	android:process=":worker1"
	tools:targetApi="n">
	<intent-filter>
		<action android:name="android.intent.action.BOOT_COMPLETED" />
		<action android:name="android.intent.action.TIME_SET" />
		<action android:name="android.intent.action.TIMEZONE_CHANGED" />
	</intent-filter>
</receiver>
@VisibleForTesting
public void scheduleWorkInBackground() {
	WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
	Schedulers.schedule(
			workManager.getConfiguration(),
			workManager.getWorkDatabase(),
			workManager.getSchedulers());
}

这部分就是任务调度的实现。拿到 WorkManager 对象后调用了 Schedulers.schedule() 方法,传入了 Configuration, WorkDatabase, Scheduler 这三个对象。执行 schedule() 方法:

// Schedulers.java
public class Schedulers {

    public static final String GCM_SCHEDULER = "androidx.work.impl.background.gcm.GcmScheduler";
    private static final String TAG = Logger.tagWithPrefix("Schedulers");

    /**
     * Schedules {@link WorkSpec}s while honoring the {@link Scheduler#MAX_SCHEDULER_LIMIT}.
     *
     * @param workDatabase The {@link WorkDatabase}.
     * @param schedulers   The {@link List} of {@link Scheduler}s to delegate to.
     */
    public static void schedule(
            @NonNull Configuration configuration,
            @NonNull WorkDatabase workDatabase,
            List<Scheduler> schedulers) {
        if (schedulers == null || schedulers.size() == 0) {
            return;
        }

        WorkSpecDao workSpecDao = workDatabase.workSpecDao();
        List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
        List<WorkSpec> allEligibleWorkSpecs;

        workDatabase.beginTransaction();
        try {
            // Enqueued workSpecs when scheduling limits are applicable.
            eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
                    configuration.getMaxSchedulerLimit());

            // Enqueued workSpecs when scheduling limits are NOT applicable.
            allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(
                    MAX_GREEDY_SCHEDULER_LIMIT);

            if (eligibleWorkSpecsForLimitedSlots != null
                    && eligibleWorkSpecsForLimitedSlots.size() > 0) {
                long now = System.currentTimeMillis();

                // Mark all the WorkSpecs as scheduled.
                // Calls to Scheduler#schedule() could potentially result in more schedules
                // on a separate thread. Therefore, this needs to be done first.
                for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {
                    workSpecDao.markWorkSpecScheduled(workSpec.id, now);
                }
            }
            workDatabase.setTransactionSuccessful();
        } finally {
            workDatabase.endTransaction();
        }

        if (eligibleWorkSpecsForLimitedSlots != null
                && eligibleWorkSpecsForLimitedSlots.size() > 0) {

            WorkSpec[] eligibleWorkSpecsArray =
                    new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
            eligibleWorkSpecsArray =
                    eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);

            // Delegate to the underlying schedulers.
            for (Scheduler scheduler : schedulers) {
                if (scheduler.hasLimitedSchedulingSlots()) {
                    scheduler.schedule(eligibleWorkSpecsArray);
                }
            }
        }

        if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
            WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
            enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
            // Delegate to the underlying schedulers.
            for (Scheduler scheduler : schedulers) {
                if (!scheduler.hasLimitedSchedulingSlots()) {
                    scheduler.schedule(enqueuedWorkSpecsArray);
                }
            }
        }
    }
    // ...
}
// GreedyScheduler.java
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
	if (mInDefaultProcess == null) {
		checkDefaultProcess();
	}

	if (!mInDefaultProcess) {
		Logger.get().info(TAG, "Ignoring schedule request in a secondary process");
		return;
	}

	registerExecutionListenerIfNeeded();

	// Keep track of the list of new WorkSpecs whose constraints need to be tracked.
	// Add them to the known list of constrained WorkSpecs and call replace() on
	// WorkConstraintsTracker. That way we only need to synchronize on the part where we
	// are updating mConstrainedWorkSpecs.
	Set<WorkSpec> constrainedWorkSpecs = new HashSet<>();
	Set<String> constrainedWorkSpecIds = new HashSet<>();

	for (WorkSpec workSpec : workSpecs) {
		long nextRunTime = workSpec.calculateNextRunTime();
		long now = System.currentTimeMillis();
		if (workSpec.state == WorkInfo.State.ENQUEUED) {
			if (now < nextRunTime) {
				// Future work
				if (mDelayedWorkTracker != null) {
					mDelayedWorkTracker.schedule(workSpec);
				}
			} else if (workSpec.hasConstraints()) {
				if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
					// Ignore requests that have an idle mode constraint.
					Logger.get().debug(TAG,
							String.format("Ignoring WorkSpec %s, Requires device idle.",
									workSpec));
				} else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
					// Ignore requests that have content uri triggers.
					Logger.get().debug(TAG,
							String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
									workSpec));
				} else {
					constrainedWorkSpecs.add(workSpec);
					constrainedWorkSpecIds.add(workSpec.id);
				}
			} else {
				Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
				mWorkManagerImpl.startWork(workSpec.id);
			}
		}
	}

	// onExecuted() which is called on the main thread also modifies the list of mConstrained
	// WorkSpecs. Therefore we need to lock here.
	synchronized (mLock) {
		if (!constrainedWorkSpecs.isEmpty()) {
			Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
					TextUtils.join(",", constrainedWorkSpecIds)));
			mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
			mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
		}
	}
}

WorkSpecENQUEUED 的状态;非周期性任务;非延迟任务;非撤销的任务;没有其它约束的任务 满足这五个条件后,直接调用:

// GreedyScheduler
mWorkManagerImpl.startWork(workSpec.id);

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

WorkTaskExecutor 对任务进行了调度。StartWorkRunnablerun() 的实现:

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

StartWorkRunnable 会将任务的信息交给 Processor,由 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 (isEnqueued(id)) {
			Logger.get().debug(
					TAG,
					String.format("Work %s is already enqueued for processing", 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);
	Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
	return true;
}

startWork() 方法中创建了一个 WorkerWrapperRunnable 对象,交由 WorkTaskExecutor 调度处理。WorkerWrapperrun() 方法的实现:

// WorkerWrapper.java
public void run() {
	mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
	mWorkDescription = createWorkDescription(mTags);
	runWorker();
}
private void runWorker() {  
    if (tryCheckForInterruptionAndResolve()) {  
        return;  
    }  
  
    mWorkDatabase.beginTransaction();  
    try {  
        mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);  
        if (mWorkSpec == null) {  
            Logger.get().error(  
                    TAG,  
                    String.format("Didn't find WorkSpec for id %s", mWorkSpecId));  
            resolve(false);  
            mWorkDatabase.setTransactionSuccessful();  
            return;        }  
  
        // Do a quick check to make sure we don't need to bail out in case this work is already  
        // running, finished, or is blocked.        if (mWorkSpec.state != ENQUEUED) {  
            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.  
        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);  
                mWorkDatabase.setTransactionSuccessful();  
                return;            }  
        }  
  
        // Needed for nested transactions, such as when we're in a dependent work request when  
        // using a SynchronousExecutor.        mWorkDatabase.setTransactionSuccessful();  
    } finally {  
        mWorkDatabase.endTransaction();  
    }  
  
    // Merge inputs.  This can be potentially expensive code, so this should not be done inside  
    // a database transaction.    Data input;  
    if (mWorkSpec.isPeriodic()) {  
        input = mWorkSpec.input;  
    } else {  
        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);  
    }  
  
    final 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));  
  
    // Not always creating a worker here, as the WorkerWrapper.Builder can set a worker override  
    // in test mode.    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();  
        final WorkForegroundRunnable foregroundRunnable =  
                new WorkForegroundRunnable(  
                        mAppContext,  
                        mWorkSpec,  
                        mWorker,  
                        params.getForegroundUpdater(),  
                        mWorkTaskExecutor  
                );  
        mWorkTaskExecutor.getMainThreadExecutor().execute(foregroundRunnable);  
  
        final ListenableFuture<Void> runExpedited = foregroundRunnable.getFuture();  
        runExpedited.addListener(new Runnable() {  
            @Override  
            public void run() {  
                try {  
                    runExpedited.get();  
                    Logger.get().debug(TAG,  
                            String.format("Starting work for %s", mWorkSpec.workerClassName));  
                    // Call mWorker.startWork() on the main thread.  
                    mInnerFuture = mWorker.startWork();  
                    future.setFuture(mInnerFuture);  
                } catch (Throwable e) {  
                    future.setException(e);  
                }  
            }  
        }, mWorkTaskExecutor.getMainThreadExecutor());  
  
        // 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) {  
                        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();  
    }  
}
// Worker
@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;
}

run() 的实现执行了 doWork() 方法,即执行了我们 Worker1doWork() 方法。

// Worker1
class Worker1(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {
        Thread.sleep(5000)
        return Result.success()
    }
}

startWork() 返回了一个 Future 对象 mInnerFuture,调用 future.setFuture(mInnerFuture) 去处理 doWork() 返回的 result。再经过一系列判断后,最终执行了 onWorkFinished() 方法:

// WorkerWrapper.java
void onWorkFinished() {
	if (!tryCheckForInterruptionAndResolve()) {
		mWorkDatabase.beginTransaction();
		try {
			WorkInfo.State state = mWorkSpecDao.getState(mWorkSpecId);
			mWorkDatabase.workProgressDao().delete(mWorkSpecId);
			if (state == null) {
				// state can be null here with a REPLACE on beginUniqueWork().
				// Treat it as a failure, and rescheduleAndResolve() will
				// turn into a no-op. We still need to notify potential observers
				// holding on to wake locks on our behalf.
				resolve(false);
			} else if (state == RUNNING) {
				handleResult(mResult);
			} else if (!state.isFinished()) {
				rescheduleAndResolve();
			}
			mWorkDatabase.setTransactionSuccessful();
		} finally {
			mWorkDatabase.endTransaction();
		}
	}

	// Try to schedule any newly-unblocked workers, and workers requiring rescheduling (such as
	// periodic work using AlarmManager).  This code runs after runWorker() because it should
	// happen in its own transaction.

	// Cancel this work in other schedulers.  For example, if this work was
	// handled by GreedyScheduler, we should make sure JobScheduler is informed
	// that it should remove this job and AlarmManager should remove all related alarms.
	if (mSchedulers != null) {
		for (Scheduler scheduler : mSchedulers) {
			scheduler.cancel(mWorkSpecId);
		}
		Schedulers.schedule(mConfiguration, mWorkDatabase, mSchedulers);
	}
}

onWorkFinished() 会对刚刚执行完毕的任务作进一步处理。首先获取任务的当前状态 state,然后从 db 中删除这个任务,再根据 state 作进一步处理。我们看一下 handleResult(mResult) 的实现:

private void handleResult(ListenableWorker.Result result) {
	if (result instanceof ListenableWorker.Result.Success) {
		Logger.get().info(
				TAG,
				String.format("Worker result SUCCESS for %s", mWorkDescription));
		if (mWorkSpec.isPeriodic()) {
			resetPeriodicAndResolve();
		} else {
			setSucceededAndResolve();
		}

	} else if (result instanceof ListenableWorker.Result.Retry) {
		Logger.get().info(
				TAG,
				String.format("Worker result RETRY for %s", mWorkDescription));
		rescheduleAndResolve();
	} else {
		Logger.get().info(
				TAG,
				String.format("Worker result FAILURE for %s", mWorkDescription));
		if (mWorkSpec.isPeriodic()) {
			resetPeriodicAndResolve();
		} else {
			setFailedAndResolve();
		}
	}
}

小结

流程图:

Periodic 周期任务

使用:

WorkManager.getInstance(context)  
    .enqueueUniquePeriodicWork(  
        workerName,  
        ExistingPeriodicWorkPolicy.REPLACE,  
        workRequest  
    )

进入到 WorkManagerImpl.enqueueUniquePeriodicWork

// v2.7.1 WorkManagerImpl.java
public Operation enqueueUniquePeriodicWork(  
	@NonNull String uniqueWorkName,  
	@NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,  
	@NonNull PeriodicWorkRequest periodicWork) {  
  
    return createWorkContinuationForUniquePeriodicWork(  
            uniqueWorkName,  
            existingPeriodicWorkPolicy,  
            periodicWork)  
            .enqueue();  
}
// 创建了WorkContinuationImpl,
public WorkContinuationImpl createWorkContinuationForUniquePeriodicWork(  
        @NonNull String uniqueWorkName,  
        @NonNull ExistingPeriodicWorkPolicy existingPeriodicWorkPolicy,  
        @NonNull PeriodicWorkRequest periodicWork) {  
    ExistingWorkPolicy existingWorkPolicy;  
    if (existingPeriodicWorkPolicy == ExistingPeriodicWorkPolicy.KEEP) {  
        existingWorkPolicy = ExistingWorkPolicy.KEEP;  
    } else {  
        existingWorkPolicy = ExistingWorkPolicy.REPLACE;  
    }  
    return new WorkContinuationImpl(  
            this,  
            uniqueWorkName,  
            existingWorkPolicy,  
            Collections.singletonList(periodicWork));  
}

然后调用了 WorkManagerImpl.enqueue()

// v2.7.1 WorkManagerImpl.java
@Override  
public @NonNull Operation enqueue() {  
    if (!mEnqueued) {  
	    // 创建一个EnqueueRunnable
	    EnqueueRunnable runnable = new EnqueueRunnable(this);
        mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);  
        mOperation = runnable.getOperation();  
    } else {  
        Logger.get().warning(TAG,  
                String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));  
    }  
    return mOperation;  
}

TaskExecutor 中执行 EnqueueRunnable,看 run() 方法

// EnqueueRunnable.java
@Override  
public void run() {  
    try {  
        // ... 
        boolean needsScheduling = addToDatabase();  // 将Worker信息存储到数据库中去
        if (needsScheduling) {  
            // Enable RescheduleReceiver, only when there are Worker's that need scheduling.  
            final Context context = mWorkContinuation.getWorkManagerImpl().getApplicationContext();  
            PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);  
            scheduleWorkInBackground();  
        }  
        mOperation.setState(Operation.SUCCESS);  
    } catch (Throwable exception) {  
        mOperation.setState(new Operation.State.FAILURE(exception));  
    }  
}
// 安排执行
public void scheduleWorkInBackground() {  
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();  
    Schedulers.schedule(  
            workManager.getConfiguration(),  
            workManager.getWorkDatabase(),  
            workManager.getSchedulers());  
}

Schedulers.schedule()

// Schedulers.java
public static void schedule(Configuration configuration, WorkDatabase workDatabase, List<Scheduler> schedulers) {  
    // ...
  
    WorkSpecDao workSpecDao = workDatabase.workSpecDao();  
    List<WorkSpec> eligibleWorkSpecsForLimitedSlots;  
    List<WorkSpec> allEligibleWorkSpecs;  
  
    workDatabase.beginTransaction();  
    try {  
        // Enqueued workSpecs when scheduling limits are applicable.  
        eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(  
                configuration.getMaxSchedulerLimit());  
  
        // Enqueued workSpecs when scheduling limits are NOT applicable.  
        allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(  
                MAX_GREEDY_SCHEDULER_LIMIT);  
  
        if (eligibleWorkSpecsForLimitedSlots != null  
                && eligibleWorkSpecsForLimitedSlots.size() > 0) {  
            long now = System.currentTimeMillis();  
  
            // Mark all the WorkSpecs as scheduled.  
            // Calls to Scheduler#schedule() could potentially result in more schedules            // on a separate thread. Therefore, this needs to be done first.            for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {  
                workSpecDao.markWorkSpecScheduled(workSpec.id, now);  
            }  
        }  
        workDatabase.setTransactionSuccessful();  
    } finally {  
        workDatabase.endTransaction();  
    }  
  
    if (eligibleWorkSpecsForLimitedSlots != null  
            && eligibleWorkSpecsForLimitedSlots.size() > 0) {  
  
        WorkSpec[] eligibleWorkSpecsArray =  
                new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];  
        eligibleWorkSpecsArray =  
                eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);  
  
        // Delegate to the underlying schedulers.  
        for (Scheduler scheduler : schedulers) {  
            if (scheduler.hasLimitedSchedulingSlots()) {  
                scheduler.schedule(eligibleWorkSpecsArray);  
            }  
        }  
    }  
  
    if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {  
        WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];  
        enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);  
        // Delegate to the underlying schedulers.  
        for (Scheduler scheduler : schedulers) {  
            if (!scheduler.hasLimitedSchedulingSlots()) {  
                scheduler.schedule(enqueuedWorkSpecsArray);  
            }  
        }  
    }  
}

Schedulers.schedule() 查询状态是 ENQUEUED = 0 的任务执行

约束条件任务的执行过程

带约束条件使用示例:

val constraints = Constraints.Builder()
	.setRequiresBatteryNotLow(true)
	.build()
val work2Request = OneTimeWorkRequestBuilder<Worker2>()
	.setConstraints(constraints)
	.build()
WorkManager.getInstance(this).enqueue(work2Request)

任务的创建过程中,会为 WorkSpec 添加 Constraints 属性。

public final @NonNull B setConstraints(@NonNull Constraints constraints) {
	mWorkSpec.constraints = constraints;
	return getThis();
}

在任务执行的过程中,由于增加了约束条件,根据之前章节的分析,常驻的 GreedySchedulerschedule() 方法将不会 startWork(),而是根据 build version 交由 SystemJobSchedulerSystemAlarmScheduler 进行处理。先来看使用 SystemJobScheduler 的情况:

SystemJobScheduler (Build Version 大于等于 23)

SystemJobScheduler 使用的是 JobScheduler 来调度执行任务。通常 JobScheduler 的使用步骤如下:

  1. 创建 JobService
  2. 配置 JobInfo
  3. 执行

SystemJobService

SystemJobService 是执行任务的服务类,在 onStartJob() 中,会调用 WorkManagerImplstartWork() 执行任务。

// SystemJobService
@Override
public boolean onStartJob(@NonNull JobParameters params) {
	// ... ...
	String workSpecId = getWorkSpecIdFromJobParameters(params);
	// ... ...
	synchronized (mJobParameters) {
	  // ... ...
		mJobParameters.put(workSpecId, params);
	}
	// ... ...
	mWorkManagerImpl.startWork(workSpecId, runtimeExtras);
	return true;
}

在初始化 SystemJobScheduler 的时候会获取 JobScheduler 对象:

// SystemJobScheduler 
public SystemJobScheduler(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
	this(context,
		workManager,
		(JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE),
		new SystemJobInfoConverter(context));
}

SystemJobSchedulerschedule() 方法执行了 scheduleInternal():

// SystemJobScheduler
public void scheduleInternal(WorkSpec workSpec, int jobId) {
	JobInfo jobInfo = mSystemJobInfoConverter.convert(workSpec, jobId);
	Logger.get().debug(
			TAG,
			String.format("Scheduling work ID %s Job ID %s", workSpec.id, jobId));
	try {
		mJobScheduler.schedule(jobInfo);
	} catch (IllegalStateException e) {
		... ...
		throw new IllegalStateException(message, e);
	} catch (Throwable throwable) {
		// OEM implementation bugs in JobScheduler cause the app to crash. Avoid crashing.
		Logger.get().error(TAG, String.format("Unable to schedule %s", workSpec), throwable);
	}
}

SystemJobInfoConverter.convert() 方法就是创建了一个 JobInfo,并将 Constraints 里的约束条件赋予 JobInfo 对象,之后便执行了 JobScheduler.schedule(),根据约束条件对任务进行调度。

SystemAlarmScheduler (Build Version 小于 23)

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

<receiver android:directBootAware="false" android:enabled="false" android:exported="false" android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy">
    <intent-filter>
          <action android:name="android.intent.action.BATTERY_OKAY"/>
          <action android:name="android.intent.action.BATTERY_LOW"/>
    </intent-filter>
</receiver>

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

// ConstraintProxy
public static class BatteryNotLowProxy extends ConstraintProxy {
}

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

createConstraintsChangedIntent() 的执行如下:

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

SystemAlarmServiceonStartCommand() 处理如下:

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

调用了 SystemAlarmDispatcher.add() 方法。

// SystemAlarmDispatcher
@MainThread
public boolean add(@NonNull final Intent intent, final int startId) {
	// ... ...
	if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
			&& hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
		return false;
	}
	intent.putExtra(KEY_START_ID, startId);
	synchronized (mIntents) {
		boolean hasCommands = !mIntents.isEmpty();
		mIntents.add(intent);
		if (!hasCommands) {
			// Only call processCommand if this is the first command.
			// The call to dequeueAndCheckForCompletion will process the remaining commands
			// in the order that they were added.
			processCommand();
		}
	}
	return true;
}

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

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

CommandHandleronHandleIntent() 方法中,actionACTION_CONSTRAINTS_CHANGED 的执行是:

// CommandHandler
if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
	handleConstraintsChanged(intent, startId, dispatcher);
} 
private void handleConstraintsChanged(
		@NonNull Intent intent, int startId,
		@NonNull SystemAlarmDispatcher dispatcher) {

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

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

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

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

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

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

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

SystemAlarmDispatcher 调用了 ProcessorstartWork() 方法

Ref