jetpack - WorkManager 简单使用和源码分析

709 阅读9分钟

WorkManager的介绍

WorkManager API 是一个适合用来替换所有先前的 Android 后台调度 API(包括 FirebaseJobDispatcher、GcmNetworkManager和 JobScheduler的组件

WorkManager 底层作业调度服务的逻辑

image.png

WorkManager的优点

  • 工作约束

    使用约束明确定义工作运行的最佳条件(例如,仅在设备采用 Wi-Fi 网络连接时、当设备处于空闲状态或者有足够的存储空间时运行。)

  • 强大的调度

    WorkManager 允许您使用灵活的调度窗口调度工作,以运行一次性或重复工作。您还可以对工作进行标记或命名,以便调度唯一的、可替换的工作以及监控或取消工作组。已调度的工作存储在内部托管的 SQLite 数据库中,由 WorkManager 负责确保该工作持续进行,并在设备重新启动后重新调度。此外,WorkManager 遵循低电耗模式等省电功能和最佳做法

  • 灵活的重试政策

有时工作会失败。WorkManager 提供了灵活的重试政策,包括可配置的指数退避政策。

  • 工作链

    对于复杂的相关工作,您可以使用流畅自然的接口将各个工作任务串联起来,这样您便可以控制哪些部分依序运行,哪些部分并行运行。

    对于每项工作任务,您可以定义工作的输入和输出数据。将工作串联在一起时,WorkManager 会自动将输出数据从一个工作任务传递给下一个工作任务

  • 内置线程互操作性

    WorkManager 无缝集成RxJava 和协程,并可灵活地插入您自己的异步 API。

使用入门

添加依赖
implementation 'androidx.work:work-runtime-ktx:2.7.1'
简单

定义任务

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

    override fun doWork(): Result {
        uploadImages()
        return Result.success() //工作完成
    }

    /**
     * 上传图片
     */
    private fun uploadImages() {

    }
}

提交任务

binding?.clickBtn?.setOnClickListener {
    Log.d("MainActivity","按钮点击事件")
    val uploadWorkerRequest=OneTimeWorkRequestBuilder<UploadWorker>().build()

    //将任务提交给WorkManager
    WorkManager.getInstance(this).enqueue(uploadWorkerRequest)
}
有数据交换

定义任务

class ChangeDataWorker(val appContext: Context, val workerParams: WorkerParameters): Worker(appContext,workerParams){

    override fun doWork(): Result {
        //接收传递过来的数据
        val data = workerParams.inputData.getString("Data")
        Log.d("ChangeDataWorker","data:$data")
        //将数据返回出去
        var returnData=Data.Builder().putString("returnData","doWork").build()
        return  Result.success(returnData)
    }
}

提交任务

binding?.clickBtn?.setOnClickListener {
    Log.d("MainActivity","按钮点击事件")
    var data= Data.Builder().putString("Data","给workmanager传递数据").build()

    val changeDataWorker=OneTimeWorkRequestBuilder<ChangeDataWorker>()
        .setInputData(data)
        .build()
    //想接收任务回馈的数据,需要状态机 livedata
    WorkManager.getInstance(this).getWorkInfoByIdLiveData(changeDataWorker.id)
        .observe(this,
            {
                Log.d("MainActivity","状态:${it.state.name}")
                if(it.state.isFinished){
                    Log.d("MainActivity","回传的数据:${it.outputData.getString("returnData")}")
                    Log.d("MainActivity","状态:isFinished = true 后台任务完成")
                }
            })
    //将任务提交给WorkManager
    WorkManager.getInstance(this).enqueue(changeDataWorker)
}
多任务(单任务)
binding?.clickBtn?.setOnClickListener {
    Log.d("MainActivity","按钮点击事件")
    var task1=OneTimeWorkRequestBuilder<TaskWorker1>().build()
    var task2=OneTimeWorkRequestBuilder<TaskWorker2>().build()
    var task3=OneTimeWorkRequestBuilder<TaskWorker3>().build()

    //第一种 按顺序执行 task1 task2 task3
    WorkManager.getInstance(this)
        .beginWith(task1)
        .then(task2)
        .then(task3)
        .enqueue()
   //第二种
   var list= mutableListOf<OneTimeWorkRequest>()
    list.add(task1)
    list.add(task2)

    // 先执行task1 task2 最后执行task3
    WorkManager.getInstance(this).beginWith(list)
        .then(task3).enqueue()
}
重复执行任务
binding?.clickBtn?.setOnClickListener {
    // google规定,重复执行的任务时间间隔必须大于等于15分钟,如果你设置的时间小于15分钟会按照15分钟执行
    var periodicWorkRequest=PeriodicWorkRequestBuilder<ChangeDataWorker>(10,TimeUnit.SECONDS).build()
    //监听状态
    WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id)
        .observe(this,{
            //轮询任务的状态一直是ENQUEUE
            Log.d("MainActivty","状态:${it.state.name}")
            
        })
    WorkManager.getInstance(this).enqueue(periodicWorkRequest)
}
约束条件
binding?.clickBtn?.setOnClickListener {
    //约束条件
    var constraints=Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)//  网络连接中
        .setRequiresCharging(true) // 充电中
        .setRequiresDeviceIdle(true) //空闲时
        .build()

    var request=OneTimeWorkRequestBuilder<UploadWorker>()
        .setConstraints(constraints)
        .build()

    //加入队列
    WorkManager.getInstance(this).enqueue(request)
}

源码分析

初始化
WorkManager.getInstance(this)

进入getInstance方法

public static @NonNull WorkManager getInstance(@NonNull Context context) {
    return WorkManagerImpl.getInstance(context);
}

进入WorkManagerImpl类的getInstance(context)方法

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
    synchronized (sLock) {
        WorkManagerImpl instance = getInstance();
        //在APK清单文件中执行第一次,这个是第二次执行
        if (instance == null) {
            Context appContext = context.getApplicationContext();
            if (appContext instanceof Configuration.Provider) {
                initialize(
                        appContext,
                        ((Configuration.Provider) appContext).getWorkManagerConfiguration());
                instance = getInstance(appContext);
            } else {
                throw new IllegalStateException("WorkManager is not initialized properly.  You "
                        + "have explicitly disabled WorkManagerInitializer in your manifest, "
                        + "have not manually called WorkManager#initialize at this point, and "
                        + "your Application does not implement Configuration.Provider.");
            }
        }

        return instance;
    }
}

第二次执行只是为了拿到WorkManager的实现类

查看清单文件

image.png

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

进入initialize方法

public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
    WorkManagerImpl.initialize(context, configuration);
}

进入WorkManagerImpl类的initialize方法

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

最终会进入WorkManagerImpl这个构造函数

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

调用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;//room数据库
    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));
}

第一次初始化做的事情:

  • 初始化room数据库来保存任务(持久性保存)
  • 初始化GreedyScheduler(贪婪执行器)
  • 初始化配置信息
加入队列执行
public final Operation enqueue(@NonNull WorkRequest workRequest) {
    return enqueue(Collections.singletonList(workRequest));
}

enqueue是一个抽象方法

public abstract Operation enqueue(@NonNull List<? extends WorkRequest> requests);

进入实现类WorkManagerImpl的enqueue方法

@Override
@NonNull
public Operation enqueue(
        @NonNull List<? extends WorkRequest> requests) {
    if (requests.isEmpty()) {
        throw new IllegalArgumentException(
                "enqueue needs at least one WorkRequest.");
    }
    return new WorkContinuationImpl(this, requests).enqueue();
}

进入WorkContinuationImpl类的enqueue方法

@Override
public @NonNull Operation enqueue() {
    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;
}

进入EnqueueRunnable的run方法

public void run() {
    try {
        if (mWorkContinuation.hasCycles()) {
            throw new IllegalStateException(
                    String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
        }
        //建立数据库
        boolean needsScheduling = addToDatabase();
        if (needsScheduling) {
            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));
    }
}

进入addToDatabase方法,建立数据库

public boolean addToDatabase() {
    WorkManagerImpl workManagerImpl = mWorkContinuation.getWorkManagerImpl();
    WorkDatabase workDatabase = workManagerImpl.getWorkDatabase();
    workDatabase.beginTransaction();
    try {
        boolean needsScheduling = processContinuation(mWorkContinuation);
        workDatabase.setTransactionSuccessful();
        return needsScheduling;
    } finally {
        workDatabase.endTransaction();
    }
}

进入scheduleWorkInBackground方法执行调度

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) {
    if (schedulers == null || schedulers.size() == 0) {
        return;
    }

    WorkSpecDao workSpecDao = workDatabase.workSpecDao();
    List<WorkSpec> eligibleWorkSpecsForLimitedSlots;
    List<WorkSpec> allEligibleWorkSpecs;
    //开启事务(将任务更新到数据库中)
    workDatabase.beginTransaction();
    try {
        eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
                configuration.getMaxSchedulerLimit());

        allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(
                MAX_GREEDY_SCHEDULER_LIMIT);

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

            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);
          
        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);
        for (Scheduler scheduler : schedulers) {
            if (!scheduler.hasLimitedSchedulingSlots()) {
                //执行任务
                scheduler.schedule(enqueuedWorkSpecsArray);
            }
        }
    }
}

scheduler是一个接口,我们进入他的实现类GreedyScheduler中

image.png

进入schedule方法中

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

    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) {
                if (mDelayedWorkTracker != null) {
                    mDelayedWorkTracker.schedule(workSpec);
                }
            } else if (workSpec.hasConstraints()) {//有约束条件
                if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
                    Logger.get().debug(TAG,
                            String.format("Ignoring WorkSpec %s, Requires device idle.",
                                    workSpec));
                } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
                    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);
            }
        }
    }

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

public void startWork(
        @NonNull String workSpecId,
        @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
        //将任务丢给线程池执行
    mWorkTaskExecutor
            .executeOnBackgroundThread(
                    new StartWorkRunnable(this, workSpecId, runtimeExtras));
}

进入StartWorkRunnable类,查看run方法

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

进入Processor类中的startWork方法

public boolean startWork(
        @NonNull String id,
        @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {

    WorkerWrapper workWrapper;
    synchronized (mLock) {
        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;
}

进入WorkerWrapper,查看run方法

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

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

        if (mWorkSpec.isPeriodic() || mWorkSpec.isBackedOff()) {
            long now = System.currentTimeMillis();
         
            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));
                resolve(true);
                mWorkDatabase.setTransactionSuccessful();
                return;
            }
        }

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

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

    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));
                    // 关注点
                    mInnerFuture = mWorker.startWork();
                    future.setFuture(mInnerFuture);
                } catch (Throwable e) {
                    future.setException(e);
                }
            }
        }, mWorkTaskExecutor.getMainThreadExecutor());

        final String workDescription = mWorkDescription;
        future.addListener(new Runnable() {
            @Override
            @SuppressLint("SyntheticAccessor")
            public void run() {
                try {
                    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) {
                    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();
    }
}

startWork是一个抽象类里面的方法

public abstract @NonNull ListenableFuture<Result> startWork();

image.png 进入worker这个类的startWork方法

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

@WorkerThread
public abstract @NonNull Result doWork();

doWork是一个抽象方法,实现在我们自定义的任务里面。

约束的分析
abstract class ConstraintProxy extends BroadcastReceiver {
    private static final String TAG = Logger.tagWithPrefix("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);
    }
}

采用广播监听的形式接收变化,同时开启一个系统的服务

CommandHandler.createConstraintsChangedIntent 是创建一个系统的闹钟服务

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

进入SystemAlarmService类中

public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);
    if (mIsShutdown) {
        Logger.get().info(TAG,
                "Re-initializing SystemAlarmDispatcher after a request to shut-down.");
        mDispatcher.onDestroy();
        initializeDispatcher();
        mIsShutdown = false;
    }
    if (intent != null) {
        mDispatcher.add(intent, startId);
    }
    return Service.START_REDELIVER_INTENT;
}

进入SystemAlarmDispatcher类的add方法

public boolean add(@NonNull final Intent intent, final int startId) {
    Logger.get().debug(TAG, String.format("Adding command %s (%s)", intent, startId));
    assertMainThread();
    String action = intent.getAction();
    if (TextUtils.isEmpty(action)) {
        Logger.get().warning(TAG, "Unknown command. Ignoring");
        return false;
    }
    if (CommandHandler.ACTION_CONSTRAINTS_CHANGED.equals(action)
            && hasIntentWithAction(CommandHandler.ACTION_CONSTRAINTS_CHANGED)) {
        return false;
    }

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

进入processCommand方法

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();
                        postOnMainThread(
                                new DequeueAndCheckForCompletion(SystemAlarmDispatcher.this));
                    }
                }
            }
        });
    } finally {
        processCommandLock.release();
    }
}

进入CommandHandler类的 onHandleIntent(mCurrentIntent, startId,SystemAlarmDispatcher.this)方法

void onHandleIntent(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {

    String action = intent.getAction();
    //启动系统服务的是有一个标记就是ACTION_CONSTRAINTS_CHANGED
    if (ACTION_CONSTRAINTS_CHANGED.equals(action)) {
        handleConstraintsChanged(intent, startId, dispatcher);
    } else if (ACTION_RESCHEDULE.equals(action)) {
        handleReschedule(intent, startId, dispatcher);
    } else {
        Bundle extras = intent.getExtras();
        if (!hasKeys(extras, KEY_WORKSPEC_ID)) {
            Logger.get().error(TAG,
                    String.format("Invalid request for %s, requires %s.",
                            action,
                            KEY_WORKSPEC_ID));
        } else {
            if (ACTION_SCHEDULE_WORK.equals(action)) {
                handleScheduleWorkIntent(intent, startId, dispatcher);
            } else if (ACTION_DELAY_MET.equals(action)) {
                handleDelayMet(intent, startId, dispatcher);
            } else if (ACTION_STOP_WORK.equals(action)) {
                handleStopWork(intent, dispatcher);
            } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                handleExecutionCompleted(intent, startId);
            } else {
                Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
            }
        }
    }
}

进入handleConstraintsChanged方法

private void handleConstraintsChanged(
        @NonNull Intent intent, int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {

    Logger.get().debug(TAG, String.format("Handling constraints changed %s", intent));
    ConstraintsCommandHandler changedCommandHandler =
            new ConstraintsCommandHandler(mContext, startId, dispatcher);
    changedCommandHandler.handleConstraintsChanged();
}

进入ConstraintsCommandHandler类的handleConstraintsChanged方法

void handleConstraintsChanged() {
    List<WorkSpec> candidates = mDispatcher.getWorkManager().getWorkDatabase()
            .workSpecDao()
            .getScheduledWork();

    ConstraintProxy.updateAll(mContext, candidates);

    mWorkConstraintsTracker.replace(candidates);

    List<WorkSpec> eligibleWorkSpecs = new ArrayList<>(candidates.size());
    long now = System.currentTimeMillis();
    for (WorkSpec workSpec : candidates) {
        String workSpecId = workSpec.id;
        long triggerAt = workSpec.calculateNextRunTime();
        if (now >= triggerAt && (!workSpec.hasConstraints()
                || mWorkConstraintsTracker.areAllConstraintsMet(workSpecId))) {
            eligibleWorkSpecs.add(workSpec);
        }
    }

    for (WorkSpec workSpec : eligibleWorkSpecs) {
        String workSpecId = workSpec.id;
        Intent intent = CommandHandler.createDelayMetIntent(mContext, workSpecId);
        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));
    }

    mWorkConstraintsTracker.reset();
}

createDelayMetIntent方法会换掉标记 ,新标记CTION_DELAY_MET

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

会进入handleDelayMet(intent, startId, dispatcher)这个方法中

void onHandleIntent(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {
           。。。
            if (ACTION_SCHEDULE_WORK.equals(action)) {
                handleScheduleWorkIntent(intent, startId, dispatcher);
            } else if (ACTION_DELAY_MET.equals(action)) {
                handleDelayMet(intent, startId, dispatcher);
            } else if (ACTION_STOP_WORK.equals(action)) {
                handleStopWork(intent, dispatcher);
            } else if (ACTION_EXECUTION_COMPLETED.equals(action)) {
                handleExecutionCompleted(intent, startId);
            } else {
                Logger.get().warning(TAG, String.format("Ignoring intent %s", intent));
            }
        。。。
}

进入handleDelayMet方法

private void handleDelayMet(
        @NonNull Intent intent,
        int startId,
        @NonNull SystemAlarmDispatcher dispatcher) {

    Bundle extras = intent.getExtras();
    synchronized (mLock) {
        String workSpecId = extras.getString(KEY_WORKSPEC_ID);
        Logger.get().debug(TAG, String.format("Handing delay met for %s", workSpecId));

        if (!mPendingDelayMet.containsKey(workSpecId)) {
            DelayMetCommandHandler delayMetCommandHandler =
                    new DelayMetCommandHandler(mContext, startId, workSpecId, dispatcher);
            mPendingDelayMet.put(workSpecId, delayMetCommandHandler);
            delayMetCommandHandler.handleProcessWork();
        } else {
            Logger.get().debug(TAG,
                    String.format("WorkSpec %s is already being handled for ACTION_DELAY_MET",
                            workSpecId));
        }
    }
}

进入handleProcessWork方法

void handleProcessWork() {
    mWakeLock = WakeLocks.newWakeLock(
            mContext,
            String.format("%s (%s)", mWorkSpecId, mStartId));
    Logger.get().debug(TAG,
            String.format("Acquiring wakelock %s for WorkSpec %s", mWakeLock, mWorkSpecId));
    mWakeLock.acquire();

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

    if (workSpec == null) {
        stopWork();
        return;
    }

    mHasConstraints = workSpec.hasConstraints();

    if (!mHasConstraints) {
        Logger.get().debug(TAG, String.format("No constraints for %s", mWorkSpecId));
        onAllConstraintsMet(Collections.singletonList(mWorkSpecId));
    } else {
        mWorkConstraintsTracker.replace(Collections.singletonList(workSpec));
    }
}

进入onAllConstraintsMet方法

public void onAllConstraintsMet(@NonNull List<String> workSpecIds) {
    if (!workSpecIds.contains(mWorkSpecId)) {
        return;
    }

    synchronized (mLock) {
        if (mCurrentState == STATE_INITIAL) {
            mCurrentState = STATE_START_REQUESTED;

            Logger.get().debug(TAG, String.format("onAllConstraintsMet for %s", mWorkSpecId));
            //和上面的装饰任务的衔接上,
            boolean isEnqueued = mDispatcher.getProcessor().startWork(mWorkSpecId);

            if (isEnqueued) {
                mDispatcher.getWorkTimer()
                        .startTimer(mWorkSpecId, WORK_PROCESSING_TIME_IN_MS, this);
            } else {
                cleanUp();
            }
        } else {
            Logger.get().debug(TAG, String.format("Already started work for %s", mWorkSpecId));
        }
    }
}

常见面试题

workmanager的使用场景

处理非及时任务。例如:每天同步一次app的日志信息到服务,类似这种需求,不是执行执行但是又保证会执行的非及时任务。

workmanager怎么保证,当我把app杀掉后,依然会执行

记录的所有信息保存在数据库中,而不是内存中,持久性的保存记录,所以当app被杀掉之后,依然可以获取到需要执行的任务信息。

workmanager是怎么保证任务一定执行

Android操作系统会在系统级别服务中,来判定用户的约束条件,当约束条件满足时就会执行任务,但是触发检测采用的是广播的形式。