WorkManager

Android知识总结

WorkManager的主线流程.png

一、背景

1.1、特点

  • 保证用户的任务一定会执行
    记录更新每一个任务的信息/状态【Room数据库的更新】,手机重启,APP被杀掉 都一定会执行,因为同学们要记住一句话:Google说WorkManager是保证你的任务一定会执行的。

  • 非及时性的执行任务
    就是不会马上执行,哪怕是你看到的现象是马上执行的,但每次执行的时间都无法确定,因为都是非及时性的执行任务哦。

1.2、适用场景:

定期重复性任务,但时效性/及时性要求不高的,例如:定期log上传,数据备份等。

1.3、使用

  • Worker,用于数据互相传递
public class WorkManager2 extends Worker {
    public static final String TAG = "WorkManager";
    private WorkerParameters workerParams;

    public WorkManager2(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
        this.workerParams = workerParams;
    }

    // 后台任务 并且 异步的 (原理:线程池执行Runnable)
    @NonNull
    @Override
    public Result doWork() {
        // 开始执行了 ENQUEUED
        Log.d(TAG, "WorkManager doWork: 后台任务执行了");
        // 接收 Activity传递过来的数据
        String name = workerParams.getInputData().getString("name");
        Log.d(TAG, "WorkManager doWork: 接收MainActivity传递过来的数据: " + name);
        // 正在执行中 RUNNING
        // 反馈数据 给 Activity
        // 把任务中的数据回传到Activity中
        Data build = new Data.Builder().putString("name", "WorkManager2").build();
        // return new Result.Failure(); // 本地执行 doWork 任务时 失败
        // return new Result.Retry(); // 本地执行 doWork 任务时 重试一次
        // return new Result.Success(); // 本地执行 doWork 任务时 成功 执行任务完毕
        return Result.success(build);
    }
}
  • Activity
    /**
     * 数据 互相传递
     *
     * @param view
     */
    public void testBackgroundWork2(View view) {
        // 数据
        Data data = new Data.Builder().putString("name", "WorkManagerActivity").build();
        // 请求对象初始化
        OneTimeWorkRequest build = new OneTimeWorkRequest.Builder(WorkManager2.class)
                .setInputData(data)
                .build();
        // 一般都是通过 状态机 接收 WorkManager2的回馈数据
        // 状态机(LiveData) 才能接收 WorkManager回馈的数据
        WorkManager.getInstance(this).getWorkInfoByIdLiveData(build.getId())
                .observe(this, new Observer<WorkInfo>() {
                    @Override
                    public void onChanged(WorkInfo workInfo) {
                        // ENQUEUED,RUNNING,SUCCEEED
                        Log.d(WorkManager2.TAG, "状态:" + workInfo.getState().name());
                        if (workInfo.getState().isFinished()) { // 判断成功 SUCCEEDED状态
                            Log.d(WorkManager2.TAG, "取到了任务回传的数据: "
                                    + workInfo.getOutputData().getString("name"));
                        }
                    }
                });
        WorkManager.getInstance(this).enqueue(build);
    }

二、初始化源码

2.1、初始化

从这边 ContentProvider 开始执行,这是自动给我们加入的。


@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class WorkManagerInitializer extends ContentProvider {
    @Override
    public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }
}

由于WorkManager是个单例,在此时WorkManager就已经被初始化了,在initialize()之前,会创建一个默认的ConfigurationConfiguration设置了许多属性,用来管理和调度工作的方式,看initialize()的实现,由于WorkManager是个抽象类,真正的构造方法是在他的子类WorkManagerImpl实现的。

  • 执行WorkManager#initialize方法
    public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
        WorkManagerImpl.initialize(context, configuration);
    }
  • WorkManager的实现类WorkManagerImpl中初始化
    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) {
                     //初始化 WorkManagerImpl
                    sDefaultInstance = new WorkManagerImpl(
                            context,
                            configuration,
                            new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
                }
                sDelegatedInstance = sDefaultInstance;
            }
        }
    }

此时sDelegatedInstance为 null,WorkManager会先创建一个默认的WorkManagerTaskExecutor对象,用来执行WorkManager的任务,下面创建一个WorkManagerImpl对象:

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

WorkDatabase中执行 create() 方法来创建ROOM数据库。

    public static WorkDatabase create(
            @NonNull final Context context,
            @NonNull Executor queryExecutor,
            boolean useTestDatabase) {
        RoomDatabase.Builder<WorkDatabase> builder;
        if (useTestDatabase) {
            builder = Room.inMemoryDatabaseBuilder(context, WorkDatabase.class)
                    .allowMainThreadQueries();
        } else {
            String name = WorkDatabasePathHelper.getWorkDatabaseName();
            builder = Room.databaseBuilder(context, WorkDatabase.class, name);
            builder.openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
                @NonNull
                @Override
                public SupportSQLiteOpenHelper create(
                        @NonNull SupportSQLiteOpenHelper.Configuration configuration) {
                    SupportSQLiteOpenHelper.Configuration.Builder configBuilder =
                            SupportSQLiteOpenHelper.Configuration.builder(context);
                    configBuilder.name(configuration.name)
                            .callback(configuration.callback)
                            .noBackupDirectory(true);
                    FrameworkSQLiteOpenHelperFactory factory =
                            new FrameworkSQLiteOpenHelperFactory();
                    return factory.create(configBuilder.build());
                }
            });
        }

        return builder.setQueryExecutor(queryExecutor)
                .addCallback(generateCleanupCallback())
                .addMigrations(WorkDatabaseMigrations.MIGRATION_1_2)
                .addMigrations(
                        new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_2,
                                VERSION_3))
                .addMigrations(MIGRATION_3_4) //版本升级
                .addMigrations(MIGRATION_4_5)
                .addMigrations(
                        new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_5,
                                VERSION_6))
                .addMigrations(MIGRATION_6_7)
                .addMigrations(MIGRATION_7_8)
                .addMigrations(MIGRATION_8_9)
                .addMigrations(new WorkDatabaseMigrations.WorkMigration9To10(context))
                .addMigrations(
                        new WorkDatabaseMigrations.RescheduleMigration(context, VERSION_10,
                                VERSION_11))
                .fallbackToDestructiveMigration()
                .build();
    }

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

    @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, workTaskExecutor); 
        //步骤二
        Processor processor = new Processor(
                context,
                configuration,
                workTaskExecutor,
                database,
                schedulers);
        //步骤三
        internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
    }

这里有三个重要的初始化步骤。

  • 步骤一:createSchedulers()创建的Schedulers进行任务调度
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @NonNull
    public List<Scheduler> createSchedulers(
            @NonNull Context context,
            @NonNull TaskExecutor taskExecutor) {
        
        //返回一个Scheduler数组。其中GreedyScheduler() 是实例化好了哦
        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this),
                // 埋下伏笔 GreedyScheduler贪婪调度程序
                new GreedyScheduler(context, taskExecutor, this));
    }
  • 步骤二:Processor()用来管理Schedulers的执行。
public WorkManagerImpl(...) {
      ...
        List<Scheduler> schedulers = createSchedulers(applicationContext, workTaskExecutor);
        Processor processor = new Processor(
                context,
                configuration,
                workTaskExecutor,
                database,
                schedulers);
        ....
    }
  • 步骤三:internalInit()真正的初始化。先看createSchedulers()的实现。
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;

        // Checks for app force stops.  
        // 这里的 【检查应用程序强制停止】 
        //例如:正在执行任务的时候,手机关机了,或者发生了意外,这里就会重试之前失败的任务哦
        mWorkTaskExecutor.executeOnBackgroundThread(new ForceStopRunnable(context, this));
    }
  • 任务是执行ForceStopRunnable#run方法
public class ForceStopRunnable implements Runnable { 
    private final Context mContext;
    private final WorkManagerImpl mWorkManager;

    public ForceStopRunnable(@NonNull Context context, @NonNull WorkManagerImpl workManager) {
        mContext = context.getApplicationContext();
        mWorkManager = workManager;
    }

    @Override
    public void run() {
        WorkDatabasePathHelper.migrateDatabase(mContext);
        Logger.get().debug(TAG, "Performing cleanup operations.");
        try {
            boolean needsScheduling = cleanUp();
            if (shouldRescheduleWorkers()) {
                Logger.get().debug(TAG, "Rescheduling Workers.");
                mWorkManager.rescheduleEligibleWork();
                // Mark the jobs as migrated.
                mWorkManager.getPreferenceUtils().setNeedsReschedule(false);
            } else if (isForceStopped()) {
                Logger.get().debug(TAG, "Application was force-stopped, rescheduling.");
                mWorkManager.rescheduleEligibleWork();
            } else if (needsScheduling) {
                Logger.get().debug(TAG, "Found unfinished work, scheduling it.");
                Schedulers.schedule(
                        mWorkManager.getConfiguration(),
                        mWorkManager.getWorkDatabase(),
                        mWorkManager.getSchedulers());
            }
            mWorkManager.onForceStopRunnableCompleted();
        } catch (SQLiteCantOpenDatabaseException
                | SQLiteDatabaseCorruptException
                | SQLiteAccessPermException exception) {
            String message =
                    "The file system on the device is in a bad state. WorkManager cannot access "
                            + "the app's internal data store.";
            Logger.get().error(TAG, message, exception);
            throw new IllegalStateException(message, exception);
        }
    }
}

以上代码的Runnable的作用就是在WorkManager初始化过程中,发现了未完成的,需要重新执行的任务。

2.2、总结

WorkManager的初始化 总结:

① WorkManager的初始化是由WorkManagerInitializer这个ContentProvider执行的

② 会初始化 Configuration,WorkManagerTaskExecutor,WorkDatabase,Schedulers,Processor

③ GreedyScheduler 埋下伏笔

④ 发现了未完成的,需要重新执行的任务(之前 意外 中断 的继续执行)

流程图

三、非约束条件任务的执行源码

3.1、源码流程

  • 开始执行
WorkManager.getInstance(this).enqueue(request)

进入WorkManager#enqueue方法

    public final Operation enqueue(@NonNull WorkRequest workRequest) {
        return enqueue(Collections.singletonList(workRequest));
    }
public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);
  • 执行 WorkManager的实现类WorkManagerImpl的实现方法
    public Operation enqueue(
            @NonNull List<? extends WorkRequest> workRequests) {
        //请求 = null,抛出移出
        if (workRequests.isEmpty()) {
            throw new IllegalArgumentException(
                    "enqueue needs at least one WorkRequest.");
        }
        return new WorkContinuationImpl(this, workRequests).enqueue();
    }

创建一个WorkContinuationImpl()对象,再执行enqueue()方法。WorkContinuationImplWorkContinuation的子类。

  • 创建WorkContinuationImpl
public class WorkContinuationImpl extends WorkContinuation {
    private final WorkManagerImpl mWorkManagerImpl;
    private final String mName;
    private final ExistingWorkPolicy mExistingWorkPolicy;
    private final List<? extends WorkRequest> mWork;
    private final List<String> mIds;
    private final List<String> mAllIds;
    private final List<WorkContinuationImpl> mParents;

    private boolean mEnqueued;
    private Operation mOperation;

    WorkContinuationImpl(
            @NonNull WorkManagerImpl workManagerImpl,
            @NonNull List<? extends WorkRequest> work) {
        this(
                workManagerImpl,
                null,
                ExistingWorkPolicy.KEEP,
                work,
                null);
    }

    WorkContinuationImpl(
            @NonNull WorkManagerImpl workManagerImpl,
            String name,
            ExistingWorkPolicy existingWorkPolicy,
            @NonNull List<? extends WorkRequest> work) {
        this(workManagerImpl, name, existingWorkPolicy, work, null);
    }

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

WorkContinuation保存任务相关的所有信息,像这个 WorkManager,WorkRequest,父WorkContinuation ....

  • 我们再接着继续看WorkContinuationImpl的enqueue()方法的实现:

WorkContinuationImpl#enqueue方法

    public @NonNull Operation enqueue() {
        // Only enqueue if not already enqueued.
        if (!mEnqueued) {
            //包装 任务
            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执行了EnqueueRunnable,那么需要看EnqueueRunnablerun函数:

public class EnqueueRunnable implements Runnable {

    private static final String TAG = Logger.tagWithPrefix("EnqueueRunnable");

    private final WorkContinuationImpl mWorkContinuation;
    private final OperationImpl mOperation;

    public EnqueueRunnable(@NonNull WorkContinuationImpl workContinuation) {
        mWorkContinuation = workContinuation;
        mOperation = new OperationImpl();
    }

    @Override
    public void run() {
        try {
            if (mWorkContinuation.hasCycles()) {
                throw new IllegalStateException(
                        String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
            }
            //addToDatabase()的作用是把WorkSpec存入到数据库,并对任务的状态进行校验
            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));
        }
    }
}
  • 执行EnqueueRunnable#scheduleWorkInBackground方法
    @VisibleForTesting
    public void scheduleWorkInBackground() {
        WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
        
        // 下面重点就是看着这个函数了
        Schedulers.schedule(
                workManager.getConfiguration(),
                workManager.getWorkDatabase(),
                workManager.getSchedulers());
    }

下面 调用了Schedulers.schedule()方法,传入了Configuration, WorkDatabase, Scheduler这三个对象。执行schedule()函数:

  • Schedulers#schedule
// Schedulers.java 类
    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> eligibleWorkSpecs;
        //开始事务
        workDatabase.beginTransaction();
        try {
            eligibleWorkSpecs = workSpecDao.getEligibleWorkForScheduling(
                    configuration.getMaxSchedulerLimit());
            if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
                long now = System.currentTimeMillis();
                for (WorkSpec workSpec : eligibleWorkSpecs) {
                    //先进行了一系列的数据库操作,然后开始根据条件每个任务进行调度,更新保存数据库等操作
                    workSpecDao.markWorkSpecScheduled(workSpec.id, now);
                }
            }
            workDatabase.setTransactionSuccessful();
        } finally {
            //结束事务
            workDatabase.endTransaction();
        }

        if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
            WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
            // Delegate to the underlying scheduler.
            for (Scheduler scheduler : schedulers) {
                // scheduler.schedule()对每个任务进行调度处理,我们是没有约束的一次性任务,所以看一下GreedyScheduler对于schedule()方法的实现
                scheduler.schedule(eligibleWorkSpecsArray);
            }
        }
    }
  • 回到上面初始化创建的GreedyScheduler类中的schedule方法
  // GreedyScheduler.java 类 贪婪调度程序执行 schedule
    @Override
    public void schedule(@NonNull WorkSpec... workSpecs) {
        if (mIsMainProcess == null) {
            // The default process name is the package name.
            mIsMainProcess = TextUtils.equals(mContext.getPackageName(), getProcessName());
        }

        if (!mIsMainProcess) {
            Logger.get().info(TAG, "Ignoring schedule request in non-main process");
            return;
        }

        registerExecutionListenerIfNeeded();

        List<WorkSpec> constrainedWorkSpecs = new ArrayList<>();
        List<String> constrainedWorkSpecIds = new ArrayList<>();
        for (WorkSpec workSpec : workSpecs) {
            if (workSpec.state == WorkInfo.State.ENQUEUED
                    && !workSpec.isPeriodic()
                    && workSpec.initialDelay == 0L
                    && !workSpec.isBackedOff()) {
                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));
                    // 由于无约束条件,所以进入此else阶段 的startWork这个函数
                    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);
            }
        }
    }
  • 执行WorkManagerImpl#startWork方法
    public void startWork(@NonNull String workSpecId) {
        startWork(workSpecId, null);
    }
    // WorkManagerImpl.java 类
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public void startWork(
            @NonNull String workSpecId,
            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
        
        // WorkTaskExecutor对任务进行了调度,下面进入StartWorkRunnable的run()的实现,
        //切入后台线程
        mWorkTaskExecutor
                .executeOnBackgroundThread(
                        new StartWorkRunnable(this, workSpecId, runtimeExtras));
    }
  • StartWorkRunnable
public class StartWorkRunnable implements Runnable {

    private WorkManagerImpl mWorkManagerImpl;
    private String mWorkSpecId;
    private WorkerParameters.RuntimeExtras mRuntimeExtras;

    public StartWorkRunnable(
            WorkManagerImpl workManagerImpl,
            String workSpecId,
            WorkerParameters.RuntimeExtras runtimeExtras) {
        mWorkManagerImpl = workManagerImpl;
        mWorkSpecId = workSpecId;
        mRuntimeExtras = runtimeExtras;
    }

    @Override
    public void run() {
        //将任务的信息交给Processor,由Processor调用startWork()去执行任务,下面进入Processor类:
        mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
    }
}
  • 进入Processor#startWork 方法
    public boolean startWork(
            @NonNull String id,
            @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
        
        WorkerWrapper workWrapper;
        synchronized (mLock) {
            if (mEnqueuedWorkMap.containsKey(id)) {
                Logger.get().debug(
                        TAG,
                        String.format("Work %s is already enqueued for processing", id));
                return false;
            }
            //创建WorkerWrapper  任务
            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);
        }
        //创建了一个WorkerWrapper的Runnable对象,交由WorkTaskExecutor调度处理。
        //WorkerWrapper的run()方法的实现,下面就要进入 WorkerWrapper类了
        mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);
        Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
        return true;
    }
  • 进入WorkerWrapper#run方法
    // WorkerWrapper.java类
    @WorkerThread
    @Override
    public void run() {
        mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
        mWorkDescription = createWorkDescription(mTags);
        // 下面会进入此函数哦
        runWorker();
    }
    //WorkerWrapper.java类
    private void runWorker() {
        if (tryCheckForInterruptionAndResolve()) {
            return;
        }

        mWorkDatabase.beginTransaction();
        try {
            mWorkSpec = mWorkSpecDao.getWorkSpec(mWorkSpecId);
            ...
            mWorkDatabase.setTransactionSuccessful();
        } finally {
            mWorkDatabase.endTransaction();
        }

        // Merge inputs.  This can be potentially expensive code, so this should not be done inside
        // a database transaction.
        ...

        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.
        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 {
                                Logger.get().debug(TAG, String.format("Starting work for %s",
                                        mWorkSpec.workerClassName));
                                
                                //我们看到调用了mWorker.startWork()方法
                                mInnerFuture = mWorker.startWork();
                                future.setFuture(mInnerFuture);
                            } catch (Throwable e) {
                                future.setException(e);
                            }

                        }
                    });

            // Avoid synthetic accessors.
            ...
    }
  • 执行Worker#startWork方法
    //需要自己实现的抽象方法
    public abstract @NonNull Result doWork();

    @Override
    public final @NonNull ListenableFuture<Result> startWork() {
        mFuture = SettableFuture.create();
        getBackgroundExecutor().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    // 此doWork函数 就是调用自定义的Workr
                    Result result = doWork();
                    mFuture.set(result);
                } catch (Throwable throwable) {
                    mFuture.setException(throwable);
                }

            }
        });
        return mFuture;
    }

同学们,这就是我们自己写的,自定义任务:

public class UploadWorker extends Worker {

    private final static String TAG = UploadWorker.class.getSimpleName();

    public UploadWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG, "doWork: run ... ");
        return Result.success();
    }
}

3.2、总结

WorkManager的 无约束的任务的执行源码 总结:

  1. WorkManager执行了enqueue()后,创建WorkContinuationImpl对象执行enqueue()方法。
  2. WorkContinuationImpl持有的EnqueueRunnable对象将任务添加到db,并交给Schedulers去调度。
  3. Schedulers将任务交给每一个Scheduler去处理,GreedyScheduler会先处理这个任务。
  4. GreedyScheduler经过一系列判断后,调用WorkManager的startWork()方法执行这种一次性,非延迟,无约束的任务。
  5. WorkManager持有的StartWorkRunnable对象会将任务交给Processor去处理,执行startWork()方法。
  6. Processor创建一个WorkerWrapper对象,由它去调用Worker的startWork()方法。

四、有约束条件任务的执行源码

WorkManager的有约束条件任务的执行源码:(非低电量才能执行)

4.1、源码分析

<receiver
     android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
      android:enabled="false"
      android:exported="false"
      android:directBootAware="false">
      <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.java 类 约束代理广播接收者类
abstract class ConstraintProxy extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Logger.get().debug(TAG, String.format("onReceive : %s", intent));
        Intent constraintChangedIntent = CommandHandler.createConstraintsChangedIntent(context);
        context.startService(constraintChangedIntent);
    }
}
  • 执行 CommandHandler#createConstraintsChangedInten方法
    // ConstraintProxy.java类
    static Intent createConstraintsChangedIntent(@NonNull Context context) {
        Intent intent = new Intent(context, SystemAlarmService.class);
        intent.setAction(ACTION_CONSTRAINTS_CHANGED);
        return intent;
    }
  • 会启动此服务SystemAlarmService,执行onStartCommand()方法处理如下:
@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        ... ...

        if (intent != null) {
            // 调用了SystemAlarmDispatcher.add()方法
            mDispatcher.add(intent, startId);
        }
        return Service.START_REDELIVER_INTENT;
    }
  • SystemAlarmDispatcher#add
//SystemAlarmDispatcher
@MainThread
    public boolean add(@NonNull final Intent intent, final int startId) {
        //检测线程
        assertMainThread();
        String action = intent.getAction();
        if (TextUtils.isEmpty(action)) {
            Logger.get().warning(TAG, "Unknown command. Ignoring");
            return false;
        }
        if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
                && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
            return false;
        }

        intent.putExtra(KEY_START_ID, startId);
        synchronized (mIntents) {
            boolean hasCommands = !mIntents.isEmpty();
            mIntents.add(intent);
            if (!hasCommands) {
                // 方法中执行了processCommand()
                processCommand();
            }
        }
        return true;
    }

    private void assertMainThread() {
        if (mMainHandler.getLooper().getThread() != Thread.currentThread()) {
            throw new IllegalStateException("Needs to be invoked on the main thread.");
        }
    }
// SystemAlarmDispatcher.java 类
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);
                        Logger.get().debug(TAG,
                                String.format("Processing command %s, %s", mCurrentIntent,
                                        startId));
                        final PowerManager.WakeLock wakeLock = WakeLocks.newWakeLock(
                                mContext,
                                String.format("%s (%s)", action, startId));
                        try {
                            Logger.get().debug(TAG, String.format(
                                    "Acquiring operation wake lock (%s) %s",
                                    action,
                                    wakeLock));

                            wakeLock.acquire();
                            
                            // 这个代码就是核心,等下下面我们就要分析
                            mCommandHandler.onHandleIntent(mCurrentIntent, startId,
                                    SystemAlarmDispatcher.this);
                        } catch (Throwable throwable) {
                            Logger.get().error(
                                    TAG,
                                    "Unexpected error in onHandleIntent",
                                    throwable);
                        }  finally {
                            Logger.get().debug(
                                    TAG,
                                    String.format(
                                            "Releasing operation wake lock (%s) %s",
                                            action,
                                            wakeLock));
                            wakeLock.release();
                            // Check if we have processed all commands
                            postOnMainThread(
                                    new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
                        }
                    }
                }
            });
        } finally {
            processCommandLock.release();
        }
    }
  • 执行 CommandHandler#onHandleIntent方法,action为ACTION_CONSTRAINTS_CHANGED
//CommandHandler 类
 if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
            handleConstraintsChanged(intent, startId, dispatcher);
        } 
    //CommandHandler.java 类
    private void handleConstraintsChanged(
            @NonNull Intent intent, int startId,
            @NonNull SystemAlarmDispatcher dispatcher) {
        ConstraintsCommandHandler changedCommandHandler =
                new ConstraintsCommandHandler(mContext, startId, dispatcher);
        changedCommandHandler.handleConstraintsChanged();
    }

创建一个action为ACTION_DELAY_MET的Intent然后由SystemAlarmDispatcher发送出去,实际上也是调用了SystemAlarmDispatcher.add()方法,注意会回到SystemAlarmDispatcher的add()流程哦:

//ConstraintsCommandHandler
    void handleConstraintsChanged() {
        ...
        for (WorkSpec workSpec : eligibleWorkSpecs) {
            String workSpecId = workSpec.id;
            Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
            mDispatcher.postOnMainThread(
                    new SystemAlarmDispatcher.AddRunnable(mDispatcher, intent, mStartId));
        }
        mWorkConstraintsTracker.reset();
    }
  • 调用 CommandHandler#createDelayMetIntent创建 Intent
    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;
    }

当再次回到onHandleIntent()方法,在CommandHandleronHandleIntent()方法中,action为ACTION_DELAY_MET的执行是。

// CommandHandler.java 类
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));
            }
        }
    }
流程图
抽象图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270

推荐阅读更多精彩内容