iT邦幫忙

2025 iThome 鐵人賽

DAY 25
0
Mobile Development

Android 性能戰爭:從 Profiler 開始的 30 天優化實錄系列 第 25

# Day 25:【資源戰爭】後台任務的紀律:WorkManager

  • 分享至 

  • xImage
  •  

各位戰士,歡迎來到第二十五天的戰場。我們的應用程式經過瘦身,已經成功減負。但一場戰爭的勝利,不僅取決於衝鋒陷陣時的勇猛,也取決於休整期間的紀律。一個在背景(Background)下肆意活動、頻繁喚醒設備、大量消耗電量的應用程式,就像一支軍紀渙散的部隊,遲早會被使用者「勒令退役」(卸載)。

在 Android 的早期戰場上,ServiceAlarmManagerBroadcastReceiver 曾是我們執行後台任務的常用武器。但隨著 Android 系統對電源管理的日益收緊(例如 Doze 模式、應用待機模式),這些武器如果使用不當,很容易成為電量殺手。

為此,Google 為我們提供了一支現代化的「特種部隊」,它受過嚴格的訓練,懂得如何與系統的省電策略協同作戰。它就是我們今天的主角——WorkManager


為何需要 WorkManager 這支特種部隊?

WorkManager 不是一個簡單的 API,它是一套完整的後台任務解決方案,是 Google 官方推薦的、處理絕大多數後台任務的唯一指定武器。它天生就具備了現代化戰爭所需的紀律性:

  1. 保證執行 (Guaranteed Execution):WorkManager 會將你的任務持久化儲存。即便應用程式被關閉甚至手機重啟,只要時機成熟,任務依然會被執行。你無需擔心任務丟失。
  2. 可延遲性 (Deferrable):它是為那些不需要立即執行的任務而設計的。這給了 Android 系統極大的彈性,可以將多個應用的任務「批次處理」,在同一個時間點喚醒設備一次完成,而不是讓每個應用輪流喚醒設備一百次。
  3. 條件感知 (Constraint-Aware):這是 WorkManager 最強大的能力。你可以為任務設定嚴格的「執行紀律」,例如:
    • 只有在連接 Wi-Fi 時才上傳日誌。
    • 只有在設備充電時才進行資料庫備份。
    • 只有在設備閒置時才去執行非緊急的資料同步。
  4. 向後相容:完美支援 API 14 以上的所有 Android 版本。

一次後台任務的完整作戰計畫

使用 WorkManager 執行一次後台任務,就像制定一次完整的作戰計畫,包含三個核心部分:

1. 士兵 (Worker):定義具體任務

Worker 是一個類別,它定義了「具體要做什麼事」。我們通常使用 CoroutineWorker,因為它原生支援 Kotlin 協程。

// SyncWorker.kt
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters

class SyncWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {

    // doWork() 會自動在背景執行緒上執行
    override suspend fun doWork(): Result {
        return try {
            // 在這裡模擬執行耗時的網路同步或資料庫操作
            Log.d("SyncWorker", "開始同步資料...")
            delay(3000) // 模擬耗時
            Log.d("SyncWorker", "同步成功!")
            
            // 任務成功
            Result.success()
        } catch (e: Exception) {
            Log.e("SyncWorker", "同步失敗", e)
            
            // 任務失敗
            Result.failure()
        }
    }
}

2. 任務簡報 (WorkRequest):設定執行方式與紀律

WorkRequest 描述了「要執行哪個任務」以及「在什麼條件下執行」。

import androidx.work.Constraints
import androidx.work.NetworkType
import androidx.work.OneTimeWorkRequestBuilder

// 步驟一:定義執行紀律 (Constraints)
val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED) // 要求連接非計量的網路 (通常指 Wi-Fi)
    .setRequiresCharging(true)                    // 要求設備處於充電狀態
    .build()

// 步驟二:建立一次性的任務請求 (OneTimeWorkRequest)
val syncRequest = OneTimeWorkRequestBuilder<SyncWorker>()
    .setConstraints(constraints) // 將紀律應用到任務上
    .build()

// 註:如果需要週期性任務,可以使用 PeriodicWorkRequestBuilder

3. 派遣任務 (Enqueue):將任務交給指揮部

計畫制定完成後,我們只需要將它交給 WorkManager 這個總指揮部即可。

// 在你的 Activity, Fragment 或 ViewModel 中
import androidx.work.WorkManager

// 獲取 WorkManager 實例,並將任務加入佇列
WorkManager.getInstance(applicationContext).enqueue(syncRequest)

一旦任務被 enqueue,你就完成了你的職責。WorkManager 會接管一切,它會耐心等待,直到使用者手機的狀態同時滿足「正在充電」和「連接著 Wi-Fi」這兩個條件時,才會派遣 SyncWorker 去執行任務。

觀察任務狀態

你還可以像一個指揮官一樣,在控制室裡監控任務的執行狀態。

// 透過請求的 ID 來觀察任務
val workId = syncRequest.id
WorkManager.getInstance(applicationContext)
    .getWorkInfoByIdLiveData(workId)
    .observe(this, { workInfo ->
        if (workInfo != null && workInfo.state == WorkInfo.State.SUCCEEDED) {
            Toast.makeText(this, "資料同步成功!", Toast.LENGTH_SHORT).show()
        }
    })

這允許你的 UI 對後台任務的完成做出回應。

今日總結

今天,我們學習了如何使用 WorkManager 這支紀律嚴明的特種部隊來處理所有可延遲的後台任務。

  • 我們理解了 WorkManager 的核心優勢:保證執行、可延遲、條件感知。

  • 我們掌握了建立一個完整後台任務的作戰流程:定義 Worker -> 建立 WorkRequestConstraints -> Enqueue 任務。

  • 我們養成了新的開發思維:從「立即執行」,轉變為「在對系統最有利的時機執行」,從而最大限度地保護使用者的電量。

我們已經學會了如何「做正確的事」。但是,在複雜的專案和團隊協作中,我們如何確保自己或他人不小心「做了錯誤的事」?明天,我們將學習如何在開發階段設定自動化的「哨兵」——StrictMode,讓它在我們無意中違反軍規時(例如在主執行D緒上讀寫磁碟),立刻發出警報。

我們明天見!


上一篇
# Day 24:【資源戰爭】APK 瘦身術(下):App Bundles 與動態交付
系列文
Android 性能戰爭:從 Profiler 開始的 30 天優化實錄25
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言