1. Pengantar
Ada banyak opsi di Android untuk pekerjaan latar belakang yang dapat ditangguhkan. Codelab ini mencakup WorkManager, library sederhana, fleksibel, dan memiliki kompatibilitas mundur untuk pekerjaan latar belakang yang dapat ditangguhkan. WorkManager adalah penjadwal tugas yang direkomendasikan di Android untuk pekerjaan yang dapat ditangguhkan, dan dijamin akan dieksekusi.
Apa WorkManager itu
WorkManager adalah bagian dari Android Jetpack dan Komponen Arsitektur untuk pekerjaan latar belakang yang memerlukan kombinasi eksekusi oportunistik dan terjamin. Eksekusi oportunistik berarti WorkManager akan melakukan pekerjaan latar belakang Anda sesegera mungkin. Eksekusi terjamin berarti WorkManager akan menangani logika untuk memulai pekerjaan dalam berbagai situasi, meskipun Anda keluar dari aplikasi.
WorkManager adalah library yang sangat fleksibel yang memiliki banyak manfaat tambahan. Ini mencakup:
- Dukungan untuk tugas satu kali atau berkala asinkron
- Dukungan untuk batasan seperti kondisi jaringan, ruang penyimpanan, dan status pengisian daya
- Merangkai permintaan pekerjaan yang kompleks, termasuk menjalankan pekerjaan secara paralel
- Output dari satu permintaan pekerjaan digunakan sebagai input untuk permintaan berikutnya
- Menangani kompatibilitas API level kembali ke API level 14 (lihat catatan)
- Bekerja dengan atau tanpa layanan Google Play
- Mengikuti praktik terbaik kesehatan sistem
- Dukungan LiveData agar dapat dengan mudah menampilkan status permintaan pekerjaan di UI
Kapan harus menggunakan WorkManager
Library WorkManager adalah pilihan tepat untuk tugas yang berguna untuk diselesaikan, bahkan jika pengguna keluar dari layar tertentu atau aplikasi Anda.
Beberapa contoh tugas yang menggunakan WorkManager dengan baik:
- Mengupload log
- Menerapkan filter ke gambar dan menyimpan gambar
- Menyinkronkan data lokal dengan jaringan secara berkala
WorkManager menawarkan eksekusi terjamin, dan tidak semua tugas memerlukannya. Dengan demikian, tidak semua tugas akan dijalankan di thread utama. Untuk detail selengkapnya tentang kapan harus menggunakan WorkManager, lihat Panduan pemrosesan latar belakang.
Yang akan Anda build
Saat ini, smartphone hampir sempurna dalam mengambil gambar. Lewatlah sudah hari-hari saat seorang fotografer dapat mengambil gambar yang cukup buram dari sesuatu yang misterius.
Dalam codelab ini, Anda akan menjalankan Blur-O-Matic, sebuah aplikasi untuk memburamkan foto dan menyimpan hasilnya ke file. Apakah itu monster Loch Ness atau kapal selam mainan evelopera? Dengan Blur-O-Matic, tidak akan ada yang tahu.
Yang akan Anda pelajari
- Menambahkan WorkManager ke project Anda
- Menjadwalkan tugas sederhana
- Parameter input dan output
- Perantaian pekerjaan
- Pekerjaan unik
- Menampilkan status pekerjaan di UI
- Membatalkan pekerjaan
- Batasan pekerjaan
Yang Anda butuhkan
- Versi Android Studio terbaru yang stabil
- Anda juga harus memahami
LiveData
danViewModel
. Jika Anda masih baru di class ini, lihat Codelab komponen berbasis Siklus Proses Android (khusus untuk ViewModel dan LiveData) atau Room dengan Codelab View (pengantar Komponen Arsitektur).
2. Mempersiapkan
Langkah 1 - Download Kode
Klik link berikut guna mendownload semua kode untuk codelab ini:
Atau jika mau, Anda dapat meng-clone codelab WorkManager dari GitHub:
$ git clone -b start_kotlin https://github.com/googlecodelabs/android-workmanager
Langkah 2 - Jalankan aplikasi
Jalankan aplikasi. Anda akan melihat layar berikut:
Layar harus memiliki tombol pilihan tempat Anda dapat memilih tingkat keburaman gambar. Menekan tombol Go pada akhirnya akan memburamkan dan menyimpan gambar.
Sekarang, aplikasi tidak menerapkan efek buram.
Kode awal berisi:
WorkerUtils
: Class ini berisi kode untuk benar-benar memburamkan gambar, dan beberapa metode praktis yang nantinya akan digunakan untuk menampilkanNotifications
, menyimpan bitmap ke file, dan memperlambat aplikasi.BlurActivity
:* Aktivitas yang menampilkan gambar dan menyertakan tombol pilihan untuk memilih tingkat keburaman.BlurViewModel
:* Model tampilan ini menyimpan semua data yang diperlukan untuk menampilkanBlurActivity
. Kode ini juga akan menjadi class tempat Anda memulai pekerjaan latar belakang menggunakan WorkManager.Constants
: Class statis dengan beberapa konstanta yang akan Anda gunakan selama codelab.res/activity_blur.xml
: file tata letak untukBlurActivity
.
***** Hanya file-file ini yang akan menjadi tempat Anda menulis kode.
3. Menambahkan WorkManager ke aplikasi Anda
WorkManager
memerlukan dependensi gradle di bawah ini. Dependensi ini sudah disertakan dalam file build:
app/build.gradle
dependencies {
// WorkManager dependency
implementation "androidx.work:work-runtime-ktx:$versions.work"
}
Anda harus mendapatkan versi stabil terbaru work-runtime-ktx
dari sini dan menempatkan versi yang benar. Untuk saat ini, versi terbaru adalah:
build.gradle
versions.work = "2.7.1"
Jika Anda mengupdate versi ke yang lebih baru, pastikan untuk memilih Sync Now guna menyinkronkan project Anda dengan file gradle yang diubah.
4. Membuat WorkRequest pertama Anda
Pada langkah ini, Anda akan mengambil gambar di folder res/drawable
bernama android_cupcake.png
dan menjalankan beberapa fungsi di dalamnya di latar belakang. Fungsi ini akan memburamkan gambar dan menyimpannya ke file sementara.
Dasar-dasar WorkManager
Ada beberapa class WorkManager yang perlu Anda ketahui:
Worker
: Ini adalah tempat Anda menempatkan kode untuk pekerjaan yang sebenarnya yang ingin Anda lakukan di latar belakang. Anda akan memperluas class ini dan mengganti metodedoWork()
.WorkRequest
: Ini mewakili permintaan untuk melakukan beberapa pekerjaan. Anda akan meneruskanWorker
sebagai bagian dari pembuatanWorkRequest
. Saat membuatWorkRequest
, Anda juga dapat menentukan hal-hal sepertiConstraints
terkait kapanWorker
harus dijalankan.WorkManager
: Class ini sebenarnya menjadwalkanWorkRequest
Anda dan menjalankannya. Class ini menjadwalkanWorkRequest
dengan cara menyebarkan beban pada resource sistem, sekaligus memenuhi batasan yang Anda tetapkan.
Dalam kasus ini, Anda akan menentukan BlurWorker
baru yang akan berisi kode untuk memburamkan gambar. Saat tombol Go diklik, WorkRequest
akan dibuat lalu diantrekan oleh WorkManager
.
Langkah 1 - Buat BlurWorker
Pada paket workers
, buat class Kotlin baru bernama BlurWorker
.
Langkah 2 - Tambahkan konstruktor
Tambahkan dependensi ke Worker
untuk class BlurWorker
:
class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
}
Langkah 3 - Ganti dan implementasikan doWork()
Worker
Anda akan memburamkan gambar cupcake yang ditampilkan.
Untuk melihat dengan lebih baik kapan pekerjaan dijalankan, Anda akan menggunakan makeStatusNotification()
WorkerUtil. Metode ini akan memungkinkan Anda menampilkan banner notifikasi dengan mudah di bagian atas layar.
Ganti metode doWork()
, lalu implementasikan hal berikut. Anda dapat melihat kode lengkap di akhir bagian:
- Dapatkan
Context
dengan memanggil propertiapplicationContext
. Tetapkan keval
baru yang diberi namaappContext
. Anda akan memerlukan ini untuk berbagai manipulasi bitmap yang akan Anda lakukan. - Tampilkan notifikasi status menggunakan fungsi,
makeStatusNotification
untuk memberi tahu pengguna tentang memburamkan gambar. - Buat
Bitmap
dari gambar cupcake:
val picture = BitmapFactory.decodeResource(
appContext.resources,
R.drawable.android_cupcake)
- Dapatkan versi buram bitmap dengan memanggil metode
blurBitmap
dariWorkerUtils
. - Tulis bitmap tersebut ke file sementara dengan memanggil metode
writeBitmapToFile
dariWorkerUtils
. Pastikan untuk menyimpan URI yang ditampilkan ke variabel lokal. - Buat Notifikasi yang menampilkan URI dengan memanggil metode
makeStatusNotification
dariWorkerUtils
. - Kembalikan
Result.success()
. - Gabungkan kode dari langkah 3-6 dalam pernyataan try/catch. Tangkap
Throwable
generik. - Pada pernyataan catch, cetak pesan error menggunakan Laporan log:
Log.e(TAG, "Error applying blur")
. - Dalam pernyataan catch, tampilkan
Result.failure()
.
Kode yang sudah selesai untuk langkah ini ada di bawah.
**BlurWorker.**kt
package com.example.background.workers
import android.content.Context
import android.graphics.BitmapFactory
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.example.background.R
private const val TAG = "BlurWorker"
class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
override fun doWork(): Result {
val appContext = applicationContext
makeStatusNotification("Blurring image", appContext)
return try {
val picture = BitmapFactory.decodeResource(
appContext.resources,
R.drawable.android_cupcake)
val output = blurBitmap(picture, appContext)
// Write bitmap to a temp file
val outputUri = writeBitmapToFile(appContext, output)
makeStatusNotification("Output is $outputUri", appContext)
Result.success()
} catch (throwable: Throwable) {
Log.e(TAG, "Error applying blur")
Result.failure()
}
}
}
Langkah 4 - Dapatkan WorkManager di ViewModel
Buat variabel class untuk instance WorkManager
di ViewModel
:
BlurViewModel.kt
private val workManager = WorkManager.getInstance(application)
Langkah 5 - Antrekan WorkRequest di WorkManager
Baik, saatnya membuat WorkRequest
dan memberi tahu WorkManager untuk menjalankannya. Ada dua jenis WorkRequest
:
OneTimeWorkRequest
:WorkRequest
yang hanya akan dieksekusi satu kali.PeriodicWorkRequest
:WorkRequest
yang akan diulang pada siklus.
Kita hanya ingin gambar diburamkan satu kali saat tombol Go diklik. Metode applyBlur
dipanggil saat tombol Go diklik, jadi buat OneTimeWorkRequest
dari BlurWorker
di sana. Lalu, gunakan instance WorkManager
untuk mengantrekan WorkRequest.
Tambahkan baris kode berikut ke metode applyBlur()
BlurViewModel's
:
BlurViewModel.kt
internal fun applyBlur(blurLevel: Int) {
workManager.enqueue(OneTimeWorkRequest.from(BlurWorker::class.java))
}
Langkah 6 - Jalankan kode Anda.
Jalankan kode. Kode harus mengompilasi dan Anda akan melihat Notifikasi saat Anda menekan tombol Go. Perhatikan bahwa untuk melihat hasil yang lebih buram, Anda harus memilih opsi 'More blurred' atau 'The most blurred'.
Untuk mengonfirmasi bahwa gambar berhasil diburamkan, Anda dapat membuka Device File Explorer di Android Studio:
Lalu buka data > data > com.example.background > files > blur_filter_outputs> <URI> dan pastikan bahwa cupcake tersebut telah diburamkan:
5. Menambahkan input dan output
Pemburaman aset gambar pada direktori resource telah dilakukan dengan baik, tetapi agar Blur-O-Matic benar-benar menjadi aplikasi pengeditan gambar yang revolusioner seperti yang diharapkan, Anda harus mengizinkan pengguna memburamkan gambar yang mereka lihat di layar, lalu dapat untuk menampilkan hasil yang diburamkan.
Untuk melakukannya, kita akan memberikan URI gambar cupcake yang ditampilkan sebagai input ke WorkRequest
yang ditampilkan, lalu menggunakan output WorkRequest kami untuk menampilkan gambar akhir yang diburamkan.
Langkah 1 - Buat Objek input data
Input dan output diteruskan ke dan dikeluarkan melalui objek Data
. Objek Data
adalah container ringan untuk key-value pair. Objek tersebut dimaksudkan untuk menyimpan sejumlah kecil data yang dapat diteruskan ke dan keluar dari WorkRequest
.
Anda akan meneruskan URI untuk gambar pengguna ke dalam paket. URI tersebut disimpan dalam variabel bernama imageUri
.
Di BlurViewModel
, buat metode pribadi bernama createInputDataForUri
. Metode ini harus:
- Membuat objek
Data.Builder
. Mengimporandroidx.work.Data
bila diminta. - Jika
imageUri
adalahURI
non-null, tambahkan ke objekData
menggunakan metodeputString
. Metode ini mengambil kunci dan nilai. Anda dapat menggunakan konstanta StringKEY_IMAGE_URI
dari classConstants
. - Panggil
build()
pada objekData.Builder
untuk membuat objekData
Anda, lalu menampilkannya.
Berikut adalah metode createInputDataForUri
yang telah selesai:
BlurViewModel.kt
/**
* Creates the input data bundle which includes the Uri to operate on
* @return Data which contains the Image Uri as a String
*/
private fun createInputDataForUri(): Data {
val builder = Data.Builder()
imageUri?.let {
builder.putString(KEY_IMAGE_URI, imageUri.toString())
}
return builder.build()
}
Langkah 2 - Teruskan Objek data ke WorkRequest
Anda akan mengubah metode applyBlur
di BlurViewModel
sehingga metode tersebut:
- Membuat
OneTimeWorkRequestBuilder
baru. - Memanggil
setInputData
, meneruskan hasil daricreateInputDataForUri
. - Mem-build
OneTimeWorkRequest
. - Mengantrekan permintaan pekerjaan menggunakan permintaan
WorkManager
agar pekerjaan akan dapat dijadwalkan untuk dijalankan.
Berikut adalah metode applyBlur
yang telah selesai:
BlurViewModel.kt
internal fun applyBlur(blurLevel: Int) {
val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(createInputDataForUri())
.build()
workManager.enqueue(blurRequest)
}
Langkah 3 - Perbarui doWork() BlurWorker untuk mendapatkan input
Sekarang mari kita memperbarui metode doWork()
BlurWorker
untuk mendapatkan URI yang kita teruskan dari objek Data
:
BlurWorker.kt
override fun doWork(): Result {
val appContext = applicationContext
// ADD THIS LINE
val resourceUri = inputData.getString(KEY_IMAGE_URI)
// ... rest of doWork()
}
Langkah 4 - Buramkan URI yang ditentukan
Dengan URI, sekarang mari kita buramkan gambar cupcake di layar.
- Hapus kode sebelumnya yang mendapatkan resource gambar.
val picture = BitmapFactory.decodeResource(appContext.
resources
, R.drawable.
android_cupcake
)
- Pastikan
resourceUri
yang diperoleh dariData
yang diteruskan tidak kosong. - Tetapkan variabel
picture
untuk menjadi gambar yang diteruskan seperti berikut:
val picture = BitmapFactory.decodeStream(
appContext.
contentResolver
.
`openInputStream(Uri.parse(resourceUri)))`
BlurWorker.kt
override fun doWork(): Result {
val appContext = applicationContext
val resourceUri = inputData.getString(KEY_IMAGE_URI)
makeStatusNotification("Blurring image", appContext)
return try {
// REMOVE THIS
// val picture = BitmapFactory.decodeResource(
// appContext.resources,
// R.drawable.android_cupcake)
if (TextUtils.isEmpty(resourceUri)) {
Log.e(TAG, "Invalid input uri")
throw IllegalArgumentException("Invalid input uri")
}
val resolver = appContext.contentResolver
val picture = BitmapFactory.decodeStream(
resolver.openInputStream(Uri.parse(resourceUri)))
val output = blurBitmap(picture, appContext)
// Write bitmap to a temp file
val outputUri = writeBitmapToFile(appContext, output)
Result.success()
} catch (throwable: Throwable) {
Log.e(TAG, "Error applying blur")
throwable.printStackTrace()
Result.failure()
}
}
Langkah 5 - Buat output URI sementara
Sekarang Anda sudah selesai dengan Pekerja ini dan dapat menampilkan URI output di Result.success()
. Berikan URI Output sebagai Data output agar gambar sementara ini dapat diakses dengan mudah oleh pekerja lain untuk operasi selanjutnya. Hal ini akan berguna dalam bab berikutnya saat Anda membuat Rantai pekerja. Untuk melakukannya:
- Buat
Data
baru, seperti yang Anda lakukan dengan input, dan simpanoutputUri
sebagaiString
. Gunakan kunci yang sama,KEY_IMAGE_URI
- Tampilkan ini ke WorkManager menggunakan metode
Result.success(Data outputData)
.
BlurWorker.kt
Ubah baris Result.success()
di doWork()
menjadi:
val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())
Result.success(outputData)
Langkah 6 - Jalankan aplikasi Anda
Pada tahap ini, Anda harus menjalankan aplikasi. Aplikasi harus mengompilasi dan memiliki perilaku yang sama tempat Anda dapat melihat gambar yang diburamkan melalui Device File Explorer, tetapi belum di layar.
Untuk memeriksa gambar buram lainnya, Anda dapat membuka Device File Explorer di Android Studio dan membuka data/data/com.example.background/files/blur_filter_outputs/<URI> seperti yang Anda lakukan di langkah terakhir.
Perhatikan bahwa Anda mungkin perlu memilih Synchronize untuk melihat gambar Anda:
Bagus sekali! Anda berhasil memburamkan gambar input menggunakan WorkManager
.
6. Membuat rantai Pekerjaan Anda
Saat ini, Anda sedang melakukan satu tugas pekerjaan: memburamkan gambar. Pekerjaan ini adalah langkah pertama yang bagus, tetapi tidak memiliki beberapa fungsi inti:
- Pekerjaan ini tidak membersihkan file sementara.
- Pekerjaan ini tidak benar-benar menyimpan gambar ke file permanen.
- Pekerjaan ini selalu memburamkan gambar dengan jumlah yang sama.
Kita akan menggunakan rantai pekerjaan WorkManager untuk menambahkan fungsi ini.
WorkManager memungkinkan Anda membuat WorkerRequest
terpisah yang berjalan sesuai urutan atau paralel. Pada langkah ini, Anda akan membuat rantai pekerjaan yang terlihat seperti ini:
WorkRequest
digambarkan sebagai kotak.
Fitur lain yang benar-benar bagus dari perantaian adalah output dari satu WorkRequest
menjadi input dari WorkRequest
berikutnya dalam rantai. Input dan output yang diteruskan di antara setiap WorkRequest
ditampilkan sebagai teks biru.
Langkah 1 - Buat Pembersihan dan Simpan Pekerja
Pertama, Anda akan menentukan semua class Worker
yang dibutuhkan. Anda sudah memiliki Worker
untuk memburamkan gambar, tetapi Anda juga memerlukan Worker
yang membersihkan file sementara dan Worker
yang menyimpan gambar secara permanen.
Buat dua class baru di paket workers
yang memperluas Worker
.
Class pertama harus disebut CleanupWorker
, class kedua harus disebut SaveImageToFileWorker
.
Langkah 2 - Perluas Pekerja
Perluas class CleanupWorker
dari class Worker
. Tambahkan parameter konstruktor yang diperlukan.
class CleanupWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
}
Langkah 3 - Ganti dan implementasikan doWork() untuk CleanupWorker
CleanupWorker
tidak perlu mengambil input apa pun atau meneruskan output apa pun. Pekerja ini selalu menghapus file sementara jika ada. Karena manipulasi file berada di luar cakupan codelab ini, Anda dapat menyalin kode untuk CleanupWorker
di bawah ini:
CleanupWorker.kt
package com.example.background.workers
import android.content.Context
import android.util.Log
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.example.background.OUTPUT_PATH
import java.io.File
/**
* Cleans up temporary files generated during blurring process
*/
private const val TAG = "CleanupWorker"
class CleanupWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
override fun doWork(): Result {
// Makes a notification when the work starts and slows down the work so that
// it's easier to see each WorkRequest start, even on emulated devices
makeStatusNotification("Cleaning up old temporary files", applicationContext)
sleep()
return try {
val outputDirectory = File(applicationContext.filesDir, OUTPUT_PATH)
if (outputDirectory.exists()) {
val entries = outputDirectory.listFiles()
if (entries != null) {
for (entry in entries) {
val name = entry.name
if (name.isNotEmpty() && name.endsWith(".png")) {
val deleted = entry.delete()
Log.i(TAG, "Deleted $name - $deleted")
}
}
}
}
Result.success()
} catch (exception: Exception) {
exception.printStackTrace()
Result.failure()
}
}
}
Langkah 4 - Ganti dan implementasikan doWork() untuk SaveImageToFileWorker
SaveImageToFileWorker
akan mengambil input dan output. Inputnya adalah String
dari URI gambar yang diburamkan sementara yang disimpan dengan kunci KEY_IMAGE_URI
. Dan outputnya juga String
, URI gambar buram yang disimpan dengan kunci KEY_IMAGE_URI
.
Karena codelab ini belum membahas manipulasi file, kodenya diberikan di bawah ini. Perhatikan cara nilai resourceUri
dan output
diambil dengan kunci KEY_IMAGE_URI
. Ini sangat mirip dengan kode yang Anda tulis untuk input dan output di langkah sebelumnya (kode ini menggunakan semua kunci yang sama).
SaveImageToFileWorker.kt
package com.example.background.workers
import android.content.Context
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import androidx.work.workDataOf
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.example.background.KEY_IMAGE_URI
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* Saves the image to a permanent file
*/
private const val TAG = "SaveImageToFileWorker"
class SaveImageToFileWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
private val title = "Blurred Image"
private val dateFormatter = SimpleDateFormat(
"yyyy.MM.dd 'at' HH:mm:ss z",
Locale.getDefault()
)
override fun doWork(): Result {
// Makes a notification when the work starts and slows down the work so that
// it's easier to see each WorkRequest start, even on emulated devices
makeStatusNotification("Saving image", applicationContext)
sleep()
val resolver = applicationContext.contentResolver
return try {
val resourceUri = inputData.getString(KEY_IMAGE_URI)
val bitmap = BitmapFactory.decodeStream(
resolver.openInputStream(Uri.parse(resourceUri)))
val imageUrl = MediaStore.Images.Media.insertImage(
resolver, bitmap, title, dateFormatter.format(Date()))
if (!imageUrl.isNullOrEmpty()) {
val output = workDataOf(KEY_IMAGE_URI to imageUrl)
Result.success(output)
} else {
Log.e(TAG, "Writing to MediaStore failed")
Result.failure()
}
} catch (exception: Exception) {
exception.printStackTrace()
Result.failure()
}
}
}
Langkah 5 - Ubah Notifikasi BlurWorker
Sekarang setelah rantai Worker
menangani penyimpanan gambar dalam folder yang benar, kita dapat memperlambat pekerjaan dengan menggunakan metode sleep()
yang ditentukan di class WorkerUtils
, sehingga lebih mudah untuk melihat setiap WorkRequest
dimulai, bahkan pada perangkat yang diemulasikan. Versi akhir BlurWorker
menjadi:
BlurWorker.kt
class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
override fun doWork(): Result {
val appContext = applicationContext
val resourceUri = inputData.getString(KEY_IMAGE_URI)
makeStatusNotification("Blurring image", appContext)
// ADD THIS TO SLOW DOWN THE WORKER
sleep()
// ^^^^
return try {
if (TextUtils.isEmpty(resourceUri)) {
Timber.e("Invalid input uri")
throw IllegalArgumentException("Invalid input uri")
}
val resolver = appContext.contentResolver
val picture = BitmapFactory.decodeStream(
resolver.openInputStream(Uri.parse(resourceUri)))
val output = blurBitmap(picture, appContext)
// Write bitmap to a temp file
val outputUri = writeBitmapToFile(appContext, output)
val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())
Result.success(outputData)
} catch (throwable: Throwable) {
throwable.printStackTrace()
Result.failure()
}
}
Langkah 6 - Buat rantai WorkRequest
Anda harus mengubah metode applyBlur
BlurViewModel
untuk mengeksekusi rantai WorkRequest
, bukan hanya satu. Saat ini, kode terlihat seperti ini:
BlurViewModel.kt
val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(createInputDataForUri())
.build()
workManager.enqueue(blurRequest)
Jangan panggil workManager.enqueue()
, tetapi panggil workManager.beginWith()
. Tindakan ini akan menampilkan WorkContinuation
, yang menentukan rantai WorkRequest
. Anda dapat menambahkan ke rantai permintaan pekerjaan ini dengan memanggil metode then()
, misalnya, jika Anda memiliki tiga objek WorkRequest
, workA
, workB
, dan workC
, Anda dapat melakukan yang berikut ini:
// Example code, don't copy to the project
val continuation = workManager.beginWith(workA)
continuation.then(workB) // FYI, then() returns a new WorkContinuation instance
.then(workC)
.enqueue() // Enqueues the WorkContinuation which is a chain of work
Tindakan ini akan menghasilkan dan menjalankan rantai WorkRequest berikut:
Buat rantai CleanupWorker
WorkRequest
, BlurImage
WorkRequest
, dan SaveImageToFile
WorkRequest
di applyBlur
. Teruskan input ke BlurImage
WorkRequest
.
Kode untuk ini ada di bawah ini:
BlurViewModel.kt
internal fun applyBlur(blurLevel: Int) {
// Add WorkRequest to Cleanup temporary images
var continuation = workManager
.beginWith(OneTimeWorkRequest
.from(CleanupWorker::class.java))
// Add WorkRequest to blur the image
val blurRequest = OneTimeWorkRequest.Builder(BlurWorker::class.java)
.setInputData(createInputDataForUri())
.build()
continuation = continuation.then(blurRequest)
// Add WorkRequest to save the image to the filesystem
val save = OneTimeWorkRequest.Builder(SaveImageToFileWorker::class.java).build()
continuation = continuation.then(save)
// Actually start the work
continuation.enqueue()
}
Kode harus mengompilasi dan berjalan. Anda sekarang dapat menekan tombol Go dan melihat notifikasi saat pekerja yang berbeda dieksekusi. Anda masih dapat melihat gambar yang diburamkan di Device File Explorer, dan di langkah selanjutnya Anda akan menambahkan tombol tambahan agar pengguna dapat melihat gambar yang diburamkan di perangkat.
Dalam screenshot di bawah, Anda akan melihat bahwa pesan notifikasi menampilkan pekerja mana yang sedang berjalan.
Langkah 7 - Ulangi BlurWorker
Saatnya menambahkan kemampuan untuk memburamkan gambar dalam jumlah yang berbeda. Ambil parameter blurLevel
yang diteruskan ke applyBlur
dan tambahkan sejumlah operasi WorkRequest
pemburaman tersebut ke rantai. Hanya WorkRequest
pertama yang memerlukan dan harus mengambil input URI.
Cobalah sendiri, lalu bandingkan dengan kode di bawah ini:
BlurViewModel.kt
internal fun applyBlur(blurLevel: Int) {
// Add WorkRequest to Cleanup temporary images
var continuation = workManager
.beginWith(OneTimeWorkRequest
.from(CleanupWorker::class.java))
// Add WorkRequests to blur the image the number of times requested
for (i in 0 until blurLevel) {
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
// Input the Uri if this is the first blur operation
// After the first blur operation the input will be the output of previous
// blur operations.
if (i == 0) {
blurBuilder.setInputData(createInputDataForUri())
}
continuation = continuation.then(blurBuilder.build())
}
// Add WorkRequest to save the image to the filesystem
val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
.build()
continuation = continuation.then(save)
// Actually start the work
continuation.enqueue()
}
Buka Device File Explorer untuk melihat gambar yang diburamkan. Perhatikan bahwa folder output berisi beberapa gambar yang diburamkan, gambar yang berada di tengah tahap pemburaman, dan gambar terakhir yang menampilkan gambar yang diburamkan berdasarkan jumlah pemburaman yang dipilih.
"Pekerjaan" yang hebat! Anda sekarang dapat memburamkan gambar sebanyak atau sesedikit yang Anda inginkan. Benar-benar misterius!
7. Memastikan pekerjaan unik
Setelah Anda menggunakan rantai, sekarang saatnya menangani fitur canggih lain dari WorkManager - rantai pekerjaan unik.
Terkadang, Anda hanya ingin menjalankan satu rantai pekerjaan dalam satu waktu. Misalnya, mungkin Anda memiliki rantai pekerjaan yang menyinkronkan data lokal dengan server - Anda mungkin ingin menyelesaikan sinkronisasi data pertama sebelum memulai yang baru. Untuk melakukannya, gunakan beginUniqueWork
, bukan beginWith
; dan berikan nama String
yang unik. Tindakan ini memberikan nama ke seluruh rantai permintaan pekerjaan sehingga Anda dapat merujuk dan mengkuerinya bersama-sama.
Pastikan rantai pekerjaan untuk memburamkan file Anda unik dengan menggunakan beginUniqueWork
. Teruskan IMAGE_MANIPULATION_WORK_NAME
sebagai kuncinya. Anda juga harus meneruskan ExistingWorkPolicy
. Opsi Anda adalah REPLACE
, KEEP
, atau APPEND
.
Anda akan menggunakan REPLACE
karena jika pengguna memutuskan untuk memburamkan gambar lain sebelum gambar saat ini selesai, kita ingin menghentikan pemburaman gambar saat ini dan mulai memburamkan gambar baru.
Kode untuk memulai kelanjutan pekerjaan unik Anda ada di bawah ini:
BlurViewModel.kt
// REPLACE THIS CODE:
// var continuation = workManager
// .beginWith(OneTimeWorkRequest
// .from(CleanupWorker::class.java))
// WITH
var continuation = workManager
.beginUniqueWork(
IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker::class.java)
)
Blur-O-Matic kini hanya akan memburamkan satu gambar dalam satu waktu.
8. Memberi tag dan menampilkan status Pekerjaan
Bagian ini akan sering menggunakan LiveData, jadi agar sepenuhnya memahami apa yang terjadi, Anda harus memahami LiveData. LiveData adalah penyimpan data yang dapat diamati dan berbasis siklus proses.
Anda dapat melihat dokumentasi atau Codelab komponen berbasis Siklus Proses Android jika ini adalah pertama kalinya Anda bekerja dengan LiveData atau observable.
Perubahan besar berikutnya yang akan Anda lakukan adalah untuk benar-benar mengubah apa yang ditampilkan di aplikasi saat Pekerjaan dieksekusi.
Anda bisa mendapatkan status WorkRequest
dengan mendapatkan LiveData
yang menyimpan objek WorkInfo
. WorkInfo
adalah objek yang berisi detail tentang status WorkRequest
saat ini, termasuk:
- Apakah pekerjaan tersebut
BLOCKED
,CANCELLED
,ENQUEUED
,FAILED
,RUNNING
, atauSUCCEEDED
. - Jika
WorkRequest
selesai, semua data output dari pekerjaan.
Tabel berikut menampilkan tiga cara berbeda untuk mendapatkan objek LiveData<WorkInfo>
atau LiveData<List<WorkInfo>>
beserta fungsinya.
Jenis | Metode WorkManager | Deskripsi |
Mendapatkan pekerjaan menggunakan id |
| Setiap |
Mendapatkan pekerjaan menggunakan nama rantai unik |
| Seperti yang baru saja Anda lihat, |
Mendapatkan pekerjaan menggunakan tag |
| Terakhir, Anda dapat memberi tag pada WorkRequest mana pun dengan String. Anda dapat memberi tag beberapa |
Anda akan memberi tag SaveImageToFileWorker
WorkRequest
, agar Anda bisa mendapatkannya menggunakan getWorkInfosByTag
. Anda akan menggunakan tag untuk memberi label pekerjaan, bukan menggunakan ID WorkManager, karena jika pengguna memburamkan beberapa gambar, semua WorkRequest
gambar yang disimpan akan memiliki tag yang sama, namun bukan ID yang sama. Anda juga dapat memilih tag.
Anda tidak akan menggunakan getWorkInfosForUniqueWork
karena akan menampilkan WorkInfo
untuk semua WorkRequest
pemburaman dan WorkRequest
pembersihan juga; serta akan membutuhkan logika tambahan untuk menemukan WorkRequest
gambar yang disimpan.
Langkah 1 - Beri tag pekerjaan Anda
Di applyBlur
, saat membuat SaveImageToFileWorker
, beri tag pekerjaan Anda menggunakan konstanta String
TAG_OUTPUT
:
BlurViewModel.kt
val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
.addTag(TAG_OUTPUT) // <-- ADD THIS
.build()
Langkah 2 - Dapatkan WorkInfo
Setelah memberi tag pada pekerjaan, Anda bisa mendapatkan WorkInfo
:
- Di
BlurViewModel
, deklarasikan variabel class baru bernamaoutputWorkInfos
yang merupakanLiveData<List<WorkInfo>>
- Dalam
BlurViewModel
, tambahkan blok init untuk mendapatkanWorkInfo
menggunakanWorkManager.getWorkInfosByTagLiveData
Kode yang Anda butuhkan ada di bawah ini:
BlurViewModel.kt
// New instance variable for the WorkInfo
internal val outputWorkInfos: LiveData<List<WorkInfo>>
// Modify the existing init block in the BlurViewModel class to this:
init {
imageUri = getImageUri(application.applicationContext)
// This transformation makes sure that whenever the current work Id changes the WorkInfo
// the UI is listening to changes
outputWorkInfos = workManager.getWorkInfosByTagLiveData(TAG_OUTPUT)
}
Langkah 3 - Tampilkan WorkInfo
Setelah memiliki LiveData
untuk WorkInfo
, Anda dapat mengamatinya di BlurActivity
. Dalam observer:
- Periksa apakah daftar
WorkInfo
bukan null dan apakah ada objekWorkInfo
di dalamnya - jika tidak ada, maka tombol Go belum diklik. Jadi, kembali. - Dapatkan
WorkInfo
pertama dalam daftar; hanya akan ada satuWorkInfo
yang diberi tag denganTAG_OUTPUT
karena kita membuat rantai pekerjaan unik. - Periksa apakah status pekerjaan telah selesai, menggunakan
workInfo.state.isFinished
. - Jika belum selesai, panggil
showWorkInProgress()
yang menyembunyikan tombol Go dan menampilkan tombol Cancel Work serta status progres. - Jika sudah selesai, panggil
showWorkFinished()
yang menyembunyikan tombol Cancel Work dan status progres serta menampilkan tombol Go.
Berikut kodenya:
Catatan: Impor androidx.lifecycle.Observer
bila diminta.
BlurActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
...
// Observe work status, added in onCreate()
viewModel.outputWorkInfos.observe(this, workInfosObserver())
}
// Define the observer function
private fun workInfosObserver(): Observer<List<WorkInfo>> {
return Observer { listOfWorkInfo ->
// Note that these next few lines grab a single WorkInfo if it exists
// This code could be in a Transformation in the ViewModel; they are included here
// so that the entire process of displaying a WorkInfo is in one location.
// If there are no matching work info, do nothing
if (listOfWorkInfo.isNullOrEmpty()) {
return@Observer
}
// We only care about the one output status.
// Every continuation has only one worker tagged TAG_OUTPUT
val workInfo = listOfWorkInfo[0]
if (workInfo.state.isFinished) {
showWorkFinished()
} else {
showWorkInProgress()
}
}
}
Langkah 4 - Jalankan aplikasi Anda
Jalankan aplikasi Anda - aplikasi harus mengompilasi dan berjalan, dan kini menampilkan status progres saat berfungsi serta tombol batal:
9. Menampilkan output akhir
Setiap WorkInfo
juga memiliki metode getOutputData
yang memungkinkan Anda untuk mendapatkan objek Data
output dengan gambar akhir yang disimpan. Di Kotlin, Anda dapat mengakses metode ini menggunakan variabel yang dihasilkan bahasa untuk Anda: outputData
. Mari kita tampilkan tombol See File setiap kali ada gambar buram yang siap ditampilkan.
Langkah 1 - Buat tombol 'See File'
Sudah ada tombol di tata letak activity_blur.xml
yang tersembunyi. Tombol berada di BlurActivity
dan disebut outputButton
.
Di BlurActivity
, di dalam onCreate()
, siapkan pemroses klik untuk tombol tersebut. Tombol harus mendapatkan URI, lalu membuka aktivitas untuk melihat URI tersebut. Anda dapat menggunakan kode di bawah ini:
BlurActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
// Setup view output image file button
binding.seeFileButton.setOnClickListener {
viewModel.outputUri?.let { currentUri ->
val actionView = Intent(Intent.ACTION_VIEW, currentUri)
actionView.resolveActivity(packageManager)?.run {
startActivity(actionView)
}
}
}
}
Langkah 2 - Tetapkan URI dan tampilkan tombol
Ada beberapa penyesuaian terakhir yang perlu Anda terapkan ke observer WorkInfo
agar pembuatan tombol berhasil:
- Jika
WorkInfo
selesai, dapatkan data output menggunakanworkInfo.outputData
. - Lalu dapatkan URI output, ingat bahwa URI disimpan dengan kunci
Constants.KEY_IMAGE_URI
. - Kemudian, jika tidak kosong, URI akan disimpan dengan benar; tampilkan
outputButton
dan panggilsetOutputUri
pada model tampilan dengan URI.
BlurActivity.kt
private fun workInfosObserver(): Observer<List<WorkInfo>> {
return Observer { listOfWorkInfo ->
// Note that these next few lines grab a single WorkInfo if it exists
// This code could be in a Transformation in the ViewModel; they are included here
// so that the entire process of displaying a WorkInfo is in one location.
// If there are no matching work info, do nothing
if (listOfWorkInfo.isNullOrEmpty()) {
return@Observer
}
// We only care about the one output status.
// Every continuation has only one worker tagged TAG_OUTPUT
val workInfo = listOfWorkInfo[0]
if (workInfo.state.isFinished) {
showWorkFinished()
// Normally this processing, which is not directly related to drawing views on
// screen would be in the ViewModel. For simplicity we are keeping it here.
val outputImageUri = workInfo.outputData.getString(KEY_IMAGE_URI)
// If there is an output file show "See File" button
if (!outputImageUri.isNullOrEmpty()) {
viewModel.setOutputUri(outputImageUri)
binding.seeFileButton.visibility = View.VISIBLE
}
} else {
showWorkInProgress()
}
}
}
Langkah 3 - Jalankan kode Anda
Jalankan kode. Anda akan melihat tombol See File baru yang dapat diklik, yang akan mengarahkan Anda ke file output yang dihasilkan:
10. Membatalkan pekerjaan
Anda menambahkan tombol Cancel Work ini, jadi mari kita tambahkan kode untuk membuatnya melakukan sesuatu. Dengan WorkManager, Anda dapat membatalkan pekerjaan menggunakan ID, dengan tag dan dengan nama rantai unik.
Dalam hal ini, Anda dapat membatalkan pekerjaan dengan nama rantai unik, karena Anda ingin membatalkan semua pekerjaan dalam rantai, bukan satu langkah tertentu.
Langkah 1 - Batalkan pekerjaan dengan nama
Di BlurViewModel
, tambahkan metode baru bernama cancelWork()
untuk membatalkan pekerjaan unik. Di dalam panggilan fungsi cancelUniqueWork
di workManager
, teruskan tag IMAGE_MANIPULATION_WORK_NAME
.
BlurViewModel.kt
internal fun cancelWork() {
workManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME)
}
Langkah 2 - Panggil metode pembatalan
Lalu, hubungkan tombol cancelButton
untuk memanggil cancelWork
:
BlurActivity.kt
// In onCreate()
// Hookup the Cancel button
binding.cancelButton.setOnClickListener { viewModel.cancelWork() }
Langkah 3 - Jalankan dan batalkan pekerjaan Anda
Jalankan aplikasi Anda. Aplikasi harus mengompilasi dengan baik. Mulai buramkan gambar, lalu klik tombol batal. Seluruh rantai dibatalkan.
Perlu diperhatikan bahwa sekarang hanya ada tombol GO setelah pekerjaan dibatalkan karena WorkState tidak lagi dalam status FINISHED.
11. Batasan pekerjaan
Terakhir, WorkManager
mendukung Constraints
. Untuk Blur-O-Matic, Anda akan menggunakan batasan sehingga perangkat harus mengisi daya. Ini berarti permintaan kerja hanya akan dijalankan jika perangkat sedang diisi daya.
Langkah 1 - Buat dan tambahkan batasan pengisian daya
Untuk membuat objek Constraints
, gunakan Constraints.Builder
. Lalu, tetapkan batasan yang diinginkan dan tambahkan ke WorkRequest
menggunakan metode, setRequiresCharging()
, seperti yang ditunjukkan di bawah ini:
Impor androidx.work.Constraints
bila diminta.
BlurViewModel.kt
// Put this inside the applyBlur() function, above the save work request.
// Create charging constraint
val constraints = Constraints.Builder()
.setRequiresCharging(true)
.build()
// Add WorkRequest to save the image to the filesystem
val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
.setConstraints(constraints)
.addTag(TAG_OUTPUT)
.build()
continuation = continuation.then(save)
// Actually start the work
continuation.enqueue()
Langkah 2 - Uji dengan emulator atau perangkat
Sekarang Anda dapat menjalankan Blur-O-Matic. Jika menggunakan perangkat, Anda dapat melepaskan atau mencolokkan perangkat. Pada emulator, Anda dapat mengubah status pengisian daya di jendela Extended controls:
Jika perangkat tidak mengisi daya, perangkat akan menangguhkan SaveImageToFileWorker,
yang mengeksekusinya hanya setelah Anda mencolokkan perangkat.
12. Selamat
Selamat! Anda telah menyelesaikan aplikasi Blur-O-Matic dan dalam prosesnya Anda telah mempelajari:
- Menambahkan WorkManager ke Project Anda
- Menjadwalkan
OneTimeWorkRequest
- Parameter Input dan Output
- Membuat rantai pekerjaan
WorkRequest
- Menamai Rantai
WorkRequest
unik - Memberi tag pada
WorkRequest
- Menampilkan
WorkInfo
di UI - Membatalkan
WorkRequest
- Menambahkan batasan ke
WorkRequest
"Kerja" bagus! Untuk melihat status akhir kode dan semua perubahan, lihat:
Atau jika mau, Anda dapat meng-clone codelab WorkManager dari GitHub:
$ git clone https://github.com/googlecodelabs/android-workmanager
WorkManager mendukung banyak hal, lebih dari yang dapat kita bahas dalam codelab ini, termasuk pekerjaan berulang, support library pengujian, permintaan pekerjaan paralel, dan penggabungan input. Untuk mempelajari lebih lanjut, baca dokumentasi WorkManager atau lanjutkan ke codelab WorkManager lanjutan.