📕 WorkManager란?
Android Jetpack Library의 WorkManager는 Background Task를 위해 만들어졌습니다.
안드로이드 M(API 23) 부터 Background Task 제약 사항들이 점차 추가되며 개발자들은 Service, JobSchduler, JobDispatcher등을 사용해 개발했지만 API의 문제들로 인해 WorkManager가 등장하게 되었습니다. 이 글에선 기존 API의 문제점과 Workmanager에 대해 알아보겠습니다.
🤔 Background 상태의 앱이 왜 자동으로 종료될까요 ?
안드로이드 커널은 리눅스 커널은 기반으로 개발 되었지만 가장 큰 차이점은 Swap Space가 존재하지 않습니다.
📌Swap Space
- 주기억장치(RAM)의 용량이 부족할 때 비활성화 된 메모리를 HDD로 이동시킵니다.
- 이 때, HDD의 일부 공간을 Swap Space로 제공합니다.
- HDD는 RAM에 비해 속도가 느리기 때문에 성능에 영향을 미칠 수 있습니다.
- 대표적인 스왑 알고리즘으로 LRU(Least Recently Used)이 있습니다.
안드로이드는 기본적으로 Swap Space가 없습니다. LKM(Low Memory Killer) OOM(Out of Memory) Killer를 사용해 프로세스를 강제 종료 시킵니다. 자세한 내용은 아래글을 참조해 주세요 !
📌Background Task
1️⃣ Service
안드로이드를 처음 공부할 때 안드로이드 4대 컴포넌트 중 Service는 백그라운드 작업을 수행한다고 배웠습니다. 그 외에 서비스를 사용해야 하는 이유는 뭐가 있을까요 ?
- 시스템에게 해당 프로세스가 오래 걸리는 작업임을 알립니다.
- 서비스를 사용함으로써 알맞은 oom_adj_score를 부여합니다.
- 독립적인 프로세스에서 동작을 실행할 수 있습니다.
이 중 "독립적인 프로세스에서 동작을 실행할 수 있습니다." 이 부분이 바로 서비스에서 문제를 발생시킵니다. 별도의 프로세스에서 동작한다는 것은 배터리를 많이 소모시킵니다. 이를 해결하기 위해 구글은 "Doze 모드"를 도입하였고 구글은 앱 마켓에 등록할 수 있는 앱에 제약을 하나씩 추가 하며 점점 발전시켰습니다.
이에 Service를 대체하기 위해 나온것이 Job API 입니다.
2️⃣ Job
잡 스케쥴러는 안드로이드 5버전(API Lv21) 부터 제공했지만 안드로이드 8 버전부터 백그라운드 제약이 생기며 더욱 중요했습니다. JobScheduler를 사용하여 일정한 간격으로 또는 특정 조건이 충족될 때 실행되어야 하는 작업을 예약할 수 있습니다.
🔨 잡 스케쥴러의 3가지 구성 요소
- 잡 서비스 : Background Task를 구현한 서비스 입니다.
- 잡 인포 : 잡 서비스 정보와 실행될 조건을 지정합니다.
- 잡 스케쥴러 : 잡 인포를 시스템에 등록합니다.
이후 버그가 너무 많아 JobDispatcher로 대체됩니다.
하지만 JobDispatcher 또한 제대로 동작하지 않는 기기가 있기 때문에
이후 JobIntentService로 대체됩니다.
하지만 JobIntentService 또한 원하는 타이밍에 딱 맞게 동작하지 않는 문제가 있었기 때문에
WorkManager가 등장하게 됩니다.
2️⃣ WorkManager
- 시스템 기반의 Background Processing API를 제공하여 구현을 단순화 할 수 있도록 도와줍니다.
- 앱이 포그라운드에 없더라도 백그라운드에서 Job이 수행될 수 있도록 합니다.
- 개발자가 백그라운드 처리과정을 신경쓰지 않아도 되도록 합니다.
- 그렇기 때문에 Service, JobIntentService 등 대부분의서비스 기능을 대체 가능합니다
- 짧은 작업, 오래 걸리는 작업 모두 사용가능합니다.
- 네트워크 상태, 배터리 상태 등으로 트리거가 가능합니다.
- 포그라운드에서도 사용 가능합니다.
- FCM과 같이 사용할 수 있습니다.
🔨 workerManager Component
- WorkManager
처리해야 하는 작업을 자신의 큐에 넣고 관리합니다. 싱글톤으로 구성되어 getInstance()로 WorkManager의 인스턴스를 받아 사용합니다. - Worker
- 추상 클래스이며, 처리해야 하는 백그라운드 작업의 처리 코드를 이 클래스를 상속받아 doWork() 메서드를 오버라이드 하여 작성하게 됩니다.
- doWork()
- 작업을 완료하고 결과에 따라 Worker클래스 내에 정의된 enum인 Result의 값중 하나를 리턴
- SUCCESS, FAILURE, RETRY의 3개 값이 있으며 리턴되는 이 값에 따라 WorkManager는 해당 작업을 마무리 할것인지, 재시도 할것인지, 실패로 정의하고 중단할 것인지를 결정
- doWork()
- 추상 클래스이며, 처리해야 하는 백그라운드 작업의 처리 코드를 이 클래스를 상속받아 doWork() 메서드를 오버라이드 하여 작성하게 됩니다.
- WorkRequest
- WorkManager를 통해 실제 요청하게 될 개별 작업
- 처리해야 할 작업인 Work와 작업 반복 여부 및 작업 실행 조건, 제약 사항등 이 작업을 어떻게 처리할 것인지에 대한 정보가 담겨있습니다
- 반복여부에 따라 onTimeWorkRequest, PeriodicWorkRequest로 나뉜다
- onTimeWorkRequest
- 반복하지 않을 작업, 즉 한번만 실행할 작업의 요청을 나타내는 클래스
- PeriodicWorkRequest
- 여러번 실행할 작업의 요청을 나타내는 클래스
- WorkState
- WorkRequst의 id와 해당 WorkRequest의 현재 상태를 담는 클래스
- 이 WorkState의 상태정보를 이용해서 자신이 요청한 작업의 현재 상태를 파악할 수 있다
- ENQUEUED, RUNNING, SUCCEEDED, FAILED, BLOCKED, CANCLLED의 6개 상태를 가진다
📌 기본 WorkManager 생성
val workManagerA = OneTimeWorkRequestBuilder<WorkManagerA>().build()
WorkManager.getInstance(this).enqueue(workManagerA)
class WorkManagerA(context: Context, workerParameters: WorkerParameters)
: Worker(context, workerParameters) {
override fun doWork(): Result {
for (i in 1..10){
sleep(1000)
Log.d("WorkManagerA", i.toString())
}
return Result.success()
}
}
📌 WorkManager 데이터 전달
- inputData.getInt(key, default Value) : Activity나 Fragment에서 전달한 데이터를 받습니다.
- setInputData( ) : Data Type의 데이터를 전달합니다.
- workDataof ( ): key와 value 형태로 데이터를 전달합니다.
class WorkManagerB(context: Context, workerParameters: WorkerParameters)
: Worker(context, workerParameters) {
override fun doWork(): Result {
val a = inputData.getInt("a", 1000)
val b = inputData.getInt("b", 2000)
val c = inputData.getInt("c", 3000)
Log.d("WorkManagerB", a.toString())
Log.d("WorkManagerB", b.toString())
Log.d("WorkManagerB", c.toString())
val output: Data = workDataOf("result" to 10)
return Result.success(output)
}
}
val myData: Data = workDataOf(
"a" to 10,
"b" to 20
)
val workManagerB = OneTimeWorkRequestBuilder<WorkManagerB>().setInputData(myData).build()
WorkManager.getInstance(this).enqueue(workManagerB)
📌 WorkManager channing
workManager 의 작업 순서를 정합니다.
val workManagerC = OneTimeWorkRequestBuilder<WorkManagerC>().build()
val workManagerD = OneTimeWorkRequestBuilder<WorkManagerD>().build()
val workManagerE = OneTimeWorkRequestBuilder<WorkManagerE>().build()
WorkManager.getInstance(this)
.beginWith(listOf(workManagerC, workManagerD))
.then(workManagerE)
.enqueue()
📌 WorkManager + Coroutine
class WorkManagerF(context: Context, workerParameters: WorkerParameters)
: CoroutineWorker(context, workerParameters) {
override suspend fun doWork(): Result {
return Result.success()
}
suspend fun test1(){
for (i in 1..3){
delay(1000)
Log.d("CoroutineWorker", i.toString())
}
}
suspend fun test2(){
for (i in 1..3){
delay(1000)
Log.d("CoroutineWorker", i.toString())
}
}
}
'Android' 카테고리의 다른 글
[Android] Room TypeConverter (0) | 2024.01.21 |
---|---|
[Android] Room Database (0) | 2024.01.21 |
[Android]Android Memory Process (1) | 2024.01.07 |
Android Daum 우편번호 서비스 API With FireBase Hosting (2) | 2024.01.04 |
Android Base64 Decoding (0) | 2024.01.03 |