Android Jetpack 중에 하나인 WorkManager는 백그라운드 작업을 도와주기 위해 개발되었습니다.
WorkManager의 기능은 다음과 같습니다.
실행이 보장됩니다. 또한 제약 조건을 가지고 실행할 수 있습니다.
-> 예) 네트워크 연결시에만 처리되는 작업을 추가하면 네트워크가 연결되면 반드시 실행됩니다.
장치의 상태를 존중합니다.
-> 도즈모드에 진입하면 일을 처리하기 위해 기기를 깨우지 않습니다.
구글 서비스의 유무에 상관없이 동작합니다.
실행중인가, 대기중인가, 완료되었는가 등의 상태 조회가 가능합니다.
작업 A의 결과에 따라 B 또는 C를 선택하여 처리하고 D를 이어서 처리하는 등의 작업 연결 처리가 가능합니다.
어떠한 제한조건이 충족되었을때 즉시 실행됩니다.
WorkManager API의 주요 클래스는 WorkManager, Worker, WorkRequest, WorkState입니다.
Worker : 추상 클래스입니다. 처리해야하는 백그라운드 작업의 처리 코드를 이 클래스를 상속받아 doWork() 메서드를 오버라이드 하여 작성하게 됩니다. doWork의 결과에 따라 Worker 클래스 내에서 Result의 값 중 하나를 리턴해야 합니다.
WorkRequest : WorkManager를 통해 실제 요청하게될 개별 작업입니다. WorkRequest는 처리해야 할 작업인 Worker와 작업 반복 여부 및 작업 실행조건, 제약 사항 등 이 작업을 어떻게 처리할 것인지에 대한 정보가 담겨 있습니다. WorkRequest는 반복 여부에 따라 다음의 두 가지로 나뉘어 집니다.
OneTimeWorkRequest : 반복하지 않을 작업, 즉 한번만 실행할 작업의 요청을 나타내는 클래스입니다.
PeriodicWorkRequest : 여러 번 실행할 작업의 요청을 나타내는 클래스입니다.
import androidx.work.worker
class EXWorker : Worker() {
override fun doWork() : Result {
// .. do something
return Result.sucess()
}
}
/* 코틀린에 정의된 인라인 함수 OneTimeWorkRequestBuilder */
var workRequest = OneTimeWorkRequestBuilder<EXWorker>().build()
/* 자바에서는 OneTimeWorkRequest 클래스내에 정의된 OneTimeWorkRequest.Builder를 사용해야 함 */
var workRequest = OneTimeWorkRequest.Builder(EXWorker::class.java)
var WorkRequest = OneTimeWorkRequestBuilder<EXWorker>().build()
val workManager = WorkManager.getInstance()
workManager?.enqueue(workRequest)
/* 반복 시간에 사용할 수 있는 가장 짧은 최소값은 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)
val workA = OneTimeWorkRequestBuilder<AWorker>().build()
val workB = OneTimeWorkRequestBuilder<BWorker>().build()
WorkManager.getInstance()?.apply{
beginWith(workA).the(workB).enqueue()
}
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 객체로 전달됩니다.
val input = mapOf("question" to "answer")
/* Data클래스의 Builder를 사용해서 Data 객체를 생성한다 */
val inputData = Data.Builder().putAll(input).build()
val requestWork = OneTimeWorkRequestBuilder<EXWorker>()
.setInputData(inputData)
.build()
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
// cancelWork 작업을 WorkManager의 큐에 추가
WorkManager.getInstance()?.enqueue(cancelWork)
// cancelWork의 id를 이용해서 작업을 취소
WorkManager.getInstnace()?.cancelWorkById(cancelWork.id)
val cancelWork = OnetimeRequestBuilder<CancelWorker>()
.addTag("cancel work tag")
WorkManager.getInstnace()?.cancelWorkById("cancel work tag")
-> isCancelled() == true
override fun doWork() : Result {
if (isStopped) {
/* 작업이 멈추었을 때 대비한 코드 */
if (isCancelled) {
/* 작업이 취소 되었을 때 대비한 코드 */
}
}
}
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)
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