Android

안드로이드 [Kotlin] - WorkManager의 탄생배경과 활용

🤖 Play with Android 🤖 2022. 10. 24. 16:19
728x90


🗒  WorkManager 이론

📌  안드로이드의 백그라운드 작업

  • 실행 시점에 다른 분류
    • Exact Timing : 즉시 처리되어야 하는 작업
    • Deferrable : 처리를 위한 조건이 만족될 때까지 기다릴 수 있는 작업
  • 실행 완료 여부에 따른 분류
    • Best-Effort : 처리를 위해 노력하지만 취소될 수도 있는 작업
    • Guranteed Execution : 앱이 종료되거나 기기가 재부팅되어도 수행되어야 하는 작업 

 

  • 안드로이드에는 위 표처럼 이미 백그라운드 Task를 처리하기 위한 많은 라이브러리들이 존재한다.

 

 

  • WorkManager는 이 중에서 Defferd와 Guaranteed Execution의 작업들을 대체하기 위해서 탄생하였다.
  • 그렇다면 이미 라이브러리가 존재하는데 왜 구글은 WorkManger를 새로 만든 것일까?

그 이유를 알기 위해 안드로이드 백그라운드 API 변천 과정을 살펴보자

 

 

📌  안드로이드 백그라운드 API 변천 과정

  • API 18 : AlarmManager
    • 안드로이드 API 18까지는 시스템에 Broadcast Receiver를 등록하고 앱에서 AlarmManger로 알람을 전달하면 호출되는 Service로 백그라운드 작업을 수행하였다.
  • API 19 : AlarmManager(X)
    • 그런데 API 19부터는 기기의 WakeUp Alarm과 배터리 소모를 최소화하기 위해 시스템이 알람 타이밍을 변경할 수 있게 되었다.
    • 따라서 이러한 AlarmManager가 더이상 정확한 시간에 동작하는 것을 보장할 수 없게 되었다.
  • Google I/O 2014 - JobScehduler (Project Volta 중 하나)
    • 이러던 와중에 구글은 2014 Google I/O에서 Project Volta를 발표한다.
    • 이 중에 JobScehduler라는 것이 소개되었는데 이 JobScehduler는 백그라운드 작업의 실행 타이밍을 앱이 아닌 시스템이 판단하게 함으로써 배터리 시간을 갉아먹는 무분별한 백그라운드 작업을 제한한다.
    • 발표에 따르면 AlarmManager에 비해서 약 25% 정도의 배터리개선 효과를 내었다고 한다.
  • 그 이후로도 구글은 백그라운드 작업을 제한하는 정책을 지속해서 내게 된다.

 

 

📌  WorkManager의 발표

그러던 와중 Google I/O 2018에서 WorkManger가 발표된다.

  • Guranteed, constraint-aware execution
    • 실행이 보장되며 제약조건을 붙일 수 있다. 
    • 예를 들면 네트워크 연결시에만, 혹은 기기가 충전 중 일 때만 작업이 처리되도록 할 수 있다.
  • Respectful of system background restrictions 
    • 장치의 상태를 존중한다.
    • 예를 들면 앱이 도즈모드일 경우 작업 처리를 위해 기기를 깨우지 않는다.
  • Backwards compatible with or without Google Play Services
    • 구글 플레이 서비스와 관계 없이 동작한다.
  • Queryable
    • 작업이 실행 혹은 대기중인지? 성공 혹은 실패했는지 등의 상태 조회를 할 수 있다.
  • Chainable
    • 작업 A, B 결과에 따라 처리되는 작업 C를 만들고, 다시 작업 C 결과에 의존하는 작업 D를 만들 수 있다.
  • Opportunisitic
    • 사용자를 간섭하지 않아도 제약조건이 만족되면 작업이 즉시 실행된다.

 

📌  WorkManger의 동작 구조

 

 

  • 그림과 같이 시스템에 따라서 적절한 백그라운드 API를 선택하도록 설계되어있다.
    • API 23보다 높은가?
      • 그렇다 : JobSceduler 사용
      • 아니다 : Firebase JobDispatcher 포함 여부로 이동
    • Firebase JobDispatcher를 포함하고 있는가?
      • 그렇다 : Firebase JobDispatcher 사용
      • 아니다 : AlarmManager와 Broadcast Receivers 사용

 

📌  구글에서 제안하는 Use Case

  • WorkManger는 만능 라이브러리가 아니다. 
  • Defferable & Guaranteed Excution 한, 즉 처리를 위한 조건이 만족될 때까지 기다릴 수 있는 작업 & 앱이 종료되거나 기기가 재부팅되어도 수행되어야 하는 작업, 두 가지 조건을 만족하는 작업을 위해 설계되었다
  • 구글은 다음과 같은 Use Case를 제안한다.

구글이 제안하는 예시에서 WorkManger를 사용하는 경우는 로그를 서버에 업로드하는 경우라던지 암호화, 복호화된 콘텐츠를 업로드 혹은 다운로드할 때 혹은 새로운 온라인 이메일을 싱크 할 때 사용하라고 제안하고 있다.

 

 

 


 

 

 

💻  코드 예제 (실습)

📌  의존성 추가 

dependencies {
    def work_version = "2.71"

    // java 를 사용한다면 다음의 줄을 추가
    implementation "android.arch.work:work-runtime:$work_version"
    
    // kotlin 을 사용한다면 다음의 줄을 추가
    implementation "android.arch.work:work-runtime-ktx:$work_version"

    // Firebase JobDispatcher 를 사용하려면 다음의 줄을 추가
    implementation "android.arch.work:work-firebase:$work_version"
}
  • 글을 포스팅하는 시점의 Stable version은 2.71이다. 
  • 버전은 지속적으로 올라갈 수 있다.

 

 

📌  Worker 작업 정의

class UploadWorkder(
    appContext: Context, 
    workerParams: WorkerParameters
): Worker() {
	
    override fun doWork(): Result {
    	// 이곳에서 작업을 수행
        // 이 경우에는 uploadImage 함수를 수행한다.
        uploadImages()
        
        // 작업이 성공적으로 끝났는지 여부를 정의
        return Result.success()
    }
}

 

 

📌  제약조건 설정

  • NetworkType
    • 네트워크 여부가 필요한지 제약조건을 설정
  • BatteryNotLow
    • true로 설정되어 있다면 기기의 배터리가 낮다고 판단되면 작업이 수행되지 않는다.
  • RequiresCharging
    • true로 설정되어 있다면 기기가 충전중일때만 작업이 수행된다.
  • Deviceldle
    • true로 설정되어 있다면 작업이 수행되기 전에 장치가 idle(유휴 상태, 프로세스가 실행하고 있지 않은 상태)이어야 한다.
    • 이 기능은 기기에서 활발하게 실행되는 다른 앱에 부정적인 영향을 미칠 수 있는 작업을 일괄적으로 실행하는 데 유용할 수 있다.
  • StorageNotLow
    • true로 설정되어 있다면 기기의 저장공간이 너무 낮다고 판단되면 작업이 수행되지 않는다.

 

 

📌  WorkRequest 만들기

val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()
   
val uploadWorkRequest: WorkRequest = 
   OneTimeWorkRequestBuilder<UploadWorker>()
       .setConstraints(constraints)
       .build()
  • WorkRequest를 통해서  위의 Worker 클래스에서 정의한 작업을 어떠한 형태로 수행할지 정의한다.
  • PeriodWorkRequest를 통해 주기적으로 수행되도록 하거나, OneTimeWorkRequest를 통해 단 한번만 수행되게 예약을 할 수 있다.
  • 위 코드는 네트워크 타입과 충전 상태에 대한 제약조건을 설정하고 한 번만 수행되도록 WorkRequest를 만든 예시이다.

 

 

📌  WorkRequest 제출

WorkManager
    .getInstance(myContext)
    .enqueue(uploadWorkRequest)

 

 

  • 제출된 WorkRequest는 WorkManager 내부의 Database에서 관리된다.
  • Database는 작업 내용, 작업의 현재 상태, 작업의 제약조건 등이 포함된다.
  • Contraints met 즉 제약조건이 만족되면 내부의 TaskExcutor에 의해서 작업이 실행되는 구조를 가지고 있다.

 

 

📌  Work States 확인하기

// by id
workManager.getWorkInfoById(syncWorker.id) // ListenableFuture<WorkInfo>

// by name
workManager.getWorkInfosForUniqueWork("sync") // ListenableFuture<List<WorkInfo>>

// by tag
workManager.getWorkInfosByTag("syncTag") // ListenableFuture<List<WorkInfo>>
  • WorkRequest를 큐에 추가하고 나면 id, name 혹은 tag를 통해 언제든 Work State 즉 작업 상태를 확인할 수 있다.

 

Work State

 

1회성 작업

 

1회성 작업

  • ENQUEUED
  • RUNNING
  • SUCCEEDED
  • FAILED
  • CANCELLED

 

 

 

주기적 작업

 

주기적 작업

  • ENQUEUED
  • RUNNUG
  • CANCELLED