Android源码阅读WorkMangaer - 6

前言

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

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

上文主要深挖到了两大调度器的调度原理,接下来先将继续深入。

正文

WokerFactory

用途

WorkerFactory 允许开发者自定义工作实例(Worker 实例)的创建过程。使用 WorkerFactory,开发者可以在工作执行时注入依赖项、实现复杂的构造逻辑,或者基于运行时条件选择不同类型的工作实现。

假设有一个需要依赖注入的 Worker 类:

class MyWorker(context: Context,workerParams: WorkerParameters,private val myDependency: MyDependency
) : Worker(context, workerParams) {override fun doWork(): Result {// 使用依赖完成工作...return Result.success()}
}

创建一个自定义 WorkerFactory 来处理 MyWorker 的实例化,并注入所需的依赖:

class MyWorkerFactory(private val myDependency: MyDependency) : WorkerFactory() {override fun createWorker(appContext: Context,workerClassName: String,workerParameters: WorkerParameters): ListenableWorker? {return when (workerClassName) {MyWorker::class.java.name ->MyWorker(appContext, workerParameters, myDependency)else -> null // 对于其他的 Worker 类,返回 null 让 WorkManager 使用默认逻辑}}
}

接下来在应用启动时配置 WorkManager 使用自定义 WorkerFactory:

class MyApplication : Application() {override fun onCreate() {super.onCreate()// 创建依赖实例val myDependency = MyDependency()// 配置 WorkManagerval config = Configuration.Builder().setWorkerFactory(MyWorkerFactory(myDependency)).build()// 初始化 WorkManagerWorkManager.initialize(this, config)}
}

自定义 WorkerFactory 允许在工作实例创建时注入依赖项,对于使用 Dagger 或其他依赖注入框架的项目非常有用。

同时自定义WorkerFactory 可以基于运行时条件(如用户偏好设置、设备类型等)动态选择或配置工作实例。

接下来就深入WorkFactory的源码

如下抽象方法四个参数的含义如下:

appContext: Context 正常Android中常接触的context对象

workerClassName: String 要创建的 Worker 类的完全限定名(包括包名的类名),WorkerFactory 可以通过这个类名决定如何和何时创建 Worker 的实例。这个机制允许 WorkerFactory 支持多种类型的 Worker,并且根据类名动态地实例化它们。

workerParameters: WorkerParameters 包含了执行工作所需的所有参数和配置。这些参数可能包括工作的输入数据、工作的唯一标识符、延迟执行工作的持续时间、重试策略等。

defaultWorkerFactory源码如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
object DefaultWorkerFactory : WorkerFactory() {override fun createWorker(appContext: Context,workerClassName: String,workerParameters: WorkerParameters) = null
}

关键源码createWorkerWithDefaultFallback如下:

    @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)fun createWorkerWithDefaultFallback(appContext: Context,workerClassName: String,workerParameters: WorkerParameters): ListenableWorker {fun getWorkerClass(workerClassName: String): Class<out ListenableWorker> {return try {Class.forName(workerClassName).asSubclass(ListenableWorker::class.java)} catch (throwable: Throwable) {Logger.get().error(TAG, "Invalid class: $workerClassName", throwable)throw throwable}}fun fallbackToReflection(workerClassName: String,workerParameters: WorkerParameters): ListenableWorker {val clazz = getWorkerClass(workerClassName)return try {val constructor = clazz.getDeclaredConstructor(Context::class.java, WorkerParameters::class.java)constructor.newInstance(appContext, workerParameters)} catch (e: Throwable) {Logger.get().error(TAG, "Could not instantiate $workerClassName", e)throw e}}val worker = createWorker(appContext, workerClassName, workerParameters)?: fallbackToReflection(workerClassName, workerParameters)if (worker.isUsed) {val message = "WorkerFactory (${javaClass.name}) returned an instance of" +" a ListenableWorker ($workerClassName) which has already been invoked. " +"createWorker() must always return a new instance of a ListenableWorker."throw IllegalStateException(message)}return worker}
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)限制仅允许在库的内部(即 WorkManager 库本身)使用。

  • getWorkerClass是一个局部函数,用于通过反射获取与 workerClassName 参数对应的 Class 对象。如果类名有效,这个函数会返回该类的 Class 对象;如果无效,会捕获异常,记录错误日志,并将异常抛出。
  • fallbackToReflection也是一个局部函数,当 createWorker 方法返回 null 时调用,使用反射来创建 ListenableWorker 实例。首先通过 getWorkerClass 函数获取 ListenableWorkerClass 对象,然后尝试找到一个接受 ContextWorkerParameters 为参数的构造函数,并通过该构造函数创建实例。如果反射失败,会捕获异常,记录错误,并抛出异常。
  • 然后代码尝试通过 createWorker 方法创建实例。如果 createWorker 返回 null,则调用 fallbackToReflection 方法通过反射创建实例。
  • 一旦创建了 ListenableWorker 实例,代码会检查 isUsed 属性,确保返回的实例是新创建的并且之前没有被使用过。如果检测到实例已经被使用,会抛出 IllegalStateException,因为每个 ListenableWorker 实例都应该是新创建的并且在创建后尚未执行任何工作。
  • 最后如果实例成功创建并通过了 isUsed 的检查,方法会返回这个 ListenableWorker 实例。

综上,两者关系如下:

createWorker 是一个抽象方法,需要开发者重写来实现自定义的 ListenableWorker 创建逻辑。这个方法让开发者完全自由度来决定如何根据工作类名和参数创建工作实例,,该方法返回 ListenableWorker? 类型的对象,允许返回 null。如果开发者的实现返回了 null,表明自定义工厂无法创建该工作实例。

工厂方法和回退策略结合:createWorker 提供了自定义创建工作实例的接口,而 createWorkerWithDefaultFallback 确保了这一过程的稳健性和可靠性。

ConstriantTracker

当工作请求添加到 WorkManager 时,会根据需要的约束条件注册相应的 ConstraintTracker 监听器,ConstraintTracker 将跟踪系统约束的状态变化。

这是一个抽象类

构造参数、属性有如下:

泛型 <T>:不同的 ConstraintTracker 子类可以用来跟踪不同类型的系统约束状态。

  • context: Context:上下文,可用于访问系统服务。
  • taskExecutor: TaskExecutor:一般用于执行异步任务。
  • appContext: Context:应用程序级的上下文。
  • currentState: T?:当前的约束状态。
  • listeners: LinkedHashSet<ConstraintListener<T>>():一个包含所有已注册监听器的集合。

关键函数有如下:

添加一个监听器,如果添加的是第一个监听器,将触发状态读取和开始跟踪函数。

移除一个监听器,如果后面没有监听器,跟踪停止。

    fun addListener(listener: ConstraintListener<T>) {synchronized(lock) {if (listeners.add(listener)) {if (listeners.size == 1) {currentState = readSystemState()Logger.get().debug(TAG, "${javaClass.simpleName}: initial state = $currentState")startTracking()}@Suppress("UNCHECKED_CAST")listener.onConstraintChanged(currentState as T)}}}fun removeListener(listener: ConstraintListener<T>) {synchronized(lock) {if (listeners.remove(listener) && listeners.isEmpty()) {stopTracking()}}}

代码还定义了一个state 属性,负责管理和通知约束状态的变化。

get() 方法检查 currentState 是否为 null

  • 如果 currentState 不为 null,则直接返回当前保存的状态。
  • 如果 currentStatenull,则调用 readSystemState()并返回这个状态。

设置 state 属性的值时,set(newState) 方法会被调用, newState 是尝试设置的新状态值

  1. 通过 synchronized(lock) 确保同一时刻只有一个线程可以更新状态。
  2. 检查新状态 newState 是否与当前状态 currentState 相同。
  3. 状态确实发生了变化,将 currentState 更新为新的状态 newState
  4. 通知所有注册的监听器关于状态变化。先将监听器集合复制到一个列表 listenersList 中,然后遍历这个列表来通知每个监听器。复制操作应该是为了避免在遍历集合时修改集合导致的异常。
  5. 使用 taskExecutor.mainThreadExecutor.execute { ... } 确保监听器的 onConstraintChanged 方法在主线程上被调用。
var state: Tget() {return currentState ?: readSystemState()}set(newState) {synchronized(lock) {if (currentState != null && (currentState == newState)) {return}currentState = newState// onConstraintChanged may lead to calls to addListener or removeListener.// This can potentially result in a modification to the set while it is being// iterated over, so we handle this by creating a copy and using that for// iteration.val listenersList = listeners.toList()taskExecutor.mainThreadExecutor.execute {listenersList.forEach { listener ->// currentState was initialized by now@Suppress("UNCHECKED_CAST")listener.onConstraintChanged(currentState as T)}}}}

 抽象方法如下,需要时被重写。

/*** Reads the state of the constraints from source of truth. (e.g. NetworkManager for* NetworkTracker). It is always accurate unlike `state` that can be stale after stopTracking* call.*/abstract fun readSystemState(): T/*** Start tracking for constraint state changes.*/abstract fun startTracking()/*** Stop tracking for constraint state changes.*/abstract fun stopTracking()

WorkManager系列的源码阅读应该会到此结束了,每次阅读源码都觉得是个很累的过程,尤其是WorkManager的源码,本身十分庞大,笔者的6篇文章也只是探索了笔者认为的主要部分,但还是有很多地方没有探索,比如存储WorkSepc的数据库,相关Dao,JobScehdule的调度策略等,还是没有很理顺这些,不过先放着吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/571448.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vs2022安装和使用教程(详细)

vs2022和vs2019一样强大&#xff0c;C/C&#xff0c;Python&#xff0c;F#&#xff0c;ios&#xff0c;Android&#xff0c;Web&#xff0c;Node.js&#xff0c;Azure&#xff0c;Unity&#xff0c;HTML&#xff0c;JavaScript等开发都可以执行&#xff0c;大家快来使用它吧~ 如…

【C++】1323. 扩建花圃问题

问题&#xff1a;1323. 扩建花圃问题 类型&#xff1a;整数运算 题目描述&#xff1a; 梅山小学有一块长方形花圃&#xff08;花圃的长宽都是整数&#xff09;&#xff0c;长 m 米&#xff0c;宽未知。 在修建校园时&#xff0c;花圃的长增加了 n 米&#xff0c;此时发现增加…

左值引用、右值引用及移动语义

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 左值 概念 可以取到地址的值就是左值&#xff0c;并且一般情况下可以修改&#xff08;const类型左值不可修改&#xff09;。 左值举例&#xff1a; //左值 int a 0; const int b 1; int* p &a; 右值 概念 不能…

【C语言】Infiniband驱动mlx4_init_one

一、注释 这是Linux内核中Mellanox Ethernet网卡驱动程序mlx4模块的一部分代码&#xff0c;主要用于初始化一个PCI设备。以下是其注释&#xff1a; // 驱动的主结构体&#xff0c;包含了供PCI核心使用的勾子&#xff08;hooks&#xff09; static struct pci_driver mlx4_dri…

分布式系统的发展史

目录 &#x1f433;今日良言&#xff1a;且视他人之疑目如盏盏鬼火&#xff0c;大胆地去走自己的夜路 &#x1f407;一、常见概念 &#x1f407;二、发展史 今日良言&#xff1a;且视他人之疑目如盏盏鬼火&#xff0c;大胆地去走自己的夜路 一、常见概念 在正式介绍分布式系…

【AutoML】一个用于图像、文本、时间序列和表格数据的AutoML

一个用于图像、文本、时间序列和表格数据的AutoML AutoGluon介绍安装AutoGluon快速上手 参考资料 AutoGluon自动化机器学习任务&#xff0c;使您能够在应用程序中轻松实现强大的预测性能。只需几行代码就可以训练和部署有关图像&#xff0c;文本&#xff0c;时间序列和表格数据…

Python学习:函数

函数定义 在Python中&#xff0c;函数&#xff08;Function&#xff09;是一组用于完成特定任务或计算的语句块。定义函数可以让我们将一段代码重用多次&#xff0c;提高代码的可读性和可维护性。以下是定义函数的基本语法和结构&#xff1a; def function_name(parameters):&…

开通抖音小店后要做什么?这个流程你必须知道!建议收藏避免遗漏

哈喽~我是电商月月 在入驻抖音小店前&#xff0c;大家了解的抖店步骤应该是&#xff1a;入驻-选品-找达人-售后 但真的入驻后大家可不敢这样做&#xff01;操作不当可能违规&#xff0c;严重的还会扣除保证金&#xff0c;做清店处理 这些细节流程大家一定要知道&#xff0c;…

3D开发工具HOOPS更新:高效、轻量化模型处理再突破!

随着数字化转型的深入发展&#xff0c;高性能图形显示成为了软件开发领域的重要研究方向。在众多工具和库中&#xff0c;HOOPS因其强大的三维图形处理能力而受到广泛关注。 HOOPS也与时俱进&#xff0c;持续更进与创新&#xff0c;近期又推出了一系列新功能&#xff0c;这些功…

鸿蒙开发之了解ArkTS

鸿蒙开发者官网 &#xff1a; https://developer.huawei.com/consumer/cn/ 开发鸿蒙要用的软件是 DevEco Studio ArkTS建立在JS和TS的基础之上&#xff0c;扩展了声明式UI开发范式和状态管理&#xff0c;提供更简洁和自然的开发方式。 ArkTS引入了渲染引擎的增强&#xff0c…

【Java】IDEA集成开发工具中英文切换

大家好&#xff0c;我是全栈小5&#xff0c;欢迎阅读小5的系列文章。 这是《Java》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识…

怎么批量修改文件名中的一部分?

怎么批量修改文件名中的一部分&#xff1f;批量修改文件名中的一部分文字是我们在处理大量文件时经常需要做的任务之一。这项工作可以极大地提高工作效率&#xff0c;节省宝贵的时间。无论是对于个人用户还是企业组织来说&#xff0c;都是非常实用的技能。首先&#xff0c;批量…