• WorkManager 一篇文章就够了


    使用篇

    首先引入WorkManager库(使用java和kotlin引入库不同,需要注意):

    1. // Kotlin + coroutines
    2. implementation 'androidx.work:work-runtime-ktx:2.7.1'

    单任务场景

    我们开始来写一个最简单的使用,先写一个Worker:

    1. const val TAG = "UploadWorker"
    2. class UploadWorker(appContext: Context, workerParameters: WorkerParameters) :
    3. Worker(appContext, workerParameters) {
    4. override fun doWork(): Result {
    5. Thread.sleep(6000)
    6. Log.d(TAG, "lzy是大帅哥!")
    7. return Result.success()
    8. }
    9. }

    注意:这个doWork方法是异步的,后面分析源码时会讲到。

    我们首先来个一次性任务:

    1. class MainActivity : AppCompatActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?) {
    3. super.onCreate(savedInstanceState)
    4. setContentView(R.layout.activity_main)
    5. initView()
    6. }
    7. private fun initView() {
    8. val btn = findViewById
    9. btn.setOnClickListener {
    10. val oneTimeWorkRequest = OneTimeWorkRequest
    11. .Builder(UploadWorker::class.java).build()
    12. WorkManager.getInstance().enqueue(oneTimeWorkRequest)
    13. }
    14. }
    15. }

    OneTimeRequest顾名思义就是一个一次性任务,然后通过enqueue方法放到workmanager里面去执行。

    布局文件:

    1. <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    2. xmlns:app="http://schemas.android.com/apk/res-auto"
    3. xmlns:tools="http://schemas.android.com/tools"
    4. android:layout_width="match_parent"
    5. android:layout_height="match_parent"
    6. tools:context=".MainActivity">
    7. <LinearLayout
    8. android:layout_width="match_parent"
    9. android:layout_height="match_parent"
    10. android:orientation="vertical"
    11. >
    12. <Button
    13. android:id="@+id/btn_01"
    14. android:layout_width="match_parent"
    15. android:layout_height="50dp"
    16. android:text="测试1"
    17. />
    18. <Button
    19. android:layout_marginTop="20dp"
    20. android:id="@+id/btn_02"
    21. android:layout_width="match_parent"
    22. android:layout_height="50dp"
    23. android:text="测试2"
    24. />
    25. ···
    26. LinearLayout>
    27. androidx.constraintlayout.widget.ConstraintLayout>

    运行效果图:

     点击测试1,等个6s:

    1. 2022-08-13 12:57:51.578 5703-5742/com.example.workmanagertest
    2. D/UploadWorker: lzy是大帅哥!
    3. 2022-08-13 12:57:51.581 5703-5730/com.example.workmanagertest
    4. I/WM-WorkerWrapper: Worker result SUCCESS for Work [
    5. id=74aa4669-28f0-4e20-93fc-2716f4ebc686,
    6. tags={ com.example.workmanagertest.UploadWorker } ]

    数据互传

    我们工程中一定会有大量activity和WorkManager传递参数的场景,那么该如何传递参数呢?接着往下看:

    1. const val TAG2 = "LzyWorkManager2"
    2. class LzyWorkManager2(appContext: Context, var workerParameters: WorkerParameters) :
    3. Worker(appContext, workerParameters) {
    4. override fun doWork(): Result {
    5. //接收数据
    6. val data = workerParameters.inputData.getString("lzy")
    7. Log.d(TAG2, "$data")
    8. return Result.success()
    9. }
    10. }
    11. val btn2 = findViewById
    12. btn2.setOnClickListener {
    13. val sendData = Data.Builder()
    14. .putString("lzy", "lzy是个人才啊!")
    15. .build()
    16. val oneTimeWorkRequest = OneTimeWorkRequest
    17. .Builder(LzyWorkManager2::class.java)
    18. .setInputData(sendData)
    19. .build()
    20. WorkManager.getInstance().enqueue(oneTimeWorkRequest)
    21. }

    activity里面先构建data,然后通过setInputData传入data,最后woker里面从params里面接收data。既然activity可以向worker里面发送data,那么woker里面该如何回传data呢?接着看:

    1. const val TAG2 = "LzyWorkManager2"
    2. class LzyWorkManager2(appContext: Context, var workerParameters: WorkerParameters) :
    3. Worker(appContext, workerParameters) {
    4. override fun doWork(): Result {
    5. //接收数据
    6. val data = workerParameters.inputData.getString("lzy")
    7. Log.d(TAG2, "$data")
    8. //回传数据
    9. val data2 = Data.Builder().putString("st", "st也很帅").build()
    10. return Result.success(data2)
    11. }
    12. }

    worker里面我们通过Result.success返回数据。再看activity如何处理:

    1. class MainActivity : AppCompatActivity() {
    2. override fun onCreate(savedInstanceState: Bundle?) {
    3. super.onCreate(savedInstanceState)
    4. setContentView(R.layout.activity_main)
    5. initView()
    6. }
    7. private fun initView() {
    8. val btn = findViewById
    9. btn.setOnClickListener {
    10. val oneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java).build()
    11. WorkManager.getInstance().enqueue(oneTimeWorkRequest)
    12. }
    13. val btn2 = findViewById
    14. btn2.setOnClickListener {
    15. val sendData = Data.Builder()
    16. .putString("lzy", "lzy是个人才啊!")
    17. .build()
    18. val oneTimeWorkRequest = OneTimeWorkRequest
    19. .Builder(LzyWorkManager2::class.java)
    20. .setInputData(sendData)
    21. .build()
    22. WorkManager.getInstance().enqueue(oneTimeWorkRequest)
    23. WorkManager.getInstance()
    24. .getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
    25. .observe(this) {
    26. val getData = it.outputData.getString("st")
    27. Log.d(TAG2, "$getData")
    28. }
    29. }
    30. }
    31. }

    很明显,WorkManager里面又封装了LiveData,是通过LiveData监听数据返回的。

    注:it是WorkInfo类型。

    按下测试2按钮,打印日志:

    1. 2022-08-13 13:35:57.619 6215-6215/com.example.workmanagertest
    2. D/LzyWorkManager2: null
    3. 2022-08-13 13:35:57.630 6215-6215/com.example.workmanagertest
    4. D/LzyWorkManager2: null
    5. 2022-08-13 13:35:57.631 6215-6248/com.example.workmanagertest
    6. D/LzyWorkManager2: lzy是个人才啊!
    7. 2022-08-13 13:35:57.632 6215-6240/com.example.workmanagertest
    8. I/WM-WorkerWrapper: Worker result SUCCESS for Work
    9. [ id=7555eb6d-95ab-4a25-8b5d-15b8d4709316, tags=
    10. { com.example.workmanagertest.LzyWorkManager2 } ]
    11. 2022-08-13 13:35:57.645 6215-6215/com.example.workmanagertest
    12. D/LzyWorkManager2: st也很帅

    我们发现有两个日志打印了null,这是什么原因呢?原来这个LiveData监听的是WorkInfo的状态变化,我们只有当数据拿到后再处理,优化一下代码:

    1. WorkManager.getInstance()
    2. .getWorkInfoByIdLiveData(oneTimeWorkRequest.id)
    3. .observe(this) {
    4. if (it.state.isFinished) {
    5. val getData = it.outputData.getString("st")
    6. Log.d(TAG2, "$getData")
    7. }
    8. }

    如果workInfo的状态是完成了,那么:

    1. 2022-08-13 13:50:31.397 6390-6425/com.example.workmanagertest
    2. D/LzyWorkManager2: lzy是个人才啊!
    3. 2022-08-13 13:50:31.397 6390-6412/com.example.workmanagertest
    4. I/WM-WorkerWrapper: Worker result SUCCESS for Work
    5. [ id=b7ea9abe-d89b-4c2c-b4b3-66ddb779f675,
    6. tags={ com.example.workmanagertest.LzyWorkManager2 } ]
    7. 2022-08-13 13:50:31.401 6390-6390/com.example.workmanagertest
    8. D/LzyWorkManager2: st也很帅

    这样就过滤掉一些中间状态了。

    多任务场景

    我们多定义几个Woker:

    1. class LzyWorkManager3(appContext: Context, workerParameters: WorkerParameters) :
    2. Worker(appContext, workerParameters) {
    3. override fun doWork(): Result {
    4. Log.e(TAG3, "work3 执行了")
    5. return Result.success()
    6. }
    7. }
    8. class LzyWorkManager4(appContext: Context, workerParameters: WorkerParameters) :
    9. Worker(appContext, workerParameters) {
    10. override fun doWork(): Result {
    11. Log.e(TAG3, "work4 执行了")
    12. return Result.success()
    13. }
    14. }
    15. class LzyWorkManager5(appContext: Context, workerParameters: WorkerParameters) :
    16. Worker(appContext, workerParameters) {
    17. override fun doWork(): Result {
    18. Log.e(TAG3, "work5 执行了")
    19. return Result.success()
    20. }
    21. }
    22. class LzyWorkManager6(appContext: Context, workerParameters: WorkerParameters) :
    23. Worker(appContext, workerParameters) {
    24. override fun doWork(): Result {
    25. Log.e(TAG3, "work6 执行了")
    26. return Result.success()
    27. }
    28. }

    activity里面事件:

    1. val btn3 = findViewById
    2. btn3.setOnClickListener {
    3. val oneTimeWorkRequest3 =
    4. OneTimeWorkRequest
    5. .Builder(LzyWorkManager3::class.java).build()
    6. val oneTimeWorkRequest4 =
    7. OneTimeWorkRequest
    8. .Builder(LzyWorkManager4::class.java).build()
    9. val oneTimeWorkRequest5 =
    10. OneTimeWorkRequest
    11. .Builder(LzyWorkManager5::class.java).build()
    12. val oneTimeWorkRequest6 =
    13. OneTimeWorkRequest
    14. .Builder(LzyWorkManager6::class.java).build()
    15. val requestList = ArrayList()
    16. requestList.add(oneTimeWorkRequest3)
    17. requestList.add(oneTimeWorkRequest5)
    18. requestList.add(oneTimeWorkRequest6)
    19. WorkManager.getInstance().beginWith(requestList)
    20. .then(oneTimeWorkRequest4)
    21. .enque

    首先获取每个单任务事件,然后把3,5,6加到一个集合里面,beginWith意思是开始执行任务,then意思是然后再执行这个任务,想必执行顺序大家已经猜到了:

    1. E/多任务: work3 执行了
    2. E/多任务: work6 执行了
    3. E/多任务: work5 执行了
    4. E/多任务: work4 执行了

    注意:beginWith和then是有顺序的,集合里面执行并不是按照加入顺序执行。

    周期性任务(重点)

    我们经常会接到类似这样的的需求:每隔一天上传一次日志,看起来很简单,但是实现起来却异常困难!Google应该也考虑到了这样的场景,于是在WorkManager里面加入了周期性支持!这样的话我们的代码逻辑就会非常优雅。

    1. val btn4 = findViewById
    2. btn4.setOnClickListener {
    3. val periodicWorkRequest =
    4. PeriodicWorkRequest
    5. .Builder(
    6. LzyWorkManager2::class.java,
    7. 10,
    8. TimeUnit.SECONDS
    9. )
    10. .build()
    11. WorkManager.getInstance().enqueue(periodicWorkRequest)
    12. }

    注意:周期性任务最少15min一次,如果设置小于15min,按15分钟一次执行。 

    原理篇

    我们只关注主线流程,注意下面的代码:

    WorkManager.getInstance().enqueue(oneTimeWorkRequest)

    这个流程可以拆解成两个部分,先来看第一部分:

    1. @Deprecated
    2. public static @NonNull WorkManager getInstance() {
    3. WorkManager workManager = WorkManagerImpl.getInstance();
    4. if (workManager == null) {
    5. throw new IllegalStateException("WorkManager is not initialized properly. The most "
    6. + "likely cause is that you disabled WorkManagerInitializer in your manifest "
    7. + "but forgot to call WorkManager#initialize in your Application#onCreate or a "
    8. + "ContentProvider.");
    9. } else {
    10. return workManager;
    11. }
    12. }

    我们发现里面调用了WorkManagerImpl的getInstance方法,暂且先不进去看。按照单例的常规思维,如果为null,初始化。但是发现没有,直接抛出一个异常(英文不好的自行翻译):

    1. WorkManager is not initialized properly.
    2. The most likely cause is that you disabled WorkManagerInitializer
    3. in your manifest but forgot to call
    4. WorkManager#initialize in your
    5. Application#onCreate or a ContentProvider.

    说明这个方法只是获取的而并非初始化的,那么初始化是在什么地方呢?

    我们点击生成的apk文件:

    进AndroidManifest.xml查看,注意这个provider:

    1. <provider
    2. android:name="androidx.startup.InitializationProvider"
    3. android:exported="false"
    4. android:authorities="com.example.workmanagertest.androidx-startup">
    5. <meta-data
    6. android:name="androidx.work.WorkManagerInitializer"
    7. android:value="androidx.startup" />
    8. provider>

    点进去看一看:

    1. public final class WorkManagerInitializer implements Initializer {
    2. private static final String TAG = Logger.tagWithPrefix("WrkMgrInitializer");
    3. @NonNull
    4. @Override
    5. public WorkManager create(@NonNull Context context) {
    6. // Initialize WorkManager with the default configuration.
    7. Logger.get().debug(TAG, "Initializing WorkManager with default configuration.");
    8. WorkManager.initialize(context, new Configuration.Builder().build());
    9. return WorkManager.getInstance(context);
    10. }
    11. @NonNull
    12. @Override
    13. public Listextends androidx.startup.Initializer>> dependencies() {
    14. return Collections.emptyList();
    15. }
    16. }

    很明显了,初始化是在ContentProvider里面进行的。进初始化方法看下:

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

    前面我们讲了,获取WorkManager是通过WorkManagerImpl进行的,初始化同样也是。

    1. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    2. public static void initialize(@NonNull Context context, @NonNull Configuration configuration) {
    3. synchronized (sLock) {
    4. if (sDelegatedInstance != null && sDefaultInstance != null) {
    5. throw new IllegalStateException("WorkManager is already initialized. Did you "
    6. + "try to initialize it manually without disabling "
    7. + "WorkManagerInitializer? See "
    8. + "WorkManager#initialize(Context, Configuration) or the class level "
    9. + "Javadoc for more information.");
    10. }
    11. if (sDelegatedInstance == null) {
    12. context = context.getApplicationContext();
    13. if (sDefaultInstance == null) {
    14. sDefaultInstance = new WorkManagerImpl(
    15. context,
    16. configuration,
    17. new WorkManagerTaskExecutor(configuration.getTaskExecutor()));
    18. }
    19. sDelegatedInstance = sDefaultInstance;
    20. }
    21. }
    22. }

    进WorkManagerImpl构造看下,最终的构造函数如下:

    1. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    2. public WorkManagerImpl(
    3. @NonNull Context context,
    4. @NonNull Configuration configuration,
    5. @NonNull TaskExecutor workTaskExecutor,
    6. @NonNull WorkDatabase database) {
    7. Context applicationContext = context.getApplicationContext();
    8. Logger.setLogger(new Logger.LogcatLogger(configuration.getMinimumLoggingLevel()));
    9. List schedulers =
    10. createSchedulers(applicationContext, configuration, workTaskExecutor);
    11. Processor processor = new Processor(
    12. context,
    13. configuration,
    14. workTaskExecutor,
    15. database,
    16. schedulers);
    17. internalInit(context, configuration, workTaskExecutor, database, schedulers, processor);
    18. }

    我们注意两个点就够了:

    • 使用了database:WorkManager能保证任务一定会被执行,即使你的应用程序当前不在运行中,哪怕你的设备重启,任务仍然会在适当的时候被执行。这是因为WorkManager有自己的数据库,关于任务的所有信息和数据都保存在这个数据库中,因此,只要你的任务交给了WorkManager,哪怕你的应用程序彻底退出,或者设备重新启动,WorkManager依然能够保证完成你交给的任务。
    • 使用了schedulers
    1. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    2. @NonNull
    3. public List createSchedulers(
    4. @NonNull Context context,
    5. @NonNull Configuration configuration,
    6. @NonNull TaskExecutor taskExecutor) {
    7. return Arrays.asList(
    8. Schedulers.createBestAvailableBackgroundScheduler(context, this),
    9. // Specify the task executor directly here as this happens before internalInit.
    10. // GreedyScheduler creates ConstraintTrackers and controllers eagerly.
    11. new GreedyScheduler(context, configuration, taskExecutor, this));
    12. }

    最终使用了贪婪调度器。

    再来分析第二步:

    1. @NonNull
    2. public final Operation enqueue(@NonNull WorkRequest workRequest) {
    3. return enqueue(Collections.singletonList(workRequest));
    4. }
    5. @NonNull
    6. public abstract Operation enqueue(@NonNull
    7. List requests);

    发现enqueue是一个抽象方法,我们去实现类WorkManagerImpl看看:

    1. @Override
    2. @NonNull
    3. public Operation enqueue(
    4. @NonNull List requests) {
    5. // This error is not being propagated as part of the Operation, as we want the
    6. // app to crash during development. Having no workRequests is always a developer error.
    7. if (requests.isEmpty()) {
    8. throw new IllegalArgumentException(
    9. "enqueue needs at least one WorkRequest.");
    10. }
    11. return new WorkContinuationImpl(this, requests).enqueue();
    12. }

    又交给了WorkContinuationImpl处理:构造方法里面传入了requests。

    1. rivate final Listextends WorkRequest> mWork;
    2. public WorkContinuationImpl(
    3. @NonNull WorkManagerImpl workManagerImpl,
    4. @NonNull List work) {
    5. this(
    6. workManagerImpl,
    7. null,
    8. ExistingWorkPolicy.KEEP,
    9. work,
    10. null);
    11. }
    12. public WorkContinuationImpl(@NonNull WorkManagerImpl workManagerImpl,
    13. @Nullable String name,
    14. @NonNull ExistingWorkPolicy existingWorkPolicy,
    15. @NonNull List work,
    16. @Nullable List parents) {
    17. mWorkManagerImpl = workManagerImpl;
    18. mName = name;
    19. mExistingWorkPolicy = existingWorkPolicy;
    20. mWork = work;
    21. mParents = parents;
    22. mIds = new ArrayList<>(mWork.size());
    23. mAllIds = new ArrayList<>();
    24. if (parents != null) {
    25. for (WorkContinuationImpl parent : parents) {
    26. mAllIds.addAll(parent.mAllIds);
    27. }
    28. }
    29. for (int i = 0; i < work.size(); i++) {
    30. String id = work.get(i).getStringId();
    31. mIds.add(id);
    32. mAllIds.add(id);
    33. }
    34. }

    我们再来看看WorkContinuationImpl的enqueue方法:

    1. @Override
    2. public @NonNull Operation enqueue() {
    3. // Only enqueue if not already enqueued.
    4. if (!mEnqueued) {
    5. // The runnable walks the hierarchy of the continuations
    6. // and marks them enqueued using the markEnqueued() method, parent first.
    7. EnqueueRunnable runnable = new EnqueueRunnable(this);
    8. mWorkManagerImpl.getWorkTaskExecutor()
    9. .executeOnBackgroundThread(runnable);
    10. mOperation = runnable.getOperation();
    11. } else {
    12. Logger.get().warning(TAG,
    13. String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
    14. }
    15. return mOperation;
    16. }

    先来看EnqueueRunnable是个啥:

    1. public class EnqueueRunnable implements Runnable {
    2. private final WorkContinuationImpl mWorkContinuation;
    3. private final OperationImpl mOperation;
    4. public EnqueueRunnable(@NonNull WorkContinuationImpl workContinuation) {
    5. mWorkContinuation = workContinuation;
    6. mOperation = new OperationImpl();
    7. }
    8. @Override
    9. public void run() {
    10. try {
    11. if (mWorkContinuation.hasCycles()) {
    12. throw new IllegalStateException(
    13. String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
    14. }
    15. boolean needsScheduling = addToDatabase();
    16. if (needsScheduling) {
    17. // Enable RescheduleReceiver, only when there are Worker's that need scheduling.
    18. final Context context =
    19. mWorkContinuation.getWorkManagerImpl().getApplicationContext();
    20. PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
    21. scheduleWorkInBackground();
    22. }
    23. mOperation.setState(Operation.SUCCESS);
    24. } catch (Throwable exception) {
    25. mOperation.setState(new Operation.State.FAILURE(exception));
    26. }
    27. }
    28. ```
    29. }

    首先将任务相关的信息加到数据库里面持久化,然后关注scheduleWorkInBackground方法:

    1. @VisibleForTesting
    2. public void scheduleWorkInBackground() {
    3. WorkManagerImpl workManager = mWorkContinuation.getWorkManagerImpl();
    4. Schedulers.schedule(
    5. workManager.getConfiguration(),
    6. workManager.getWorkDatabase(),
    7. workManager.getSchedulers());
    8. }
    1. public static void schedule(
    2. @NonNull Configuration configuration,
    3. @NonNull WorkDatabase workDatabase,
    4. List schedulers) {
    5. if (schedulers == null || schedulers.size() == 0) {
    6. return;
    7. }
    8. WorkSpecDao workSpecDao = workDatabase.workSpecDao();
    9. List eligibleWorkSpecsForLimitedSlots;
    10. List allEligibleWorkSpecs;
    11. workDatabase.beginTransaction();
    12. try {
    13. // Enqueued workSpecs when scheduling limits are applicable.
    14. eligibleWorkSpecsForLimitedSlots = workSpecDao.getEligibleWorkForScheduling(
    15. configuration.getMaxSchedulerLimit());
    16. // Enqueued workSpecs when scheduling limits are NOT applicable.
    17. allEligibleWorkSpecs = workSpecDao.getAllEligibleWorkSpecsForScheduling(
    18. MAX_GREEDY_SCHEDULER_LIMIT);
    19. if (eligibleWorkSpecsForLimitedSlots != null
    20. && eligibleWorkSpecsForLimitedSlots.size() > 0) {
    21. long now = System.currentTimeMillis();
    22. // Mark all the WorkSpecs as scheduled.
    23. // Calls to Scheduler#schedule() could potentially result in more schedules
    24. // on a separate thread. Therefore, this needs to be done first.
    25. for (WorkSpec workSpec : eligibleWorkSpecsForLimitedSlots) {
    26. workSpecDao.markWorkSpecScheduled(workSpec.id, now);
    27. }
    28. }
    29. workDatabase.setTransactionSuccessful();
    30. } finally {
    31. workDatabase.endTransaction();
    32. }
    33. if (eligibleWorkSpecsForLimitedSlots != null
    34. && eligibleWorkSpecsForLimitedSlots.size() > 0) {
    35. WorkSpec[] eligibleWorkSpecsArray =
    36. new WorkSpec[eligibleWorkSpecsForLimitedSlots.size()];
    37. eligibleWorkSpecsArray =
    38. eligibleWorkSpecsForLimitedSlots.toArray(eligibleWorkSpecsArray);
    39. // Delegate to the underlying schedulers.
    40. for (Scheduler scheduler : schedulers) {
    41. if (scheduler.hasLimitedSchedulingSlots()) {
    42. scheduler.schedule(eligibleWorkSpecsArray);//1
    43. }
    44. }
    45. }
    46. if (allEligibleWorkSpecs != null && allEligibleWorkSpecs.size() > 0) {
    47. WorkSpec[] enqueuedWorkSpecsArray = new WorkSpec[allEligibleWorkSpecs.size()];
    48. enqueuedWorkSpecsArray = allEligibleWorkSpecs.toArray(enqueuedWorkSpecsArray);
    49. // Delegate to the underlying schedulers.
    50. for (Scheduler scheduler : schedulers) {
    51. if (!scheduler.hasLimitedSchedulingSlots()) {
    52. scheduler.schedule(enqueuedWorkSpecsArray);
    53. }
    54. }
    55. }
    56. }

    首先将一些配置持久化到数据库里面,我们注释1处:

    最终调用的是初始化的时候初始化好的贪婪调度器的schedule方法:

    1. @Override
    2. public void schedule(@NonNull WorkSpec... workSpecs) {
    3. if (mInDefaultProcess == null) {
    4. checkDefaultProcess();
    5. }
    6. if (!mInDefaultProcess) {
    7. Logger.get().info(TAG, "Ignoring schedule request in a secondary process");
    8. return;
    9. }
    10. registerExecutionListenerIfNeeded();
    11. // Keep track of the list of new WorkSpecs whose constraints need to be tracked.
    12. // Add them to the known list of constrained WorkSpecs and call replace() on
    13. // WorkConstraintsTracker. That way we only need to synchronize on the part where we
    14. // are updating mConstrainedWorkSpecs.
    15. Set constrainedWorkSpecs = new HashSet<>();
    16. Set constrainedWorkSpecIds = new HashSet<>();
    17. for (WorkSpec workSpec : workSpecs) {
    18. long nextRunTime = workSpec.calculateNextRunTime();
    19. long now = System.currentTimeMillis();
    20. if (workSpec.state == WorkInfo.State.ENQUEUED) {
    21. if (now < nextRunTime) {
    22. // Future work
    23. if (mDelayedWorkTracker != null) {
    24. mDelayedWorkTracker.schedule(workSpec);
    25. }
    26. } else if (workSpec.hasConstraints()) {
    27. if (SDK_INT >= 23 && workSpec.constraints.requiresDeviceIdle()) {
    28. // Ignore requests that have an idle mode constraint.
    29. Logger.get().debug(TAG,
    30. String.format("Ignoring WorkSpec %s, Requires device idle.",
    31. workSpec));
    32. } else if (SDK_INT >= 24 && workSpec.constraints.hasContentUriTriggers()) {
    33. // Ignore requests that have content uri triggers.
    34. Logger.get().debug(TAG,
    35. String.format("Ignoring WorkSpec %s, Requires ContentUri triggers.",
    36. workSpec));
    37. } else {
    38. constrainedWorkSpecs.add(workSpec);
    39. constrainedWorkSpecIds.add(workSpec.id);
    40. }
    41. } else {
    42. Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
    43. mWorkManagerImpl.startWork(workSpec.id);//1
    44. }
    45. }
    46. }
    47. // onExecuted() which is called on the main thread also modifies the list of mConstrained
    48. // WorkSpecs. Therefore we need to lock here.
    49. synchronized (mLock) {
    50. if (!constrainedWorkSpecs.isEmpty()) {
    51. Logger.get().debug(TAG, String.format("Starting tracking for [%s]",
    52. TextUtils.join(",", constrainedWorkSpecIds)));
    53. mConstrainedWorkSpecs.addAll(constrainedWorkSpecs);
    54. mWorkConstraintsTracker.replace(mConstrainedWorkSpecs);
    55. }
    56. }
    57. }

    如果没有约束,走注释1处:

    又回到了WorkManagerImpl里面:

    1. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    2. public void startWork(@NonNull String workSpecId) {
    3. startWork(workSpecId, null);
    4. }
    5. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    6. public void startWork(
    7. @NonNull String workSpecId,
    8. @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    9. mWorkTaskExecutor
    10. .executeOnBackgroundThread(
    11. new StartWorkRunnable(this, workSpecId, runtimeExtras));
    12. }

    很明显,这是一个线程池,进StartWorkRunable方法里面看看:

    1. @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    2. public class StartWorkRunnable implements Runnable {
    3. private WorkManagerImpl mWorkManagerImpl;
    4. private String mWorkSpecId;
    5. private WorkerParameters.RuntimeExtras mRuntimeExtras;
    6. public StartWorkRunnable(
    7. WorkManagerImpl workManagerImpl,
    8. String workSpecId,
    9. WorkerParameters.RuntimeExtras runtimeExtras) {
    10. mWorkManagerImpl = workManagerImpl;
    11. mWorkSpecId = workSpecId;
    12. mRuntimeExtras = runtimeExtras;
    13. }
    14. @Override
    15. public void run() {
    16. mWorkManagerImpl.getProcessor().startWork(mWorkSpecId, mRuntimeExtras);
    17. }
    18. }

    继续看Process的startWork:

    1. public boolean startWork(
    2. @NonNull String id,
    3. @Nullable WorkerParameters.RuntimeExtras runtimeExtras) {
    4. WorkerWrapper workWrapper;
    5. synchronized (mLock) {
    6. // Work may get triggered multiple times if they have passing constraints
    7. // and new work with those constraints are added.
    8. if (isEnqueued(id)) {
    9. Logger.get().debug(
    10. TAG,
    11. String.format("Work %s is already enqueued for processing", id));
    12. return false;
    13. }
    14. workWrapper =
    15. new WorkerWrapper.Builder(
    16. mAppContext,
    17. mConfiguration,
    18. mWorkTaskExecutor,
    19. this,
    20. mWorkDatabase,
    21. id)
    22. .withSchedulers(mSchedulers)
    23. .withRuntimeExtras(runtimeExtras)
    24. .build();
    25. ListenableFuture future = workWrapper.getFuture();
    26. future.addListener(
    27. new FutureListener(this, id, future),
    28. mWorkTaskExecutor.getMainThreadExecutor());
    29. mEnqueuedWorkMap.put(id, workWrapper);
    30. }
    31. mWorkTaskExecutor.getBackgroundExecutor().execute(workWrapper);//1
    32. Logger.get().debug(TAG, String.format("%s: processing %s", getClass().getSimpleName(), id));
    33. return true;
    34. }

    将其封装成workWrapper,并执行execute方法:我们猜想workWrapper也是一个runnable!

    1. public class WorkerWrapper implements Runnable {
    2. ```
    3. @WorkerThread
    4. @Override
    5. public void run() {
    6. mTags = mWorkTagDao.getTagsForWorkSpecId(mWorkSpecId);
    7. mWorkDescription = createWorkDescription(mTags);
    8. runWorker();
    9. }
    10. private void runWorker() {
    11. ···
    12. runExpedited.addListener(new Runnable() {
    13. @Override
    14. public void run() {
    15. try {
    16. runExpedited.get();
    17. Logger.get().debug(TAG,
    18. String.format("Starting work for %s", mWorkSpec.workerClassName));
    19. // Call mWorker.startWork() on the main thread.
    20. mInnerFuture = mWorker.startWork();//1
    21. future.setFuture(mInnerFuture);
    22. } catch (Throwable e) {
    23. future.setException(e);
    24. }
    25. }
    26. }, mWorkTaskExecutor.getMainThreadExecutor());
    27. ···
    28. }
    29. ```
    30. }

    关注注释1处:

    1. public abstract class ListenableWorker {
    2. ```
    3. @MainThread
    4. public abstract @NonNull ListenableFuture startWork();
    5. ```
    6. }

    而Worker又是实现了ListenableWorker:

    1. @Override
    2. public final @NonNull ListenableFuture startWork() {
    3. mFuture = SettableFuture.create();
    4. getBackgroundExecutor().execute(new Runnable() {
    5. @Override
    6. public void run() {
    7. try {
    8. Result result = doWork();//happy
    9. mFuture.set(result);
    10. } catch (Throwable throwable) {
    11. mFuture.setException(throwable);
    12. }
    13. }
    14. });
    15. return mFuture;
    16. }

    Wow,终于我们熟悉的doWork出来了!

    一张图总结一下流程吧:

     关于WorkManager的线程池设计也非常有意思,有时间会单独写一篇文章详细聊聊!

  • 相关阅读:
    code:-9907磁盘不足如何处理?帮你整理了几个必备的!
    基于SSH开发美食网系统网站+论文+PPT+开题报告+任务书+外文翻译 毕业设计
    我对《RAG/大模型/非结构化数据知识库类产品》技术架构的思考、杂谈
    JavaBean专项练习(学生信息管理系统)(增删改查)(每步详细解释和思路)
    安卓常见设计模式13------过滤器模式(Kotlin版)
    CF1703G Good Key, Bad Key
    3.81 OrCAD软件绘制原理图时如何使用任意角度的走线?OrCAD软件怎么统一查看哪些元器件是没有PCB封装的?
    【业务功能篇105】 微服务-springcloud-springboot-电商订单模块--秒杀服务-定时任务【上篇】
    Vue开发实战二十二:列表中表格动态合并的实现
    HDFS集成Kerberos并使用Python调用
  • 原文地址:https://blog.csdn.net/qq_36428821/article/details/126317993