使用 WorkManager 處理須要馬上執行的後臺任務

當須要執行長時間運行的任務,而應用處於後臺狀態時,您會遇到 後臺執行限制,該特性是在 Android 8.0 以後增長的。咱們鼓勵開發者進行行爲變動以提高整個平臺的用戶體驗。java

爲了避免同的使用場景更易於適配,咱們經過對 WorkManager 添加功能,提高了開發者在遵循後臺任務限制方面的體驗。android

咱們推薦使用 WorkManager 處理需當即執行的長時間運行任務。git

閱讀本文,瞭解經過 WorkManager 處理的需長時間運行而且當即執行的任務的好處以及如何進行配置。github

API 介紹

WorkManager 版本 2.3.0 起,每一個 Worker 均可以在前臺服務中調用方法。ListenableWorker 做爲 Worker 的基類,提供了新的 setForegroundAsync() 函數。app

本文以 CoroutineWorker 爲例。在 CoroutineWorker 中,setForegroundAsync() 被封裝在一個掛起的 setForeground() 函數中。該類也提供掛起的 doWork 函數,它支持代碼脫離主線程運行。可是,本文的所有內容一樣適用於其餘 Worker 類的相關函數。異步

當調用 setForeground(Async) 時,一旦知足約束條件,預約的任務將會在前臺服務中當即執行。此外,WorkManager 會負責處理服務的生命週期。而在前臺服務的 Worker 中運行的任務也不會受到後臺任務十分鐘的限制。async

從當即執行開始

讓咱們來看一下如何讓一個已存在的 worker 在前臺服務中執行任務。ide

咱們從一個很是簡單的 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 示例代碼:

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

關於長時間運行 worker 和前臺服務的詳細指南,以及主題更多信息,請查閱:

WorkManager 系列文章助您瞭解 WorkManager 從基礎到高級的各項特性:

Google IssueTracker 提交所遇到的任何問題,這將幫助咱們第一時間優化特性和修復漏洞。

相關文章
相關標籤/搜索