Android源码阅读WorkMangaer - 6

news2024/12/27 0:40:46

前言

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

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()

        // 配置 WorkManager
        val config = Configuration.Builder()
            .setWorkerFactory(MyWorkerFactory(myDependency))
            .build()

        // 初始化 WorkManager
        WorkManager.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: T
        get() {
            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.coloradmin.cn/o/1549650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录 AI绘图 Stable Diffusion的本地安装使用,可搭建画图服务端

开头 最近刷短视频看到了很多关于AI绘图&#xff0c;Midjourney&#xff0c;gittimg.ai&#xff0c;Stable Diffusion等一些绘图AI工具&#xff0c;感受到了AI绘画的魅力。通过chatGPT生成关键词再加上绘图工具&#xff0c;真是完美&#xff0c;文末教大家如何用gpt提词 Midj…

Anaconda的GEE环境中安装torch库

打开Anaconda&#xff0c;点击运行&#xff0c;打开terminal 输入pip install torch 而且由于anaconda中自己配置好了镜像源&#xff0c;在pip时自动使用清华镜像源

2024年4月份 风车IM即时通讯系统APP源码 版完整苹果安卓教程

关于风车IM&#xff0c;你在互联网上能随便下载到了基本都是残缺品&#xff0c; 经过我们不懈努力最终提供性价比最高&#xff0c;最完美的版本&#xff0c; 懂货的朋友可以直接下载该版本使用&#xff0c;经过严格测试&#xff0c;该版本基本完美无缺。 下载地址&#xff1a;…

【正点原子FreeRTOS学习笔记】————(4)FreeRTOS中断管理

这里写目录标题 一、什么是中断&#xff1f;&#xff08;了解&#xff09;二、中断优先级分组设置&#xff08;熟悉&#xff09;三、中断相关寄存器&#xff08;熟悉&#xff09;四、FreeRTOS中断管理实验&#xff08;掌握&#xff09; 一、什么是中断&#xff1f;&#xff08;…

华为数通 HCIP-Datacom H12-831 题库补充(3/27)

2024年 HCIP-Datacom&#xff08;H12-831&#xff09;最新题库&#xff0c;完整题库请扫描上方二维码&#xff0c;持续更新。 如图所示&#xff0c;关于R4路由器通过IS-IS计算出来的IPv6路由&#xff0c;哪一选项的描述是错误的&#xff1f; A&#xff1a;R4通过IS—IS只学习到…

【企业动态】吉利雷达汽车来访东胜物联,考察交流,洽谈车联网生态合作

近日&#xff0c;我们非常高兴接待吉利雷达汽车一行莅临东胜物联位于湖州市的生产工厂&#xff0c;进行参观考察&#xff0c;并就未来的合作展开深入商讨与交流。 雷达新能源汽车隶属于吉利控股集团&#xff0c;是一家专注于户外生态的中高端新能源智能汽车企业。雷达通过共享吉…

【启发式算法】同核分子优化算法 Homonuclear Molecules Optimization HMO算法【Matlab代码#70】

文章目录 【获取资源请见文章第4节&#xff1a;资源获取】1. 算法简介2. 部分代码展示3. 仿真结果展示4. 资源获取 【获取资源请见文章第4节&#xff1a;资源获取】 1. 算法简介 同核分子优化算法&#xff08;Homonuclear Molecules Optimization&#xff0c;HMO&#xff09;是…

网页版短信系统后台开发要点|短信平台软件开发搭建

在开发网页版短信系统的后台时&#xff0c;有一些关键要点需要注意&#xff0c;以确保系统的稳定性、安全性和高效性。以下是一些开发网页版短信系统后台时的重要要点&#xff1a; 用户管理&#xff1a;实现用户权限管理功能&#xff0c;包括用户注册、登录、角色分配等&#x…

Redis为什么快

引言 Redis是一个高性能的开源内存数据库,以其快速的读写速度和丰富的数据结构支持而闻名。作为一个轻量级、灵活的键值存储系统,Redis在各种应用场景下都展现出了惊人的性能优势。无论是作为缓存工具、会话管理组件、消息传递媒介,还是在实时数据处理任务和复杂的分布式系…

YOLOv9改进策略:卷积魔改 | SPD-Conv,低分辨率图像和小物体涨点明显

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;SPD-Conv由一个空间到深度(SPD)层和一个无卷积步长(Conv)层组成,特别是在处理低分辨率图像和小物体等更困难的任务时。 &#x1f4a1;&#x1f4a1;&#x1f4a1;SPD-Conv在多个数据集验证能够暴力涨点&#x…

python3字典的排序

创建一个字典 dict1{a:2,b:3,c:8,d:4} 1、分别取键、值 取字典的所有键&#xff0c;所有的值&#xff0c;利用dict1.keys()&#xff0c;dict1.vaules()&#xff0c; 由于键&#xff0c;值有很多个&#xff0c;所以要加s&#xff0c;另外注意这里要加括号&#xff0c;这样的小…

java spirng和 mybatis 常用的注解有哪些

当在Java Spring和MyBatis中进行开发时&#xff0c;常用的注解对于简化配置和提高开发效率非常重要。以下是更多常用的注解以及它们的详细说明和用途&#xff1a; 在Spring中常用的注解&#xff1a; Component&#xff1a; 用途&#xff1a;表明一个类会作为组件被Spring容器管…

YOLOv9改进策略:block优化 | Transformer架构ConvNeXt 网络在检测中大放异彩

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;Transformer架构 ConvNeXt 网络在图像分类和识别、分割领域大放异彩&#xff0c;同时对比 Swin-T 模型&#xff0c;在多种任务中其模型的大小和准确率均有一些提升&#xff0c;模型的 FLOPs 较大的减小且 Acc …

创建AI智能体

前言 灵境矩阵是百度推出的基于文心大模型的智能体&#xff08;Agent&#xff09;平台&#xff0c;支持广大开发者根据自身行业领域、应用场景&#xff0c;选取不同类型的开发方式&#xff0c;打造大模型时代的产品能力。开发者可以通过 prompt 编排的方式低成本开发智能体&am…

保姆级指导0基础如何快速搭建“对话机器人”类ChatGPT

参考了CDSN上的文章&#xff0c;但发现不work&#xff0c; 不是这里有问题&#xff0c;就是那里有问题&#xff0c;查阅了大量的资料&#xff0c;做了无数次试验&#xff0c;终于整理出来了一个完整的教程&#xff0c;保可用&#xff0c;保真~~~~~如果各位遇到什么问题&#xf…

哈希的一些题目

题目1&#xff1a;星空之夜 1402. 星空之夜 - AcWing题库 刚开始看到这个题目感觉一懵&#xff0c;因为这个哈希是关于形状的哈希&#xff0c;不知道要怎么表示。 但是这道题的数据范围比较小&#xff0c;暴力也可以过。 暴力的方法是&#xff1a;搜索每一个连通块并保存下来…

Redis中的客户端(一)

客户端 概述 Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接&#xff0c;每个客户端可以向服务器发送命令请求&#xff0c;而服务器则接收并处理客户端发送的命令请求&#xff0c;并向客户端返回命令回复。通过使用由IO多路复用技术实现的文件…

LeetCode:1319. 连通网络的操作次数(并查集 Java)

目录 1319. 连通网络的操作次数 题目描述&#xff1a; 实现代码与解析&#xff1a; 并查集 原理思路&#xff1a; 1319. 连通网络的操作次数 题目描述&#xff1a; 用以太网线缆将 n 台计算机连接成一个网络&#xff0c;计算机的编号从 0 到 n-1。线缆用 connections 表示…

JAVA面试大全之JVM和调休篇

目录 1、类加载机制 1.1、类加载的生命周期&#xff1f; 1.2、类加载器的层次? 1.3、Class.forName()和ClassLoader.loadClass()区别? 1.4、JVM有哪些类加载机制&#xff1f; 2、内存结构 2.1、说说JVM内存整体的结构&#xff1f;线程私有还是共享的&#xff1f; 2.2…

.NET高级面试指南专题二十三【 B+ 树作为索引有什么优势】

B 树作为索引有许多优势&#xff0c;这些优势使其成为许多数据库管理系统中首选的索引结构之一。以下是 B 树作为索引的一些主要优势&#xff1a; 高效的查询性能&#xff1a;B 树是一种平衡树结构&#xff0c;具有良好的平衡性和高度平衡的性质&#xff0c;这使得在 B 树上进行…