[Android] WorkManager - 2
참조: https://developer.android.com/codelabs/android-workmanager#3
WorkManager를 사용한 백그라운드 작업 - Kotlin | Android Developers
Android에는 보장된 백그라운드 작업을 위한 다수의 옵션이 있으며, 이러한 옵션에는 각각 다양한 장단점이 있습니다. Android용 WorkManager API는 백그라운드 작업을 간편하게 만듭니다. WorkManager는 예
developer.android.com
WorkManager 클래스
- Worker
- 백그라운드에서 실행하고자 하는 실제 작업의 코드를 입력
- 이 클래스를 확장하고 doWork() 메서드를 재정의
- WorkRequest: 작업 실행 요청
- WorkRequest를 만드는 과정에서 Worker를 전달
- WorkRequest를 만들 때 Worker를 실행할 시점에 적용되는 Constraints등을 지정 가능
- WorkerManager: WorkRequest를 예약하고 실행
- 지정된 제약 조건을 준수하면서 시스템 리소스에 부하를 분산하는 방식으로 WorkRequest를 예약
WorkRequest의 종류
- OneTimeWorkRequest: 한 번만 실행할 WorkRequest입니다.
- PeriodicWorkRequest: 일정 주기로 반복할 WorkRequest입니다.
예제 실습
준비된 코드랩 프로젝트 다운후
workers 패키지 안에 BlurWorker 클래스를 생성한다.
package com.example.background.workers
import android.content.Context
import androidx.work.Worker
import androidx.work.WorkerParameters
class BlurWorker(ctx: Context, params: WorkerParameters): Worker(ctx, params) {
override fun doWork(): Result {
TODO("Not yet implemented")
}
}
생성후 다음과 같이 생성자를 추가하고 Worker를 상속받는 클래스를 작성한다.
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)
val outputUri = writeBitmapToFile(appContext, output)
makeStatusNotification("Output is $outputUri", appContext)
Result.success()
} catch (throwable: Throwable){
Log.e(TAG, "Error applying blur")
Result.failure()
}
}
}
그리고 doWork() 메서드를 재정의 해준다.
코드 해설
- applicationContext 속성을 호출하여 Context를 가져오고 appContext에 할당합니다. (처리할 다양한 비트맵 조작을 위해)
- 사용자에게 이미지 블러 처리에 관해 알리는 makeStatusNotification 함수를 사용하여 상태 알림 표시
- 컵케이크 이미지에서 Bitmap을 만듬
- WorkerUtils에서 blurBitmap 메서드를 호출하여 블러 처리된 버전의 비트맵을 가져옴
- WorkerUtils에서 writeBitmapToFile 메서드를 호출하여 이 비트맵을 임시 파일에 씀. 반환된 URI를 로컬 변수에 저장해야 함
- WorkerUtils에서 makeStatusNotification 메서드를 호출하여 URI를 표시하는 알림을 만듬
- Result.success()를 반환합니다.
- 3~6단계의 코드를 try/catch 문으로 래핑합니다. 일반 Throwable을 포착
- catch 문에서 로그 구문을 사용하여 오류 메시지를 출력합니다.
- catch 문에서 Result.failure()를 반환합니다.
class BlurViewModel(application: Application) : ViewModel() {
private val workerManager = WorkManager.getInstance(application)
//...
internal fun applyBlur(blurLevel: Int) {
workerManager.enqueue(OneTimeWorkRequest.from(BlurWorker::class.java))
}
//...
}
BlurViewModel 클래스에서 workManager 인스턴스 변수를 선언해주고
applyBlur에서 한번만 실행하는 BlurWorker에서 OnTimeWorkRequest를 만들고 WorkManager를 이용하여 WorkRequest를 큐에 추가한다.
앱을 실행하고 블러 실행 버튼을 누르면 위와 같이 WorkRequest가 시작되었다고 알림이 뜨게 된다.
이미지를 확인하는 방법은
메뉴바에서 View -> Tool Windows -> Device File Exploer에 들어가
data -> data -> com.example.background -> files -> blur_filter_outputs에서 확인할 수 있다.
이번엔 입력과 출력 기능을 추가해보자.
먼저 BlurdviewModel에서 WorkRequest의 입출력을 해줄 메소드를 만든다.
private fun createInputDataForUri(): Data {
val builder = Data.Builder()
imageUri?.let {
builder.putString(KEY_IMAGE_URI, imageUri.toString())
}
return builder.build()
}
코드 해설
- Data.Builder 객체 생성. 요청이 있는 경우 androidx.work.Data를 가져옴
- imageUri가 null이 아닌 URI이면 putString 메서드를 사용하여 Data 객체에 추가. (Key-Value를 사용) Constants 클래스의 문자열 상수 KEY_IMAGE_URI를 사용할 수 있습니다.
- Data.Builder 객체에서 build()를 호출하여 Data 객체를 만들고 반환
internal fun applyBlur(blurLevel: Int) {
val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
.setInputData(createInputDataForUri())
.build()
workerManager.enqueue(blurRequest)
}
applyBlur 메서드를 변경해준다.
- OneTimeWorkRequestBuilder 생성.
- setInputData를 호출하여 createInputDataForUri의 결과 전달
- OneTimeWorkRequest 빌드
- 작업 실행이 예약되도록 WorkManager 요청을 사용하여 작업 요청을 큐에 추가
override fun doWork(): Result {
val appContext = applicationContext
val resourceUri = inputData.getString(KEY_IMAGE_URI)
makeStatusNotification("Blurring image", appContext)
return try {
// 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)
val outputUri = writeBitmapToFile(appContext, output)
makeStatusNotification("Output is $outputUri", appContext)
Result.success()
} catch (throwable: Throwable){
Log.e(TAG, "Error applying blur")
Result.failure()
}
}
BlurWorker 클래스의 doWork 메소드를 업데이트
전달한 URI를 Data 객체에서 가져오도록 resourceUri 추가
URI를 이용하여 블러 처리할 수 있게 처리
- 이전에 있던 picture 변수는 삭제 (주석)
- 전달된 Data에서 얻은 resourceUri가 비어 있지 않은지 확인
- picture 변수를 다음과 같이 전달된 이미지로 할당 (위 코드 참조)
val outputData = workDataOf(KEY_IMAGE_URI to outputUri.toString())
Result.success(outputData)
출력 을 위해 위 코드를 추가
1. 새 Data를 만들고 입력의 경우와 마찬가지로 outputUri 를 String으로 저장합니다. 같은 키(KEY_IMAGE_URI)를 사용
2. Result.success(Data outputData) 메서드를 사용하여 WorkManager에 반환한다.
앱을 실행하고 아까 경로로 들어가면 블러 처리가 된 이미지가 있다.
(만약 없을경우 동기화)
다음 포스트에선 조금 더 기능을 세부화 해볼것이다.