目录
- 一、WorkManager概述
- 1. WorkManager的作用:
- 2. WorkManager的各个角色
- 二、依赖库的导入
- 三、WorkManager几种基本使用
- 1. 单一任务的执行
- 2. 数据 互相传递
- 3. 多个任务 顺序执行
- 4. 重复执行后台任务
- 5. 约束条件
- 6. 证明 app被杀掉之后,还在后台执行
- 四、WorkManager源码流程图
一、WorkManager概述
WorkManage: 排队和管理工作请求;将WorkRequest对象传递WorkManager的任务队列
(注意:如果未指定任何约束,WorkManager立即运行任务)
WorkStatus: 包含有关特点任务的信息;可以使用LiveData保存WorkStatus对象,监听任务状态;如LiveData
你的任务不可能总是在前台,但是还要确保你的那么重要任务执行,就可以放置在后台执行,那么WorkManager就发挥其作用了。
WorkManager是Android Jetpack提供执行后台任务管理的组件,它适用于需要保证系统即使应用程序退出也会运行的任务,WorkManager API可以轻松指定可延迟的异步任务以及何时运行它们,这些API允许您创建任务并将其交给WorkManager立即允许或在适当的时间运行。
WorkManager根据设备API级别和应用程序状态等因素选择适当的方式来运行任务。如果WorkManager在应用程序运行时执行您的任务之一,WorkManager可以在您应用程序进程的新进程钟运行您的任务。如果您的应用程序未运行,WorkManager会选择一种合适的方式来安排后台任务–具体取决于设备API级别和包含的依赖项,WorkManager可能会使用JobScheduler,Firebase JobDispatcher或AlarmManager。
1. WorkManager的作用:
-
- 确保重要的后台任务,一定会被执行,后台任务(例如:非及时性的(请求服务器 及时性)上传、下载 ,同步数据等)
-
- 内部对电量进行了优化,不需要我们去处理电量优化了
-
- API 14 到最新版本,都可以使用WorkManager来管理你的后台任务
-
- 注意:WorkManager不能做保活操作
-
- 调度,管理,执行的后台任务的场景,通常是可延迟的后台任务
2. WorkManager的各个角色
- Worker:可以这样理解,指定需要执行的任务,可以成为Worker类的之类,在实现的方法钟,就可以执行任务逻辑了。
- WorkRequest:可以这样理解,执行一项单一的任务
- 第一点:必须知道WorkRequest对象必须指定Work执行的任务
- 第二点:需要知道WorkRequest都有一个自动生成的唯一ID,可以使用ID执行取消排队任务 或 获取任务状态等操作
- 第三点:需要知道WorkRequest是一个抽象的类;系统默认实现子类OneTimeWorkRequest 或 PeriodicWorkRequest
- 第四点:需要知道WorkRequest.Builder创建WorkRequest对象;相应的子类:OneTimeWorkRequest.Builder 或 PeriodicWorkRequest.Builder
- 第五点:需要知道Constraints:指定对任务运行时间的限制(任务约束);使用Constraints.Builder创建Constraints对象,并传递给WorkRequest.Builder
二、依赖库的导入
在app目录下的build.gradle导入依赖库
// 导入WorkManager依赖
def work_version = "2.3.4"
implementation "androidx.work:work-runtime:$work_version"
三、WorkManager几种基本使用
布局文件 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"android:orientation="vertical"><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="测试后台任务1"android:onClick="testBackgroundWork1"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="测试后台任务2"android:onClick="testBackgroundWork2"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="测试后台任务3"android:onClick="testBackgroundWork3"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="测试后台任务4"android:onClick="testBackgroundWork4"/><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="测试后台任务5"android:onClick="testBackgroundWork5"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><Buttonandroid:id="@+id/bt6"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="测试后台任务六"android:onClick="testBackgroundWork6"/><Buttonandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="SP归零"android:onClick="spReset"/></LinearLayout><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="分析源码"android:onClick="codeStudy"android:layout_marginTop="20dp"/></LinearLayout>
1. 单一任务的执行
MainWorker1.kt
package com.example.myworkmanagerimport android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters// 最简单的执行任务
class MainWorker1(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {// 定义静态变量 相对于java static const val相对于定义常量companion object { const val TAG = "abc" }// 后台任务 并且 异步的(原理:线程池执行Runnable)override fun doWork(): Result {Log.d(TAG, "MainWorker1 doWork: run started ...")try {Thread.sleep(3000) // 睡眠} catch (e : InterruptedException) {e.printStackTrace()Result.failure() // 本次任务失败} finally {Log.d(TAG, "MainWorker1 doWork: run end ...")}return Result.success() // 本次任务完成}
}
MainActivity.kt
/*** 最简单的 执行任务* 测试后台任务1*/fun testBackgroundWork1(view: View) {// OneTimeWorkRequest 单个 一次的任务val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker1::class.java).build()WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)}
2. 数据 互相传递
MainWorker2.kt
package com.example.myworkmanagerimport android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Data
import androidx.work.Worker
import androidx.work.WorkerParametersclass MainWorker2(context: Context, private val workerParameters: WorkerParameters) : Worker(context, workerParameters) {companion object {const val TAG = "abc"}// 后台任何 并且异步的(原理:线程池执行Runnable)@SuppressLint("RestrictedApi")override fun doWork(): Result {Log.d(TAG, "MainWorker2 dowork: 后台任务执行了")// 接收MainActivity传递过来的数据val dataString = workerParameters.inputData.getString("maiworker2")Log.d(TAG, "MainWorker2 dowork: 接收MainActivity传递过来的数据:$dataString")// 反馈数据给MainActivity// 把任务中的数据回传到MainActivity中val outputData = Data.Builder().putString("mainworker2", "mainworker2回传的数据").build()/*return Result.Failure() // 本次执行 doWork任务时 失败return Result.Retry() // 本次执行 doWork任务时 重试一次return Result.Success() // 本次执行 doWork任务时 成功 执行任务完毕*/return Result.Success(outputData);}
}
MainActivity.kt
/*** 数据 互相传递* 测试后台任务2* 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED*/fun testBackgroundWork2(view: View) {// 单一、一次的任务val oneTimeWorkRequest : OneTimeWorkRequest// 数据val sendData = Data.Builder().putString("mainworker2", "传给mainworker2的数据").build()// 请求对象初始化oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker2::class.java).setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给WorkManager2.build()// 一般都是通过 状态机 接收 WorkManager2的回馈数据// 状态机(LiveData)才能接收 WorkManager 回馈的数据WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this, Observer{ workInfo ->// 状态包含:ENQUEUED、RUNNING、SUCCEEDEDLog.d(MainWorker2.TAG, "状态:" + workInfo.state.name);// ENQUEUED, RUNNING 都取不到 回馈的数据,都是null// Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))if (workInfo.state.isFinished) { // 判断成功 SUCCEEDED状态Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))}})WorkManager.getInstance(this).equals(oneTimeWorkRequest)}
3. 多个任务 顺序执行
MainWorker3.kt
package com.example.myworkmanagerimport android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MainWorker3(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){companion object { const val TAG = "abc" }@SuppressLint("RestrictedApi")override fun doWork(): Result {Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")return Result.Success(); // 本次执行 doWork任务时 成功 执行任务完毕}
}
MainWorker4.kt
package com.example.myworkmanagerimport android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MainWorker4(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){companion object { const val TAG = "abc" }@SuppressLint("RestrictedApi")override fun doWork(): Result {Log.d(TAG, "MainWorker4 doWork: 后台任务执行了")return Result.Success(); // 本次执行 doWork任务时 成功 执行任务完毕}
}
MainWorker5.kt
package com.example.myworkmanagerimport android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MainWorker5(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){companion object { const val TAG = "abc" }@SuppressLint("RestrictedApi")override fun doWork(): Result {Log.d(TAG, "MainWorker5 doWork: 后台任务执行了")return Result.Success(); // 本次执行 doWork任务时 成功 执行任务完毕}
}
MainWorker6.kt
package com.example.myworkmanagerimport android.annotation.SuppressLint
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParametersclass MainWorker6(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters){companion object { const val TAG = "abc" }@SuppressLint("RestrictedApi")override fun doWork(): Result {Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")return Result.Success(); // 本次执行 doWork任务时 成功 执行任务完毕}
}
MainActivity.kt
/*** 多个任务 顺序执行* 测试后台任务3*/fun testBackgroundWork3(view: View) {// 单一、一次的任务val oneTimeWorkRequest3 = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()val oneTimeWorkRequest4 = OneTimeWorkRequest.Builder(MainWorker4::class.java).build()val oneTimeWorkRequest5 = OneTimeWorkRequest.Builder(MainWorker5::class.java).build()val oneTimeWorkRequest6 = OneTimeWorkRequest.Builder(MainWorker6::class.java).build()// 顺序执行 3 4 5 6WorkManager.getInstance(this).beginWith(oneTimeWorkRequest3).then(oneTimeWorkRequest4).then(oneTimeWorkRequest5).then(oneTimeWorkRequest6).enqueue()// 另外一种使用// 需求: 先执行3 4 最后执行6val oneTimeWorkRequests : MutableList<OneTimeWorkRequest> = ArrayList() // 集合方式oneTimeWorkRequests.add(oneTimeWorkRequest3)oneTimeWorkRequests.add(oneTimeWorkRequest4)WorkManager.getInstance(this).beginWith(oneTimeWorkRequests).then(oneTimeWorkRequest6).enqueue()}
4. 重复执行后台任务
MainActivity.kt
/*** 重复执行后台任务 非单个任务 ,多个任务* 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED*/fun testBackgroundWork4(view: View) {// OneTimeWorkRequest 单个任务 不会轮询 执行一次就结束// 重复的任务 多次/循环/轮询 哪怕设置为10s轮询一次,那么最少轮询/循环一次 也要15分钟(google规定)// 不能小于15分钟,否则默认修改成15分钟val periodicWorkRequest = PeriodicWorkRequest.Builder(MainWorker3::class.java,10, TimeUnit.SECONDS).build()// 状态机 为什么一直都是ENQUEUED 和 RUNNING,因为 你是轮询的任务,SUCCEEDED// 如果是单个任务,就会看到SUCCEEDED结束任务// 监听状态WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id).observe(this, Observer {workInfo ->Log.d("abc", "状态:" + workInfo.state.name) // ENQUEUED RUNNING 循环反复if(workInfo.state.isFinished) {Log.d("abc", "状态:isFinished = true 注意:后台任务已经完成了...")}})WorkManager.getInstance(this).equals(periodicWorkRequest)// 取消 任务的执行//WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.id);}
5. 约束条件
MainActivity.kt
/*** 约束条件,约束后台任务执行* 测试后台任务5*/fun testBackgroundWork5(view: View) {val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) // 必须是联网中.setRequiresCharging(true) // 必须是充当中.setRequiresDeviceIdle(true) // 必须是空闲时.build()/*** 除了上面设置的约束外,WorkManager还提供了以下的约束作为Work执行的条件:* setRequiredNetworkType:网络连接设置* setRequiresBatteryNotLow:是否为低电量时运行,默认false* setRequiresCharging:是否要插入设备(接入电源),默认false* setRequiresDeviceIdle:设备是否空闲,默认false* setRequiresStorageNotLow:设备可用存储是否不低于临界阈值*/// 请求对象val request = OneTimeWorkRequest.Builder(MainWorker3::class.java).setConstraints(constraints).build()// 加入队列WorkManager.getInstance(this).enqueue(request)}
6. 证明 app被杀掉之后,还在后台执行
MainActivity.kt
class MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}
/*** 证明 app被杀掉之后,还在后台执行,通过写入文件的方式(SP)* 测试后台任务6*/fun testBackgroundWork6(view: View) {// 约束条件val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件 必须是网络连接.build()// 构建Requestval request = OneTimeWorkRequest.Builder(MainWorker7::class.java).setConstraints(constraints).build()// 加入队列WorkManager.getInstance(this).enqueue(request)}// SP归零fun spReset(view: View) {val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)sp.edit().putInt(MainWorker7.SP_KEY, 0).apply()updateToUI()}// 从SP里面获取值,显示到界面给用户看private fun updateToUI() {val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)bt6?.text = "测试后台任务6---$resultValue"}// 文件内容只要有变化,此函数就会执行override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) = updateToUI()}
MainActivity.kt 完整代码:
package com.example.myworkmanagerimport android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.lifecycle.Observer
import androidx.work.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnitclass MainActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferenceChangeListener {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}/*** 最简单的 执行任务* 测试后台任务1*/fun testBackgroundWork1(view: View) {// OneTimeWorkRequest 单个 一次的任务val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker1::class.java).build()WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)}/*** 数据 互相传递* 测试后台任务2* 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED*/fun testBackgroundWork2(view: View) {// 单一、一次的任务val oneTimeWorkRequest : OneTimeWorkRequest// 数据val sendData = Data.Builder().putString("mainworker2", "传给mainworker2的数据").build()// 请求对象初始化oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker2::class.java).setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给WorkManager2.build()// 一般都是通过 状态机 接收 WorkManager2的回馈数据// 状态机(LiveData)才能接收 WorkManager 回馈的数据WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest.id).observe(this, Observer{ workInfo ->// 状态包含:ENQUEUED、RUNNING、SUCCEEDEDLog.d(MainWorker2.TAG, "状态:" + workInfo.state.name);// ENQUEUED, RUNNING 都取不到 回馈的数据,都是null// Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))if (workInfo.state.isFinished) { // 判断成功 SUCCEEDED状态Log.d(MainWorker2.TAG, "取到了任务回传的数据:" + workInfo.outputData.getString("mainworker2"))}})WorkManager.getInstance(this).equals(oneTimeWorkRequest)}/*** 多个任务 顺序执行* 测试后台任务3*/fun testBackgroundWork3(view: View) {// 单一、一次的任务val oneTimeWorkRequest3 = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()val oneTimeWorkRequest4 = OneTimeWorkRequest.Builder(MainWorker4::class.java).build()val oneTimeWorkRequest5 = OneTimeWorkRequest.Builder(MainWorker5::class.java).build()val oneTimeWorkRequest6 = OneTimeWorkRequest.Builder(MainWorker6::class.java).build()// 顺序执行 3 4 5 6WorkManager.getInstance(this).beginWith(oneTimeWorkRequest3).then(oneTimeWorkRequest4).then(oneTimeWorkRequest5).then(oneTimeWorkRequest6).enqueue()// 另外一种使用// 需求: 先执行3 4 最后执行6val oneTimeWorkRequests : MutableList<OneTimeWorkRequest> = ArrayList() // 集合方式oneTimeWorkRequests.add(oneTimeWorkRequest3)oneTimeWorkRequests.add(oneTimeWorkRequest4)WorkManager.getInstance(this).beginWith(oneTimeWorkRequests).then(oneTimeWorkRequest6).enqueue()}/*** 重复执行后台任务 非单个任务 ,多个任务* 单个任务状态:ENQUEUED、RUNNING、SUCCEEDED*/fun testBackgroundWork4(view: View) {// OneTimeWorkRequest 单个任务 不会轮询 执行一次就结束// 重复的任务 多次/循环/轮询 哪怕设置为10s轮询一次,那么最少轮询/循环一次 也要15分钟(google规定)// 不能小于15分钟,否则默认修改成15分钟val periodicWorkRequest = PeriodicWorkRequest.Builder(MainWorker3::class.java,10, TimeUnit.SECONDS).build()// 状态机 为什么一直都是ENQUEUED 和 RUNNING,因为 你是轮询的任务,SUCCEEDED// 如果是单个任务,就会看到SUCCEEDED结束任务// 监听状态WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id).observe(this, Observer {workInfo ->Log.d("abc", "状态:" + workInfo.state.name) // ENQUEUED RUNNING 循环反复if(workInfo.state.isFinished) {Log.d("abc", "状态:isFinished = true 注意:后台任务已经完成了...")}})WorkManager.getInstance(this).equals(periodicWorkRequest)// 取消 任务的执行//WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.id);}/*** 约束条件,约束后台任务执行* 测试后台任务5*/fun testBackgroundWork5(view: View) {val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) // 必须是联网中.setRequiresCharging(true) // 必须是充当中.setRequiresDeviceIdle(true) // 必须是空闲时.build()/*** 除了上面设置的约束外,WorkManager还提供了以下的约束作为Work执行的条件:* setRequiredNetworkType:网络连接设置* setRequiresBatteryNotLow:是否为低电量时运行,默认false* setRequiresCharging:是否要插入设备(接入电源),默认false* setRequiresDeviceIdle:设备是否空闲,默认false* setRequiresStorageNotLow:设备可用存储是否不低于临界阈值*/// 请求对象val request = OneTimeWorkRequest.Builder(MainWorker3::class.java).setConstraints(constraints).build()// 加入队列WorkManager.getInstance(this).enqueue(request)}/*** 证明 app被杀掉之后,还在后台执行,通过写入文件的方式(SP)* 测试后台任务6*/fun testBackgroundWork6(view: View) {// 约束条件val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件 必须是网络连接.build()// 构建Requestval request = OneTimeWorkRequest.Builder(MainWorker7::class.java).setConstraints(constraints).build()// 加入队列WorkManager.getInstance(this).enqueue(request)}// SP归零fun spReset(view: View) {val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)sp.edit().putInt(MainWorker7.SP_KEY, 0).apply()updateToUI()}// 从SP里面获取值,显示到界面给用户看private fun updateToUI() {val sp = getSharedPreferences(MainWorker7.SP_NAME, Context.MODE_PRIVATE)val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)bt6?.text = "测试后台任务6---$resultValue"}// 文件内容只要有变化,此函数就会执行override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) = updateToUI()// TODO ------------------------下面是源码分析环节/*** TODO 分析源码*/fun codeStudy(view: View) {// 没有约束条件// 请求对象val request = OneTimeWorkRequest.Builder(MainWorker3::class.java).build()// 第一次初始化是在 ContentProvider/*** APK 清单文件里面(第一次初始化)执行* 成果 WorkManagerImpl构建出来了* 1. 初始化 数据库 ROOM 来保存你的任务(持久性保存的)手机重启 APP被杀掉 没关系 一定执行* 2. 初始化 埋下伏笔 new GreedyScheduler(context, taskExcutor, this)* 3. 初始化 配置信息 configuration (执行信息 线程池任务)*/WorkManager.getInstance(this) // 这里已经是第二次初始化了.enqueue(request) // 执行流程源码分析}}