使用 WorkManager 处理需要立刻执行的后台任务

当需要执行长时间运行的任务,而应用处于后台状态时,您会遇到后台执行限制,该特性是在 Android 8.0 之后增加的。我们鼓励开发者进行行为变更以提升整个平台的用户体验。

  • 后台执行限制
    https://developer.android.google.cn/about/versions/oreo/background

为了不同的使用场景更易于适配,我们通过对 WorkManager 添加功能,提升了开发者在遵循后台任务限制方面的体验。

 

我们推荐使用 WorkManager 处理需立即执行的长时间运行任务。

 

阅读本文,了解通过 WorkManager 处理的需长时间运行并且立即执行的任务的好处以及如何进行配置。

 API 介绍

自 WorkManager 版本 2.3.0 起,每个 Worker 都可以在前台服务中调用方法。ListenableWorker 作为 Worker 的基类,提供了新的 setForegroundAsync() 函数。

 

本文以 CoroutineWorker 为例。在 CoroutineWorker 中,setForegroundAsync() 被封装在一个挂起的 setForeground() 函数中。该类也提供挂起的 doWork 函数,它支持代码脱离主线程运行。但是,本文的全部内容同样适用于其他 Worker 类的相关函数。

 

当调用 setForeground(Async) 时,一旦满足约束条件,预定的任务将会在前台服务中立即执行。此外,WorkManager 会负责处理服务的生命周期。而在前台服务的 Worker 中运行的任务也不会受到后台任务十分钟的限制。

  • WorkManager 版本 2.3.0

    https://developer.android.google.cn/jetpack/androidx/releases/work#version_230_3

  • ListenableWorker

    https://developer.android.google.cn/reference/androidx/work/ListenableWorker

  • setForegroundAsync()

    https://developer.android.google.cn/reference/kotlin/androidx/work/ListenableWorker#setforegroundasync

  • CoroutineWorker

    https://developer.android.google.cn/reference/kotlin/androidx/work/CoroutineWorker

  • setForeground()

    https://developer.android.google.cn/reference/kotlin/androidx/work/CoroutineWorker#setforeground

 从立即执行开始

让我们来看一下如何让一个已存在的 worker 在前台服务中执行任务。

 

我们从一个非常简单的 doWork() 函数开始。代码是异步执行的,无论成功或失败,都会有相应的 Result 返回。

/* Copyright 2020 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */


override suspend fun doWork(): Result {
    try {
       //要执行的代码
        return Result.success()
    } catch (throwable: Throwable) {
       //进行清理并输出
        return Result.failure()
    }
}

在 doWork() 中,您也需要告知 WorkManager 该任务应该在前台服务中立即执行。

 

为此,您需要创建一个 ForegroundInfo 对象作为 setForeground() 的参数。ForegroundInfo 需要两个参数,一个是 Notification ID,另一个是将要被显示的 Notification。

 

当约束条件满足时,下列信息可用于创建和运行前台服务。

  • ForegroundInfo

    https://developer.android.google.cn/reference/androidx/work/ForegroundInfo

  • Notification

    https://developer.android.google.cn/reference/android/app/Notification

 创建 ForegroundInfo

正确创建 ForegroundInfo 只需如下三步:

  1. 创建一个 Notification

  2. 创建一个 Notification Channel

  3. 将通知引入 ForegroundInfo

在下列代码中,createForegroundInfo() 调用 createForegroundInfo(),createNotification() 函数会对 notification 进行填充并创建相应的 channel。

/* Copyright 2020 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */


/**
  *为前台服务运行的 Worker 创建 ForegroundInfo 
  */
private fun createForegroundInfo(): ForegroundInfo {
    //每一个 Notification 需要使用不同的 id
    val notificationId = 1
    return ForegroundInfo(notificationId, createNotification())
}




/**
  * 为前台服务运行任务创建 Notification 和所需的 channel (Andrid O版本以上)
  */
private fun createNotification(): Notification {
    //PendingIntent 可用来取消 Worker
    val intent = WorkManager.getInstance(context).createCancelPendingIntent(id)


    val builder = Builder(context, channelId)
        .setContentTitle(title)
        .setTicker(title)
        .setSmallIcon(R.drawable.baseline_gradient)
        .setOngoing(true)
        .addAction(drawable.ic_delete, cancel, intent)
    if (VERSION.SDK_INT >= VERSION_CODES.O) {
        createNotificationChannel(channelId, name).also {
            builder.setChannelId(it.id)
        }
    }
    return builder.build()
}


/**
  * 为 Android O 及以上版本的设备创建所需的 notification channel
 */
@TargetApi(VERSION_CODES.O)
private fun createNotificationChannel(
    channelId: String,
    name: String
): NotificationChannel {
    return NotificationChannel(
        channelId, name, NotificationManager.IMPORTANCE_LOW
    ).also { channel ->
        notificationManager.createNotificationChannel(channel)
    }
}

 在前台服务中执行任务

现在把这些整合起来。我们已经实现了 doWork 函数,我们可以调用 setForeground(),并且通过调用 createForegroundInfo() 来传递所需的信息。

/* Copyright 2020 Google LLC.
   SPDX-License-Identifier: Apache-2.0 */


override suspend fun doWork(): Result {
    try {
        setForeground(createForegroundInfo())
        //需要执行的代码
        return Result.success(workDataOf(KEY_RESULT to result))
    } catch (throwable: Throwable) {
        //进行清理并且输出日志
        return Result.failure()
    }
}

⚠️⚠️⚠️ 

在长时间运行任务开始之前,先调用 setForeground()。 

 

否则在 setForeground() 被调用之前,您的 Worker 将会被视为非前台服务,这样可能会导致这个任务被取消或引起其余不希望出现的结果。 

⚠️⚠️⚠️

 下一步

现在大家已经知道何时以及如何利用长时间运行的 worker 了,那么可以进行下一步,开始在应用中实现它们。获取更多相关信息,请参阅以下资源:

 

在 GitHub 中查看 WorkManager 示例代码:

  • 示例代码

    https://github.com/android/architecture-components-samples/tree/master/WorkManagerSample

在前台服务中执行任务的代码,请查阅:

  • BaseFilterWorker 类

    https://github.com/android/architecture-components-samples/blob/master/WorkManagerSample/lib/src/main/java/com/example/background/workers/BaseFilterWorker.kt

  • 提交记录

    https://github.com/android/architecture-components-samples/commit/160f148b5ea4c943028c73acd4667fd134a8674e

关于长时间运行 worker 和前台服务的详细指南,以及主题更多信息,请查阅:

  • WorkManager 的高级指南|支持长时间运行的工作器

    https://developer.android.google.cn/topic/libraries/architecture/workmanager/advanced/long-running

  • 后台处理指南

    https://developer.android.google.cn/guide/background

  • Android 上的 Kotlin 协程

    https://developer.android.google.cn/kotlin/coroutines

WorkManager 系列文章助您了解 WorkManager 从基础到高级的各项特性:

Google IssueTracker 提交所遇到的任何问题,这将帮助我们第一时间优化特性和修复漏洞。

  • Google IssueTracker

    http://goo.gle/workmanager-issue

 点击屏末 阅读原文 | 查看 Android 官方中文文档 —— 支持长时间运行的工作器


推荐阅读

WorkManager: 周期性任务 | 自定义 WorkManager —— 基础概念 | 使用 Dagger 自定义 WorkManager


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值