티스토리 뷰

안드로이드

WorkManager 사용해보기 1

알렌보이스 2022. 6. 5. 22:03
728x90

 

이번에는 Jetpack Library 중 하나인 WorkManager를 사용해보려고 합니다.

WorkMager도 백그라운드 작업을 해주는 라이브러리 중 하나입니다.

특징 관련해서는 코드를 보면서 확인하는게 이해하기 쉬울 것 같아서 아래에 다루겠습니다.

 

1. 종속 항목 추가

// WorkManger
def work_version = "2.7.1"
// (Java only)
implementation("androidx.work:work-runtime:$work_version")
// Kotlin + coroutines
implementation("androidx.work:work-runtime-ktx:$work_version")
// optional - GCMNetworkManager support
implementation("androidx.work:work-gcm:$work_version")
// optional - Test helpers
androidTestImplementation("androidx.work:work-testing:$work_version")
// optional - Multi process support
implementation "androidx.work:work-multiprocess:$work_version"

 

우선 기본 사용법을 알아보겠습니다.

2. Worker 클래스 만들기

class TestWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    
    override fun doWork(): Result {
    
        return Result.success()
        
    }

}

Worker 클래스를 만드는 방법은 단순합니다.

Worker를 상속 받고 doWork()만 구현해 주면 됩니다.

doWork()에서 백그라운드에서 작업하고 싶은 내용을 추가해주시고 상태 값을 리턴해줍니다.

Result의 생태값으로는 아래와 같습니다.

  - Result.success() : 작업이 성공적으로 완료
  - Result.failure() : 작업에 실패
  - Result.retry() : 작업에 실패하였으며 재시도 정책에 따라 다른 시점에 시도

마지막 retry()에서 언급되는 재시도 정책은 아래에 설명 될 예정입니다!

 

3. WorkManger 실행 (일회성 작업)

val workRequest : WorkRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .build()

WorkManager
    .getInstance(this)
    .enqueue(workRequest)

우선 WorkRequest를 만들어 주어야하는데요

이것을 이해하기 위해서는 WorkManger의 실행 유형을 알아야합니다.

 - 즉시 실행 : 즉시 시작 하고 곧 완료 되어야하는 작업을 처리합니다. (1회)
 - 장기 실행 : 10분 이상의 시간동안 실행 될 수 있는 작업을 처리합니다. (1회 또는 주기적)
 - 지연 실행 : 나중에 시작하며 주기적으로 실행될 수 있는 예적된 작업을 처리합니다. (1회 또는 주기적)

이번 코드에서 사용한건 즉시 실행으로, OneTimeWorkRequest를 사용한 예제입니다.

이름에서 예측할 수 있듯이 한 번만 실행을 하는 WorkRequest입니다.

다른 WorkRequset는 아래에 계속해서 설명하도록 하겠습니다.

OneTimeWorkRequest.from(TestWorker::class.java)

다른 기능들을 추가하지 않을 경우 위와 같이 간략하게 사용이 가능합니다.

WorkManger의 enqueue를 통해 WorkRequest의 요청에 따라 위에서 만든 TestWorker가 동작하게 됩니다.

 

4. Worker에게 데이터 전송

val workRequest : WorkRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .setInputData(
            workDataOf(
                TestWorker.WORKER_INPUT_DATA to TestWorker.SUCCESS,
                TestWorker.WORKER_INPUT_DATA to TestWorker.FAILURE
            )
        )
        .build()

Worker에게 데이터를 정송해주고 싶다면 setInputData 함수를 이용하면 됩니다.

저는 예시를 두기 위해 같은 키값을 사용했는데 실제로 이용할땐 다른 키값을 넣어주세요~

class TestWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    override fun doWork(): Result {
        // Worker 에게 전달한 데이터 받기
        val inputData = inputData.getString(WORKER_INPUT_DATA)
        return Result.success()
    }

    companion object {
        const val WORKER_INPUT_DATA = "worker_input_data"
        const val SUCCESS = "success"
        const val FAILURE = "failure"
    }

}

데이터를 받는 방식은 intent와 유사합니다.

 

5.  주기적 반복 실행

val saveRequest = 
    PeriodicWorkRequestBuilder<TestWorker>(1, TimeUnit.DAYS)
        .build()

WorkManager
    .getInstance(this)
    .enqueue(saveRequest)

 

주기적으로 반복 작업을 실행해야 하는 경우에는 PeriodicWorkRequest를 사용해주시면 됩니다.

최소 시간은 15분이며 TimeUnit은

NANOSECONDS,
MICROSECONDS,
MILLISECONDS,
SECONDS,
MINUTES,
HOURS,
DAYS;

위 와 같은 종류들이 존재하며 상황에 따라 선택해서 사용하면 됩니다.

주의할점은 WorkManaer에서 나오는 모든 시간들은 정확하게 지켜진다는 보장은 없습니다.

만약 15분이라고 설정 한 뒤 15분이 지나도 조금의 딜레이가 발생할 수 있기 때문에 이 점은 고려해야합니다.

원인은 다음에 나올 제약 조건이나 시스템 최적화 상태에 따라 조금식 시간이 변동 될 수 있기 때문입니다.

제가 회사에서 하루 단위 실행되어야하는 기능 구현할 때에는 최대 4분 이내에는 실행 됬던것 같습니다.

 

6. 제약 조건

val constraints = 
    Constraints
        .Builder()
        .setRequiresBatteryNotLow(true)
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .setRequiresStorageNotLow(true)
        .setRequiresDeviceIdle(true)
        .setRequiresCharging(true)
        .build()

val workRequest : WorkRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .setInputData(
            workDataOf(
                TestWorker.WORKER_INPUT_DATA to TestWorker.SUCCESS,
            )
        )
        .setConstraints(constraints)
        .build()

제약 조건은 해당 제약 조건들이 충족될 때까지 지연되었다가 실행하게 하는 기능입니다.

  - NetWorkeType : 네트워크 유형 제한
     (종류 - NOT_REQUIRED, CONNECTED, METRED, UNMETERED, NOT_ROAMING)
  - BatteryNotLow : 베터리 상태 제한 (true - 베터리가 부족 시 실행 안함)
  - RequiresCharging : 기기 충전 상태 제한 (true - 충전 중일 때 실행)
  - DeviceIdle : 기기의 유휴 상태 제한 (true - 유휴 상태일 때 실행)
  - StorageNotLow : 저장 공간 제한 (true - 저장 공간이 너무 부족할 땐 실행 안함)

상황에 맞게 필요한 제약 조건들을 추가해서 사용이 가능합니다.

 

7. 지연 실행

val workRequest : WorkRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .setInputData(
            workDataOf(
                TestWorker.WORKER_INPUT_DATA to TestWorker.SUCCESS,
            )
        )
        .setConstraints(constraints)
        .setInitialDelay(10, TimeUnit.MINUTES)
        .build()

setInitialDelay를 이용하여 실행을 지연할 수 있습니다.

위의 예에서는 10분 이후에 실행하게 됩니다.

 

8. 재시도 및 Back off 정책

val workRequest : WorkRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .setInputData(
            workDataOf(
                TestWorker.WORKER_INPUT_DATA to TestWorker.SUCCESS,
            )
        )
        .setConstraints(constraints)
        .setInitialDelay(10, TimeUnit.MINUTES)
        .setBackoffCriteria(
            BackoffPolicy.LINEAR,
            OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
            TimeUnit.MILLISECONDS
        )
        .build()

setBackoffCriteria를 통해 위에서 언급된 재시도에 관련해서 정의할 수 있습니다.

public final @NonNull B setBackoffCriteria(
        @NonNull BackoffPolicy backoffPolicy,
        long backoffDelay,
        @NonNull TimeUnit timeUnit) {
        ...
}

기본적으로 BasckoffPolicy는 EXPONENTAL로 설정되어 있고 10초의 지연을 가지고 있습니다.

EXPONENTAL로 설정은 지연시간을 기하급수적으로 증가 시킵니다.

LINEAR 방식은 정의한 backoffDelay 만큼씩 시간이 증가합니다. (ex 10, 20, 30, 40...)

backoffDelay의 최소 시간은 10초 입니다.

 

제약 조건, 지연 실행, 재시도 모두 OneTimeWorkRequest, PeriodicWorkRequest 사용 가능하며
반복 실행에 지연을 추가 할 시 첫 번째 실행에만 영향을 줍니다.

 

9. 작업 취소

val workRequest =
    OneTimeWorkRequestBuilder<TestWorker>()
        .setInputData(
            workDataOf(
                TestWorker.WORKER_INPUT_DATA to TestWorker.SUCCESS,
            )
        )
        .setConstraints(constraints)
        .setInitialDelay(10, TimeUnit.MINUTES)
        .setBackoffCriteria(
            BackoffPolicy.LINEAR,
            OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
            TimeUnit.MILLISECONDS
        )
        .addTag("tag")
        .build()


WorkManager.getInstance(this).cancelWorkById(workRequest.id)
WorkManager.getInstance(this).cancelAllWorkByTag("tag")
WorkManager.getInstance(this).cancelUniqueWork("uniqueWorkName")
WorkManager.getInstance(this).cancelAllWork()

취소 방법에는 4가지 방식이 있습니다.

첫번 째는 cancelWorkById(id: UUID)로
WorkRequest를 만든 변수에서 얻을수 있는 UUID를 가지고 취소하는 방법입니다.

두번 째는 cancelAllWorkByTag(tag : String)로
WorkRequest 생성 시에 addTag를 통해 넣은 Tag를 가진 작업중인 모든 Worker를 취소 시킵니다.

세번 째는 cancelUniqueWork(uniqueWorkName: String)로
다음에 나올 예정인데 맛보기만 하자면

WorkManager
    .getInstance(this)
    .enqueueUniqueWork("uniqueWorkName", ExistingWorkPolicy.KEEP, workRequest)

이렇게 enqueueUniqueWork로 실행한 Worker를 취소시키는 방법입니다.

마지막은 cancelAllWork()로 실행 중인 모든 워커를 종료하는 방식입니다.

 

10. 작업 상태

일회성 실행의 상태 다이어그램입니다.

주기적 반복 실행의 상태 다이어그램입니다.

상태 다이어그램을 보면 주기적 반복 실행 방식의 경우 CANCLLED의 상태 값만 있습니다.

그렇기 때문에 직접 종료를 해주지 않고 앱을 배포하게 되면 다음 배포시에 종료 작업이 들어 가기 전까지는 취소할 방법이 일반적으론 불가능 합니다.

그러므로 주기적 반복 실행을 사용 할 때에는 이점을 주의해서 개발을 해야합니다.

 

분량상 이번 포스팅은 이것으로 마치고 2편에 추가적으로 작성하겠습니다~

 

참고자료
공식 사이트 : https://developer.android.com/topic/libraries/architecture/workmanager?hl=ko
728x90

'안드로이드' 카테고리의 다른 글

1. CloudType - MjApp 제작기  (0) 2023.04.30
Android Duplicate class found 오류 해결  (2) 2022.10.28
Compose 기초 1 : Column, Row, Box  (0) 2022.06.29
WorkManger 사용해보기 2  (0) 2022.06.07
댓글