iT邦幫忙

2024 iThome 鐵人賽

DAY 22
0
Mobile Development

Jetpack Compose 從心開始系列 第 22

Jetpack Compose 從心開始 Day22 - 使用 WorkManager 安排工作

  • 分享至 

  • xImage
  •  

前言

    WorkManager 是 Android Jetpack 提供的一種工具,專門用於處理那些需要延遲執行、即使應用程式關閉或裝置重新啟動也能可靠執行的背景工作。

為什麼選擇 WorkManager?

  • 可靠性: WorkManager 能夠在各種情況下保證工作執行,包括裝置重新啟動、電池優化、低記憶體等。
  • 彈性: 支援一次性任務、週期性任務、鏈式任務等多種任務類型。
  • 簡單易用: 提供直觀的 API,方便開發者使用。
  • 與其他 Jetpack 組件整合良好: 可以與 LiveData、Room 等組件無縫整合。

WorkManager 的主要概念

  • Worker: 實際執行工作的類。
  • WorkRequest: 代表一個工作請求,包含了工作執行條件、約束等資訊。
  • WorkManager: 用來管理 WorkRequest,將其排入佇列,並監控其執行狀態。

使用場景

  • 同步資料: 定期從伺服器同步資料。
  • 上傳圖片或影片: 在網路條件允許的情況下上傳檔案。
  • 清理快取: 定期清理應用程式的快取檔案。
  • 執行複雜的計算任務: 將耗時的計算任務放在背景執行。

新增 WorkManager 依附元件

在你的 app/build.gradle.kts 檔案中添加 WorkManager 的程式庫

dependencies {
    // WorkManager dependency
    implementation("androidx.work:work-runtime-ktx:2.8.1")
}

建立 Worker:

範例:

class MyWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    override fun doWork(): Resul   
t {
        // 執行工作邏輯
        return Result.success()
    }
}

實作:

class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result {

        val resourceUri = inputData.getString(KEY_IMAGE_URI)
        val blurLevel = inputData.getInt(KEY_BLUR_LEVEL, 1)

        return withContext(Dispatchers.IO){
            // This is an utility function added to emulate slower work.
            delay(DELAY_TIME_MILLIS)
            return@withContext try {

                require(!resourceUri.isNullOrBlank()) {
                    val errorMessage =
                        applicationContext.resources.getString(R.string.invalid_input_uri)
                    Log.e(TAG, errorMessage)
                    errorMessage
                }
                val resolver = applicationContext.contentResolver

                val picture = BitmapFactory.decodeStream(
                    resolver.openInputStream(Uri.parse(resourceUri))
                )

                val output = blurBitmap(picture, blurLevel)
                // Write bitmap to a temp file
                val outputUri = writeBitmapToFile(applicationContext, output)

                makeStatusNotification(
                    "Output is $outputUri",
                    applicationContext
                )

                val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())

                //Result.success()
                Result.success(outputData)
            } catch (throwable: Throwable) {
                Log.e(
                    TAG,
                    applicationContext.resources.getString(R.string.error_applying_blur),
                    throwable
                )
                Result.failure()
            }
            }
    }
}

建立 WorkRequest:

範例:

val workRequest = OneTimeWorkRequestBuilder<MyWorker>()
    .setConstraints(Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build 

實作

class WorkManagerBluromaticRepository(context: Context) : BluromaticRepository {

    private var imageUri: Uri = context.getImageUri()
    private val workManager = WorkManager.getInstance(context)

    override val outputWorkInfo: Flow<WorkInfo> =
        workManager.getWorkInfosByTagLiveData(TAG_OUTPUT).asFlow().mapNotNull {
            if (it.isNotEmpty()) it.first() else null
        }

    /**
     * Create the WorkRequests to apply the blur and save the resulting image
     * @param blurLevel The amount to blur the image
     */
    override fun applyBlur(blurLevel: Int) {
        val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()

        val constraints = Constraints.Builder()
            .setRequiresBatteryNotLow(true)
            .build()

        blurBuilder.setInputData(createInputDataForWorkRequest(blurLevel, imageUri))

        blurBuilder.setConstraints(constraints)

        // Add WorkRequest to Cleanup temporary images
        var continuation = workManager
            .beginUniqueWork(
                IMAGE_MANIPULATION_WORK_NAME,
                ExistingWorkPolicy.REPLACE,
                OneTimeWorkRequest.from(CleanupWorker::class.java)
            )

        continuation = continuation.then(blurBuilder.build())
        val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
            .addTag(TAG_OUTPUT)
            .build()
        continuation = continuation.then(save)
        
        // Start the work
        continuation.enqueue()
    }

    /**
     * Cancel any ongoing WorkRequests
     * */
    override fun cancelWork() {
        workManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME)
    }

    private fun createInputDataForWorkRequest(blurLevel: Int, imageUri: Uri): Data {
        val builder = Data.Builder()
        builder.putString(KEY_IMAGE_URI, imageUri.toString()).putInt(KEY_BLUR_LEVEL, blurLevel)
        return builder.build()
    }
}

Enqueue WorkRequest:

範例:

WorkManager.getInstance(context).enqueue(workRequest)
override fun applyBlur(blurLevel: Int) {
    // Create WorkRequest to blur the image
    val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()

    // Start the work
    workManager.enqueue(blurBuilder.build())
}

執行結果

https://ithelp.ithome.com.tw/upload/images/20241002/20121643B84U1ZwtGp.png

https://ithelp.ithome.com.tw/upload/images/20241002/20121643UZ7HKYvdq7.png

App Inspection

https://ithelp.ithome.com.tw/upload/images/20241002/20121643lr1i7XeQZq.png

參考資料

https://developer.android.com/courses/pathways/android-basics-compose-unit-7-pathway-1?hl=zh-tw


上一篇
Jetpack Compose 從心開始 Day21 - 使用 DataStore 儲存
下一篇
Jetpack Compose 從心開始 Day23 - View 和 Compose
系列文
Jetpack Compose 從心開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言