天天看點

使用 WorkManager 處理需要立刻執行的背景任務

當需要執行長時間運作的任務,而應用處于背景狀态時,您會遇到 背景執行限制,該特性是在 Android 8.0 之後增加的。我們鼓勵開發者進行行為變更以提升整個平台的使用者體驗。

為了不同的使用場景更易于适配,我們通過對 WorkManager 添加功能,提升了開發者在遵循背景任務限制方面的體驗。

我們推薦使用 WorkManager 處理需立即執行的長時間運作任務。

閱讀本文,了解通過 WorkManager 處理的需長時間運作并且立即執行的任務的好處以及如何進行配置。

API 介紹

自 WorkManager 版本 2.3.0 起,每個 Worker 都可以在前台服務中調用方法。ListenableWorker 作為 Worker 的基類,提供了新的 setForegroundAsync() 函數。

本文以 CoroutineWorker 為例。在 CoroutineWorker 中,setForegroundAsync() 被封裝在一個挂起的 setForeground() 函數中。該類也提供挂起的 doWork 函數,它支援代碼脫離主線程運作。但是,本文的全部内容同樣适用于其他 Worker 類的相關函數。

當調用 setForeground(Async) 時,一旦滿足限制條件,預定的任務将會在前台服務中立即執行。此外,WorkManager 會負責處理服務的生命周期。而在前台服務的 Worker 中運作的任務也不會受到背景任務十分鐘的限制。

從立即執行開始

讓我們來看一下如何讓一個已存在的 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

正确建立 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 示例代碼:

在前台服務中執行任務的代碼,請查閱:

  • BaseFilterWorker 類
  • 送出記錄

關于長時間運作 worker 和前台服務的詳細指南,以及主題更多資訊,請查閱:

  • WorkManager 的進階指南|支援長時間運作的工作器
  • 背景處理指南
  • Android 上的 Kotlin 協程

WorkManager 系列文章助您了解 WorkManager 從基礎到進階的各項特性:

  • Android Jetpack WorkManager | Android 中文教學視訊
  • WorkManager 在 Kotlin 中的實踐
  • WorkManager: 周期性任務
  • 自定義 WorkManager —— 基礎概念
  • 使用 Dagger 自定義 WorkManager

Google IssueTracker 送出所遇到的任何問題,這将幫助我們第一時間優化特性和修複漏洞。