본문 바로가기

안드로이드/Schedule

WorkManager(feat.Hilt)

Background Task

image

백그라운드 작업을 선택할 때 위와 같이 조건에 따라 API를 선택할 수 있다.

WorkManager

image

특징

1. 제약 조건

  • Wifi 상태에서 작업 실행
  • 배터리 충전 중일 경우 실행
  • 기기가 유휴 상태일 경우

다양한 조건을 설정하여 조건에 맞는 경우 작업을 실행할 수 있다.

2. 작업 예약 관리

  • 일회성 또는 반복적으로 실행할 작업을 예약할 수 있다.
  • 작업 그룹을 함께 모니터링하거나 취소할 수 있다.
  • 예약된 작업은 내부적으로 관리되는 SQLite 데이터베이스에 저장되며 WorkManager에서 기기를 재부팅해도 작업이 유지되고 다시 예약되도록 보장
  • 절전 기능을 사용하고 권장사항(Doze Mode)을 준수
    • 배터리 소모를 우려하지 않아도 됨

3. 신속 처리 작업

  • 백그라운드에서 즉시 실행할 작업을 예약할 수 있다.

4. 재시도 정책

  • 작업이 실패해도 다시 실행할 수 있다. Result.retry()

5. 작업 체이닝

  • 순차적으로 실행되어야 할 작업과 동시에 실행될 작업을 설정할 수 있다.

주요 클래스

  • Worker
    • 백그라운드에서 실행하고자 하는 실제 작업의 코드를 작성하는 클래스
    • Worker 클래스를 상속 받고 doWork() 메서드를 오버라이드하여 사용
  • WorkRequest
    • 작업 실행 요청에 관한 클래스
    • WorkRequest를 만드는 과정에서 Worker를 전달
    • WorkRequest를 만들 때 Worker를 실행할 시점에 적용되는 Constraints등을 지정할 수도 있다.
      • Constraints는 여러가지 조건을 지정할 수 있는 객체 (ex: WIFI 연결 시에만 작업 실행, 배터리 충전 중일 경우 작업 실행 등등)
  • WorkManager
    • 실제로 WorkRequest를 예약하고 실행
    • 지정된 제약 조건을 준수하면서 시스템 리소스에 부하를 분산하는 방식으로 WorkRequest를 예약

사용 방법 (Hilt WorkManager)

1. 초기 세팅

app/build.gradle

dependencies {
    // WorkManager dependency
    implementation "androidx.work:work-runtime-ktx:2.10.0"

    // Hilt with WorkManager
    implementation("androidx.hilt:hilt-work:1.7.5")
    kapt("androidx.hilt:hilt-compiler:1.2.0")
}

2. Worker Class 생성

class WorkerClass(
	appContext: Context,
	workerParams: WorkerParameters
): Worker(appContext, workerParams) {

    override fun doWork(): Result {
        // TODO TASK
    }
}

2-2. WorkManager Hilt로 주입 받기

WorkManager에서도 Repository 등 여러가지 객체를 사용해야 하는 경우가 생긴다.

Hilt는 WorkManager 또한 지원해주기 때문에 Hilt를 통해 주입 받을 수 있다.

 

Setting

1. Manifest 추가

<application
	...
>
	...
	<provider
	    android:name="androidx.startup.InitializationProvider"
	    android:authorities="${applicationId}.androidx-startup"
	    tools:node="remove">
	</provider>
	...
</application>

 

2. Application

@HiltAndroidApp
class MyApplication: Application(), Configuration.Provider {
    @Inject
    lateinit var workerFactory: HiltWorkerFactory

    override val workManagerConfiguration: Configuration
        get() = Configuration.Builder()
            .setWorkerFactory(workerFactory)
            .build()
}

 

  1. ApplicationWorkerManager가 Hilt를 사용하기 위해서는 HiltWorkerFactory가 필요하다.
  2. WorkManager가 HiltWorkerFactory를 통해 Worker를 생성해주기 때문에 위와 같이 HiltWorkerFactory를 주입해주는 작업이 필요하다.

3. Worker

@HiltWorker
class WorkerClass @AssistedInject constructor(
	@Assisted appContext: Context,
	@Assisted workerParams: WorkerParameters,
	private val repository: Repository
): Worker(appContext, workerParams) {

	override fun doWork(): Result {
        // TODO TASK
	}
}
  1. @HiltWorker 를 통해 androidx.work.ListenableWorker 를 주입 받는다.
  2. ListenableWorker는 WorkManager에서 비동기로 작업을 수행할 수 있는 클래스이다.
    • @AssistedInject : 런타임 데이터를 포함한 객체를 factory 패턴으로 생성할 수 있다.
      • 기존 @Inject 는 미리 명시 된 모듈에서만 의존성을 주입 받을 수 있다면 @AssistedInject 는 런타임 도중에 주입이 가능하다.
      • 위의 Application에서 런타임 도중 초기화 시켜준 HiltWorkerFactory를 @AssistedInject 로 주입 시켜주는 것으로 보인다.
    • @Assisted : 해당 어노테이션이 달린 생성자들은 @AssistedInject 로 주입 받은 HiltWorkerFactory를 통해 데이터를 주입 받는다.
      • 해당 어노테이션이 달리지 않은 생성자는 기존과 같이 모듈에서 주입 받는다.

3. WorkRequest

  1. 일회성 작업 - OneTimeWorkRequest , OneTimeWorkRequestBuilder
  2. 주기적인 작업 - PeriodicWorkRequestBuilder
val val constraints = Constraints.Builder()
                .setRequiredNetworkType(NetworkType.UNMETERED) //Wifi 연결 시 실행
                .setRequiresCharging(true) // 충전 중
                .setRequiresBatteryNotLow() // 배터리 충분
                .setRequiresDeviceIdle() // 유휴 상태
                .setRequiresStorageNotLow() // 저장 공간
                .build()

val request = OneTimeWorkRequestBuilder<SyncWorker>()
                .setInitialDelay() //작업 지연
                .setConstraints(constraints)
                .build()

이외에도 다양한 작업과 기능들이 존재한다.

자세한 내용은 공식문서에서 확인이 가능하다.


특정 시간은 어떻게?

WorkManager를 사용하다보니 특정 시간에 작업을 수행하는 기능이 없었다.

그렇다면 AlarmManager를 사용해야 하는가?

동기화는 네트워크와 연결하여 이미지를 포함한 데이터들을 주고 받게 되는데 이를 AlarmManager에서 작업을 하게 되면 배터리 소모가 클 수 있다. (이에 대한 자세한 내용은 추가적인 학습 필요)

다행이 WorkManager에는 지연이라는 기능이 있었다. 등록한 시점부터 지연 시간을 지정하여 해당 시간 이후에 작업이 실행되게 할 수 있었고, 이를 통해 문제를 해결할 수 있었다.

 

현재 시간과 트리거 시간 사이의 남은 시간을 구한 후 이를 WokrManager 지연 시간으로 등록하면 특정 시간에 작업을 수행할 수 있게 된다.

val duration = getDurationTime()
val workRequest = OneTimeWorkRequestBuilder<SynchronizationWorker>()
                                // 현재 시간부터 정각까지 지연 후 실행
                .setInitialDelay(duration.seconds, TimeUnit.SECONDS)
                .setConstraints(constraints)
                .build()

private fun getDurationTime(): Duration {
    val triggerHour = HOUR
    val triggerMinute = MINUTE

    val newSyncTime = LocalTime.of(triggerHour, triggerMinute)
    val now: LocalDateTime = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)
    val nowTime: LocalTime = now.toLocalTime()

    val plusDay = if (nowTime == newSyncTime || nowTime.isAfter(newSyncTime)) 1 else 0

    val nextTriggerTime = now.plusDays(plusDay.toLong())
        .withHour(newSyncTime.hour)
        .withMinute(newSyncTime.minute)

    return Duration.between(LocalDateTime.now(), nextTriggerTime) //다음 정각까지 남은 시간
}

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

[안드로이드] Alarm Manager  (0) 2024.01.17