Android源码阅读WorkMangaer - 1

慈云数据 1年前 (2024-03-22) 技术支持 74 0

前言

由于笔者目前水平限制,表达能力有限,尽请见谅。

WorkManager 是 Android Jetpack 库的一部分,提供了一种向后兼容的方式来安排可延迟的异步任务,这些任务即使在应用退出或设备重启后也应该继续执行。它是 Android 推荐的解决方案,用于处理需要保证执行的后台任务。WorkManager 适合用于那些不需要立即执行的任务,但最终需要完成的任务。

正文

特性、用法、使用场景

有如下特性

  • 向后兼容:WorkManager 适用于 Android 4.0(API 级别 14)及以上版本。
  • 灵活的调度条件:可以根据网络状态、设备充电状态和其他约束条件来安排任务。
  • 周期性任务:支持一次性执行的任务和周期性执行的任务。
  • 可链式任务:可以将多个工作请求串联起来按顺序执行。
  • 输出和输入:每个工作请求可以接收输入数据,并产生输出数据。

    常用于如下场景

    1. 数据同步

    应用程序需要定期从网络同步数据。

    2. 上传日志

    在用户使用应用期间收集日志,然后当设备连接到 Wi-Fi 时上传这些日志。

    3. 延迟任务

    需要延迟执行任务如提醒用户完成某些活动,可使用 WorkManager 设置一次性工作请求,甚至设定精确的执行时间

    简单的代码示例Kotlin版本

    val myWorkRequest = OneTimeWorkRequestBuilder()
        .setConstraints(Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build())
        .build()
    WorkManager.getInstance(context).enqueue(myWorkRequest)
    class MyWorker(appContext: Context, workerParams: WorkerParameters) :
        Worker(appContext, workerParams) {
        override fun doWork(): Result {
            // 执行后台任务
            return Result.success()
        }
    }
    

    源码

    WorkManager.java

    抬头写着如下内容,可以知道这是androidx引入的,取代了之前的方法的新方法。

    package androidx.work;

    与SharedPreference相同,FrameWork层的workManager类本身是一个抽象类,获取wordkManager实例时也是通过WorkManagerImpl.getInstance();的方式。

    值得注意的是不带context得方式不推荐使用了。

    下一步我们就直接看向具体在runtime里,

    @SuppressLint("AddedAbstractMethod")
    public abstract class WorkManager {
        @Deprecated
        public static @NonNull WorkManager getInstance() {
            WorkManager workManager = WorkManagerImpl.getInstance();
            if (workManager == null) {
                throw new IllegalStateException("WorkManager is not initialized properly.  The most "
                        + "likely cause is that you disabled WorkManagerInitializer in your manifest "
                        + "but forgot to call WorkManager#initialize in your Application#onCreate or a "
                        + "ContentProvider.");
            } else {
                return workManager;
            }
        }
        public static @NonNull WorkManager getInstance(@NonNull Context context) {
            return WorkManagerImpl.getInstance(context);
        }
    WokrManagerImpl

    方法的实现如下

    预先定义了private static final Object sLock = new Object();

    然后使用 synchronized (sLock) 保证了在多线程环境下该方法的线程安全。

    通过调用 getInstance() 获取 WorkManagerImpl 的当前实例,如果为null,先检查上下文是否实现Configuration.Provider 接口,这个接口允许应用程序提供一个自定义的 Configuration 实例给 WorkManager。

    如果 appContext 实现了 Configuration.Provider 接口,那么 WorkManagerImpl 会使用从 appContext.getWorkManagerConfiguration() 获取的配置进行初始化(通过调用 initialize(appContext, configuration) 方法)。初始化后,再次尝试获取 WorkManagerImpl 的实例。

    额外补充一下instanceof关键字,instanceof 是 Java 中的一个关键字,用于检查一个对象是否是特定类或其子类的实例。它通常用于在运行时确定对象的类型,从而确保在向下转型之前对象属于正确的类型。

        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        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;
            }
        }

    至于不带Context的getInstance类则比较简单,其中sDelegatedInstance是一个全局变量,同时也用到了synchronized保证线程安全。 

    对于WorkManager的initialize方法定义如下

    如果 sDelegatedInstance(可以叫做代理实例)或 sDefaultInstance(可以叫做默认实例)不为 null,说明 WorkManager 已经被初始化过了。

    如果 sDelegatedInstance 为 null,方法继续进行初始化:

    • 首先,通过调用 context.getApplicationContext()),确保使用的是应用级别的上下文。
    • 然后,检查 sDefaultInstance 是否为 null。如果是,使用提供的 context 和 configuration 参数创建 WorkManagerImpl 的新实例,并将其赋值给 sDefaultInstance。这里,new WorkManagerTaskExecutor(configuration.getTaskExecutor()) 是根据提供的配置创建一个任务执行器,用于 WorkManager 的异步操作。
    • 最后,将 sDelegatedInstance 设置为 sDefaultInstance,完成初始化过程。

      在WorkManagerImpl类中,使用sDelegatedInstance和sDefaultInstance两个静态实例变量是为了支持灵活的初始化和测试策略。这种设计模式允许开发者在测试和生产环境中使用不同的配置或行为,而不需要修改应用的主要业务逻辑。

      DefaultInstance代表WorkManagerImpl的默认单例实例。在应用的正常运行中,当通过getInstance(Context context)方法获取WorkManagerImpl的实例时,会返回这个默认实例。这保证了整个应用中WorkManagerImpl的唯一性和全局可访问性。

      sDelegatedInstance用于测试目的,允许开发者设置一个WorkManagerImpl的代理实例。这可以是一个标准的实例,也可以是一个特别定制(例如,具有模拟行为)的实例。

      通过设置delegate,测试代码可以绕过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;
                  }
              }
          }

      下一篇文章计划将继续深入研究WorkManager相关其他源码,加深对WorkManager的理解。

微信扫一扫加客服

微信扫一扫加客服