Android Kotlin 基础知识 09.2:WorkManager

此 Codelab 是“Android Kotlin 基础知识”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘此课程的价值。“Android Kotlin 基础知识”Codelab 着陆页列出了所有课程 Codelab。

简介

大多数实际应用都需要执行长时间运行的后台任务。例如,应用可能会将文件上传到服务器、从服务器同步数据并将其保存到 Room 数据库、向服务器发送日志,或对数据执行代价高昂的操作。此类操作应在后台界面线程(主线程)之外执行。后台任务会消耗设备的有限资源,例如 RAM 和电池。如果处理不当,可能会导致用户体验不佳。

在此 Codelab 中,您将学习如何使用 WorkManager 以优化、高效的方式安排后台任务。如需详细了解 Android 中可用于后台处理的其他解决方案,请参阅后台处理指南

您应当已掌握的内容

  • 如何使用 ViewModelLiveDataRoom Android 架构组件。
  • 如何对 LiveData 类执行转换。
  • 如何构建和启动协程
  • 如何在数据绑定中使用绑定适配器
  • 如何使用存储库模式加载缓存的数据。

学习内容

  • 如何创建表示工作单元的 Worker
  • 如何创建 WorkRequest 以请求执行工作。
  • 如何为 WorkRequest 添加约束条件以定义工作器的运行方式和时间。
  • 如何使用 WorkManager 调度后台任务。

您将执行的操作

  • 创建一个工作器来执行后台任务,以便从网络中预提取 DevBytes 视频播放列表。
  • 安排工作器定期运行。
  • WorkRequest 添加约束条件。
  • 安排每天执行一次的定期 WorkRequest

在此 Codelab 中,您将开发在上一个 Codelab 中开发的 DevBytes 应用。(如果您没有此应用,可以下载本课程的起始代码)。

DevBytes 应用列出了 DevByte 视频,这些视频是由 Google Android 开发者关系团队制作的简短教程。这些视频介绍了 Android 开发方面的开发者功能和最佳做法。

每天预提取一次视频,以改善应用中的用户体验。这样可确保用户在打开应用时就能看到新鲜内容。

在此任务中,您将下载并检查起始代码。

第 1 步:下载并运行起始应用

您可以继续学习在之前的 Codelab 中构建的 DevBytes 应用(如果您有的话)。或者,您也可以下载起始应用。

在此任务中,您将下载并运行起始应用并检查起始代码。

  1. 如果您还没有 DevBytes 应用,请从 GitHub 的 DevBytesRepository 项目下载此 Codelab 的 DevBytes 起始代码。
  2. 解压缩代码并在 Android Studio 中打开项目。
  3. 将测试设备或模拟器连接到互联网(如果尚未连接)。构建并运行应用。应用会从网络中获取 DevByte 视频列表并显示这些视频。
  4. 在该应用中,点按任意视频,以在 YouTube 应用中打开。

第 2 步:浏览代码

起始应用附带上一个 Codelab 中引入的大量代码。本 Codelab 的起始代码包含网络、界面、离线缓存和代码库模块。您可以专注于使用 WorkManager 调度后台任务。

  1. 在 Android Studio 中,展开所有软件包。
  2. 探索 database 软件包。该软件包包含数据库实体和本地数据库(使用 Room 实现)。
  3. 探索 repository 软件包。软件包中包含 VideosRepository 类,该类可从应用的其余部分中提取数据层。
  4. 在上一个 Codelab 的帮助下,自行探索其余的起始代码。

WorkManagerAndroid 架构组件之一,也是 Android Jetpack 的一部分。WorkManager 适用于可延期且需要保证执行的后台工作:

  • 可延期表示工作无需立即运行。例如,可以将分析数据发送到服务器或在后台同步数据库,这样做可以推迟。
  • 保证执行意味着,即使应用退出或设备重启,任务也会运行。

WorkManager 运行后台工作时,它可以处理电池问题和系统运行状况方面的兼容性问题和最佳做法。WorkManager 可向后兼容至 API 级别 14。WorkManager 根据设备 API 级别选择后台任务的调度方式。它可能会使用 JobScheduler(在 API 23 及更高版本中),或结合使用 AlarmManagerBroadcastReceiver

WorkManager 还可让您设置有关何时运行后台任务的条件。例如,您可能只想在电池状态、网络状态或充电状态满足特定条件时运行任务。此 Codelab 后面会介绍如何设置限制条件。

在此 Codelab 中,您可以安排一项任务,即每天从网络中预提取 DevBytes 视频播放列表一次。如需安排此任务,请使用 WorkManager 库。

  1. 打开 build.gradle (Module:app) 文件并将 WorkManager 依赖项添加到项目中。

    如果您使用该库的最新版本,解决方案应用应按预期编译。如果不能解决问题,请尝试解决问题,或恢复如下所示的库版本。
// WorkManager dependency
def work_version = "1.0.1"
implementation "android.arch.work:work-runtime-ktx:$work_version"
  1. 同步您的项目,并确保没有编译错误。

在向项目添加代码之前,请先熟悉 WorkManager 库中的以下类:

  • Worker
    您可以在这个类中定义在后台运行的实际工作(任务)。您可以扩展此类并替换 doWork() 方法。doWork() 方法用于放置要在后台执行的代码,例如与服务器同步数据或处理图片。在此任务中,您将实现 Worker
  • WorkRequest
    这个类表示在后台运行工作器的请求。在 Constraints 的帮助下,使用 WorkRequest 配置如何以及何时运行工作器任务,例如插入设备或连接到 Wi-Fi。在后续任务中实现 WorkRequest
  • WorkManager
    此课程安排并运行您的WorkRequestWorkManager 在安排工作请求时采用一种在系统资源上分散负载的方式,同时遵循您指定的约束条件。在后续任务中实现 WorkManager

第 1 步:创建工作器

在此任务中,您将添加 Worker 以在后台预取 DevBytes 视频播放列表。

  1. devbyteviewer 软件包内,创建一个名为 work 的新软件包。
  2. work 软件包内,新建一个名为 RefreshDataWorker 的 Kotlin 类。
  3. CoroutineWorker 类扩展 RefreshDataWorker 类。传入 contextWorkerParameters 作为构造函数参数。
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {
}
  1. 要解决抽象类错误,请替换 RefreshDataWorker 类中的 doWork() 方法。
override suspend fun doWork(): Result {
  return Result.success()
}

挂起函数是一种可以在以后暂停和恢复的函数。挂起函数可以执行一项长时间运行的操作并等待其完成,而不会阻塞主线程。

第 2 步:实现 doWork()

Worker 类中的 doWork() 方法在后台线程上调用。该方法会同步执行工作,应返回 ListenableWorker.Result 对象。Android 系统会给 Worker 最多 10 分钟的时间完成执行,并返回 ListenableWorker.Result 对象。此期限过后,系统会强行停止 Worker

要创建 ListenableWorker.Result 对象,请调用以下静态方法之一,指示后台工作的完成状态:

在此任务中,您将实现 doWork() 方法以从网络获取 DevBytes 视频播放列表。您可以重复使用 VideosRepository 类中的现有方法,从网络检索数据。

  1. RefreshDataWorker 类中的 doWork() 内,创建并实例化一个 VideosDatabase 对象和一个 VideosRepository 对象。
override suspend fun doWork(): Result {
   val database = getDatabase(applicationContext)
   val repository = VideosRepository(database)

   return Result.success()
}
  1. RefreshDataWorker 类的 doWork() 中的 return 语句上方,调用 try 块内的 refreshVideos() 方法。添加日志以跟踪工作器的运行时间。
try {
   repository.refreshVideos( )
   Timber.d("Work request for sync is run")
   } catch (e: HttpException) {
   return Result.retry()
}

如需解决“未解析的引用”错误,请导入 retrofit2.HttpException

  1. 以下是完整的 RefreshDataWorker 类,供您参考:
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
       CoroutineWorker(appContext, params) {

   override suspend fun doWork(): Result {
       val database = getDatabase(applicationContext)
       val repository = VideosRepository(database)
       try {
           repository.refreshVideos()
       } catch (e: HttpException) {
           return Result.retry()
       }
       return Result.success()
   }
}

Worker 定义工作单元,WorkRequest 定义工作的运行方式和时间。WorkRequest 类有两个具体实现:

任务可以是一次性的,也可以是周期性的,因此请相应地选择类。如需详细了解如何安排周期性工作,请参阅周期性工作文档

在此任务中,您将定义并安排一个 WorkRequest 以运行您在上一个任务中创建的工作器。

第 1 步:设置周期性工作

在 Android 应用中,Application 类是包含其他所有组件(例如 Activity 和服务)的基类。创建应用或进程进程后,Application 类(或 Application 的任何子类)都会先于任何其他类进行实例化。

在此示例应用中,DevByteApplication 类是 Application 类的子类。DevByteApplication 类非常适合调度 WorkManager

  1. DevByteApplication 类中,创建一个名为 setupRecurringWork() 的方法以设置周期性后台工作。
/**
* Setup WorkManager background job to 'fetch' new network data daily.
*/
private fun setupRecurringWork() {
}
  1. setupRecurringWork() 方法内,使用 PeriodicWorkRequestBuilder() 方法创建并初始化定期运行的请求,每天运行一次。传入您在上一个任务中创建的 RefreshDataWorker 类。传入时间间隔为 TimeUnit.DAYS 的重复 1
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .build()

如需解决此错误,请导入 java.util.concurrent.TimeUnit

第 2 步:使用 WorkManager 调度 WorkRequest

定义 WorkRequest 后,您可以使用 enqueueUniquePeriodicWork() 方法通过 WorkManager 对其进行调度。使用此方法,您可以将具有唯一名称的 PeriodicWorkRequest 添加到队列中,每次只能有一个特定名称的 PeriodicWorkRequest 处于活动状态。

例如,您可能只想执行一项同步操作。如果某个同步操作正在等待处理,您可以选择使用 ExistingPeriodicWorkPolicy 让该操作运行或将其替换为新工作。

如需详细了解 WorkRequest 调度方法,请参阅 WorkManager 文档。

  1. RefreshDataWorker 类中,在该类的开头添加伴生对象。定义工作名称以唯一标识此工作器。
companion object {
   const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
  1. DevByteApplication 类中,在 setupRecurringWork() 方法的末尾,使用 enqueueUniquePeriodicWork() 方法调度工作。传入 ExistingPeriodicWorkPolicy 的 KEEP 枚举。传入 repeatingRequest 作为 PeriodicWorkRequest 参数。
WorkManager.getInstance().enqueueUniquePeriodicWork(
       RefreshDataWorker.WORK_NAME,
       ExistingPeriodicWorkPolicy.KEEP,
       repeatingRequest)

如果存在具有相同名称的待处理(未完成)工作,ExistingPeriodicWorkPolicy.KEEP 参数可让 WorkManager 保留以前的定期工作并舍弃新的工作请求。

  1. DevByteApplication 类的开头,创建一个 CoroutineScope 对象。传入 Dispatchers.Default 作为构造函数参数。
private val applicationScope = CoroutineScope(Dispatchers.Default)
  1. DevByteApplication 类中,添加一个名为 delayedInit() 的新方法来启动协程。
private fun delayedInit() {
   applicationScope.launch {
   }
}
  1. delayedInit() 方法内,调用 setupRecurringWork()
  2. 将 Timber 初始化从 onCreate() 方法移至 delayedInit() 方法。
private fun delayedInit() {
   applicationScope.launch {
       Timber.plant(Timber.DebugTree())
       setupRecurringWork()
   }
}
  1. DevByteApplication 类的 onCreate() 方法的末尾,添加对 delayedInit() 方法的调用。
override fun onCreate() {
   super.onCreate()
   delayedInit()
}
  1. 打开 Android Studio 窗口底部的 Logcat 窗格。按“RefreshDataWorker”过滤。
  2. 运行应用。WorkManager 会立即安排您的周期性工作。

    Logcat 窗格中,请注意显示已安排工作请求,然后成功运行的日志语句。
D/RefreshDataWorker: Work request for sync is run
I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

系统会显示 WorkManager 库中的 WM-WorkerWrapper 日志,因此您无法更改此日志消息。

第 3 步:(可选)为 WorkRequest 安排最短时间间隔

在此步骤中,您将时间间隔从 1 天缩短为 15 分钟。这样做是为了查看定期工作请求的实际日志。

  1. DevByteApplication 类的 setupRecurringWork() 方法内,注释掉当前的 repeatingRequest 定义。添加工作周期为 15 分钟的新工作请求。
// val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
//        .build()
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
       .build()
  1. 在 Android Studio 中打开 Logcat 窗格并按 RefreshDataWorker 进行过滤。如需清除之前的日志,请点击 Clear logcat 图标 .
  2. 运行应用,WorkManager 会立即安排您的周期性工作。在 Logcat 窗格中,请注意日志 - 工作请求每 15 分钟运行一次。等待 15 分钟以查看另一组工作请求日志。您可以让应用保持运行状态或关闭;工作管理器应仍会运行。

    请注意,间隔有时小于 15 分钟,有时超过 15 分钟。(确切时间取决于操作系统的电池优化情况。)
12:44:40 D/RefreshDataWorker: Work request for sync is run
12:44:40 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
12:59:24 D/RefreshDataWorker: Work request for sync is run
12:59:24 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:15:03 D/RefreshDataWorker: Work request for sync is run
13:15:03 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:29:22 D/RefreshDataWorker: Work request for sync is run
13:29:22 I/WM-WorkerWrapper: Worker result SUCCESS for Work 
13:44:26 D/RefreshDataWorker: Work request for sync is run
13:44:26 I/WM-WorkerWrapper: Worker result SUCCESS for Work
 

恭喜!您已创建工作器,并使用 WorkManager 安排了工作请求。但存在一个问题:您未指定任何限制条件。WorkManager 每天会调度 1 次工作,即使设备电量不足、进入休眠状态或未连接到网络时也是如此。这会影响设备的电池和性能,并可能导致用户体验不佳。

在下一个任务中,您将通过添加约束条件来解决此问题。

在上一个任务中,您使用 WorkManager 安排了工作请求。在此任务中,您将添加何时执行工作的条件。

定义 WorkRequest 时,您可以指定何时运行 Worker 的约束条件。例如,您可能希望指定工作应仅在设备处于空闲状态时运行,或仅在设备插入并连接到 Wi-Fi 时运行。您也可以为重试工作指定退避政策。支持的约束条件Constraints.Builder 中的 set 方法。如需了解详情,请参阅定义工作请求

第 1 步:添加 Constraints 对象并设置一个约束条件

在此步骤中,您将创建一个 Constraints 对象,并针对该对象设置一个限制条件,即网络类型限制条件。(仅注意一个约束条件更容易获得日志。在后续步骤中,您需要添加其他限制条件。)

  1. DevByteApplication 类中的 setupRecurringWork() 开头,定义 Constraints 类型的 val。使用 Constraints.Builder() 方法。
val constraints = Constraints.Builder()

如需解决此错误,请导入 androidx.work.Constraints

  1. 使用 setRequiredNetworkType() 方法向 constraints 对象添加网络类型约束条件。使用 UNMETERED 枚举,使工作请求仅在设备使用的是不按流量计费的网络时运行。
.setRequiredNetworkType(NetworkType.UNMETERED)
  1. 使用 build() 方法从构建器生成约束条件。
val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .build()

现在,您需要将新创建的 Constraints 对象设置为工作请求。

  1. DevByteApplication 类的 setupRecurringWork() 方法内,将 Constraints 对象设置为定期工作请求 repeatingRequest。要设置约束条件,请在 build() 方法调用上方添加 setConstraints() 方法。
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(15, TimeUnit.MINUTES)
               .setConstraints(constraints)
               .build()

第 2 步:运行应用并注意日志

在此步骤中,您将运行应用,并注意到后台中定时运行的受限工作请求。

  1. 如需取消之前安排的所有任务,请在设备或模拟器中卸载该应用。
  2. 在 Android Studio 中打开 Logcat 窗格。在 Logcat 窗格中,点击左侧的 Clear logcat 图标 以清除之前的日志。按“work”过滤。
  3. 您可以在设备或模拟器中关闭 Wi-Fi,以便了解约束条件的运作方式。当前代码仅设置一个限制条件,表示请求应仅在不按流量计费的网络上运行。由于 WLAN 处于关闭状态,设备未连接到网络(按流量计费或不按流量计费)。因此,不会满足此限制条件。
  4. 运行应用并注意 Logcat 窗格。WorkManager 会立即安排后台任务。由于未满足网络约束条件,因此任务不会运行。
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
  1. 在设备或模拟器中开启 WLAN,然后查看 Logcat 窗格。现在,只要符合网络限制,安排的后台任务大约每 15 分钟运行一次。
11:31:44 D/DevByteApplication: Periodic Work request for sync is scheduled
11:31:47 D/RefreshDataWorker: Work request for sync is run
11:31:47 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]
11:46:45 D/RefreshDataWorker: Work request for sync is run
11:46:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:03:05 D/RefreshDataWorker: Work request for sync is run
12:03:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:16:45 D/RefreshDataWorker: Work request for sync is run
12:16:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:31:45 D/RefreshDataWorker: Work request for sync is run
12:31:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
12:47:05 D/RefreshDataWorker: Work request for sync is run
12:47:05 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...] 
13:01:45 D/RefreshDataWorker: Work request for sync is run
13:01:45 I/WM-WorkerWrapper: Worker result SUCCESS for Work [...]

第 3 步:添加更多限制条件

在此步骤中,您将向 PeriodicWorkRequest 添加以下约束:

  • 电池电量不足。
  • 设备正在充电。
  • 设备处于空闲状态;仅在 API 级别 23 (Android M) 及更高版本中可用。

DevByteApplication 类中执行以下操作。

  1. DevByteApplication 类的 setupRecurringWork() 方法内,指明工作请求仅在电池电量不足时运行。在调用 build() 方法之前添加约束条件,并使用 setRequiresBatteryNotLow() 方法。
.setRequiresBatteryNotLow(true)
  1. 更新工作请求,使其仅在设备充电时运行。在调用 build() 方法之前添加约束条件,并使用 setRequiresCharging() 方法。
.setRequiresCharging(true)
  1. 更新工作请求,使其仅在设备空闲时运行。在调用 build() 方法之前添加约束条件,并使用 setRequiresDeviceIdle() 方法。只有在用户没有主动使用设备的情况下,此约束条件才会运行工作请求。此功能仅在 Android 6.0 (Marshmallow) 及更高版本中提供,因此请为 SDK 版本 M 及更高版本添加条件。
.apply {
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       setRequiresDeviceIdle(true)
   }
}

以下是 constraints 对象的完整定义。

val constraints = Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresBatteryNotLow(true)
       .setRequiresCharging(true)
       .apply {
           if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
               setRequiresDeviceIdle(true)
           }
       }
       .build()
  1. setupRecurringWork() 方法内,将请求间隔更改回每天一次。
val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
       .setConstraints(constraints)
       .build()

下面是 setupRecurringWork() 方法的完整实现,其中包含一个日志,以便您可以跟踪定期工作请求的安排时间。

private fun setupRecurringWork() {

       val constraints = Constraints.Builder()
               .setRequiredNetworkType(NetworkType.UNMETERED)
               .setRequiresBatteryNotLow(true)
               .setRequiresCharging(true)
               .apply {
                   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                       setRequiresDeviceIdle(true)
                   }
               }
               .build()
       val repeatingRequest = PeriodicWorkRequestBuilder<RefreshDataWorker>(1, TimeUnit.DAYS)
               .setConstraints(constraints)
               .build()
       
       Timber.d("Periodic Work request for sync is scheduled")
       WorkManager.getInstance().enqueueUniquePeriodicWork(
               RefreshDataWorker.WORK_NAME,
               ExistingPeriodicWorkPolicy.KEEP,
               repeatingRequest)
   }
  1. 如需移除之前安排的工作请求,请从设备或模拟器中卸载 DevBytes 应用。
  2. 运行应用,WorkManager 会立即安排工作请求。工作请求每天运行一次,当所有约束条件都得到满足时。
  3. 只要应用安装,此工作请求就会在后台运行,即使应用未运行也是如此。因此,您应从手机中卸载该应用。

太棒了!您为 DevBytes 应用中的每日视频预取工作实现了并实现了电池供电的工作请求。WorkManager 将调度并运行工作,从而优化系统资源。用户及其电池将非常满意。

Android Studio 项目:DevBytesWorkManager

  • 借助 WorkManager API,您可以轻松地调度必须可靠运行的可延期异步任务。
  • 大多数实际应用都需要执行长时间运行的后台任务。如需以优化且高效的方式安排后台任务,请使用 WorkManager
  • WorkManager 库中的主要类是 WorkerWorkRequestWorkManager
  • Worker 类代表一个工作单元。如需实现后台任务,请扩展 Worker 类并替换 doWork() 方法。
  • WorkRequest 类表示执行工作单元的请求。WorkRequest 是为 WorkManager 中调度的工作指定参数的基类。
  • WorkRequest 类的具体实现有两种:OneTimeWorkRequest 用于一次性任务,PeriodicWorkRequest 用于定期工作请求。
  • 定义 WorkRequest 时,您可以指定 Constraints,指示 Worker 应何时运行。限制条件包括设备是否已插入、设备是否处于空闲状态或者是否连接到 WLAN。
  • 如需为 WorkRequest 添加约束条件,请使用 Constraints.Builder 文档中列出的 set 方法。例如,如需指示 WorkRequest 在设备电池电量不足时不应运行,请使用 setRequiresBatteryNotLow() set 方法。
  • 定义 WorkRequest 后,请将任务移交给 Android 系统。为此,请使用 WorkManager enqueue 方法之一安排任务。
  • 执行 Worker 的确切时间取决于 WorkRequest 中使用的约束以及系统优化方式。按照这些限制,WorkManager 旨在提供最佳行为。

Udacity 课程:

Android 开发者文档:

其他:

此部分列出了在由讲师主导的课程中,学生学习此 Codelab 后可能需要完成的家庭作业。讲师自行决定是否执行以下操作:

  • 根据需要布置作业。
  • 告知学生如何提交家庭作业。
  • 给家庭作业评分。

讲师可以酌情采纳这些建议,并且可以自由布置自己认为合适的任何其他家庭作业。

如果您是在自学此 Codelab,可随时通过这些家庭作业来检测您的知识掌握情况。

问题 1

WorkRequest 类的具体实现是什么?

OneTimeWorkPeriodicRequest

OneTimeWorkRequestPeriodicWorkRequest

OneTimeWorkRequestRecurringWorkRequest

OneTimeOffWorkRequestRecurringWorkRequest

问题 2

WorkManager 使用以下哪个类在 API 23 及更高级别上调度后台任务?

▢ 仅 JobScheduler

BroadcastReceiverAlarmManager

AlarmManagerJobScheduler

SchedulerBroadcastReceiver

问题 3

您使用哪个 API 来为 WorkRequest 添加约束条件?

setConstraints()

addConstraints()

setConstraint()

addConstraintsToWorkRequest()

开始学习下一课:10.1 样式和主题

如需本课程中其他 Codelab 的链接,请参阅“Android Kotlin 基础知识”Codelab 着陆页