WorkManager 源码解析

1,144 阅读6分钟

WorkManager 简介链接

概述

上一篇我们聊到了 WorkManager 的简单使用,接着我们来看一看 WorkManager 源码,他是怎么如何执行 Work 的。

当我们使用 WorkManager创建任务时,WorkManager会默认在 Manifest 中注册一系列的活动以及 androidx.startup.InitializationProvider。如下图

image.png

在这个 Provider 中我们发现会默认注册一个 WorkManagerInitializer 的类。

/**
 * Initializes {@link androidx.work.WorkManager} using {@code androidx.startup}.
 */
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();
    }
}

看注释我们知道,WorkManagerInitializer 会有 startup 进行初始化,最终会调用到 onCreate 方法。该方法中会调用 WorkManager.initialize 方法,并且传入了两个参数 contextConfiguration,这里我们看下 Configuration 里面有些什么。

public final class Configuration {
// 构造方法
Configuration(@NonNull Configuration.Builder builder) {
    if (builder.mExecutor == null) {
        mExecutor = createDefaultExecutor(false);
    } else {
        mExecutor = builder.mExecutor;
    }

    if (builder.mTaskExecutor == null) {
        mIsUsingDefaultTaskExecutor = true;
        mTaskExecutor = createDefaultExecutor(true);
    } else {
        mIsUsingDefaultTaskExecutor = false;
        mTaskExecutor = builder.mTaskExecutor;
    }

    if (builder.mWorkerFactory == null) {
        mWorkerFactory = WorkerFactory.getDefaultWorkerFactory();
    } else {
        mWorkerFactory = builder.mWorkerFactory;
    }

    if (builder.mInputMergerFactory == null) {
        mInputMergerFactory = InputMergerFactory.getDefaultInputMergerFactory();
    } else {
        mInputMergerFactory = builder.mInputMergerFactory;
    }

    if (builder.mRunnableScheduler == null) {
        mRunnableScheduler = new DefaultRunnableScheduler();
    } else {
        mRunnableScheduler = builder.mRunnableScheduler;
    }
    ···
}

private @NonNull Executor createDefaultExecutor(boolean isTaskExecutor) {
    return Executors.newFixedThreadPool(
            // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
            Math.max(2, Math.min(Runtime.getRuntime().availableProcessors() - 1, 4)),
            createDefaultThreadFactory(isTaskExecutor));
}
}

Configuration 构造方法中调用了 createDefaultExecutor(true); 方法,创建了固定大小的线程池用于后面执行 work,以及 InputMergerFactory 用于数据合并的工厂类等等。简单的说 Configuration 就是为 WorkManager 配置一些基础的设施。看完 Configuration 我们接着走 WorkManager.initialize 方法,最终调用到了 WorkManagerImpl.initialize 方法。

// WorkManagerImpl
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerImpl extends WorkManager {
     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;
        }
    }
}
}

WorkManagerImpl 源码我们看到 initialize 方法中初始化了一个 WorkManagerImpl 对象,并且传入了一个 WorkManagerTaskExecutor 的一个对象。这里我们先看下 WorkManagerTaskExecutor 是个啥

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerTaskExecutor implements TaskExecutor {
    public WorkManagerTaskExecutor(@NonNull Executor backgroundExecutor) {
        // Wrap it with a serial executor so we have ordering guarantees on commands
        // being executed.
        mBackgroundExecutor = new SerialExecutor(backgroundExecutor);
    }
}

WorkManagerTaskExecutor 里面创建了一个 SerialExecutor 对象,OK 我们看下 SerialExecutor

public class SerialExecutor implements Executor {
    private final ArrayDeque<Task> mTasks;
    private final Executor mExecutor;
    private final Object mLock;
    private volatile Runnable mActive;

    public SerialExecutor(@NonNull Executor executor) {
        mExecutor = executor;
        mTasks = new ArrayDeque<>();
        mLock = new Object();
    }

    @Override
    public void execute(@NonNull Runnable command) {
        synchronized (mLock) {
            mTasks.add(new Task(this, command));
            if (mActive == null) {
                scheduleNext();
            }
        }
    }
}

SerialExecutor 里面初始化了一个 ArrayDeque 双端队列,用于保证后期创建的任务顺序执行。注意 ExecutorConfiguration 中创建的固定大小线程池。简单的说最终执行任务的就是 Configuration 中的固定大小线程池并且在执行任务的时候保证线性执行。分析完 WorkManagerTaskExecutor 我们回到 WorkManagerImpl.initialize 方法。接着会走到下面这个构造方法

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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)
    );
}

并且从 R.bool.workmanager_test_configuration 获取当前是否是测试,并且 WorkDatabase 会根据当前的 Executor 生成数据库。WorkDatabase 通过 RoomDatabase 创建了一个数据库。有兴趣的朋友可以自己去看下,我们接着往下走

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
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);
}

OK,在该方法中会调用 createSchedulers 方法,我们看下这个方法

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@NonNull
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));
}

createSchedulers 方法中会调用 Schedulers 类的 createBestAvailableBackgroundScheduler 方法

// Schedulers
@NonNull
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;
}

从该方法中我们可以知道,当前SDK版本>23的情况下,是由jobservice提供调用支持,如果是小于23的情况下是AlarmService提供调用,这里就对应了 WorkManager 简介 中的 image.png

到这里 WorkManager 实例已经创建的差不多了,初始化基本上已经结束了。

image.png

接着我们看下他是如何区执行一个 Work ,我们看下面的栗子

val request = OneTimeWorkRequest.Builder(BlurWorker::class.java).build() // 第一种写法
WorkManager.getInstance(this).enqueue(request)

当我们执行一个 Work 的时候, WorkManager 调用 enqueue 最终会调用到 WorkManagerImpl.enqueue 的方法

// WorkManagerImpl
@Override
@NonNull
public Operation enqueue(
        @NonNull List<? extends WorkRequest> requests) {

    // This error is not being propagated as part of the Operation, as we want the
    // app to crash during development. Having no workRequests is always a developer error.
    if (requests.isEmpty()) {
        throw new IllegalArgumentException(
                "enqueue needs at least one WorkRequest.");
    }
    return new WorkContinuationImpl(this, requests).enqueue();
}

enqueue 方法中,会调用 WorkContinuationImpl.enqueue 方法,看源码

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

在这个方法中会将 WorkContinuationImpl 的实例放到一个 EnqueueRunnable 交给线程池执行,我们开始分析过这个线程池就是 WorkManager 初始化的时候 Configuration 创建的固定大小线程池。OK,最终就会执行 EnqueueRunnablerun 方法。看源码

// EnqueueRunnable
@Override
public void run() {
    try {
        if (mWorkContinuation.hasCycles()) {
            throw new IllegalStateException(
                    String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
        }
        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));
    }
}

run 方法中首先会调用 addToDatabase 根据策略不同将任务添加到数据库或者移除。然后会调用 PackageManagerHelper 将允许将 RescheduleReceiver 注册到 manifest.xml 内,我们可以打开编译后 apkAndroidManifests.xml 看到对应的 recevier。最终调用 scheduleWorkInBackground 方法

@VisibleForTesting
public void scheduleWorkInBackground() {
    WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
    Schedulers.schedule(
            workManager.getConfiguration(),
            workManager.getWorkDatabase(),
            workManager.getSchedulers());
}

最终会调用 Schedulers.schedule 方法

public static void schedule(
        @NonNull Configuration configuration,
        @NonNull WorkDatabase workDatabase,
        List<Scheduler> schedulers) {
    ···
    try {
        // Enqueued workSpecs when scheduling limits are applicable.
        eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
                configuration.getMaxSchedulerLimit());
        ···
    } 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);
            }
        }
    }
}

Schedulers.schedule 方法中,会调用 workSpecDao.getEligibleWorkForScheduling 方法获取到需要执行的任务数据。最终调用到 scheduler.schedule 方法

// GreedyScheduler
@Override
public void schedule(@NonNull WorkSpec... workSpecs) {
    ...
    for (WorkSpec workSpec : workSpecs) {
        ...
        if (workSpec.state == WorkInfo.State.ENQUEUED) {
            ...
            } else {
                Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                mWorkManagerImpl.startWork(workSpec.id);
            }
        }
    }
    ...
}

看代码可以看到,最终会执行到 WorkManagerImpl.startWork 方法中

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(@NonNull String workSpecId) {
    startWork(workSpecId, null);
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public void startWork(
        @NonNull String workSpecId,
        @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    mWorkTaskExecutor
            .executeOnBackgroundThread(
                    new StartWorkRunnable(this, workSpecId, runtimeExtras));
}

最终会执行到 StartWorkRunnablerun 方法

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

接着会执行到 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)) {
            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;
}

首先在 Processor.startWork 方法中,会把数据封装成 WorkerWrapper,然后将封装后的 WorkerWrapper 交给 BackgroundExecutor 进行执行。看源码我们知道 WorkerWrapper 实现了 Runnable 接口,最终就执行到了 WorkerWrapperrun 方法

// WorkerWrapper
@WorkerThread
@Override
public void run() {
    mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
    mWorkDescription = createWorkDescription(mTags);
    runWorker();
}
private void runWorker() {
    ...
    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));
            if (mWorker == null) {
                mWorker = mConfiguration.getWorkerFactory().createWorkerWithDefaultFallback(
                mAppContext,
                mWorkSpec.workerClassName,
                params);
            }
    if (trySetRunning()) {
        ...
        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());
        ...
    } else {
        resolveIncorrectStatus();
    }
}

代码比较多这里看重点,首先会对数据进行处理,存在相同的 key 值,ArrayCreatingInputMerger 将每个键与数组配对。如果每个键都是唯一的,您会得到一系列一元数组。最终将数据封装成 WorkerParameters 并且创建 WorkForegroundRunnable,监听他的回调最终就执行了 mWorker.startWork() 接着看 Worker 的源码

// Worker
@WorkerThread
public abstract @NonNull Result doWork();

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

        }
    });
    return mFuture;
}

到这里就回调到了我们自定义的 WorkerdoWork 方法。到这里完整的调用已经结束了

总结下

  1. 架构上用 JobServiceAlarmService 进行调用执行

  2. 数据传递上用 Database 进行存储

  3. 执行角度利用线程池提供具体执行能力