ViewModel

非及时任务

使用

请求对象初始化

val oneTimeWorkRequest0 = OneTimeWorkRequest.Builder(MainWorker0::class.java).build()

WorkManager.getInstance(this).enqueue(oneTimeWorkRequest) //执行

1.最简单的 执行任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 最简单的 执行任务
* 测试后台任务 1
*
* @param view
*/
fun testBackgroundWork1(view: View) {
// OneTimeWorkRequest 单个 一次的

val oneTimeWorkRequest = OneTimeWorkRequest.Builder(MainWorker7::class.java).build()

WorkManager.getInstance(this).enqueue(oneTimeWorkRequest)
}


// 最简单的 执行任务
class MainWorker1(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {

companion object { const val TAG = "Derry" }

// 后台任务 并且 异步的 (原理:线程池执行Runnable)
override fun doWork(): Result {
Log.d(TAG, "MainWorker1 doWork: run started ... ")

try {
Thread.sleep(8000) // 睡眠
} catch (e: InterruptedException) {
e.printStackTrace()
Result.failure(); // 本次任务失败
} finally {
Log.d(TAG, "MainWorker1 doWork: run end ... ")
}

return Result.success(); // 本次任务成功
}
}

2.数据 互相传递

数据:val sendData = Data.Builder().putString("Derry", "九阳神功").build()

发送:

1
2
3
oneTimeWorkRequest1 = OneTimeWorkRequest.Builder(MainWorker2::class.java)
.setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给 WorkManager2
.build()

接收: 一般都是通过状态机(LiveData) 才能接收 WorkManager回馈的数据

1
2
3
4
5
6
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest1.id)
.observe(this, { workInfo ->
if (workInfo.state.isFinished) { // 判断成功 SUCCEEDED状态
Log.d(MainWorker2.TAG, "取到了任务回传的数据: " + workInfo.outputData.getString("Derry"))
}
})

Work中接收:val dataString = workerParams.inputData.getString("Derry")

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 数据 互相传递
* 测试后台任务 2
*
* @param view
*/
fun testBackgroundWork2(view: View?) {
val oneTimeWorkRequest1: OneTimeWorkRequest

//从MainActivity传递数据到WorkManager2--------------------
val sendData = Data.Builder().putString("Derry", "九阳神功").build()// 数据
// 请求对象初始化
oneTimeWorkRequest1 = OneTimeWorkRequest.Builder(MainWorker2::class.java)
.setInputData(sendData) // 数据的携带 发送 一般都是 携带到Request里面去 发送数据给 WorkManager2
.build()

//接收从WorkManager2传递过来的数据--------------------------
WorkManager.getInstance(this).getWorkInfoByIdLiveData(oneTimeWorkRequest1.id)
.observe(this, { workInfo ->

// ENQUEUED,RUNNING,SUCCEEED
Log.d(MainWorker2.TAG, "状态:" + workInfo.state.name)

// ENQUEUED, RUNNING 都取不到 回馈的数据 都是 null
// Log.d(MainWorker2.TAG, "取到了任务回传的数据: " + workInfo.outputData.getString("Derry"))

if (workInfo.state.isFinished) { // 判断成功 SUCCEEDED状态
Log.d(MainWorker2.TAG, "取到了任务回传的数据: " + workInfo.outputData.getString("Derry"))
}
})

WorkManager.getInstance(this).enqueue(oneTimeWorkRequest1)
}


class MainWorker2(context: Context, private val workerParams: WorkerParameters) : Worker(context, workerParams) {

companion object { const val TAG = "Derry" }

// 后台任务 并且 异步的 (原理:线程池执行Runnable)
@SuppressLint("RestrictedApi")
override fun doWork(): Result { // 开始执行了 ENQUEUED
Log.d(MainWorker2.TAG, "MainWorker2 doWork: 后台任务执行了")

// 接收 MainActivity传递过来的数据--------------------------------
val dataString = workerParams.inputData.getString("Derry")
Log.d(MainWorker2.TAG, "MainWorker2 doWork: 接收MainActivity传递过来的数据:$dataString")

// 正在执行中 RUNNING

// 反馈数据 给 MainActivity
// 把任务中的数据回传到MainActivity中---------------------------
val outputData = Data.Builder().putString("Derry", "三分归元气").build()

return Result.Success(outputData) // if (workInfo.state.isFinished) Success
}
}
  • return new Result.Failure(); // 本地执行 doWork 任务时 失败
  • return new Result.Retry(); // 本地执行 doWork 任务时 重试一次
  • return new Result.Success(); // 本地执行 doWork 任务时 成功 执行任务完毕

3.多个任务 顺序执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 多个任务 顺序执行
* 测试后台任务 3
*
* @param view
*/
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 6
WorkManager.getInstance(this)
.beginWith(oneTimeWorkRequest3) // 做初始化检查的任务 成功后
.then(oneTimeWorkRequest4) // 业务1 任务 成功后
.then(oneTimeWorkRequest5) // 业务2 任务 成功后
.then(oneTimeWorkRequest6) // 最后检查工作任务
.enqueue()

// 需求:先执行 3 4 最后执行 6
val oneTimeWorkRequests: MutableList<OneTimeWorkRequest> = ArrayList() // 集合方式
oneTimeWorkRequests.add(oneTimeWorkRequest3) // 先同步日志信息
oneTimeWorkRequests.add(oneTimeWorkRequest4) // 先更新服务器数据信息

WorkManager.getInstance(this)
.beginWith(oneTimeWorkRequests)
.then(oneTimeWorkRequest6) // 最后再 检查同步
.enqueue()
}


/**
* 后台任务3
*/
class MainWorker3 (context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {

companion object { const val TAG = "Derry" }

@SuppressLint("RestrictedApi")
override fun doWork(): Result {
Log.d(TAG, "MainWorker3 doWork: 后台任务执行了")

return Result.Success() // 本地执行 doWork 任务时 成功 执行任务完毕
}
}

4.重复执行后台任务 非单个任务,多个任务

前面三个任务不会轮询,执行一次就OK

重复的任务 多次/循环/轮询

规定的轮询时间,不能小于15分钟,否则默认修改成15分钟(Google规定的)

WorkManager会控制优化耗电

轮询的状态只有ENQUEUED、RUNNING,没有SUCCESS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 重复执行后台任务 非单个任务,多个任务
* 测试后台任务 4
*
* @param view
*/
fun testBackgroundWork4(view: View) {

val periodicWorkRequest =
PeriodicWorkRequest.Builder(MainWorker3::class.java, 10, TimeUnit.SECONDS)
.build()

// 监听状态
WorkManager.getInstance(this).getWorkInfoByIdLiveData(periodicWorkRequest.id)
.observe(this, { workInfo ->
Log.d(MainWorker2.TAG, "状态:" + workInfo.state.name) // ENQUEEN RUNN 循环反复
if (workInfo.state.isFinished) {
Log.d(MainWorker2.TAG, "状态:isFinished=true 同学们注意:后台任务已经完成了...")
}
})
WorkManager.getInstance(this).enqueue(periodicWorkRequest)

// 取消 任务的执行
// WorkManager.getInstance(this).cancelWorkById(periodicWorkRequest.getId());
}

5.约束后台任务执行(联网中、充电中、空闲时)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 约束条件,约束后台任务执行
* 测试后台任务 5
*
* @param view
*/
@RequiresApi(api = Build.VERSION_CODES.M)
fun testBackgroundWork5(view: View?) {
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 必须是联网中
.setRequiresCharging(true) // 必须是充电中
.setRequiresDeviceIdle(true) // 必须是空闲时(例如:你没有玩游戏 例如:你有没有看大片 1亿像素的)
.build()

/**
* 除了上面设置的约束外,WorkManger还提供了以下的约束作为Work执行的条件:
* setRequiredNetworkType:网络连接设置
* setRequiresBatteryNotLow:是否为低电量时运行 默认false
* setRequiresCharging:是否要插入设备(接入电源),默认false
* setRequiresDeviceIdle:设备是否为空闲,默认false
* setRequiresStorageNotLow:设备可用存储是否不低于临界阈值
*/

// 请求对象
val request = OneTimeWorkRequest.Builder(MainWorker3::class.java)
.setConstraints(constraints) // Request 关联 约束条件
.build()

// 加入队列
WorkManager.getInstance(this).enqueue(request)
}

6.后台任务(任务被杀掉了还在后台执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* (你怎么知道,他被杀掉后,还在后台执行?)写入文件的方式(SP)
* 测试后台任务 6
*
* @param view
*/
fun testBackgroundWork6(view: View?) {
// 约束条件
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED) // 约束条件,必须是网络连接
.build()

// 构建Request
val request = OneTimeWorkRequest.Builder(MainWorker7::class.java)
.setConstraints(constraints)
.build()

// 加入队列
WorkManager.getInstance(this).enqueue(request)
}

// 从SP里面获取值,显示到界面给用户看就行了
private fun updateToUI() {
val sp = getSharedPreferences(MainWorker7.SP_NAME, MODE_PRIVATE)
val resultValue = sp.getInt(MainWorker7.SP_KEY, 0)
bt6 ?.setText("测试后台任务六 -- $resultValue")
}

// SP归零 点击事件
fun spReset(view: View?) {
val sp = getSharedPreferences(MainWorker7.SP_NAME, MODE_PRIVATE)
sp.edit().putInt(MainWorker7.SP_KEY, 0).apply()
updateToUI()
}

// 文件内容只要敢变,此函数执行
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?)= updateToUI()




class MainWorker7(context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {

// 三个静态常量 SP的标记
companion object {

const val TAG = "Derry"

const val SP_NAME = "spNAME" // SP name

const val SP_KEY = "spKEY" // SP Key

}

// 后台任务
@SuppressLint("RestrictedApi")
override fun doWork(): Result {
Log.d(TAG, "MainWorker7 doWork: 后台任务执行了 started")

// 睡眠八秒钟
try {
Thread.sleep(8000)
} catch (e: InterruptedException) {
e.printStackTrace()
}

// 获取SP
val sp = applicationContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)

// 获取 sp 里面的值
var spIntValue = sp.getInt(SP_KEY, 0)

sp.edit().putInt(SP_KEY, ++spIntValue).apply() // 每隔8秒钟 更新文件 0 1 2 3

Log.d(TAG, "MainWorker7 doWork: 后台任务执行了 end")

return Result.Success() // 本地执行 doWork 任务时 成功 执行任务完毕
}

}

原理

WorkManager特点:

保证用户的任务一定会执行(记录更新每一个任务的信息/状态【Room数据库的更 新】,手机重启,APP被杀掉 都一定会执行,因为同学们要记住一句话:Google说 WorkManager是保证你的任务一定会执行的)

非及时性的执行任务(就是不会马上执行,哪怕是你看到的现象是马上执行的,但每 次执行的时间都无法确定,因为都是非及时性的执行任务哦)

适用场景: 定期重复性任务,但时效性/及时性要求不高的,例如:定期log上传,数据备份,等 等

会把任务保存在Room数据库中,会由安卓操作系统级别的服务,读取你的任务去执行(WorkManager会ContentProvider暴露你的app给系统)

//请求对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val request = OneTimeWorkRequest.Builder(MainWorker0::class.java).build()

// 第一次 在ContentProvider
/**
* APK清单文件里面(第一次)执行 面试官
* 成果WorkManagerImpl构建出来了
* 1.初始化 数据库 ROOM 来保存你的任务 (持久性保存的) 手机重启,APP被杀掉 没关系 一定执行
* 2.初始化 埋下伏笔 贪婪执行器 new GreedyScheduler(context, taskExecutor, this)
* 3.初始化 配置信息 configuration (执行信息,线程池任务)
*/
WorkManager.getInstance(this) //这里其实已经是第二次初始化了

//前面的所有准备任务都是为了最后enqueue

//执行
.enqueue(request )

为什么一打开,它就能收到信息去执行任务?因为其内部就是用了广播

是一个广播集 有很多广播

Service保活

在 Android 中,WorkManager 是官方推荐的 后台任务调度库,适合用于 周期性任务延迟任务,但它 并不能直接保活 Service(因为 WorkManager 的设计初衷是平衡任务执行和系统资源,而非长期运行后台服务)。

不过,可以通过 结合 WorkManager 和 Service 的方式,间接实现类似“保活”的效果(例如:定时检查 Service 是否存活,并在需要时重新启动)。


1. WorkManager 的基本原理

  • 适用场景:延迟任务、周期性任务、需持久化的任务(如数据同步、日志上传)。
  • 优势
    • 兼容 Android 4.0+(使用 JobSchedulerAlarmManagerBroadcastReceiver 作为底层实现)。
    • 系统会自动优化执行时机(避免耗电)。
    • 任务持久化(即使 App 被杀死或设备重启,任务仍会执行)。

⚠️ 注意:WorkManager 不适合 以下场景:

  • 需要 长期运行 的任务(如音乐播放、实时定位)。
  • 需要 立即执行 的任务(WorkManager 可能会延迟)。

2. 使用 WorkManager 定期检查并重启 Service

如果希望 Service 长期存活,可以:

  1. 使用前台 Service 运行核心任务(如音乐播放)。
  2. 使用 WorkManager 定期检查 Service 是否存活,如果被杀则重新启动。

(1) 定义一个 Worker(定时检查 Service 状态)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class ServiceKeepAliveWorker(
context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {

override fun doWork(): Result {
// 检查 Service 是否在运行
if (!isServiceRunning<MyService>()) {
// 如果 Service 不在运行,则重新启动
val intent = Intent(applicationContext, MyService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
applicationContext.startForegroundService(intent)
} else {
applicationContext.startService(intent)
}
}
return Result.success()
}

// 检查 Service 是否正在运行
private inline fun <reified T : Service> isServiceRunning(): Boolean {
val manager = applicationContext.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
manager.getRunningServices(Int.MAX_VALUE).forEach { service ->
if (T::class.java.name == service.service.className) {
return true
}
}
return false
}
}

(2) 使用 WorkManager 定期调度 Worker

1
2
3
4
5
6
7
8
9
10
11
// 在 Application 或 Activity 中初始化
val keepAliveRequest = PeriodicWorkRequestBuilder<ServiceKeepAliveWorker>(
15, // 每 15 分钟检查一次
TimeUnit.MINUTES
).build()

WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"ServiceKeepAlive", // 唯一工作名称
ExistingPeriodicWorkPolicy.KEEP, // 如果已存在相同任务,则保留旧的
keepAliveRequest
)

(3) 在 AndroidManifest.xml 中声明 Worker

1
2
3
4
5
6
7
8
9
<application ...>
<service android:name=".MyService" />

<!-- 注册 Worker -->
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:exported="false" />
</application>

面试

WorkManager做什么的?

处理非即时任务的,举个例子:每天同步一次数据到服务器,这种类似的需求,不是及时执行,但是又保证会执行的非及时任务(用于确保重要的后台任务一定会执行,比如上传,下载,同步服务器等操作)

WorkManager是怎么保证,当我把app杀掉后执行的?

记录用户的所有任务信息并全部保存到数据库,这样做的好处,就是持久性保存记录,所有app被杀掉后,依然可以获取所有任务信息

任务是怎么保证一定执行的?

Android操作系统会在系统级别服务中,来判断用户的约束条件,当约束条件满足时就会执行任务,但是触发检测是采用广播的形式处理的,例如:网络连接成功就触发

Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2023-2025 Annie
  • Visitors: | Views:

嘿嘿 请我吃小蛋糕吧~

支付宝
微信