Android Jetpack组件之WorkManger使用介绍

1、前言

最近简单看了下google推出的框架Jetpack,感觉此框架的内容可以对平时的开发有很大的帮助,也可以解决很多开发中的问题,对代码的逻辑和UI界面实现深层解耦,打造数据驱动型UI界面。

Android Architecture组件是Android Jetpack的一部分,它们是一组库,旨在帮助开发者设计健壮、可测试和可维护的应用程序,包含一下组件:

  • Android Jetpack组件总览
  • Android Jetpack 组件之 Lifecycle使用
  • Android Jetpack 组件之 Lifecycle源码
  • Android Jetpack组件之ViewModel使用
  • Android Jetpack组件之 LiveData使用-源码
  • Android Jetpack组件之 Paging使用-源码
  • Android Jetpack组件之 Room使用-源码
  • Android Jetpack组件之Navigation使用-源码
  • Android Jetpack组件之WorkManger使用介绍
  • Android Jetpack组件App Startup简析
  • Android Jetpack组件之Hilt使用

本系列文章是各处copy过来的,个人感觉所有的开发者都应该尽早的熟悉Jetpack组件,相信一定会被它的魅力所吸引,最近也在完成一个使用以上所有组件实现的项目,作为对Jetpack组件的项目实践,下面来分析一下每个组件对项目开发的帮助。

2、WorkManger简介

WorkManger是Android Jetpack提供执行后台任务管理的组件,它适用于需要保证系统即使应用程序退出也会运行的任务,WorkManager API可以轻松指定可延迟的异步任务以及何时运行它们,这些API允许您创建任务并将其交给WorkManager立即运行或在适当的时间运行。

WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以在您应用程序进程的新线程中运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务 - 具体取决于设备API级别和包含的依赖项,WorkManager可能会使用 JobScheduler,Firebase JobDispatcher或AlarmManager

为什么在有了service以后,google还有出WorkManager框架呢?

  1. service的滥用导致手机后台任务不断执行,耗电量大。
  2. 从开发者来说,Android8.0以后,Android对于后台service管理的更加严格,应用在后台启动的服务必须是前台服务,否则会导致应用crash。当然你也可以选择降低targetSdkVersion。
  3. 针对targetSdkVersion Google也针对的出了一些限制。ps:2019 年起,每次发布新版本后,所有应用都必须在一年内将 Target API 更新到最新版本。

3、WorkManager基础知识

3.1、使用WorkManager之前需先了解几个类:

  • Worker:指定需要执行的任务,可以继承抽象的Worker类,在实现的方法中编写执行的逻辑
  • WorkRequest:执行一项单一任务
  1. WorkRequest对象必须指定Work执行的任务
  2. WorkRequest都有一个自动生成的唯一ID; 您可以使用ID执行取消排队任务或获取任务状态等操作
  3. WorkRequest是一个抽象的类;系统默认实现子类 OneTimeWorkRequest或PeriodicWorkRequest(循环执行)
  4. WorkRequest.Builder创建WorkRequest对象;相应的子类:OneTimeWorkRequest.Builder或PeriodicWorkRequest.Builder
  5. Constraints:指定对任务运行时间的限制(任务约束);使用Constraints.Builder创建Constraints对象 ,并传递给WorkRequest.Builder
  • WorkManager:排队和管理工作请求;将WorkRequest 对象传递WorkManager的任务队列
  1. 如果您未指定任何约束, WorkManager立即运行任务
  2. WorkStatus:包含有关特定任务的信息;可以使用LiveData保存 WorkStatus对象,监听任务状态;如LiveData<WorkStatus>

3.2、工作流程

添加WorkManger的工作依赖

def work_version = "1.0.0-alpha10"
implementation "android.arch.work:work-runtime:$work_version" // use -ktx for Kotlin
androidTestImplementation "android.arch.work:work-testing:$work_version"

定义Worker类,并覆盖其 doWork()方法

class TestWorker(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
 
    override fun doWork(): Result {
        Log.e("TestWorker", "执行了 doWork() 操作!")
        return Result.SUCCESS
    }
}

使用 OneTimeWorkRequest.Builder 创建对象Worker,然后将任务排入WorkManager队列

  • OneTimeWorkRequest.Builder创建一个单次执行的WorkQuest
val workRequest = OneTimeWorkRequest.Builder(TestWorker::class.java).build()
WorkManager.getInstance().enqueue(workRequest)

执行结果:

WorkStatus

  • 调用WorkManger的getStatusByIdLiveData()返回任务执行的状态LiveData<WorkStatus>
  • 添加Observe()执行观察回调
WorkManager.getInstance().getStatusByIdLiveData(workRequest.id)  // 返回LiveData
    .observe(this, Observer {
        Log.e("TestWorker", it?.state?.name)
        if (it?.state!!.isFinished) {
            Log.e("TestWorker", "Finish")
        }
    })

从上面执行过程中看出,回调了Work的三个运行状态RUNNING、SUCCESSESD、FINISH。

任务约束

  • 使用Constraints.Builder()创建并配置Constraints对象
  • 可以指定任务运行时间的约束,例如,可以指定该任务仅在设备空闲并连接到电源时运行
// 在连接网络、插入电源且设备处于空闲时运行
val myConstraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.CONNECTED) 
    .setRequiresCharging(true)
    .setRequiresDeviceIdle(true)
    .build()

除了上面设置的约束外,WorkManger还提供了以下的约束作为Work执行的条件:

  • setRequiredNetworkType:网络连接设置
  • setRequiresBatteryNotLow:是否为低电量时运行 默认false
  • setRequiresCharging:是否要插入设备(接入电源),默认false
  • setRequiresDeviceIdle:设备是否为空闲,默认false
  • setRequiresStorageNotLow:设备可用存储是否不低于临界阈值

配置好Constraints后,创建Work对象

val compressionWork = OneTimeWorkRequest.Builder(TestWorker::class.java)
        .setConstraints(myConstraints)
        .build()

执行结果:在为连接网络时,Work处于阻塞状态当连接网络后Work立即执行变为SUCCESSED状态:

取消任务

  • 从WorkRequest()获取Worker的ID
  • 根据ID取消任务
WorkManager.getInstance().cancelWorkById(workRequest.id)

添加TAG

  • 通过为WorkRequest对象分配标记字符串来对任务进行分组
  • WorkManager.getStatusesByTag() 返回WorkStatus具有该标记的所有任务的所有任务的列表
  • WorkManager.cancelAllWorkByTag() 取消具有特定标记的所有任务
 OneTimeWorkRequestBuilder<MyCacheCleanupWorker>()
    .addTag("cleanup")
    .build()
 
   /./ 使用:
 WorkManager.getInstance().getStatusesByTag("TAG")
 WorkManager.getInstance().cancelAllWorkByTag("TAG")

重复的任务

  • 前面创建了单次任务使用OneTimeWorkRequest,对于重复任务使用 PeriodicWorkRequest.Builder该类创建PeriodicWorkRequest对象
  • 然后将任务添加到WorkManager的任务队列
val timesRequest = PeriodicWorkRequest.Builder(TestWorker::class.java,10,TimeUnit.SECONDS)
   ......
    .build()

4、Workmanger的高级用法

链式任务

  • WorkManager允许您创建和排队指定多个任务的工作序列,以及它们应运行的顺序
  • 使用该WorkManager.beginWith() 方法创建一个序列 ,并传递第一个OneTimeWorkRequest对象; 该方法返回一个WorkContinuation对象
  • 使用 WorkContinuation.then()添加剩余的对象
  • 最后调用WorkContinuation.enqueue()将整个序列排入队列 
  • 如果任何任务返回 Worker.Result.FAILURE,则整个序列结束

Example:

WorkManager.getInstance()
    .beginWith(workA)
    .then(workB)   
    .then(workC)
    .enqueue()

执行结果:任务会按照设置的顺序依次执行A、B、C

有一点就是WorkManger在执行过程中,当遇到一个WOrk不成功,则会停止执行,现修改WorkB返回FAILURE状态,再次运行程序结果如下:

override fun doWork(): Result {
    Log.e("Worker", "WorkerB 执行了 doWork() 操作!")
    return Result.FAILURE
}

代码执行到WorkB就已经结束了,WorkC并未执行。

  •  beginWith() 和 then()传递多个对象 : 同一方法传递的对象将会并行
WorkManager.getInstance()
    .beginWith(workA1, workA2, workA3)   // 三个对象将并行
    .then(workB)                                            // 执行完3个A后,在执行B
    .then(workC1, workC2)                          //...
    .enqueue()

WorkContinuation.combine()

  • 使用 WorkContinuation.combine() 方法连接多个链来创建更复杂的序列,如:

// ......继续创建WorkD 和 WorkE 及相应的Request
val configA_B = WorkManager.getInstance().beginWith(workRequest)
     .then(workRequestB)
 
val configC_D = WorkManager.getInstance().beginWith(workRequestC)
     .then(workRequestD)
 
WorkContinuation.combine(configA_B,configC_D)
     .then(workRequestE)
     .enqueue()

执行结果与上面一致:开始时执行A/C,其余为阻塞状态,A/C执行结束后执行B/D,最后执行WoekerE。如果此时把WorkB中返回FAILURE,执行结果也一致执行到WorkerB就结束了,WorkerE不会执行。

特定的工作方式

  • 通过调用 beginUniqueWork() 来创建唯一的工作序列,被标记的Work在执行过程中只能出现一次
WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequest)
//参数:1、工作序列的名称
             2、当有相同名称序列时采取的策略方式
             3、需要执行的Worker

每个独特的工作序列都有一个名称; 同一时间只允许执行一个使用该名称工作序列

ExistingWorkPolicy提供以下策略:

  • ExistingWorkPolicy.REPLACE:取消现有序列并将其替换为新序列
  • ExistingWorkPolicy.KEEP:保留现有序列并忽略您的新请求
  • ExistingWorkPolicy.APPEND:将新序列附加到现有序列,在现有序列的最后一个任务完成后运行新序列的第一个任务

以ExistingWorkPolicy.REPLACE为例,让WorkA睡眠3秒,模拟同时运行的状态:

override fun doWork(): Result {
    Log.e("Worker", "WorkerA begin Sleep()!")
    Thread.sleep(3000)
    Log.e("Worker", "WorkerA 执行了 doWork() 操作!")
    return Result.SUCCESS
}

Append:两个都会执行

WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequest)
    .enqueue()
WorkManager.getInstance().beginUniqueWork("worker", ExistingWorkPolicy.APPEND, workRequestB)
    .enqueue()

运行结果:

REPLACE:只会执行WorkerB

KEEP:只会执行WorkerA

输入参数和返回值

  1. 将参数传递给任务并让任务返回结果。传递和返回的值是键值对
  2. 使用 Data.Builder创建 Data 对象,保存参数的键值对
  3. 在创建WorkQuest之前调用WorkRequest.Builder.setInputData()传递Data的实例
  4. 调用 Worker.getInputData()获取参数值
  5. 调用Worker.setOutputData()设置返回数据

修改WorkerA如下

const val MIN_NUMBER = "minNumber"
const val MAX_NUMBER = "maxNumber"
const val RESULT_CODE = "Result"
 
class WorkerA(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
 
    private var minNumber = 0
    private var maxNumber = 0
 
    override fun doWork(): Result {
        minNumber = inputData.getInt(MIN_NUMBER, 0) // 使用InputData获取传入的参数
        maxNumber = inputData.getInt(MAX_NUMBER, 0)
 
        val result = maxNumber - minNumber  // 计算结果
        val outData: Data = Data.Builder().putAll(mapOf(RESULT_CODE to result)).build() // 创建返回的数据Data
        outputData = outData   // 设置返回的数据Data
 
        return Result.SUCCESS
    }
}

创建Worker并传递参数

val map = mapOf(MIN_NUMBER to 5, MAX_NUMBER to 15)   
val data = Data.Builder().putAll(map).build()   // 创建输入参数Data
 
val mathWork = OneTimeWorkRequestBuilder<MathWorker>()
        .setInputData(data)   // 传递参数
        .build()

观察任务WorkStatus获取返回结果

WorkManager.getInstance().getStatusByIdLiveData(workRequest.id)
    .observe(this, Observer {
        if (it?.state!!.isFinished) {
            Log.e("WorkerA", "${it.outputData.getInt(RESULT_CODE, 0)}")  // 获取执行结果
        }
    })

链式调用

  • 对于链式调用的Work,使用方法和单个Work一样,只是前一个的结果会作为后一个传入的参数,现添加WorkB接收WorkA传入的差值并添加平方处理,然后返回计算结果,在Activity中添加两个Work的工作监听:
const val NUMBER = "NUMBER"
const val RESULT_B = "RESULT_B"
class WorkerB(context: Context,workerParameters: WorkerParameters) : Worker(context,workerParameters){
 
    override fun doWork(): Result {
       val input = inputData.getInt(RESULT_CODE,5) // 获取第一个Worker返回的结果
        Log.e("Worker", "WorkerB 接受数据 input = $input ")
        val map = mapOf(RESULT_B to input*input)
        outputData = Data.Builder().putAll(map).build()  // 设置返回数据
        return Result.SUCCESS
    }
}
 
// Activity中监听WorkerB的执行结果
WorkManager.getInstance().getStatusByIdLiveData(workRequestB.id)
    .observe(this, Observer {
        if (it?.state!!.isFinished) {
            Log.e("WorkerB", "${it.outputData.getInt(RESULT_B, 0)}")
        }
    })

执行结果:对于WorkA依次传入5和15,在WorkA中计算差值15-5 = 10,所以WorkA中输出10,那么链式调用的WorkB中接收的就是10,加平方后输出为100,输出Log与分析一致,

到此Android Jetpack组件的workmanager就介绍完了。

5、Workmanger的源码

5.1 WorkManager类

通过上面我们知道任务的执行,是通过**WorkManager.getInstance(this).enqueue(request)**执行的。WorkManager是个抽象类,通过WorkManager.getInstance方法返回的是,它的子类WorkManagerImpl的单例对象。

  • 单例模式初始化WorkManagerImpl对象
  • 调用getInstance方法返回sDelegatedInstance对象。这里sDelegatedInstance对象已经不是nulll了。下面我们就来分析一下sDelegatedInstance的初始化过程。
 public static @NonNull WorkManagerImpl getInstance(@NonNull Context context) {
        synchronized (sLock) {
            WorkManagerImpl instance = getInstance();
            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;
        }
    }

public static @Nullable WorkManagerImpl getInstance() {
        synchronized (sLock) {
            if (sDelegatedInstance != null) {
                return sDelegatedInstance;
            }

            return sDefaultInstance;
        }
    }

通过反编译我们的APP,我们在AndroidManifest.xml文件中找到了一个provider的配置项。WorkManagerInitializer类又继承自ContentProvider,关于ContentProvider的启动过程这里不过多介绍,在应用的启动时候,会通过ActivityThread初始化ContentProvider(WorkManagerInitializer),即执行了onCreate方法。

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

在WorkManagerInitializer的onCreate方法中调用了WorkManager.initialize的方法进行初始化。

 public boolean onCreate() {
        // Initialize WorkManager with the default configuration.
        WorkManager.initialize(getContext(), new Configuration.Builder().build());
        return true;
    }

在WorkManager.initialize内部通过调用 WorkManagerImpl.initialize(context, configuration)完成WorkManagerImpl的初始化任务。

下面重点看一下WorkManagerImpl.initializ内部做了那些初始化操作。

  • sDelegatedInstance和sDefaultInstance都不为空,说明已经初始化过,抛出异常
  • 调用WorkManagerImpl的构造方法完成初始化任务。
  • configuration.getTaskExecutor())内部返回默认的线程池。
  • WorkManagerTaskExecutor内部通过调用SerialExecutor实现线程的调度。
 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;
            }
        }
    }
private @NonNull Executor createDefaultExecutor() {
        return Executors.newFixedThreadPool(
                // This value is the same as the core pool size for AsyncTask#THREAD_POOL_EXECUTOR.
                Math.max(2, Math.min(Runtime.getRuntime().availableProcessors() - 1, 4)));
    }

initialize方法内部通过调用WorkManagerImpl的构造方法完成初始化任务。

  • WorkDatabase创建数据库擦操作,内部使用的是Room框架。
  • createSchedulers创建调度者集合。这里面主要有两种:GreedyScheduler和SystemJobScheduler(如果系统版本号大于23的话)。
  • 创建Processor类。接下来会分析该类的作用和代码。
public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor,
            boolean useTestDatabase) {

        Context applicationContext = context.getApplicationContext();
        WorkDatabase database = WorkDatabase.create(
                applicationContext, configuration.getTaskExecutor(), useTestDatabase);
        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主要是为了创建调度者集合。

  • createBestAvailableBackgroundScheduler创建一个最有效的后台调度者。
  • 创建GreedyScheduler调度者。
public @NonNull List<Scheduler> createSchedulers(Context context, TaskExecutor taskExecutor) {
        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this),
                // Specify the task executor directly here as this happens before internalInit.
                // GreedyScheduler creates ConstraintTrackers and controllers eagerly.
                new GreedyScheduler(context, taskExecutor, this));
    }

createBestAvailableBackgroundScheduler方法

  • 如果Android版本号>=23,返回SystemJobScheduler,内部主要是使用JobScheduler完成调度
  • 如果手机支持GCM,则返回GcmScheduler调度者,国内基本告别了。
  • 其他情况下返回SystemAlarmScheduler,内部使用AlarmManager实现原理
static Scheduler createBestAvailableBackgroundScheduler(
            @NonNull Context context,
            @NonNull WorkManagerImpl workManager) {

        Scheduler scheduler;

        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
            scheduler = new SystemJobScheduler(context, workManager);
            setComponentEnabled(context, SystemJobService.class, true);
            Logger.get().debug(TAG, "Created SystemJobScheduler and enabled SystemJobService");
        } else {
            scheduler = tryCreateGcmBasedScheduler(context);
            if (scheduler == null) {
                scheduler = new SystemAlarmScheduler(context);
                setComponentEnabled(context, SystemAlarmService.class, true);
                Logger.get().debug(TAG, "Created SystemAlarmScheduler");
            }
        }
        return scheduler;
    }

5.2 任务的入执行队列enqueue方法

通过以上分析我们知道WorkManager.getInstance返回的是WorkManagerImpl实例,所以我们进入到enqueue方法中看看。调用WorkContinuationImpl实例的enqueue方法。

 public Operation enqueue(
            @NonNull List<? extends WorkRequest> workRequests) {

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

直接进入WorkContinuationImpl.enqueue方法看看。

  • 创建EnqueueRunnable继承自Runnable
  • getWorkTaskExecutor获取WorkManagerTaskExecutor对象。
  • 通过之前在Configuration创建的线程池中执行EnqueueRunnable任务
 public @NonNull Operation enqueue() {
        // Only enqueue if not already enqueued.
        if (!mEnqueued) {
            // The runnable walks the hierarchy of the continuations
            // and marks them enqueued using the markEnqueued() method, parent first.
            EnqueueRunnable runnable = new EnqueueRunnable(this);
            mWorkManagerImpl.getWorkTaskExecutor().executeOnBackgroundThread(runnable);
            mOperation = runnable.getOperation();
        } else {
            Logger.get().warning(TAG,
                    String.format("Already enqueued work ids (%s)", TextUtils.join(", ", mIds)));
        }
        return mOperation;
    }

EnqueueRunnable类

在scheduleWorkInBackground方法中调度任务执行,内部调用Schedulers类的schedule方法,分配任务。

public void run() {
        try {
            if (mWorkContinuation.hasCycles()) {
                throw new IllegalStateException(
                        String.format("WorkContinuation has cycles (%s)", mWorkContinuation));
            }
            boolean needsScheduling = addToDatabase();
            if (needsScheduling) {
                // Enable RescheduleReceiver, only when there are Worker's that need scheduling.
                final Context context =
                        mWorkContinuation.getWorkManagerImpl().getApplicationContext();
                PackageManagerHelper.setComponentEnabled(context, RescheduleReceiver.class, true);
                scheduleWorkInBackground();
            }
            mOperation.setState(Operation.SUCCESS);
        } catch (Throwable exception) {
            mOperation.setState(new Operation.State.FAILURE(exception));
        }
    }

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

Schedulers类。

在调用Scheduler的schedule的调配任务。在分析WorkManager初始化的时候,我们知道主要有GreedyScheduler等调度类。下面重点分析一下该类。

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;
        if (eligibleWorkSpecs != null && eligibleWorkSpecs.size() > 0) {
            WorkSpec[] eligibleWorkSpecsArray = eligibleWorkSpecs.toArray(new WorkSpec[0]);
            // Delegate to the underlying scheduler.
            for (Scheduler scheduler : schedulers) {
                scheduler.schedule(eligibleWorkSpecsArray);
            }
        }
    }

GreedyScheduler类

判断何时有约束条件。没有则调用startWork执行任务。有则将任务入集合

public void schedule(@NonNull WorkSpec... workSpecs) {
        registerExecutionListenerIfNeeded();

        // Keep track of the list of new WorkSpecs whose constraints need to be tracked.
        // Add them to the known list of constrained WorkSpecs and call replace() on
        // WorkConstraintsTracker. That way we only need to synchronize on the part where we
        // are updating mConstrainedWorkSpecs.
        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()) {
                    // Exclude content URI triggers - we don't know how to handle them here so the
                    // background scheduler should take care of them.
                    if (Build.VERSION.SDK_INT < 24
                            || !workSpec.constraints.hasContentUriTriggers()) {
                        constrainedWorkSpecs.add(workSpec);
                        constrainedWorkSpecIds.add(workSpec.id);
                    }
                } else {
                    Logger.get().debug(TAG, String.format("Starting work for %s", workSpec.id));
                    mWorkManagerImpl.startWork(workSpec.id);
                }
            }
        }

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

WorkManagerImpl.startWork方法

还是调用WorkManagerTaskExecutor的executeOnBackgroundThread方法执行StartWorkRunnable。

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

StartWorkRunnable类

getProcessor方法的是我们在创建WorkManager时创建的Processor对象。这里会调用Processor的startWork方法。

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

Processor类

内部通过构建Work的包装类WorkerWrapper,然后再次调用WorkManagerTaskExecutor类执行WorkerWrapper任务。

 public boolean startWork(String id, WorkerParameters.RuntimeExtras runtimeExtras) {
        WorkerWrapper workWrapper;
        synchronized (mLock) {
            // Work may get triggered multiple times if they have passing constraints
            // and new work with those constraints are added.
            if (mEnqueuedWorkMap.containsKey(id)) {
                Logger.get().debug(
                        TAG,
                        String.format("Work %s is already enqueued for processing", id));
                return false;
            }

            workWrapper =
                    new WorkerWrapper.Builder(
                            mAppContext,
                            mConfiguration,
                            mWorkTaskExecutor,
                            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类

  • 反射机制获取到ListenableWorker对象。其中Worker类继承自ListenableWorker类。
  • 调用ListenableWorker.startWork,实际上是调用Worker类的startWork方法。
  • 在Worker类的startWork方法中又会调用doWork方法,也就是我们复写的doWork方法。

小结

  • Worker:指定我们需要执行的任务。 WorkManager API包含一个抽象的Worker类WorkManagerImpl,我们需要继承这个类并且在这里执行工作。
  • WorkRequest:代表一个单独的任务。一个WorkRequest 对象指定哪个 Woker 类应该执行该任务,而且,我们还可以向 WorkRequest 对象添加详细信息,指定任务运行的环境等。每个 WorkRequest 都有一个自动生成的唯一ID,我们可以使用该ID来执行诸如取消排队的任务或获取任务状态等内容。 WorkRequest 是一个抽象类,在代码中,我们需要使用它的直接子类,OneTimeWorkRequest 或 PeriodicWorkRequest.。
  • WorkRequest.Builder:用于创建WorkRequest对象的辅助类,同样,我们要使用它的一个子OneTimeWorkRequest.Builder 和PeriodicWorkRequest.Builder 。
  • Constraints:指定任务在何时运行(例如,“仅在连接到网络时”)。我们可以通过Constraints.Builder 来创建Constraints对象,并在创建WorkRequest之前,将 Constraints 对象传递给 WorkRequest.Builder。
  • WorkManager:将WorkRequest入队和管理WorkRequest。我们要将WorkRequest对象传递给 WorkManager ,WorkManager 以这样的方式调度任务,以便分散系统资源的负载,同时遵守我们指定的约束条件。
  • WorkStatus:包含有关特定任务的信息。WorkManager 为每个 WorkRequest 对象提供一个()LiveData,LiveData持有一个WorkStatus对象,通过观察LiveData,我们可以确定任务的当前状态,并在任务完成后获取返回的任何值。

下面以贴一张执行的类图信息。

任务约束Constraints的任务是如何被触发的呢?

就是通过监听各种约束条件变化的广播,然后经过层层转化,最终的处理逻辑和无限制条件的work流程一致。


参考

Android Jetpack之WorkManager源码分析

Android Jetpack架构组件之WorkManger

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值