[Kotlin] Android 使用 WorkManager 在背景執行任務

昨天有介紹 使用 AlarmManager 設定定時任務,AlarmManager 是負責處理精確時間的任務。 而本篇要介紹的 WorkManager 則是依照特定條件,例如已連上 wifi,正在充電中、手機閒置時等狀態來觸發任務。例如要將使用者的個人設定值上傳到伺服器中,但是剛好現在又沒有連線到網路,就可以使用 WorkManager 來註冊任務,當有 wifi 時再上傳,對於不需要即時處理的任務來說非常好用,並且在任務中還可以回傳需要重試的狀態,讓系統自動安排再次執行。

不過在使用前需要注意的是 WorkManager 並沒有辦法精確指定時間,例如下面將要建立的範例是使用 WorkManager 註冊一個任務,當連上 wifi 時呼叫 API 。
在有連上 wifi 時 大多數時候 都會立刻執行(也有少部分是會需要等好幾分鐘),而在比較特別的案例例如先關閉 wifi ,開啟 App 註冊任務後關閉 App ,再開啟 wifi ,在筆者的測試中大約需要經過 30 秒到 15 分鐘不等的時間才會呼叫 API,所以只適合不緊急的任務。
另外也因為在筆者測試時發現就算不關閉 App,有時候也不會立刻執行,是等待幾分鐘後才執行,所以也不要都只使用 WorkManager ,以「上傳使用者設定值」這個案例來說,建議先檢查網路情況,有網路連線就直接上傳,否則再使用 WorkManager ,避免無法即時處理。

程式碼

建立 MyApiWorker.kt,把任務執行時要做的事情放在這裡,這是最簡單的示範:
    
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters

class MyApiWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {

        Log.i("MyApiWorker", "執行任務")

        return Result.success() // 成功
//        return Result.failure() // 失敗
//        return Result.retry() // 失敗,要求重試
    }
}
    

這裡的範例是建立呼叫 API 的任務:
    
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL

class MyApiWorker(context: Context, workerParams: WorkerParameters) :
    Worker(context, workerParams) {
    override fun doWork(): Result {
        val url = URL("https://cdn.jsdelivr.net/gh/ruyut/TaiwanCalendar/data/2024.json")

        val urlConnection: HttpURLConnection
        return try {
            urlConnection = url.openConnection() as HttpURLConnection
            urlConnection.requestMethod = "GET"

            val responseCode = urlConnection.responseCode
            if (responseCode == HttpURLConnection.HTTP_OK) {
                val bufferedReader = BufferedReader(InputStreamReader(urlConnection.inputStream))
                val response = StringBuilder()
                bufferedReader.useLines { lines -> lines.forEach { response.append(it) } }

                Log.d("MyApiWorker","API Response: $response")

                Result.success()
            } else {
                Log.e("MyApiWorker", "API Response Code: $responseCode")
                Result.failure()
            }
        } catch (e: Exception) {
            Log.e("MyApiWorker", "API Call failed", e)
            Result.failure()
        }
    }
}
    

建立任務:
    
val constraints = Constraints.Builder()
    .setRequiredNetworkType(NetworkType.UNMETERED) // 只在連上 WiFi 時觸發
    .build()

val apiCallWorkRequest = OneTimeWorkRequestBuilder<MyApiWorker>()
    .setConstraints(constraints)
    .build()

val context: Context = LocalContext.current
WorkManager.getInstance(context).enqueue(apiCallWorkRequest)
    



參考資料:
Android Developers - Schedule tasks with WorkManager
Android Developers - WorkManager

留言