1. Sebelum memulai
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.
Prasyarat
- Pengetahuan tentang StateFlow dan ViewModel. Jika Anda baru mengenal class ini, lihat ViewModel dan Status dalam Codelab Compose (khusus untuk ViewModel dan Status) atau Membaca dan mengupdate data dengan Codelab Room (khususnya untuk Flow dan StateFlow).
- Pengetahuan tentang repositori dan injeksi dependensi. Untuk pengingat, lihat Menambahkan repositori dan DI Manual.
- Dapat menerapkan coroutine dalam aplikasi Anda.
Yang akan Anda pelajari
- Cara menambahkan WorkManager ke project Anda.
- Cara menjadwalkan tugas sederhana.
- Cara mengonfigurasi parameter input dan output untuk pekerja.
- Cara membuat rantai pekerja.
Yang akan Anda lakukan
- Memodifikasi aplikasi awal untuk menggunakan WorkManager.
- Mengimplementasikan permintaan pekerjaan untuk memburamkan gambar.
- Mengimplementasikan grup pekerjaan serial dengan membuat rantai pekerjaan.
- Meneruskan data ke dan keluar dari pekerjaan yang dijadwalkan.
Yang akan Anda butuhkan
- Versi stabil terbaru Android Studio
- Koneksi internet
2. Ringkasan aplikasi
Saat ini, smartphone hampir terlalu sempurna dalam mengambil gambar. Tidak ada lagi hari-hari ketika seorang fotografer mengambil gambar yang cukup buram dari sesuatu yang misterius.
Dalam codelab ini, Anda akan menggunakan Blur-O-Matic, aplikasi untuk memburamkan foto dan menyimpan hasilnya ke file. Apakah itu monster Loch Ness atau kapal selam mainan? Dengan Blur-O-Matic, tidak akan ada yang tahu.
Layar memiliki tombol pilihan yang dapat Anda gunakan untuk memilih tingkat blur gambar. Mengklik tombol Start akan memberikan efek blur pada gambar dan menyimpan gambar.
Saat ini, aplikasi tidak menerapkan blur atau menyimpan gambar akhir.
Codelab ini berfokus pada menambahkan WorkManager ke aplikasi, membuat pekerja untuk membersihkan file sementara yang dibuat ketika memburamkan gambar, memburamkan gambar, dan menyimpan salinan akhir gambar yang dapat Anda lihat saat mengklik tombol See File. Anda juga akan mempelajari cara memantau status pekerjaan latar belakang dan mengupdate UI aplikasi sebagaimana mestinya.
3. Menjelajahi aplikasi awal Blur-O-Matic
Mendapatkan kode awal
Untuk memulai, download kode awal:
Atau, Anda dapat membuat clone repositori GitHub untuk kode tersebut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-workmanager.git $ cd basic-android-kotlin-compose-training-workmanager $ git checkout starter
Anda dapat menjelajahi kode untuk aplikasi Blur-o-matic di repositori GitHub ini.
Menjalankan kode awal
Untuk memahami kode awal, selesaikan langkah-langkah berikut:
- Buka project dengan kode awal di Android Studio.
- Jalankan aplikasi di perangkat Android atau di emulator.
Layar memiliki tombol pilihan yang memungkinkan Anda memilih tingkat blur gambar. Saat Anda mengklik tombol Start, aplikasi akan memberikan efek blur pada gambar dan menyimpan gambar.
Saat ini, aplikasi tidak menerapkan tingkat blur apa pun saat Anda mengklik tombol Start.
Panduan kode awal
Dalam tugas ini, Anda akan memahami struktur project. Daftar berikut memberikan panduan terkait file dan folder penting dalam project.
WorkerUtils
: Metode praktis yang nantinya Anda gunakan untuk menampilkanNotifications
dan kode untuk menyimpan bitmap ke file.BlurViewModel
: Model tampilan ini menyimpan status aplikasi dan berinteraksi dengan repositori.WorkManagerBluromaticRepository
: Class tempat Anda memulai pekerjaan latar belakang dengan WorkManager.Constants
: Class statis dengan beberapa konstanta yang Anda gunakan selama codelab.BluromaticScreen
: Berisi fungsi composable untuk UI dan berinteraksi denganBlurViewModel
. Fungsi composable menampilkan gambar dan menyertakan tombol pilihan untuk memilih tingkat blur yang diinginkan.
4. Apa yang dimaksud dengan WorkManager?
WorkManager adalah bagian dari Android Jetpack dan Komponen Arsitektur untuk tugas latar belakang yang memerlukan kombinasi eksekusi oportunistik dan terjamin. Eksekusi oportunistik berarti WorkManager 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. Beberapa manfaatnya antara lain:
- Dukungan untuk tugas satu kali atau berkala asinkron.
- Dukungan untuk batasan, seperti kondisi jaringan, ruang penyimpanan, dan status pengisian daya.
- Perantaian permintaan pekerjaan yang kompleks, seperti 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 untuk menampilkan status permintaan tugas dengan mudah di UI aplikasi.
5. Kapan harus menggunakan WorkManager
Library WorkManager adalah pilihan tepat untuk tugas yang perlu Anda selesaikan. Menjalankan tugas ini tidak bergantung pada aplikasi yang terus berjalan setelah pekerjaan diantrekan. Tugas akan tetap berjalan meskipun aplikasi ditutup atau pengguna kembali ke layar utama.
Beberapa contoh tugas yang menggunakan WorkManager dengan baik:
- Membuat kueri untuk berita terbaru secara berkala.
- Menerapkan filter ke gambar, lalu menyimpan gambar.
- Menyinkronkan data lokal dengan jaringan secara berkala.
WorkManager adalah salah satu opsi untuk menjalankan tugas dari thread utama, tetapi tidak dapat digunakan untuk menjalankan semua jenis tugas dari thread utama. Coroutine adalah opsi lain yang dibahas dalam codelab sebelumnya.
Untuk detail selengkapnya tentang kapan harus menggunakan WorkManager, lihat Panduan pekerjaan latar belakang.
6. Menambahkan WorkManager ke aplikasi Anda
WorkManager
memerlukan dependensi gradle berikut. Dependensi ini sudah disertakan dalam file build:
app/build.gradle.kts
dependencies {
// WorkManager dependency
implementation("androidx.work:work-runtime-ktx:2.8.1")
}
Anda harus menggunakan versi rilis stabil work-runtime-ktx
terbaru di aplikasi Anda.
Jika Anda mengubah versi, pastikan untuk mengklik Sync Now guna menyinkronkan project Anda dengan file gradle yang telah diupdate.
7. Dasar-dasar WorkManager
Ada beberapa class WorkManager yang perlu Anda ketahui:
Worker
/CoroutineWorker
: Worker adalah class yang melakukan pekerjaan secara sinkron di thread latar belakang. Karena kita tertarik dengan pekerjaan asinkron, kita dapat menggunakan CoroutineWorker, yang memiliki interoperabilitas dengan Coroutine Kotlin. Dalam aplikasi ini, Anda memperluas dari class CoroutineWorker dan mengganti metodedoWork()
. Metode ini adalah tempat Anda menempatkan kode untuk pekerjaan yang sebenarnya, yang ingin Anda lakukan di latar belakang.WorkRequest
: Class ini mewakili permintaan untuk melakukan beberapa pekerjaan.WorkRequest
adalah tempat Anda menentukan apakah pekerja perlu dijalankan satu kali atau secara berkala. Batasan juga dapat ditempatkan diWorkRequest
yang mengharuskan kondisi tertentu terpenuhi sebelum pekerjaan berjalan. Salah satu contohnya adalah perangkat sedang diisi daya sebelum memulai pekerjaan yang diminta. Anda meneruskanCoroutineWorker
sebagai bagian dari pembuatanWorkRequest
.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 menentukan class BlurWorker
baru, yang berisi kode untuk memberikan efek blur pada gambar. Saat Anda mengklik tombol Start, WorkManager akan membuat dan mengantrekan objek WorkRequest
.
8. Membuat BlurWorker
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 memburamkan gambar.
- Klik kanan paket
com.example.bluromatic.workers
di panel project Android Anda, lalu pilih New -> Kotlin Class/File. - Beri nama class Kotlin baru
BlurWorker
. Perluas dariCoroutineWorker
dengan parameter konstruktor yang diperlukan.
workers/BlurWorker.kt
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import android.content.Context
class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
}
Class BlurWorker
memperluas class CoroutineWorker
, bukan class Worker
yang lebih umum. Implementasi class CoroutineWorker
dari doWork()
adalah fungsi penangguhan yang memungkinkannya menjalankan kode asinkron yang tidak dapat dilakukan Worker
. Seperti dijelaskan dalam panduan Threading di WorkManager, "CoroutineWorker adalah penerapan yang direkomendasikan untuk pengguna Kotlin".
Pada tahap ini, Android Studio menggambar garis goyang merah di bawah class BlurWorker
yang menunjukkan error.
Jika Anda menempatkan kursor di atas teks class BlurWorker
, IDE akan menampilkan pop-up dengan informasi tambahan terkait error tersebut.
Pesan error menunjukkan bahwa Anda tidak mengganti metode doWork()
sebagaimana diperlukan.
Pada metode doWork()
, tulis kode untuk memberikan efek blur pada gambar cupcake yang ditampilkan.
Ikuti langkah-langkah berikut untuk memperbaiki error dan menerapkan metode doWork()
:
- Tempatkan kursor di dalam kode class dengan mengklik teks "BlurWorker".
- Dari menu Android Studio, pilih Code > Override Methods...
- Dari pop-up Override Members, pilih
doWork()
. - Klik OK.
- Tepat sebelum deklarasi class, buat variabel bernama
TAG
dan tetapkan nilaiBlurWorker
. Perhatikan bahwa variabel ini tidak secara khusus terkait dengan metodedoWork()
, tetapi Anda akan menggunakannya nanti dalam panggilan keLog()
.
workers/BlurWorker.kt
private const val TAG = "BlurWorker"
class BlurWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
...
- Untuk melihat dengan lebih baik kapan tugas dieksekusi, Anda perlu menggunakan fungsi
makeStatusNotification()
WorkerUtil
. Fungsi ini memungkinkan Anda menampilkan banner notifikasi dengan mudah di bagian atas layar.
Dalam metode doWork()
, gunakan fungsi makeStatusNotification()
untuk menampilkan notifikasi status dan memberi tahu pengguna bahwa worker blur telah mulai membuat gambar menjadi blur.
workers/BlurWorker.kt
import com.example.bluromatic.R
...
override suspend fun doWork(): Result {
makeStatusNotification(
applicationContext.resources.getString(R.string.blurring_image),
applicationContext
)
...
- Tambahkan blok kode
return try...catch
, tempat pekerjaan pemburaman gambar yang sebenarnya dilakukan.
workers/BlurWorker.kt
...
makeStatusNotification(
applicationContext.resources.getString(R.string.blurring_image),
applicationContext
)
return try {
} catch (throwable: Throwable) {
}
...
- Di blok
try
, tambahkan panggilan keResult.success()
. - Di blok
catch
, tambahkan panggilan keResult.failure()
.
workers/BlurWorker.kt
...
makeStatusNotification(
applicationContext.resources.getString(R.string.blurring_image),
applicationContext
)
return try {
Result.success()
} catch (throwable: Throwable) {
Result.failure()
}
...
- Di blok
try
, buat variabel baru bernamapicture
, lalu isi dengan bitmap yang ditampilkan dari memanggil metodeBitmapFactory.decodeResource
()
dan meneruskan paket resource aplikasi serta ID resource gambar cupcake.
workers/BlurWorker.kt
...
return try {
val picture = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.android_cupcake
)
Result.success()
...
- Buramkan bitmap dengan memanggil fungsi
blurBitmap()
, lalu teruskan variabelpicture
dan nilai1
(satu) untuk parameterblurLevel
. - Simpan hasilnya dalam variabel baru bernama
output
.
workers/BlurWorker.kt
...
val picture = BitmapFactory.decodeResource(
applicationContext.resources,
R.drawable.android_cupcake
)
val output = blurBitmap(picture, 1)
Result.success()
...
- Buat variabel baru
outputUri
dan isi dengan panggilan ke fungsiwriteBitmapToFile()
. - Pada panggilan ke
writeBitmapToFile()
, teruskan konteks aplikasi dan variabeloutput
sebagai argumen.
workers/BlurWorker.kt
...
val output = blurBitmap(picture, 1)
// Write bitmap to a temp file
val outputUri = writeBitmapToFile(applicationContext, output)
Result.success()
...
- Tambahkan kode untuk menampilkan pesan notifikasi kepada pengguna yang berisi variabel
outputUri
.
workers/BlurWorker.kt
...
val outputUri = writeBitmapToFile(applicationContext, output)
makeStatusNotification(
"Output is $outputUri",
applicationContext
)
Result.success()
...
- Di blok
catch
, catat pesan error ke dalam log untuk menunjukkan adanya error saat mencoba memburamkan gambar. Panggilan keLog.e()
meneruskan variabelTAG
yang telah ditentukan sebelumnya, pesan yang sesuai, dan pengecualian yang ditampilkan.
workers/BlurWorker.kt
...
} catch (throwable: Throwable) {
Log.e(
TAG,
applicationContext.resources.getString(R.string.error_applying_blur),
throwable
)
Result.failure()
}
...
CoroutineWorker,
berjalan sebagai Dispatchers.Default
secara default, tetapi dapat diubah dengan memanggil withContext()
dan meneruskan dispatcher yang diinginkan.
- Buat blok
withContext()
. - Dalam panggilan ke
withContext()
, teruskanDispatchers.IO
sehingga fungsi lambda berjalan di kumpulan thread khusus untuk operasi IO yang berpotensi memblokir. - Pindahkan kode
return try...catch
yang telah ditulis sebelumnya ke blok ini.
...
return withContext(Dispatchers.IO) {
return try {
// ...
} catch (throwable: Throwable) {
// ...
}
}
...
Android Studio menampilkan error berikut karena Anda tidak dapat memanggil return
dari dalam fungsi lambda.
Error ini dapat diperbaiki dengan menambahkan label seperti yang ditunjukkan di pop-up.
...
//return try {
return@withContext try {
...
Karena Worker ini berjalan sangat cepat, sebaiknya tambahkan penundaan dalam kode untuk mengemulasikan tugas yang berjalan lebih lambat.
- Di dalam lambda
withContext()
, tambahkan panggilan ke fungsi utilitasdelay()
dan teruskan konstantaDELAY_TIME_MILLIS
. Panggilan ini hanya untuk codelab guna memberikan penundaan antar-pesan notifikasi.
import com.example.bluromatic.DELAY_TIME_MILLIS
import kotlinx.coroutines.delay
...
return withContext(Dispatchers.IO) {
// This is an utility function added to emulate slower work.
delay(DELAY_TIME_MILLIS)
val picture = BitmapFactory.decodeResource(
...
9. Mengupdate WorkManagerBluromaticRepository
Repositori menangani semua interaksi dengan WorkManager. Struktur ini mematuhi prinsip desain pemisahan fokus dan merupakan pola arsitektur Android yang direkomendasikan.
- Di file
data/WorkManagerBluromaticRepository.kt
, dalam classWorkManagerBluromaticRepository
, buat variabel pribadi bernamaworkManager
dan simpan instanceWorkManager
di dalamnya dengan memanggilWorkManager.getInstance(context)
.
data/WorkManagerBluromaticRepository.kt
import androidx.work.WorkManager
...
class WorkManagerBluromaticRepository(context: Context) : BluromaticRepository {
// New code
private val workManager = WorkManager.getInstance(context)
...
Membuat dan mengantrekan WorkRequest di WorkManager
Baik, saatnya membuat WorkRequest
dan memberi tahu WorkManager untuk menjalankannya. Ada dua jenis WorkRequest
:
OneTimeWorkRequest
:WorkRequest
yang hanya dieksekusi satu kali.PeriodicWorkRequest
:WorkRequest
yang dieksekusi berulang kali pada satu siklus.
Anda hanya ingin gambar menjadi blur satu kali saat tombol Start diklik.
Tugas ini terjadi di metode applyBlur()
, yang Anda panggil saat mengklik tombol Start.
Langkah-langkah berikut diselesaikan dalam metode applyBlur()
.
- Isi variabel baru bernama
blurBuilder
dengan membuatOneTimeWorkRequest
untuk pekerja pemburaman dan memanggil fungsi ekstensiOneTimeWorkRequestBuilder
dari WorkManager KTX.
data/WorkManagerBluromaticRepository.kt
import com.example.bluromatic.workers.BlurWorker
import androidx.work.OneTimeWorkRequestBuilder
...
override fun applyBlur(blurLevel: Int) {
// Create WorkRequest to blur the image
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
}
- Mulai pekerjaan dengan memanggil metode
enqueue()
pada objekworkManager
Anda.
data/WorkManagerBluromaticRepository.kt
import com.example.bluromatic.workers.BlurWorker
import androidx.work.OneTimeWorkRequestBuilder
...
override fun applyBlur(blurLevel: Int) {
// Create WorkRequest to blur the image
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
// Start the work
workManager.enqueue(blurBuilder.build())
}
- Jalankan aplikasi dan lihat notifikasi saat Anda mengklik tombol Start.
Pada saat ini, gambar menjadi blur dalam tingkat yang sama, terlepas dari opsi yang Anda pilih. Pada langkah-langkah berikutnya, jumlah pemburaman akan berubah berdasarkan opsi yang dipilih.
Untuk mengonfirmasi bahwa gambar berhasil menjadi blur, Anda dapat membuka Device Explorer di Android Studio:
Lalu buka data > data > com.example.bluromatic > files > blur_filter_outputs > <URI> dan pastikan gambar cupcake telah blur:
10. Data input dan data output
Pemburaman aset gambar di 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 menampilkan hasil yang diburamkan.
Untuk melakukannya, berikan URI gambar cupcake yang ditampilkan sebagai input ke WorkRequest
, lalu gunakan output WorkRequest
kami untuk menampilkan gambar akhir yang diburamkan.
Input dan output diteruskan ke dan dikeluarkan dari pekerja melalui objek Data
. Objek Data
adalah container ringan untuk key-value pair. Objek itu dimaksudkan untuk menyimpan sejumlah kecil data yang mungkin masuk ke dan keluar dari pekerja dari WorkRequest
.
Pada langkah berikutnya, Anda akan meneruskan URI ke BlurWorker
dengan membuat objek data input.
Membuat objek data input
- Di file
data/WorkManagerBluromaticRepository.kt
, di dalam classWorkManagerBluromaticRepository
, buat variabel pribadi baru bernamaimageUri
. - Isi variabel dengan URI gambar dengan memanggil metode konteks
getImageUri()
.
data/WorkManagerBluromaticRepository.kt
import com.example.bluromatic.getImageUri
...
class WorkManagerBluromaticRepository(context: Context) : BluromaticRepository {
private var imageUri: Uri = context.getImageUri() // <- Add this
private val workManager = WorkManager.getInstance(context)
...
Kode aplikasi berisi fungsi bantuan createInputDataForWorkRequest()
untuk membuat objek data input.
data/WorkManagerBluromaticRepository.kt
// For reference - already exists in the app
private fun createInputDataForWorkRequest(blurLevel: Int, imageUri: Uri): Data {
val builder = Data.Builder()
builder.putString(KEY_IMAGE_URI, imageUri.toString()).putInt(BLUR_LEVEL, blurLevel)
return builder.build()
}
Pertama, fungsi bantuan membuat objek Data.Builder
. Selanjutnya, kode ini akan menempatkan imageUri
dan blurLevel
ke dalamnya sebagai key-value pair. Objek Data kemudian dibuat dan ditampilkan saat memanggil return builder.build()
.
- Guna menetapkan objek data input untuk WorkRequest, Anda perlu memanggil metode
blurBuilder.setInputData()
. Anda dapat membuat dan meneruskan objek data dalam satu langkah dengan memanggil fungsi bantuancreateInputDataForWorkRequest()
sebagai argumen. Untuk panggilan ke fungsicreateInputDataForWorkRequest()
, teruskan variabelblurLevel
dan variabelimageUri
.
data/WorkManagerBluromaticRepository.kt
override fun applyBlur(blurLevel: Int) {
// Create WorkRequest to blur the image
val blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
// New code for input data object
blurBuilder.setInputData(createInputDataForWorkRequest(blurLevel, imageUri))
workManager.enqueue(blurBuilder.build())
}
Mengakses objek data input
Sekarang, mari kita update metode doWork()
di class BlurWorker
untuk mendapatkan URI dan tingkat blur yang diteruskan oleh objek data input. Jika nilai untuk blurLevel
tidak diberikan, nilai tersebut akan ditetapkan secara default ke 1
.
Dalam metode doWork()
:
- Buat variabel baru bernama
resourceUri
dan isi variabel dengan memanggilinputData.getString()
dan meneruskanKEY_IMAGE_URI
yang konstan, yang digunakan sebagai kunci saat membuat objek data input.
val resourceUri = inputData.getString(KEY_IMAGE_URI)
- Buat variabel baru bernama
blurLevel
. Isi variabel dengan memanggilinputData.getInt()
dan meneruskanBLUR_LEVEL
yang konstan, yang digunakan sebagai kunci saat membuat objek data input. Jika key-value pair ini belum dibuat, berikan nilai default1
(satu).
workers/BlurWorker.kt
import com.example.bluromatic.KEY_BLUR_LEVEL
import com.example.bluromatic.KEY_IMAGE_URI
...
override fun doWork(): Result {
// ADD THESE LINES
val resourceUri = inputData.getString(KEY_IMAGE_URI)
val blurLevel = inputData.getInt(KEY_BLUR_LEVEL, 1)
// ... rest of doWork()
}
Dengan URI, sekarang mari kita buramkan gambar cupcake di layar.
- Pastikan variabel
resourceUri
telah diisi. Jika tidak terisi, kode Anda akan menampilkan pengecualian. Kode berikutnya menggunakan pernyataanrequire()
yang menampilkanIllegalArgumentException
jika argumen pertama bernilai false.
workers/BlurWorker.kt
return@withContext try {
// NEW code
require(!resourceUri.isNullOrBlank()) {
val errorMessage =
applicationContext.resources.getString(R.string.invalid_input_uri)
Log.e(TAG, errorMessage)
errorMessage
}
Karena sumber gambar diteruskan sebagai URI, kita memerlukan objek ContentResolver untuk membaca konten yang ditunjuk oleh URI.
- Tambahkan objek
contentResolver
ke nilaiapplicationContext
.
workers/BlurWorker.kt
...
require(!resourceUri.isNullOrBlank()) {
// ...
}
val resolver = applicationContext.contentResolver
...
- Karena sumber gambar sekarang diteruskan dalam URI, gunakan
BitmapFactory.decodeStream()
, bukanBitmapFactory.decodeResource()
, untuk membuat objek Bitmap.
workers/BlurWorker.kt
import android.net.Uri
...
// val picture = BitmapFactory.decodeResource(
// applicationContext.resources,
// R.drawable.android_cupcake
// )
val resolver = applicationContext.contentResolver
val picture = BitmapFactory.decodeStream(
resolver.openInputStream(Uri.parse(resourceUri))
)
- Teruskan variabel
blurLevel
dalam panggilan ke fungsiblurBitmap()
.
workers/BlurWorker.kt
//val output = blurBitmap(picture, 1)
val output = blurBitmap(picture, blurLevel)
Membuat objek data output
Sekarang Anda sudah selesai dengan Pekerja ini dan dapat menampilkan URI output sebagai objek data output di Result.success()
. Menyediakan URI output sebagai objek data output akan membuatnya mudah diakses oleh pekerja lain untuk operasi lebih lanjut. Pendekatan ini berguna di bagian berikutnya saat Anda membuat rantai pekerja.
Untuk melakukannya, selesaikan langkah-langkah berikut:
- Sebelum kode
Result.success()
, buat variabel baru bernamaoutputData
. - Isi variabel ini dengan memanggil fungsi
workDataOf()
, lalu gunakanKEY_IMAGE_URI
konstan untuk kunci dan variabeloutputUri
sebagai nilai. FungsiworkDataOf()
membuat objek Data dari pasangan kunci dan nilai yang diteruskan.
workers/BlurWorker.kt
import androidx.work.workDataOf
// ...
val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())
- Update kode
Result.success()
untuk mengambil objek Data baru ini sebagai argumen.
workers/BlurWorker.kt
//Result.success()
Result.success(outputData)
- Hapus kode yang menampilkan notifikasi karena tidak diperlukan lagi setelah objek Data output sekarang menggunakan URI.
workers/BlurWorker.kt
// REMOVE the following notification code
//makeStatusNotification(
// "Output is $outputUri",
// applicationContext
//)
Menjalankan aplikasi
Pada tahap ini, saat menjalankan aplikasi, Anda dapat mengharapkannya dikompilasi. Anda dapat melihat gambar yang menjadi blur melalui Device Explorer, tetapi belum di layar.
Perhatikan bahwa Anda mungkin perlu memilih Synchronize untuk melihat gambar Anda:
Bagus sekali! Anda berhasil memburamkan gambar input menggunakan WorkManager
.
11. Membuat rantai Pekerjaan Anda
Saat ini, Anda sedang melakukan satu tugas pekerjaan: memburamkan gambar. Tugas ini adalah langkah pertama yang bagus, tetapi aplikasi masih belum memiliki beberapa fungsi inti:
- Aplikasi tidak membersihkan file sementara.
- Aplikasi tidak benar-benar menyimpan gambar ke file permanen.
- Aplikasi selalu memburamkan gambar dalam jumlah yang sama.
Anda dapat menggunakan rantai pekerjaan WorkManager untuk menambahkan fungsi ini. WorkManager memungkinkan Anda membuat WorkerRequest
terpisah yang berjalan secara berurutan atau paralel.
Di bagian ini, Anda akan membuat rantai pekerjaan yang terlihat seperti berikut:
Kotak tersebut mewakili WorkRequest
.
Fitur lain dari perantaian adalah kemampuannya untuk menerima input dan menghasilkan output. Output satu WorkRequest
menjadi input WorkRequest
berikutnya dalam rantai.
Anda sudah memiliki CoroutineWorker
untuk memburamkan gambar, tetapi Anda juga memerlukan CoroutineWorker
untuk membersihkan file sementara dan CoroutineWorker
untuk menyimpan gambar secara permanen.
Membuat CleanupWorker
CleanupWorker
menghapus file sementara, jika ada.
- Klik kanan paket
com.example.bluromatic.workers
di panel project Android Anda, lalu pilih New -> Kotlin Class/File. - Beri nama class Kotlin baru
CleanupWorker
. - Salin kode untuk CleanupWorker.kt, seperti ditunjukkan dalam contoh kode berikut.
Karena manipulasi file berada di luar cakupan codelab ini, Anda dapat menyalin kode berikut untuk CleanupWorker
.
workers/CleanupWorker.kt
package com.example.bluromatic.workers
import android.content.Context
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.example.bluromatic.DELAY_TIME_MILLIS
import com.example.bluromatic.OUTPUT_PATH
import com.example.bluromatic.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import java.io.File
/**
* Cleans up temporary files generated during blurring process
*/
private const val TAG = "CleanupWorker"
class CleanupWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
override suspend 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(
applicationContext.resources.getString(R.string.cleaning_up_files),
applicationContext
)
return withContext(Dispatchers.IO) {
delay(DELAY_TIME_MILLIS)
return@withContext 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) {
Log.e(
TAG,
applicationContext.resources.getString(R.string.error_cleaning_file),
exception
)
Result.failure()
}
}
}
}
Membuat SaveImageToFileWorker
Class SaveImageToFileWorker
menyimpan file sementara ke file permanen.
SaveImageToFileWorker
mengambil input dan output. Inputnya adalah String
dari URI gambar yang diburamkan sementara, yang disimpan dengan KEY_IMAGE_URI
kunci. Output-nya adalah String
dari URI gambar yang diburamkan dan disimpan dengan kunci KEY_IMAGE_URI
.
- Klik kanan paket
com.example.bluromatic.workers
di panel project Android Anda, lalu pilih New -> Kotlin Class/File. - Beri nama class Kotlin baru
SaveImageToFileWorker
. - Salin kode SaveImageToFileWorker.kt seperti ditunjukkan dalam kode contoh berikut.
Karena manipulasi file berada di luar cakupan codelab ini, Anda dapat menyalin kode berikut untuk SaveImageToFileWorker
. Dalam kode yang diberikan, perhatikan bagaimana nilai resourceUri
dan output
diambil dan disimpan dengan kunci KEY_IMAGE_URI
. Proses ini sangat mirip dengan kode yang Anda tulis sebelumnya untuk objek data input dan output.
workers/SaveImageToFileWorker.kt
package com.example.bluromatic.workers
import android.content.Context
import android.graphics.BitmapFactory
import android.net.Uri
import android.provider.MediaStore
import android.util.Log
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
import com.example.bluromatic.DELAY_TIME_MILLIS
import com.example.bluromatic.KEY_IMAGE_URI
import com.example.bluromatic.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat
import java.util.Locale
import java.util.Date
/**
* Saves the image to a permanent file
*/
private const val TAG = "SaveImageToFileWorker"
class SaveImageToFileWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
private val title = "Blurred Image"
private val dateFormatter = SimpleDateFormat(
"yyyy.MM.dd 'at' HH:mm:ss z",
Locale.getDefault()
)
override suspend 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(
applicationContext.resources.getString(R.string.saving_image),
applicationContext
)
return withContext(Dispatchers.IO) {
delay(DELAY_TIME_MILLIS)
val resolver = applicationContext.contentResolver
return@withContext 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,
applicationContext.resources.getString(R.string.writing_to_mediaStore_failed)
)
Result.failure()
}
} catch (exception: Exception) {
Log.e(
TAG,
applicationContext.resources.getString(R.string.error_saving_image),
exception
)
Result.failure()
}
}
}
}
Membuat rantai pekerjaan
Saat ini, kode ini hanya membuat dan menjalankan satu WorkRequest
.
Dalam langkah ini, Anda akan memodifikasi kode untuk membuat dan mengeksekusi rantai WorkRequest, bukan hanya satu permintaan pemburaman gambar.
Dalam rantai WorkRequest, permintaan pekerjaan pertama Anda adalah membersihkan file sementara.
- Jangan panggil
OneTimeWorkRequestBuilder
, tetapi panggilworkManager.beginWith()
.
Memanggil metode beginWith()
akan menampilkan objek WorkContinuation
dan membuat titik awal untuk rantai WorkRequest
dengan permintaan pekerjaan pertama di rantai tersebut.
data/WorkManagerBluromaticRepository.kt
import androidx.work.OneTimeWorkRequest
import com.example.bluromatic.workers.CleanupWorker
// ...
override 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 blurBuilder = OneTimeWorkRequestBuilder<BlurWorker>()
...
Anda dapat menambahkan ke rantai permintaan pekerjaan ini dengan memanggil metode then()
dan meneruskan objek WorkRequest
.
- Hapus panggilan ke
workManager.enqueue(blurBuilder.build())
, yang hanya mengantrekan satu permintaan pekerjaan. - Tambahkan permintaan pekerjaan berikutnya ke rantai dengan memanggil metode
.then()
.
data/WorkManagerBluromaticRepository.kt
...
//workManager.enqueue(blurBuilder.build())
// Add the blur work request to the chain
continuation = continuation.then(blurBuilder.build())
...
- Buat permintaan pekerjaan untuk menyimpan gambar dan menambahkannya ke rantai.
data/WorkManagerBluromaticRepository.kt
import com.example.bluromatic.workers.SaveImageToFileWorker
...
continuation = continuation.then(blurBuilder.build())
// Add WorkRequest to save the image to the filesystem
val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
.build()
continuation = continuation.then(save)
...
- Untuk memulai pekerjaan, panggil metode
enqueue()
pada objek kelanjutan.
data/WorkManagerBluromaticRepository.kt
...
continuation = continuation.then(save)
// Start the work
continuation.enqueue()
...
Kode ini menghasilkan dan menjalankan rantai WorkRequest berikut: CleanupWorker
WorkRequest
, diikuti dengan BlurWorker
WorkRequest
, diikuti dengan SaveImageToFileWorker
WorkRequest
.
- Jalankan aplikasi.
Anda sekarang dapat mengklik Start dan melihat notifikasi saat worker yang berbeda dieksekusi. Anda masih dapat melihat gambar yang menjadi blur di Device Explorer, dan di bagian selanjutnya, Anda akan menambahkan tombol tambahan agar pengguna dapat melihat gambar yang menjadi blur di perangkat.
Dalam screenshot berikut, perhatikan bahwa pesan notifikasi menampilkan worker mana yang sedang berjalan.
Perhatikan bahwa folder output berisi beberapa gambar yang menjadi blur—gambar yang berada di tengah tahap pemberian efek blur dan gambar akhir yang menampilkan gambar dengan tingkat blur yang Anda pilih.
Hebat! Sekarang, Anda dapat membersihkan file sementara, memburamkan gambar, dan menyimpannya.
12. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah berikut:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-workmanager.git $ cd basic-android-kotlin-compose-training-workmanager $ git checkout intermediate
Atau, Anda dapat mendownload repositori sebagai file ZIP, lalu mengekstraknya, dan membukanya di Android Studio.
Jika Anda ingin melihat kode solusi untuk codelab ini, lihat kode tersebut di GitHub.
13. Kesimpulan
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
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, buka dokumentasi Menjadwalkan tugas dengan WorkManager.