학습할 내용
프로젝트에 WorkManager 추가
단순한 작업 예약
입력 및 출력 매개변수
작업 체이닝
고유 작업
UI에 작업 상태 표시
작업 취소
작업 제약 조건
이미지를 블러 처리하여 결과를 파일에 저장하는 앱 Blur-O-Matic을 만들어보자.
스타터 앱 클론
git clone -b start_kotlin https://github.com/googlecodelabs/android-workmanager
gradle
WorkManager
를 사용하려면 Gradle에 추가해야한다. 여기에서 최신 버전을 확인한다.
implementation "androidx.work:work-runtime-ktx:2.7.1"
Worker
백그라운드에서 실행하고자 하는 실제 작업의 코드를 이곳에 작성한다.
WorkRequest
작업 실행을 위한 요청을 나타낸다. WorkRequest를 만드는 과정에서 Worker를 전달(pass) 한다. WorkRequest를 만들 때 Worker를 실행할 시점에 적용되는 Constraints 등을 지정할 수 있다.
WorkManager
실제로 WorkRequest를 예약하고 실행한다. 지정된 제약 조건을 준수하면서 시스템 리소스에 부하를 분산하는 방식으로 WorkRequest를 예약한다.
BlurWorker
클래스를 새로 생성한다.
class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
}
BlurWorker
클래스안에 doWork()
를 재정의한다.
override fun doWork(): Result{
val appContext = applicationContext
makeStatusNotification("Blurring Image", appContext)
return try{
val picture = BitmapFactory.decodeResource(
appContext.resources, R.drawable.test
)
val output = blurBitmap(picture, appContext)
val outputUri = writeBitmapToFile(appContext, output)
makeStatusNotification("Output is $outputUri", appContext)
Result.success()
} catch (throwable: Throwable){
// Timber.e(throwable, "Error applying blur")
Result.failure()
}
}
Bitmap
을 만들어 picture
에 담는다.WorkerUtils
에서 blurBitmap
메서드를 호출하여 블러 처리된 비트맵을 가져와 output
에 담는다.WorkerUtils
에서 writeBitmapToFile
메서드를 호출하여 이 비트맵을 임시 파일에 쓴다. 반환된 URI를 outputUri
변수에 담는다.makeStatusNotification
메서드를 호출하여 URI를 표시하는 알림을 만든다.Result.success()
를 반환BlurViewModel.kt
에 WorkManager를 가져와 workManger
변수에 담는다.
private val workManager = WorkManager.getInstance(application)
WorkRequest를 만들어 WorkManager에게 실행하도록 지시한다.
WorkRequest
유형은 2가지가 있다.
OneTimeWorkRequest
: 한 번만 실행PeriodicWorkRequest
: 일정 주기로 반복 실행적용 버튼을 클릭했을 때 한 번만 블러 처리를 적용하도록 하려면 OneTimeWorkRequest
를 사요할 수 있다.
BlurViewModel.kt
에 applyBlur()
메서드를 추가한다.
Internal fun applyBlur(blurLevel: Int) {
workManager.enqueue(OneTimeWorkRequest.from(BlurWorker::class.java))
}
사용자가 직접 이미지를 블러 처리하려면 선택한 이미지의 URI를 WorkRequest
의 입력으로 제공해야한다.
입력과 출력은 Data
객체를 통해 안팎으로 전달된다.
Data
객체는 키-값 쌍의 경량 컨테이너로 WorkRequest
의 안팎으로 소량의 데이터를 저장하는데 사용한다.
private fun createInputDataForUri(): Data {
val builder = Data.Builder()
imageUri?.let {
builder.putString(KEY_IMAGE_URI, imageUri.toString())
}
return builder.build()
}
Data.Builder
object를 만든다.imageUri
가 null이 아닌 URI
이면 putString()
을 사용하여 Data 객체에 추가한다. 키와 값을 사용한다.build()
를 호출해 Data
객체를 만들어 반환한다.applyBlure()
메서드에 다음 코드를 추가한다.
val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(createInputDataForUri())
.build()
OneTimeWorkRequest.Builder
를 만든다.createInputDataForUri
의 결과를 전달하고, build()
한다.workManager.enqueue(blurRequest)
를 통해 요청을 큐에 추가한다.
전달한 URI를 Data
객체에서 가져오도록 BlurWorker
의 doWork()
메서드를 수정한다.
단일 작업을 처리하는 방법을 배웠다. WorkManager
를 사용하면 순서대로 실행되거나 동시에 실행되는 별도의 WorkerRequest
를 만들 수 있다. 이 과정에서는 작업 체인을 사용한다.
기존 Worker는 블러처리를 담당하는 Worker이다. 임시 파일을 정리하는 Worker와 이미지를 영구적으로 저장하는 Worker를 만들어보자.
먼저 worker 패키지
에 Worker
를 확장하는 클래스 CleanupWorker
와 SaveImageToFileWorker
를 생성한다.
BlurWorker
와 유사하게 클래스를 생성하고, doWork()
를 재정의한다.
CleanupWorker
는 입력을 받거나 출력을 내보내는 기능이 없기 때문에 임시 파일이 있으면 항상 삭제한다.
SaveImageToFileWorker
도 마찬가지로 파일을 생성하고, doWork()
를 재정의한다.
WorkRequest
체인을 실행하도록 BlurViewModel
의 applyBlur
메서드를 수정한다.
이전에 작성한 blurRequest
와 workManager.enqueue()
를 호출하는 코드를 제거하고 다음과 같이 작성한다.
// add workRequest to cleanup
var continuation = workManager
.beginWith(OneTimeWorkRequest
.from(CleanupWorker::class.java))
// add workRequest to blur
val blurRequest = OneTimeWorkRequest.Builder(BlurWorker::class.java)
.setInputData(createInputDataForUri())
.build()
continuation = continuation.then(blurRequest)
// add workRequest to save image
val save = OneTimeWorkRequest.Builder(SaveImageToFileWorker::class.java).build()
continuation = continuation.then(save)
// Actually start the work
continuation.enqueue()
workManager.beginWith()
를 호출하면 WorkRequest
체인을 정의하는 WorkContinuation
이 반환된다. then()
메서드를 호출하여 이 작업 요청 체인에 추가할 수 있다.
blurViewModel
의 blurRequest
를 blurLevel에 따라 적용되도록 수정한다.
작업 체인을 한 번에 하나씩만 실행해야 하는 경우 beginWith
대신 beginUniqueWork
를 사용하고 고유한 이름을 제공한다.
사진을 한 번에 한 장만 블러 처리하도록 코드를 수정한다.
var continuation = workManager
.beginUniqueWork(
IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker::class.java)
)
IMAGE_MANIPULATION_WORK_NAME
는 키ExistingWorkPolicy
옵션은 REPLACE, KEEP, APPEND가 있는데 여기서는 현재 이미지가 완료되기 전에 다른 이미지를 블러 처리하려는 경우 현재 이미지가 중지되고 새 이미지가 블러 처리되도록 REPLACE
를 사용한다.addTag
를 추가해 태그를 지정한다.
val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
.addTag(TAG_OUTPUT)
.build()
LiveData<List<WorkInfo>>
인 outputWorkInfos
라는 새로운 변수를 선언하고,
BlurViewModel
에서 WorkManager.getWorkInfosByTagLiveData
를 사용하여 WorkInfo
를 가져오는 init 블록을 추가한다.
WorkInfo
의 LiveData
를 가져왔으며 이제 BlurActivity
에서 관찰할 수 있다.
WorkInfo
가 null이거나 비어있으면 돌아간다.TAG_OUTPUT
으로 태그된 WorkInfo
를 가져온다.workInfo.state().isFinished()
로 작업이 완료 상태인지 확인할 수 있다.Cancel Work 버튼을 눌렀을 때 작업이 취소되도록 한다. 이때 고유한 체인 이름으로 작업을 취소하는 것이 좋다.
internal fun cancelWork() {
workManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME)
}
cancelButton
에 클릭 리스너를 연결해 cancelWork()
를 호출하도록 한다.
WorkManager
는 Constraints
를 지원한다. 예를 들어 이미지를 저장할 때는 기기를 충전해야 한다는 제약 조건을 사용할 수 있다.