WorkManager流程分析和源码解析

本文字数:2830

预计阅读时间:20分钟

01 前言

WorkManager统一了对于Android后台任务的管理。在此之前,从6.0开始Google引入了Doze机制,并且在之后的几个版本对Android的后台行为及广播的限制越来越严格。在Android8.0时Google官方推荐开发者使用JobScheduler来代替Service+Broadcast的后台任务管理方式。为了兼容老版本,android-job, Firebase JobDispatcher和GCMNetworkManager都曾是开发者的选择,而Firebase JobDispatcher和GCMNetworkManager需要支持google play service,并不适用于国内app场景。2018年,google推出的jetpack中包含了WorkManager,之后android-job停止维护,google官方为Firebase JobDispatcher和GCMNetworkManager提出了WorkManager的迁移方案。今年,在Android11的行为变更中提到,如果应用以 API 级别“R”或更高级别为目标平台,则在搭载 Android 6.0(API 级别 23)或更高版本的设备上会停用 Firebase JobDispatcher 和 GcmNetworkManager API 调用。所以在一些场景中,使用WorkManager来维护我们的后台任务可以说是官方推荐的唯一方式。本文将介绍WorkManager的使用方式,并通过剖析WorkManager的内部实现原理,来帮助大家更好的理解WorkManager的实现。

02 WorkManager的特点与适用场景

特点:

  1. 保证任务一定会被执行
    WorkManager有自己的数据库,每一个任务的信息与任务状态,都会保存在本地数据库中。所以即使程序没有在运行,或者在设备重启等情况下,WorkManager依然可以保证任务的执行,只是不保证任务立即被执行。

  2. 合理使用设备资源
    在执行很多周期性或非立即执行的任务时,WorkManager提供我们API,帮助我们合理利用设备资源,避免不必要的内存,流量,电量等消耗。

适用场景:

  1. 可延迟进行的任务
    a.满足某些条件才执行的任务,如需要在充电时才执行的任务。
    b.用户无感知或可延迟感知的任务,如同步配置信息,同步资源,同步通讯录等。

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

  3. 退出应用后还应继续执行的未完成任务。

03 WorkManager的使用

WorkManager的使用非常简单,分为如下几个步骤:

  1. 创建一个后台任务Worker。

  2. 定义WorkRequest,配置运行任务的方式和时间。

  3. 将任务提交给系统处理。

  4. 观察Worker的进度或状态。

3.1创建后台任务Worker

WorkManager提供了四种Worker的创建模式:

  1. Worker:最简单的实现,WorkManager 会在后台线程上自动运行它。

  2. CoroutineWorker:CoroutineWorker针对后台工作公开挂起函数。默认情况下,它们运行默认的Dispatcher。

  3. RxWorker:如果有很多现有异步代码是用 RxJava 建模的建议使用。与所有 RxJava2 概念一样,可以自由选择所需的线程处理策略。

  4. ListenableWorker:是Worker,CoroutineWorker,RxWorker的基类,为需要与基于回调的异步 API进行交互并且不使用 RxJava2 的 Java 开发者而设计。Worker的创建示例:

class UploadWorker(appContext: Context, workerParams: WorkerParameters)
        : Worker(appContext, workerParams) {
        override fun doWork(): Result {
            // Do the work here--in this case, upload the images.
            uploadImages()
            // Indicate whether the task finished successfully with the Result
            return Result.success()
        }
    }

3.2配置运行任务方式和时间

3.2.1一次性任务

val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>().build()

3.2.2周期性任务

//执行多次任务,每隔12个小时执行一次
val uploadWorkRequest = PeriodicWorkRequestBuilder<UploadWorker>(12, TimeUnit.HOURS)
        .build()

3.2.3带约束条件的任务

// Create a Constraints object that defines when the task should run
val constraints = Constraints.Builder()
            .setRequiresDeviceIdle(true)
            .setRequiresCharging(true)
            .build()

// ...then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder<CompressWorker>()
            .setConstraints(constraints)
            .build()            

3.2.4延迟任务

val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
        .setInitialDelay(10, TimeUnit.SECONDS)//符合触发条件后,延迟10秒执行
        .build()

3.3将任务提交给系统处理

WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)

3.3.1多任务调度:

WorkManager.getInstance()
    // First, run all the A tasks (in parallel):
    .beginWith(workA1, workA2, workA3)
    // ...when all A tasks are finished, run the single B task:
    .then(workB)
    // ...then run the C tasks (in any order):
    .then(workC1, workC2)
    .enqueue()

3.4观察Worker的进度或状态

WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(uploadWorkRequest.id)
            .observe(lifecycleOwner, Observer { workInfo ->
            })

04 WorkManager流程分析与源码解析

这个章节将会从以下几个方面梳理WorkManager的流程与源码:

  1. 创建
    a.WorkManager的初始化
    b.WorkRequest的创建

  2. 非约束条件任务的执行

  3. 带约束条件任务的执行

从最基础的流程开始分析:创建一个不带任何约束条件的一次性任务。在doWork()中让线程休息5s。

val work1Request = OneTimeWorkRequestBuilder<Worker1>().build()
WorkManager.getInstance(this).enqueue(work1Request)
class Worker1(appContext: Context, workerParams: WorkerParameters) :
    Worker(appContext, workerParams) {

    override fun doWork(): Result {
        Thread.sleep(5000)
        return Result.success()
    }
}

4.1创建

首先梳理一下WorkManager的初始化过程。

4.1.1. WorkManager的初始化

在默认的情况下,WorkManager并不是在我们调用WorkManager.getInstance() 时创建的。通过反编译一下apk,会发现在AndroidManifest文件中注册了名为WorkManagerInitializer的ContentProvider。因此WorkManager在app冷启动的时候已经被创建。

//AndroidManifest.xml
  <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()方法:

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

由于WorkManager是个单例,在此时WorkManager就已经被初始化了。在initialize()之前,会创建一个默认的Configuration。Configuration设置了许多属性,用来管理和调度工作的方式。通常我们使用WorkManager默认创建的Configuration即可。如需使用自己的Configuration,可参考官方文档,有明确的使用说明。我们继续看initialize()的实现,由于WorkManager是个抽象类,真正的构造方法是在他的子类WorkManagerImpl实现的:

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

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

//WorkManagerImpl
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor) {
        this(context,
                configuration,
                workTaskExecutor,
                context.getResources().getBoolean(R.bool.workmanager_test_configuration));
    }
//WorkManagerImpl
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public WorkManagerImpl(
            @NonNull Context context,
            @NonNull Configuration configuration,
            @NonNull TaskExecutor workTaskExecutor,
            boolean useTestDatabase) {
        this(context,
                configuration,
                workTaskExecutor,
                WorkDatabase.create(
                        context.getApplicationContext(),
                        workTaskExecutor.getBackgroundExecutor(),
                        useTestDatabase)
        );
    }

WorkManager在此时创建了数据库。WorkDatabase.create()将任务列表序列化到本地,记录每一个任务的属性,执行条件,执行顺序及执行状态等。从而保证任务在冷启动或硬件重启后,可以根据条件继续执行。接着看this()的实现:

//WorkManagerImpl
    @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()来根据Build Version创建不同的Schedulers进行任务调度,Processor()用来管理Schedulers的执行,和internalInit()真正的初始化。先看createSchedulers()的实现:

//WorkManagerImpl
    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    @NonNull
    public List<Scheduler> createSchedulers(
            @NonNull Context context,
            @NonNull TaskExecutor taskExecutor) {

        return Arrays.asList(
                Schedulers.createBestAvailableBackgroundScheduler(context, this),
                // Specify the task executor directly here as this happens before internalInit.
                // GreedyScheduler creates ConstraintTrackers and controllers eagerly.
                new GreedyScheduler(context, taskExecutor, this));
    }

return一个Scheduler数组。其中GreedyScheduler()是常驻的,用来执行没有任何约束的非周期性的任务。接下来看createBestAvailableBackgroundScheduler()的实现。

//Scheduler
    @NonNull
    static Scheduler createBestAvailableBackgroundScheduler(
            @NonNull Context context,
            @NonNull WorkManagerImpl workManager) {

        Scheduler scheduler;

        if (Build.VERSION.SDK_INT >= WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) {
            scheduler = new SystemJobScheduler(context, workManager);
            setComponentEnabled(context, SystemJobService.class, true);
            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值