Android中的WorkManager

news2025/1/11 12:50:05

Android中的WorkManager

workmanager

在后台运行任务会消耗设备有限的资源,如RAM和电池。这可能会导致用户体验不佳。例如,后台任务可能会降低设备的电池寿命或用户在观看视频、玩游戏、使用相机等时可能会遇到设备性能不佳的情况。

为了提高电池性能,Android在早期版本中发布了一些资源,如Doze模式、应用程序待机、限制位置访问和其他一些内容。

最佳使用场景

WorkManager situation
WorkManager是用于后台执行的推荐解决方案,考虑到所有操作系统后台执行的限制。如果您需要保证一个任务即使被延迟也会运行,那么您应该使用WorkManager。该API允许您安排作业(一次性或重复),并链接和组合作业。您还可以将执行限制应用于它们,例如当设备处于空闲或充电状态时触发,或在内容提供程序更改时执行。

WorkManager使用场景汇总

开始使用 Work Manager

步骤 1:在 Gradle 中定义依赖项

dependencies {
    val work_version = "2.8.0"

    // (Java only)
    implementation("androidx.work:work-runtime:$work_version")

    // Kotlin + coroutines
    implementation("androidx.work:work-runtime-ktx:$work_version")

    // optional - RxJava2 support
    implementation("androidx.work:work-rxjava2:$work_version")

    // optional - GCMNetworkManager support
    implementation("androidx.work:work-gcm:$work_version")

    // optional - Test helpers
    androidTestImplementation("androidx.work:work-testing:$work_version")

    // optional - Multiprocess support
    implementation "androidx.work:work-multiprocess:$work_version"
}

步骤2:创建Worker类

使用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 work finished successfully with the Result
       return Result.success()
   }
}

步骤3 创建 Work Request

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

工作管理器提供服务以安排一次性和周期性请求,可以在一段时间内定期运行。我们将在本文后面详细了解它。

步骤4:提交工作请求

WorkManager
    .getInstance(myContext)
    .enqueue(uploadWorkRequest)

添加输入和输出

在这里插入图片描述

1. 在工作请求中添加输入

我们需要创建一个 bundle 并将其传递给 worker 请求。

以图像上传为例,我们希望将 Uri 作为输入参数传递。

  1. 创建一个 Data.Builder 对象。在请求时导入 androidx.work.Data
  2. 如果imageUri是非空的 URI,则使用 putString 方法将其添加到 Data 对象中。此方法接受一个键和一个值。您可以使用 Constants类中的字符串常量 KEY_IMAGE_URI
  3. Data.Builder对象上调用 build() 方法以创建您的 Data 对象,并将其返回。
private fun createInputDataForUri(): Data {
    val builder = Data.Builder()
    imageUri?.let {
        builder.putString(KEY_IMAGE_URI, imageUri.toString())
    }
    return builder.build()
}

2. 将数据对象传递给工作请求

val blurRequest = OneTimeWorkRequestBuilder<UploadWorker>()
            .setInputData(createInputDataForUri())
            .build()

更新UploadWorkerdoWork()以获取输入

class UploadWorker(appContext: Context, workerParams: WorkerParameters):
       Worker(appContext, workerParams) {
   override fun doWork(): Result {
  val resourceUri = inputData.getString(KEY_IMAGE_URI)
       // Do the work here--in this case, upload the images.
       uploadImages(resourceUri)

       // Indicate whether the work finished successfully with the Result
       return Result.success()
   }
}

工作请求类型

在这里插入图片描述

WorkRequest本身是一个抽象的基类。这个类有两个派生实现,你可以使用它们来创建请求,即OneTimeWorkRequest和PeriodicWorkRequest。正如它们的名字所示,OneTimeWorkRequest适用于安排不重复的工作,而PeriodicWorkRequest更适合安排在某个间隔上重复的工作。

1. 安排一次性工作

对于简单的任务

val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

对于复杂的任务

val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

2. 安排加速工作

面向 Android 12 或更高版本的应用程序在后台运行时不再能够启动前台服务。这使得 WorkManager 能够在给系统更好地控制资源访问权限的同时执行重要工作。

加速工作具有以下显著特点:

  • 重要性
  • 速度
  • 配额
  • 电源管理
  • 延迟

排序成为加急工作的潜在用例,例如当用户想要发送消息或附加图像时,在聊天应用程序中使用。同样,处理付款或订阅流程的应用程序也可能希望使用加急工作,因为这些任务对用户很重要,在后台快速执行,需要立即开始,并且即使用户关闭应用程序,也应继续执行。

配额

系统必须在运行之前为加急工作分配执行时间。 执行时间不是无限的。相反,每个应用程序都会收到执行时间配额。 当您的应用程序使用其执行时间并达到其分配配额时,您将无法再执行加急工作,直到配额刷新。 这使Android能够更有效地在应用程序之间平衡资源。

执行重要工作
调用setExpedited() 告诉框架,这项工作很重要,应优先于其他已安排的工作。请注意,我们还将OutOfQuotaPolicy参数传递给setExpedited()。基于App Standby Buckets的配额适用于加速作业,因此OutOfQuotaPolicy参数告诉WorkManager,如果您的应用程序尝试在配额不足的情况下运行加速作业,则应执行以下操作:要么完全放弃加速的工作请求(DROP_WORK_REQUEST),要么将作业作为常规工作请求处理(RUN_AS_NON_EXPEDITED_WORK_REQUEST)。

3.执行周期性工作

有时候你需要定期执行一些任务,比如同步数据、备份数据、下载最新的数据。

val saveRequest =
       PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
    // Additional configuration
           .build()

这意味着您的工作请求将每隔一小时执行一次。最小时间间隔为15分钟。

灵活的运行间隔

如果您的工作性质使其对运行时间敏感,您可以配置PeriodicWorkRequest在每个间隔期内的某个灵活期内运行。

val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
       1, TimeUnit.HOURS, // repeatInterval (the period cycle)
       15, TimeUnit.MINUTES) // flexInterval
    .build()

如果假设您的重复间隔为1小时,flexInterval为15分钟。那么您的任务将在(1小时-15分钟)至结束的15分钟之间开始。

重复间隔必须大于或等于PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS,flex间隔必须大于或等于PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS

为了理解周期性工作请求,让我们举个例子:

例如:请在每周五的下午5:00生成一份报告,总结本周的销售数据。

  1. 创建WeeklyReportWorker类。
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters

class WeeklyReportWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {

    override fun doWork(): Result {
        // Code to generate the weekly sales report goes here
        // This could involve fetching data from a server or database,
        // performing calculations, and storing the results in a file or database.
        return Result.success()
    }
}
  1. 使用WorkManager API周期性地安排工作程序运行。
import androidx.appcompat.app.AppCompatActivity
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Schedule the worker to run every Friday at 5:00 pm
        val weeklyReportRequest = PeriodicWorkRequestBuilder<WeeklyReportWorker>(
            7, // Repeat interval
            TimeUnit.DAYS // Interval unit
        )
            .setInitialDelay(calculateInitialDelay(), TimeUnit.MILLISECONDS) // Initial delay
            .build()

        WorkManager.getInstance(applicationContext).enqueue(weeklyReportRequest)
    }

    private fun calculateInitialDelay(): Long {
        // Calculate the initial delay based on the current time and the desired
        // time for the first run (Friday at 5:00 pm).
        val currentTimeMillis = System.currentTimeMillis()
        val desiredTimeMillis = getDesiredTimeMillis()
        var initialDelay = desiredTimeMillis - currentTimeMillis
        if (initialDelay < 0) {
            // If the desired time has already passed for this week, schedule the
            // first run for next week instead.
            initialDelay += TimeUnit.DAYS.toMillis(7)
        }
        return initialDelay
    }

    private fun getDesiredTimeMillis(): Long {
        val calendar = Calendar.getInstance().apply {
            set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY)
            set(Calendar.HOUR_OF_DAY, 17) // 5:00 pm
            set(Calendar.MINUTE, 0)
            set(Calendar.SECOND, 0)
            set(Calendar.MILLISECOND, 0)
        }
        return calendar.timeInMillis
    }
}

这段代码使用PeriodicWorkRequestBuilder来调度WeeklyReportWorker每7天运行一次(即每周运行一次)。第一次运行的初始延迟是基于当前时间和第一次运行的期望时间(周五下午5点)计算得出的。getDesiredTimeMillis() 方法将第一次运行的期望时间作为Long值返回,表示自 Unix 纪元以来的毫秒数。该值使用设置为期望的一周中某一天(星期五)和时间(下午5点)的 Calendar 对象计算得出。

工作约束

在这里插入图片描述

约束条件确保工作在满足特定条件之前不会启动。

在这里插入图片描述

val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       .setConstraints(constraints)
       .build()

val constraints = Constraints.Builder()
        .setRequiresBatteryNotLow(true)
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresCharging(true)
        .setRequiresStorageNotLow(true)
        .setRequiresDeviceIdle(true)
        .build()

延迟工作

添加延迟意味着你希望工作在一段时间延迟后开始,而不是立即开始。

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

重试和退避策略
这允许用户在一段时间后重试他们的工作,每次重试时间会以线性或指数方式增加。

val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

让我们用之前的示例来理解它

import androidx.appcompat.app.AppCompatActivity
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Schedule the worker to run every 7 days, with a flexible interval of up to 3 days
        val weeklyReportRequest = PeriodicWorkRequestBuilder<WeeklyReportWorker>(
            7, // Repeat interval
            TimeUnit.DAYS // Interval unit
        )
            .setInitialDelay(1, TimeUnit.DAYS) // Initial delay
            .setBackoffCriteria(
                BackoffPolicy.LINEAR,
                1, // Initial backoff delay
                TimeUnit.HOURS // Backoff delay unit
            )
            .setFlex(3, TimeUnit.DAYS) // Flexible interval
            .build()

        WorkManager.getInstance(applicationContext).enqueue(weeklyReportRequest)
    }
}

所以当我们尝试获取每周报告时失败了。我们可以在1个小时后再次重试,如果再次失败,则会呈线性增长,因此下次重试将会在2小时后发生。

任务链

当您想按特定顺序运行多个任务时,可以将任务链在一起。

WorkManager.getInstance(myContext)
   // Candidates to run in parallel
   .beginWith(listOf(plantName1, plantName2, plantName3))
   // Dependent work (only runs after all previous work in chain)
   .then(cache)
   .then(upload)
   // Call enqueue to kick things off
   .enqueue()

独特的任务

在这里插入图片描述

有时候您只希望同时运行一个工作链。例如,您可能有一个工作链可以将本地数据与服务器同步,您可能希望在启动新的同步之前让第一个数据同步完成。为了做到这一点,您需要使用beginUniqueWork而不是beginWith,并提供一个唯一的字符串名称。这将为整个工作请求链命名,以便您可以一起引用和查询它们。

val sendLogsWorkRequest =
       PeriodicWorkRequestBuilder<SendLogsWorker>(24, TimeUnit.HOURS)
           .setConstraints(Constraints.Builder()
               .setRequiresCharging(true)
               .build()
            )
           .build()
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
           "sendLogs",
           ExistingPeriodicWorkPolicy.KEEP,
           sendLogsWorkRequest
)

观察你的任务

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
workManager.getWorkInfoByIdLiveData(syncWorker.id)
               .observe(viewLifecycleOwner) { workInfo ->
   if(workInfo?.state == WorkInfo.State.SUCCEEDED) {
       Snackbar.make(requireView(), 
      R.string.work_completed, Snackbar.LENGTH_SHORT)
           .show()
   }
}

取消并停止任务

// by id
workManager.cancelWorkById(syncWorker.id)

// by name
workManager.cancelUniqueWork("sync")

// by tag
workManager.cancelAllWorkByTag("syncTag")

参考链接

https://developer.android.com/codelabs/android-workmanager#0
https://medium.com/androiddevelopers/using-workmanager-on-android-12-f7d483ca0ecb
https://developer.android.com/guide/background/persistent

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

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

相关文章

关于人力资源管理职能,你需要知道的事

每个成功的企业都有一个称职的人力资源部门。它是任何企业的重要组成部分&#xff0c;是员工和管理层之间的纽带。人力资源涵盖影响组织人员的所有任务&#xff0c;从基本的人力资源活动到战略决策。 对于任何希望可持续发展的企业来说&#xff0c;人力资源管理职能的重要性不…

0802数量积向量积混合积-向量代数与空间解析几何

文章目录 1 两向量的数量积1.1 引例1.2 定义1.3 推论1.4 运算规律1.4 数量积的坐标表示 2 两向量的向量积2.1 定义2.2 重要结论2.3 几何意义&#xff08;向量积模&#xff09;2.4 向量积的运算规律2.5 向量积的坐标表示 3 向量的混合积3.1 混合积的定义3.2 混合积的坐标表示3.3…

初识报表引擎-FineReport

简介 提到报表引擎大家可能都会说帆软。目前商用的比较突出的两个报表引擎&#xff1a;分别是帆软FineReport、RDP报表引擎&#xff0c;其中帆软功能突出且非常完整但是价格较高&#xff0c;RDP功能相对完整但是不够强大貌似还有些BUG&#xff0c;不过价格很低。就目前的情况来…

《模板的进阶》

本文主要介绍C模板知识&#xff0c;包括模板的参数类型&#xff0c;模板的特化&#xff0c;模板的分离编译 文章目录 思维导图一、非类型模板参数二、模板的特化2.1模板特化的概念2.2函数模板特化2.3类模板的特化2.3.1全特化2.3.2偏特化 2.4非类型模板参数也是可以特化的 三、模…

Android 调用系统隐藏的类和方法

1.Android系统隐藏的类和方法 阅读Android源码时&#xff0c;会发现很多被UnsupportedAppUsage注解的方法&#xff0c;这些方法不能被外部应用访问。 比如Android中的PackageParser类&#xff0c;这个类是在android.content.pm包下面&#xff1a; 可以看到这个类是隐藏的&…

有哪些自动化构建工具推荐? - 易智编译EaseEditing

以下是几个常用的自动化构建工具推荐&#xff1a; Jenkins&#xff1a; Jenkins 是一个开源的自动化构建工具&#xff0c;广泛用于持续集成和持续交付。 它支持各种编程语言和版本控制系统&#xff0c;并提供了丰富的插件生态系统&#xff0c;可实现灵活的构建流程和自动化部…

【Spring】— 动态SQL :<choose>、<when>和<otherwise>元素

<choose>、<when>和<otherwise>元素 在使用<if>元素时&#xff0c;只要test属性中的表达式为true&#xff0c;就会执行元素中的条件语句&#xff0c;但是在实际应用中&#xff0c;有时只需要从多个选项中选择一个执行。例如&#xff0c;若用户姓名不为…

九、Docker网络

Docker网络 一、docker网络介绍 Docker网络在Docker的基础知识中算比较重要的了&#xff0c;需要多多实验理解。 Docker服务安装启动后默认在host上创建了三个网络&#xff1a; [rootk8s-m1 ~]# ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UN…

毛毛莫名的大一生活总结

毛毛莫名的大一生活总结 1. 关于高考我的看法1.1 初中1.2 高中 2. 大一开学前的暑假3. 大一开学3.1 军训3.2 学生会 学校社团 运动团体3.2.1 学生会3.2.1.1 院学生会3.2.1.2校学生会 3.2.2 社团3.2.3 运动团体 4. 大学生活 1. 关于高考我的看法 1.1 初中 初一初二对学习不用太…

一种简单的Android骨架屏实现方案----0侵入0成本

对骨架屏的理解 什么是骨架屏 所谓骨架屏&#xff0c;就是在页面进行耗时加载时&#xff0c;先展示的等待 UI, 以告知用户程序目前正在运行&#xff0c;稍等即可。 等待的UI大部分是 loading 转圈的弹窗&#xff0c;有的是自己风格的小动画。其实大同小异。而骨架屏无非也是一…

本地部署github上的stable diffuion,轻松玩转ai绘画(新手小白也能懂)

你也想自己生成上面这样好看的图片吗&#xff1f;废话不多说&#xff0c;跟着博主&#xff0c;按步骤来&#xff0c;做完你也就可以了&#xff0c;而且无任何限制&#xff0c;懂得都懂&#xff01;&#x1f60e; 目录 第一步&#xff1a;准备VPN 第二步&#xff1a;安装Pyth…

LabVIEWCompactRIO 开发指南第七章48

LabVIEWCompactRIO 开发指南第七章48 5.如果控制器上已经安装了LabVIEW实时和NI-RIO&#xff0c;请选择自定义软件安装并点击下一步。如果出现警告对话框&#xff0c;请单击“是”。单击NI-工业通信用于EtherCAT的框。将自动检查所需的依赖项。单击下一步继续在控制器上安装软…

一款开源的无线CMSIS DAP ARM芯片下载调试器详细说明

文章目录 概要1. 一般概念1.1 CMSIS—DAP的一般概念1.2 支持的芯片1.3 典型应用场景 2. 原理图与尺寸图2.1 Host端&#xff08;发送端&#xff09;原理图2.2 Target&#xff08;目标&#xff09;端原理图2.3 Host尺寸图2.4 Target尺寸图2.5 实物图 3. 使用方法3.1 连接方法3.1.…

群晖折腾记1—群晖NAS使用docker中的ddns-go,DDNS阿里云ali实现Ipv6访问

群晖折腾记1—群晖NAS使用docker中的ddns-go,DDNS阿里云ali实现Ipv6访问 前置条件科普时间具体步骤1. 域名购买2. 获取AccessKey ID和Secret3. 获取免费SSL证书4. 在docker中运行ddns-go6.设置反向代理 前置条件 1、移动宽带只有IPv6公网地址&#xff0c;若你的宽带为电信或联…

Eslint配置指南

ESLint最初是由Nicholas C. Zakas 于2013年6月创建的开源项目。ESLint 是一个开源的 JavaScript 代码检查工具&#xff0c;它是用来进行代码的校验&#xff0c;检测代码中潜在的问题&#xff0c;比如某个变量定义了未使用、函数定义的参数重复、变量名没有按规范命名等等。 中…

logging 模块因权限问题写入日志失败

哈喽大家好&#xff0c;我是咸鱼 今天跟大家分享一个使用 Python 的 logging 模块写入日志文件时遇到的权限问题&#xff0c;不知道你们有没有遇到过 1.案例现象 今天上班的时候手机短信收到了 zabbix 告警&#xff0c;但是发现了不对劲的地方&#xff1a;微信没有收到告警信…

【C++】虚函数相关常见问题

【C】虚函数相关常见问题 文章目录 【C】虚函数相关常见问题1.说说为什么要虚析构&#xff1f;2. C默认的析构函数为什么不是虚函数?3. 构造函数能不能是虚函数4. 说说什么是虚继承&#xff0c;解决什么问题&#xff0c;如何实现&#xff1f;5. 说说什么是虚函数6.说说虚函数的…

VESC操作入门——PPM输入控制和ADC输入控制

目录 一、PPM输入控制1.1、硬件准备1.2、PPM信号1.3、校准电机1.4、输入设置 二、ADC输入控制2.1、硬件准备2.2、更改固件2.3、电压信号2.4、校准电机2.5、输入设置 三、电动车转把控制3.1、转把说明3.2、转把测试 ODrive、VESC和SimpleFOC 教程链接汇总&#xff1a;请点击 一、…

SpringBoot作日志切面记录

目录 1.WebLogAspect 2.配置log4j2.yml 3.效果 话不多说&#xff0c;直接上代码&#xff1a; 1.WebLogAspect import java.util.Arrays;import javax.servlet.http.HttpServletRequest;import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.Str…

第Y2周:训练自己的数据集

我的环境&#xff1a; 训练自己的数据集 一、准备自己的数据集1. 编写split_train_val.py文件2.生成训练文件索引文件 二、创建训练yaml文件三、开始训练 一、准备自己的数据集 数据集来源&#xff1a;kaggle水果检测 目录结构如下&#xff1a; 1. 编写split_train_val.py…