Unit 6: WorkManager (1)

quokka·2021년 12월 26일
0

Android Basics in Kotlin

목록 보기
24/25
post-thumbnail

WorkManger?

  • 안드로이드 백그라운드 작업을 처리하는 방법 중 하나로, Android Jatpack 아키텍처 구성 요소이다.
  • 앱이 종료되거나 기기가 다시 시작되어도 실행 예정인 지연 가능한 비동기 작업을 쉽게 예약할 수 있게 해준다.
  • 즉 workmanager는 사용자가 특정 화면이나 앱에서 나가더라도 task를 완료하도록 하는데 유용하다.
  • 서버에 로그나 분석 데이터를 전송하거나 로컬 데이터와 서버를 주기적으로 동기화 하는 작업 등에 사용하기에 적합하다.
  • Workmanager는 백그라운드 작업 처리에 사용되는 기존 기능들과 달리 API version에 상관없이 사용할 수 있다. (다른 기능들은 적적한 API 버전을 선택했다.)

학습할 내용
프로젝트에 WorkManager 추가
단순한 작업 예약
입력 및 출력 매개변수
작업 체이닝
고유 작업
UI에 작업 상태 표시
작업 취소
작업 제약 조건

시작하기

이미지를 블러 처리하여 결과를 파일에 저장하는 앱 Blur-O-Matic을 만들어보자.

스타터 앱 클론
git clone -b start_kotlin https://github.com/googlecodelabs/android-workmanager

WorkManager 기본사항

gradle
WorkManager를 사용하려면 Gradle에 추가해야한다. 여기에서 최신 버전을 확인한다.

implementation "androidx.work:work-runtime-ktx:2.7.1"

Worker
백그라운드에서 실행하고자 하는 실제 작업의 코드를 이곳에 작성한다.

WorkRequest
작업 실행을 위한 요청을 나타낸다. WorkRequest를 만드는 과정에서 Worker를 전달(pass) 한다. WorkRequest를 만들 때 Worker를 실행할 시점에 적용되는 Constraints 등을 지정할 수 있다.

WorkManager
실제로 WorkRequest를 예약하고 실행한다. 지정된 제약 조건을 준수하면서 시스템 리소스에 부하를 분산하는 방식으로 WorkRequest를 예약한다.

WorkRequest 만들기

1. BlurWorker 클래스

BlurWorker 클래스를 새로 생성한다.

class BlurWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params) {
}

2. doWork() 재정의

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()
        }
    }
  • drawable에 있는 test 이미지를 가져와 Bitmap을 만들어 picture에 담는다.
  • WorkerUtils에서 blurBitmap 메서드를 호출하여 블러 처리된 비트맵을 가져와 output에 담는다.
  • WorkerUtils에서 writeBitmapToFile 메서드를 호출하여 이 비트맵을 임시 파일에 쓴다. 반환된 URI를 outputUri 변수에 담는다.
  • makeStatusNotification 메서드를 호출하여 URI를 표시하는 알림을 만든다.
  • Result.success()를 반환

3. ViewModel에서 WorkManager 가져오기

BlurViewModel.kt에 WorkManager를 가져와 workManger 변수에 담는다.

private val workManager = WorkManager.getInstance(application)

4. WorkManager에서 WorkRequest enqueue

WorkRequest를 만들어 WorkManager에게 실행하도록 지시한다.

WorkRequest 유형은 2가지가 있다.

  • OneTimeWorkRequest: 한 번만 실행
  • PeriodicWorkRequest: 일정 주기로 반복 실행

적용 버튼을 클릭했을 때 한 번만 블러 처리를 적용하도록 하려면 OneTimeWorkRequest를 사요할 수 있다.

BlurViewModel.ktapplyBlur() 메서드를 추가한다.

Internal fun applyBlur(blurLevel: Int) {
   workManager.enqueue(OneTimeWorkRequest.from(BlurWorker::class.java))
}

입력 및 출력 추가

사용자가 직접 이미지를 블러 처리하려면 선택한 이미지의 URI를 WorkRequest의 입력으로 제공해야한다.

1. Data input object 만들기

입력과 출력은 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 객체를 만들어 반환한다.

2. WorkRequest에 Data object 전달

applyBlure() 메서드에 다음 코드를 추가한다.

val blurRequest = OneTimeWorkRequestBuilder<BlurWorker>()
            .setInputData(createInputDataForUri())
            .build()
  • OneTimeWorkRequest.Builder를 만든다.
  • createInputDataForUri의 결과를 전달하고, build()한다.

workManager.enqueue(blurRequest)를 통해 요청을 큐에 추가한다.

3. doWork() 수정하여 입력 가져오기

전달한 URI를 Data 객체에서 가져오도록 BlurWorkerdoWork() 메서드를 수정한다.

4. 사용자가 선택한 이미지 블러 처리

5. 임시 URI 출력

작업 체이닝

단일 작업을 처리하는 방법을 배웠다. WorkManager를 사용하면 순서대로 실행되거나 동시에 실행되는 별도의 WorkerRequest를 만들 수 있다. 이 과정에서는 작업 체인을 사용한다.

1. 정리 Worker, 저장 Worker 만들기

기존 Worker는 블러처리를 담당하는 Worker이다. 임시 파일을 정리하는 Worker와 이미지를 영구적으로 저장하는 Worker를 만들어보자.

먼저 worker 패키지Worker를 확장하는 클래스 CleanupWorkerSaveImageToFileWorker를 생성한다.

2. CleanupWorker

BlurWorker와 유사하게 클래스를 생성하고, doWork()를 재정의한다.

CleanupWorker는 입력을 받거나 출력을 내보내는 기능이 없기 때문에 임시 파일이 있으면 항상 삭제한다.

3. SaveImageToFileWorker

SaveImageToFileWorker도 마찬가지로 파일을 생성하고, doWork()를 재정의한다.

4. WorkRequest 체인 만들기

WorkRequest 체인을 실행하도록 BlurViewModelapplyBlur 메서드를 수정한다.

이전에 작성한 blurRequestworkManager.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() 메서드를 호출하여 이 작업 요청 체인에 추가할 수 있다.

5. blueLevel에 따라 서로 다른 블러 처리

blurViewModelblurRequest를 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를 사용한다.

Tag와 작업 상태 표시

1. 작업 Tag 지정

addTag를 추가해 태그를 지정한다.

        val save = OneTimeWorkRequestBuilder<SaveImageToFileWorker>()
            .addTag(TAG_OUTPUT) 
            .build()

2. WorkInfo 가져오기

LiveData<List<WorkInfo>>outputWorkInfos라는 새로운 변수를 선언하고,
BlurViewModel에서 WorkManager.getWorkInfosByTagLiveData를 사용하여 WorkInfo를 가져오는 init 블록을 추가한다.

3. WorkInfo 표시

WorkInfoLiveData를 가져왔으며 이제 BlurActivity에서 관찰할 수 있다.

  • WorkInfo가 null이거나 비어있으면 돌아간다.
  • TAG_OUTPUT으로 태그된 WorkInfo를 가져온다.
  • workInfo.state().isFinished()로 작업이 완료 상태인지 확인할 수 있다.
  • 완료되지 않은 경우와 완료된 경우에 따라 적절한 뷰를 숨기거나 표시할 수 있다. (작업이 진행 중이면 진행률을 표시하고 완료되면 취소 버튼을 표시하는 등)

작업 취소

Cancel Work 버튼을 눌렀을 때 작업이 취소되도록 한다. 이때 고유한 체인 이름으로 작업을 취소하는 것이 좋다.

1. 이름으로 작업 취소

internal fun cancelWork() {
    workManager.cancelUniqueWork(IMAGE_MANIPULATION_WORK_NAME)
}

2. 버튼 클릭 리스너

cancelButton에 클릭 리스너를 연결해 cancelWork()를 호출하도록 한다.

작업 제약 조건

WorkManagerConstraints를 지원한다. 예를 들어 이미지를 저장할 때는 기기를 충전해야 한다는 제약 조건을 사용할 수 있다.

0개의 댓글