各位戰士,歡迎來到第二十五天的戰場。我們的應用程式經過瘦身,已經成功減負。但一場戰爭的勝利,不僅取決於衝鋒陷陣時的勇猛,也取決於休整期間的紀律。一個在背景(Background)下肆意活動、頻繁喚醒設備、大量消耗電量的應用程式,就像一支軍紀渙散的部隊,遲早會被使用者「勒令退役」(卸載)。
在 Android 的早期戰場上,Service
、AlarmManager
、BroadcastReceiver
曾是我們執行後台任務的常用武器。但隨著 Android 系統對電源管理的日益收緊(例如 Doze 模式、應用待機模式),這些武器如果使用不當,很容易成為電量殺手。
為此,Google 為我們提供了一支現代化的「特種部隊」,它受過嚴格的訓練,懂得如何與系統的省電策略協同作戰。它就是我們今天的主角——WorkManager。
WorkManager 不是一個簡單的 API,它是一套完整的後台任務解決方案,是 Google 官方推薦的、處理絕大多數後台任務的唯一指定武器。它天生就具備了現代化戰爭所需的紀律性:
使用 WorkManager 執行一次後台任務,就像制定一次完整的作戰計畫,包含三個核心部分:
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()
}
}
}
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
計畫制定完成後,我們只需要將它交給 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
-> 建立 WorkRequest
與 Constraints
-> Enqueue
任務。
我們養成了新的開發思維:從「立即執行」,轉變為「在對系統最有利的時機執行」,從而最大限度地保護使用者的電量。
我們已經學會了如何「做正確的事」。但是,在複雜的專案和團隊協作中,我們如何確保自己或他人不小心「做了錯誤的事」?明天,我們將學習如何在開發階段設定自動化的「哨兵」——StrictMode
,讓它在我們無意中違反軍規時(例如在主執行D緒上讀寫磁碟),立刻發出警報。
我們明天見!