WorkManager를 알아보자

HEETAE HEO·2022년 7월 3일
0
post-thumbnail

WorkManager

Android Jetpack 중에 하나인 WorkManager는 백그라운드 작업을 도와주기 위해 개발되었습니다.

WorkManager의 기능은 다음과 같습니다.

  • 실행이 보장됩니다. 또한 제약 조건을 가지고 실행할 수 있습니다.
    -> 예) 네트워크 연결시에만 처리되는 작업을 추가하면 네트워크가 연결되면 반드시 실행됩니다.

  • 장치의 상태를 존중합니다.
    -> 도즈모드에 진입하면 일을 처리하기 위해 기기를 깨우지 않습니다.

  • 구글 서비스의 유무에 상관없이 동작합니다.

  • 실행중인가, 대기중인가, 완료되었는가 등의 상태 조회가 가능합니다.

  • 작업 A의 결과에 따라 B 또는 C를 선택하여 처리하고 D를 이어서 처리하는 등의 작업 연결 처리가 가능합니다.

  • 어떠한 제한조건이 충족되었을때 즉시 실행됩니다.

WorkManager의 구성

WorkManager API의 주요 클래스는 WorkManager, Worker, WorkRequest, WorkState입니다.

  • WorkManger : 처리해야하는 작업을 자신의 큐에 넣고 관리합니다. 싱글톤 인스턴스로 사용하기 위해서 내부에 WorkManager 객체를 반환하는 getInstance() 함수가 있습니다. 이 메서드를 통해 WorkManager의 인스턴스를 받아 사용합니다.
  • Worker : 추상 클래스입니다. 처리해야하는 백그라운드 작업의 처리 코드를 이 클래스를 상속받아 doWork() 메서드를 오버라이드 하여 작성하게 됩니다. doWork의 결과에 따라 Worker 클래스 내에서 Result의 값 중 하나를 리턴해야 합니다.

  • WorkRequest : WorkManager를 통해 실제 요청하게될 개별 작업입니다. WorkRequest는 처리해야 할 작업인 Worker와 작업 반복 여부 및 작업 실행조건, 제약 사항 등 이 작업을 어떻게 처리할 것인지에 대한 정보가 담겨 있습니다. WorkRequest는 반복 여부에 따라 다음의 두 가지로 나뉘어 집니다.

  1. OneTimeWorkRequest : 반복하지 않을 작업, 즉 한번만 실행할 작업의 요청을 나타내는 클래스입니다.

  2. PeriodicWorkRequest : 여러 번 실행할 작업의 요청을 나타내는 클래스입니다.

  • WorkState : WorkRequest의 id와 해당 WorkRequest의 현재 상태를 담는 클래스입니다. WorkState의 상태정보를 이용해서 자신이 요청한 작업의 현재 상태를 파악할 수 있습니다. 상태로는 ENQUEUED, RUNNING, SUCEEDED, FAILED, BLOCKED, CANCELLED 이렇게 총 6가지 입니다.

단순 작업

  • Worker 클래스를 상속받은 클래스를 만들고 doWork() 메서드를 오버라이드 합니다.
  • 처리 결과에 따른 Result값을 리턴해야 합니다.
import androidx.work.worker

class EXWorker : Worker() { 
	override fun doWork() : Result {
    	// .. do something
        return Result.sucess()
        }
}
  • OneTimeWorkRequestBuilder를 이용해서 OneTimeWorkRequest 객체를 생성합니다.
/* 코틀린에 정의된 인라인 함수 OneTimeWorkRequestBuilder */
var workRequest = OneTimeWorkRequestBuilder<EXWorker>().build()

/* 자바에서는 OneTimeWorkRequest 클래스내에 정의된 OneTimeWorkRequest.Builder를 사용해야 함 */
var workRequest = OneTimeWorkRequest.Builder(EXWorker::class.java)
  • WorkManager 클래스의 getInstance() 메서드로 싱글턴 객체를 받아서 WorkManager의 작업 큐에 OneTimeWorkRequest 객체를 추가해줍니다.
var WorkRequest = OneTimeWorkRequestBuilder<EXWorker>().build()
val workManager = WorkManager.getInstance()
workManager?.enqueue(workRequest)
  • 반복되는 작업은 PeriodicWorkRequestBuilder를 이용해서 PeriodicWorkRequest 객체를 생성한 뒤 WorkManager의 큐에 추가해주며 됩니다.
/* 반복 시간에 사용할 수 있는 가장 짧은 최소값은 15 */
val workReqeust = PeriodicWorkRequestBuilder<EXWorker>(15, TimeUnit.MINUTES).build()

val workManager = WorkManager.getInstance()

workManager?.enqueue(workRequest)

제약 조건을 가지는 작업

  • 해당 제약조건이 만족되면 작업을 수행하고, 조건이 만족되지 않으면 작업을 취소하며, 처리가 완료되지 못하고 실패한다면 제약조건이 만족되는 다음 타이밍에 다시 처리를 시도하게됩니다.

  • 제약 조건은 Constraints 클래스의 Builder를 이용해서 생성한 뒤 WorkRequest에 추가합니다.

val constraints = Constraints.Builder()
	/* 네트워크 연결상태에 대한 제약 조건 */
	.setRequiredNetworkType(NetworkType.CONNECTED)
	/* 충전 상태에 대한 제약 조건 */
	.setRequiresCharging(true)
	.build()

/* 제약조건과 함께 작업을 생성하거나 */
val requestConstraint = OneTimeWorkRequestBuilder<EXWorker>()
	.setConstraints(constraints)
	.build()

/* 작업을 생성하고 나중에 제약조건을 설정해 줄수 있다 */
workRequest.setConstraint(constraints)

연결된 작업

  • 두 작업을 연결해서 처리하는 방법
  • 각 작업을 WorkRequest로 만들어서 처음 실행될 작업을 WorkManager의 beginWith() 메서드의 인자로 추가하고 then() 메서드에 이어서 작업을 추가해줍니다.
    -> WorkManager는 workA를 수행하고 이 작업이 완료된 이후 workB 작업을 수행하게 됩니다.
val workA = OneTimeWorkRequestBuilder<AWorker>().build()
val workB = OneTimeWorkRequestBuilder<BWorker>().build()

WorkManager.getInstance()?.apply{
	beginWith(workA).the(workB).enqueue()
}

작업 처리상태 파악

  • LiveData 객체를 통해 해당 작업의 상태를 추적할 수 있습니다.
val workRequest = OneTimeWorkRequestBuilder<EXWorker>().build()

val workManager = WorkManager.getInstance()
workManager?.let {
	// WorkManager 의 큐에 작업을 추가 합니다
    it.enqueue(workRequest)
    
    /** WorkManager의 getStatusById()에 WorkRequest의 UUID 객체를 인자로 전달 하면
     *  인자값으로 주어진 ID에 해당하는 작업을 추적할 수 있도록 LiveData 객체를 반환한다
   	 */
    val statusLiveData = it.getStatusById(workRequest.id)
    /* statusLiveData에 Observer를 걸어서 작업의 상태를 추적 */
    statusLiveData.observe(this, Observer { workState ->
        Log.d("exmaple", "state: ${workState?.state}")
    })
}

저장된 Observer에 작업의 상태는 WorkState 객체로 전달됩니다.

작업간 정보 전달

  • setInputData() 메서드를 통해서 Data객체를 인자로 Worker에 정보를 전달할 수 있습니다.
val input = mapOf("question" to "answer")
/* Data클래스의 Builder를 사용해서 Data 객체를 생성한다 */
val inputData = Data.Builder().putAll(input).build()

val requestWork = OneTimeWorkRequestBuilder<EXWorker>()
	.setInputData(inputData)
	.build()
  • Worker 클래스에는 전달받은 Data 객체를 반환하는 getInputData() 메서드가 있으며, 이 메서드를 통해 Data 객체를 반환받아 사용합니다.
val question = inputData.getString("question", "")
  • InputMeger를 이용하면 여러 작업에서의 정보 전달이 가능해집니다.

  • WorkManager의 기본 InputMerger는 OverwritingInputMerger입니다.

  • overwritingInputMerger
    -> 여러개의 Data가 전달될 때 같은 Key를 가지고 value는 덮어씁니다.

  • ArrayCreatingInputMerger
    -> 여러개의 Data가 전달될 때 같은 key를 가지는 value를 배열로 전달합니다.
    -> 단 배열의 특성상 같은 key의 value의 타입이 서로 다르면 배열을 만들 수 없기 때문에 Exception이 발생합니다.

val sortWordWorker = OnetimeWorkReqeustBuilder<EXWorker>()
	.setInputMerger(ArrayCreatingInputMerger::class)
	.build

작업 취소

  • WorkRequest의 UUID 객체를 사용해 작업을 취소합니다.
// cancelWork 작업을 WorkManager의 큐에 추가 
WorkManager.getInstance()?.enqueue(cancelWork)

// cancelWork의 id를 이용해서 작업을 취소 
WorkManager.getInstnace()?.cancelWorkById(cancelWork.id)
  • UUID는 어려운 문자열이기 때문에 쉽게 접근하기 위해 태그를 달 수 있습니다.
  • 태그를 이용해 작업을 취소하면 해당 태그를 가진 모든 작업을 한 번에 취소하는 기능을 하게 됩니다.
val cancelWork = OnetimeRequestBuilder<CancelWorker>()
	.addTag("cancel work tag")

WorkManager.getInstnace()?.cancelWorkById("cancel work tag")
  • WorkManager에서 취소하고자 하는 작업이 이미 완료된 작업이라면 취소 메서드는 아무 기능도 하지않습니다.
  • 아직 실행 전 큐에 담긴 상태라면 실행하지 않고 취소됩니다.
  • 하지만 이미 실행된 작업을 임의로 멈추지는 않습니다.
    -> 그래서 작업 취소 플래그를 설정해 줄 수 있습니다.
    -> isStopped() == true
  • 작업 중지 상태 isCancelled()가 false인 경우에는 시스템에 의한 작업이 중지된 경우이므로 해당 작업은 다음 어느 시점에 다시 수행됩니다.

-> isCancelled() == true

  • 작업 취소 상태, 반드시 isStopped()가 true임을 확인 한 후 해당 플래그를 참고해야합니다.
override fun doWork() : Result {
    if (isStopped) {
        /* 작업이 멈추었을 때 대비한 코드 */
        
        if (isCancelled) {
            /* 작업이 취소 되었을 때 대비한 코드 */
        }
    }
}

유일한 작업

  • beginUniqueWork() 메서드를 통해 Unique Work 라는 작업 처리 방식을 제공합니다.
  • 작업에 유일한 이름을 부여하고 이 이름을 통해서 큐에 넣거나, 조회하거나 취소할 수 있습니다.
  • 같은 이름을 가지는 작업이 이미 WorkManager 큐에 존재하면 추가하려는 작업에 대한 동작 방식을 KEEP, REPLACE, APPEND의 3가지 중 하나로 지정할 수 있습니다.

KEEP : 작업 A가 실행 대기 중이거나 실행 중이면 작업 B는 WorkManager의 큐에 추가 되지않습니다.

REPLACE : 작업 A를 취소하고 작업 B를 큐에 추가합니다.

APPEND : 작업 B를 BLOCKED 상태로 대기시키고, 작업 A가 완료되면 작업 B를 큐에 추가합니다.

val workManager = WorkManager.getInstance()
val workers = OneTimeWorkRequestBuilder<EXWorker>()
	.build()

val config = workManager.beginUniqueWork("string", KEEP, workers)
val config = workManager.beginUniqueWork("string", REPLACE, workers)
val config = workManager.beginUniqueWork("string", APPEND, workers)

references

https://medium.com/@limgyumin/%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EB%B0%B1%EA%B7%B8%EB%9D%BC%EC%9A%B4%EB%93%9C-%EC%9E%91%EC%97%85-%EC%B2%98%EB%A6%AC%EB%B2%95-workmanager-f625e07b384c
https://medium.com/@limgyumin/workmanager-%EC%89%BD%EA%B2%8C-%EC%93%B0%EA%B8%B0-b71917ea8c1
https://medium.com/@limgyumin/workmanager-%EC%9E%98-%EC%8D%A8%EB%B3%B4%EA%B8%B0-1643a999776b
https://dongsik93.github.io/til/2020/05/15/til-jetpack-workmanager/
https://www.youtube.com/watch?v=IrKoBFLwTN0&t=1s

profile
Android 개발 잘하고 싶어요!!!

0개의 댓글