작업 요청 정의

시작 가이드에서는 간단한 WorkRequest를 만들고 이를 큐에 추가하는 방법을 살펴보았습니다.

이 가이드에서는 다음과 같은 일반적인 사용 사례를 처리하기 위해 WorkRequest 객체를 정의하고 맞춤설정하는 방법을 알아봅니다.

  • 일회성 및 반복 작업 예약
  • Wi-Fi 또는 충전과 같은 작업 제약 조건 설정
  • 작업 실행의 최소 지연 보장
  • 재시도 및 백오프 전략 설정
  • 작업에 입력 데이터 전달
  • 태그를 사용하여 관련 작업 그룹화

개요

작업은 WorkRequest를 통해 WorkManager에 정의됩니다. WorkManager로 작업을 예약하려면 먼저 WorkRequest 객체를 만든 후 이 객체를 큐에 추가해야 합니다.

Kotlin


val myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest)

Java


WorkRequest myWorkRequest = ...
WorkManager.getInstance(myContext).enqueue(myWorkRequest);

WorkRequest 객체에는 WorkManager에서 작업을 예약하고 실행하는 데 필요한 모든 정보가 들어 있습니다. 여기에는 작업을 실행하기 위해 충족해야 하는 제약 조건, 그리고 지연 또는 반복 간격과 같은 예약 정보 및 재시도 구성이 포함되며 입력 데이터를 기반으로 작업하는 경우 입력 데이터도 포함될 수 있습니다.

WorkRequest 자체는 기본 추상 클래스입니다. 이 클래스에서 파생된 구현은 OneTimeWorkRequestPeriodicWorkRequest, 두 가지이며 요청을 만드는 데 사용할 수 있습니다. 이름에서 알 수 있듯이 OneTimeWorkRequest는 일회성(반복 없음) 작업을 예약하는 데 유용하고 PeriodicWorkRequest는 일정한 간격으로 반복되는 작업을 예약하는 데 더 적합합니다.

일회성 작업 예약

추가 구성이 필요 없는 간단한 작업에는 정적 메서드 from을 사용합니다.

Kotlin


val myWorkRequest = OneTimeWorkRequest.from(MyWork::class.java)

Java


WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);

더 복잡한 작업에는 빌더를 사용할 수 있습니다.

Kotlin

val uploadWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       // Additional configuration
       .build()

Java

WorkRequest uploadWorkRequest =
   new OneTimeWorkRequest.Builder(MyWork.class)
       // Additional configuration
       .build();

신속 처리 작업 예약

WorkManager 2.7.0에는 신속 처리 작업의 개념이 도입되었습니다. 이를 통해 WorkManager는 시스템에 더 효율적인 리소스 액세스 제어 권한을 부여하면서 중요한 작업을 실행할 수 있습니다.

신속 처리 작업은 다음과 같은 특성으로 잘 알려져 있습니다.

  • 중요도: 신속 처리 작업은 사용자에게 중요하거나 사용자가 시작한 작업에 적합합니다.
  • 속도: 신속 처리 작업은 즉시 시작되어 몇 분 안에 끝나는 짧은 작업에 가장 적합합니다.
  • 할당량: 포그라운드 실행 시간을 제한하는 시스템 수준 할당량에 따라 신속 처리 작업의 시작 가능 여부가 결정됩니다.
  • 전원 관리: 절전 모드, 잠자기와 같은 전력 관리 제한사항은 신속 처리 작업에 영향을 미칠 가능성이 적습니다.
  • 지연 시간: 시스템의 현재 워크로드로 처리가 가능한 경우 시스템은 신속 처리 작업을 즉시 실행합니다. 즉, 신속 처리 작업은 지연 시간에 민감하며 나중에 실행되도록 예약할 수 없습니다.

신속 처리 작업의 잠재적 사용 사례로 채팅 앱에서 사용자가 메시지 또는 첨부된 이미지를 전송하려는 경우를 들 수 있습니다. 마찬가지로 결제/구독 흐름을 처리하는 앱도 신속 처리 작업을 사용하는 것이 좋습니다. 이러한 작업은 사용자에게 중요하고, 백그라운드에서 빠르게 실행되고, 즉시 시작해야 하고, 사용자가 앱을 닫아도 계속 실행되어야 하기 때문입니다.

할당량

시스템은 신속 처리 작업을 실행하기 전에 실행 시간을 신속 처리 작업에 할당해야 합니다. 실행 시간은 무제한이 아닙니다. 대신 각 앱은 실행 시간 할당량을 받습니다. 앱에서 실행 시간을 사용하고 할당된 할당량에 도달하면 할당량이 새로고침될 때까지 더 이상 신속 처리 작업을 실행할 수 없습니다. 이를 통해 Android는 애플리케이션 간의 리소스 균형을 더 효과적으로 유지할 수 있습니다.

앱에서 사용할 수 있는 실행 시간은 대기 버킷과 프로세스 중요도를 기준으로 합니다.

실행 시간 할당량으로 인해 신속 처리 작업을 즉시 실행할 수 없는 경우 어떻게 처리해야 할지 결정할 수 있습니다. 자세한 내용은 아래 스니펫을 참고하세요.

신속 처리 작업 실행

WorkManager 2.7부터 앱은 setExpedited()를 호출하여 신속 처리 작업을 사용하는 WorkRequest가 최대한 빨리 실행되도록 선언할 수 있습니다. 다음 코드 스니펫은 setExpedited()의 사용 방법을 보여주는 예입니다.

Kotlin

val request = OneTimeWorkRequestBuilder<SyncWorker>()
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build()

WorkManager.getInstance(context)
    .enqueue(request)

Java

OneTimeWorkRequest request = new OneTimeWorkRequestBuilder<T>()
    .setInputData(inputData)
    .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
    .build();

이 예에서는 OneTimeWorkRequest의 인스턴스를 초기화하고 그 인스턴스에서 setExpedited()를 호출합니다. 그러면 이 요청은 신속 처리 작업이 됩니다. 할당량이 허용되면 신속 처리 작업이 즉시 백그라운드에서 실행됩니다. 할당량이 사용된 경우 OutOfQuotaPolicy 매개변수는 요청이 신속 처리되지 않은 일반 작업으로 실행되어야 함을 나타냅니다.

하위 호환성 및 포그라운드 서비스

신속 처리 작업의 하위 호환성을 유지하기 위해 Android 12보다 낮은 버전의 플랫폼에서는 WorkManager가 포그라운드 서비스를 실행할 수도 있습니다. 포그라운드 서비스는 사용자에게 알림을 표시할 수 있습니다.

Worker의 getForegroundInfoAsync()getForegroundInfo() 메서드를 사용하면 Android 12 이전 버전에서 setExpedited()를 호출할 때 WorkManager가 알림을 표시할 수 있습니다.

작업이 신속 처리 작업으로 실행되도록 요청하려면 모든 ListenableWorker에서 getForegroundInfo 메서드를 구현해야 합니다.

Android 12 이상을 타겟팅하는 경우 포그라운드 서비스는 상응하는 setForeground 메서드를 통해 계속 사용할 수 있습니다.

작업자

작업자는 실행 중인 작업이 신속 처리 작업인지 알 수 없습니다. 그러나, WorkRequest가 신속 처리되면 작업자는 Android 일부 버전에서 알림을 표시할 수 있습니다.

이를 위해 WorkManager는 getForegroundInfoAsync() 메서드를 제공하며, 필요 시 WorkManager가 ForegroundService를 시작할 수 있는 알림을 표시하도록 구현해야 합니다.

CoroutineWorker

CoroutineWorker를 사용하는 경우 getForegroundInfo()를 구현해야 합니다. 그런 다음, doWork() 내의 setForeground()로 전달합니다. 이렇게 하면 Android 12 이전 버전에서 알림이 생성됩니다.

다음 예를 참고하세요.

  class ExpeditedWorker(appContext: Context, workerParams: WorkerParameters):
   CoroutineWorker(appContext, workerParams) {

   override suspend fun getForegroundInfo(): ForegroundInfo {
       return ForegroundInfo(
           NOTIFICATION_ID, createNotification()
       )
   }

   override suspend fun doWork(): Result {
       TODO()
   }

    private fun createNotification() : Notification {
       TODO()
    }

}

할당량 정책

앱이 실행 할당량에 도달할 경우 신속 처리 작업을 어떻게 처리할지 제어할 수 있습니다. 계속하려면 setExpedited()를 전달하면 됩니다.

  • OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST: 작업이 일반 작업 요청으로 실행됩니다. 위의 스니펫이 이를 보여줍니다.
  • OutOfQuotaPolicy.DROP_WORK_REQUEST: 할당량이 충분하지 않으면 요청이 취소됩니다.

샘플 앱

WorkManager 2.7.0에서 신속 처리 작업을 사용하는 방법에 관한 전체 예는 GitHub의 WorkManagerSample을 참고하세요.

신속 처리 작업 지연

시스템에서는 신속 처리 작업이 호출된 후 최대한 빨리 주어진 작업을 실행하려고 합니다. 그러나 다른 유형의 작업과 마찬가지로 다음과 같은 경우 시스템에서 새로운 신속 처리 작업의 시작을 지연시킬 수 있습니다.

  • 부하: 시스템 부하가 너무 높습니다. 이는 이미 너무 많은 작업이 실행되고 있거나 시스템에 메모리가 충분하지 않을 때 발생할 수 있습니다.
  • 할당량: 신속 처리 작업 할당량 한도가 초과되었습니다. 신속 처리 작업은 앱 대기 버킷에 기반하는 할당량 시스템을 사용하고 롤링 시간 내에서 최대 실행 시간을 제한합니다. 신속 처리 작업에 사용되는 할당량은 다른 유형의 백그라운드 작업에 사용되는 할당량보다 더 제한적입니다.

주기적 작업 예약

경우에 따라 앱에서는 특정 작업을 주기적으로 실행해야 할 수 있습니다. 예를 들어 주기적으로 데이터를 백업하거나 최신 콘텐츠를 앱에 다운로드하거나 로그를 서버에 업로드해야 할 수 있습니다.

다음은 PeriodicWorkRequest를 사용하여 주기적으로 실행되는 WorkRequest 객체를 만드는 방법입니다.

Kotlin


val saveRequest =
       PeriodicWorkRequestBuilder<SaveImageToFileWorker>(1, TimeUnit.HOURS)
    // Additional configuration
           .build()

Java


PeriodicWorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class, 1, TimeUnit.HOURS)
           // Constraints
           .build();

이 예시에서 작업은 1시간 간격으로 예약됩니다.

간격 주기는 반복 사이의 최소 시간으로 정의됩니다. Worker가 실행될 정확한 시간은 WorkRequest 객체에서 사용하는 제약 조건과 시스템에서 실행하는 최적화에 따라 달라집니다.

가변 실행 간격

작업의 특성으로 인해 실행 타이밍에 민감하다면 그림 1과 같이 각 간격 주기 안에서 가변 기간 내에 실행되도록 PeriodicWorkRequest를 구성할 수 있습니다.

주기적 작업에 가변 간격을 설정할 수 있습니다. 반복 간격, 그리고 반복 간격 후의 특정 시간을 지정하는 가변 간격을 정의합니다. WorkManager는 매 주기의 가변 간격 중 특정 시점에 작업 실행을 시도합니다.

그림 1. 반복 간격을 보여주는 다이어그램입니다. 작업을 실행할 수 있는 가변 기간이 포함되어 있습니다.

가변 기간으로 주기적 작업을 정의하려면 PeriodicWorkRequest를 만들 때 flexIntervalrepeatInterval과 함께 전달합니다. 가변 기간은 repeatInterval - flexInterval에서 시작하여 간격 끝으로 이동합니다.

다음은 한 시간마다 마지막 15분 동안 실행할 수 있는 주기적 작업의 예입니다.

Kotlin


val myUploadWork = PeriodicWorkRequestBuilder<SaveImageToFileWorker>(
       1, TimeUnit.HOURS, // repeatInterval (the period cycle)
       15, TimeUnit.MINUTES) // flexInterval
    .build()

Java


WorkRequest saveRequest =
       new PeriodicWorkRequest.Builder(SaveImageToFileWorker.class,
               1, TimeUnit.HOURS,
               15, TimeUnit.MINUTES)
           .build();

반복 간격은 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS 이상이어야 하고 가변 간격은 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS 이상이어야 합니다.

제약 조건이 주기적 작업에 미치는 영향

제약 조건을 주기적 작업에 적용할 수 있습니다. 예를 들어 사용자의 기기가 충전 중일 때만 작업이 실행되도록 작업 요청에 제약 조건을 추가할 수 있습니다. 이 경우 정의된 반복 간격이 지나도 PeriodicWorkRequest는 조건이 충족될 때까지 실행되지 않습니다. 이로 인해 작업의 특정 실행이 지연될 수 있고 실행 간격 내에 조건이 충족되지 않으면 건너뛸 수도 있습니다.

작업 제약 조건

제약 조건은 최적의 조건이 충족될 때까지 작업이 지연되도록 합니다. WorkManager에서 사용할 수 있는 제약 조건은 다음과 같습니다.

네트워크 유형 작업을 실행하는 데 필요한 네트워크 유형을 제한합니다. 예: Wi-Fi(UNMETERED)
BatteryNotLow true로 설정하면 기기가 배터리 부족 모드일 때 작업이 실행되지 않습니다.
RequiresCharging true로 설정하면 기기가 충전 중일 때만 작업이 실행됩니다.
기기 유휴 true로 설정하면 작업이 실행되기 전에 사용자 기기가 유휴 상태여야 합니다. 이는 사용자 기기에서 활발하게 실행되는 다른 앱의 성능에 부정적인 영향을 줄 수 있는 배치 작업을 실행하는 데 유용합니다.
StorageNotLow true로 설정하면 사용자의 기기 저장공간이 너무 부족하면 작업이 실행되지 않습니다.

일련의 제약 조건을 만들고 이를 일부 작업과 연결하려면 Contraints.Builder()를 사용하여 Constraints 인스턴스를 만들어 WorkRequest.Builder()에 할당합니다.

예를 들어 다음 코드는 사용자의 기기가 충전 중이고 Wi-Fi에 연결되어 있을 때만 실행되는 작업 요청을 빌드합니다.

Kotlin


val constraints = Constraints.Builder()
   .setRequiredNetworkType(NetworkType.UNMETERED)
   .setRequiresCharging(true)
   .build()

val myWorkRequest: WorkRequest =
   OneTimeWorkRequestBuilder<MyWork>()
       .setConstraints(constraints)
       .build()

Java


Constraints constraints = new Constraints.Builder()
       .setRequiredNetworkType(NetworkType.UNMETERED)
       .setRequiresCharging(true)
       .build();

WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setConstraints(constraints)
               .build();

여러 제약 조건이 지정되면 제약 조건이 모두 충족될 때만 작업이 실행됩니다.

작업 실행 중에 제약 조건이 충족되지 않는 경우 WorkManager에서 작업자를 중지합니다. 그런 다음 모든 제약 조건이 충족될 때 작업을 다시 시도합니다.

지연된 작업

작업이 큐에 추가될 때 작업에 제약 조건이 없거나 모든 제약 조건이 충족되는 경우 시스템에서 즉시 작업을 실행하기로 선택할 수 있습니다. 작업을 즉시 실행하지 않으려면 최소 초기 지연 후 시작하도록 작업을 지정할 수 있습니다.

다음은 작업이 큐에 추가되고 최소 10분 후에 실행되도록 설정하는 방법의 예입니다.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setInitialDelay(10, TimeUnit.MINUTES)
   .build()

Java


WorkRequest myWorkRequest =
      new OneTimeWorkRequest.Builder(MyWork.class)
               .setInitialDelay(10, TimeUnit.MINUTES)
               .build();

이 예에서는 OneTimeWorkRequest의 초기 지연을 설정하는 방법을 보여주지만 PeriodicWorkRequest의 초기 지연을 설정할 수도 있습니다. 이 경우 주기적 작업의 첫 번째 실행만 지연됩니다.

재시도 및 백오프 정책

WorkManager에서 작업을 다시 시도해야 하는 경우 작업자에서 Result.retry()를 반환하면 됩니다. 그러면 백오프 지연백오프 정책에 따라 작업이 다시 예약됩니다.

  • 백오프 지연은 첫 번째 시도 후 작업을 다시 시도하기 전에 대기해야 하는 최소 시간을 지정합니다. 이 값은 10초(또는 MIN_BACKOFF_MILLIS) 미만일 수 없습니다.

  • 백오프 정책은 다음 재시도의 백오프 지연이 시간 경과에 따라 증가하는 방식을 정의합니다. WorkManager에서는 두 가지 백오프 정책 LINEAR, EXPONENTIAL을 지원합니다.

모든 작업 요청에는 백오프 정책과 백오프 지연이 있습니다. 기본 정책은 30초 지연이 있는 EXPONENTIAL이지만 작업 요청 구성에서 재정의할 수 있습니다.

다음은 백오프 지연 및 정책을 맞춤설정하는 예입니다.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .setBackoffCriteria(
       BackoffPolicy.LINEAR,
       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
       TimeUnit.MILLISECONDS)
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
               .setBackoffCriteria(
                       BackoffPolicy.LINEAR,
                       OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
                       TimeUnit.MILLISECONDS)
               .build();

이 예시에서는 최소 백오프 지연이 최소 허용 값 10초로 설정됩니다. 정책이 LINEAR이므로 재시도 간격은 새로 시도할 때마다 약 10초씩 증가합니다. 예를 들어 첫 번째 실행이 Result.retry()로 끝나면 10초 후에 다시 시도되고 다음 시도 후에도 작업이 계속 Result.retry()를 반환하면 20초, 30초, 40초 등으로 재시도 간격을 늘려 작업을 다시 시도합니다. 백오프 정책이 EXPONENTIAL로 설정된 경우 재시도 길이는 20, 40, 80 등의 시퀀스로 이어집니다.

작업에 태그 지정

모든 작업 요청에는 고유 식별자가 있습니다. 고유 식별자는 작업을 취소하거나 작업 진행 상황을 관찰하기 위해 나중에 작업을 식별하는 데 사용할 수 있습니다.

논리적으로 관련된 작업 그룹이 있다면 이러한 작업 항목에 태그를 지정하는 것도 유용할 수 있습니다. 태그를 사용하면 작업 요청 그룹을 함께 실행할 수 있습니다.

예를 들어 WorkManager.cancelAllWorkByTag(String)은 특정 태그가 있는 모든 작업 요청을 취소하고 WorkManager.getWorkInfosByTag(String)은 현재 작업 상태를 확인하는 데 사용할 수 있는 WorkInfo 객체 목록을 반환합니다.

다음 코드는 작업에 'cleanup' 태그를 추가하는 방법을 보여줍니다.

Kotlin


val myWorkRequest = OneTimeWorkRequestBuilder<MyWork>()
   .addTag("cleanup")
   .build()

Java


WorkRequest myWorkRequest =
       new OneTimeWorkRequest.Builder(MyWork.class)
       .addTag("cleanup")
       .build();

마지막으로 단일 작업 요청에 여러 태그를 추가할 수 있습니다. 내부적으로 이러한 태그는 문자열 집합으로 저장됩니다. WorkRequest와 연결된 태그 집합을 가져오려면 WorkInfo.getTags()를 사용하세요.

ListenableWorker.getTags()를 통해 Worker 클래스에서 태그 집합을 가져올 수 있습니다.

입력 데이터 할당

작업을 실행하려면 작업에 입력 데이터가 필요할 수 있습니다. 예를 들어 이미지 업로드를 처리하는 작업은 이미지의 URI를 입력으로 업로드해야 할 수 있습니다.

입력값은 Data 객체에 키-값 쌍으로 저장되고 작업 요청에서 설정할 수 있습니다. WorkManager는 작업을 실행할 때 작업에 입력 Data를 제공합니다. Worker 클래스는 Worker.getInputData()를 호출하여 입력 인수에 액세스할 수 있습니다. 아래 코드는 입력 데이터가 필요한 Worker 인스턴스를 만드는 방법과 이 인스턴스를 작업 요청에 전송하는 방법을 보여줍니다.

Kotlin


// Define the Worker requiring input
class UploadWork(appContext: Context, workerParams: WorkerParameters)
   : Worker(appContext, workerParams) {

   override fun doWork(): Result {
       val imageUriInput =
           inputData.getString("IMAGE_URI") ?: return Result.failure()

       uploadFile(imageUriInput)
       return Result.success()
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
val myUploadWork = OneTimeWorkRequestBuilder<UploadWork>()
   .setInputData(workDataOf(
       "IMAGE_URI" to "http://..."
   ))
   .build()

Java


// Define the Worker requiring input
public class UploadWork extends Worker {

   public UploadWork(Context appContext, WorkerParameters workerParams) {
       super(appContext, workerParams);
   }

   @NonNull
   @Override
   public Result doWork() {
       String imageUriInput = getInputData().getString("IMAGE_URI");
       if(imageUriInput == null) {
           return Result.failure();
       }

       uploadFile(imageUriInput);
       return Result.success();
   }
   ...
}

// Create a WorkRequest for your Worker and sending it input
WorkRequest myUploadWork =
      new OneTimeWorkRequest.Builder(UploadWork.class)
           .setInputData(
               new Data.Builder()
                   .putString("IMAGE_URI", "http://...")
                   .build()
           )
           .build();

마찬가지로 Data 클래스를 사용하여 반환 값을 출력할 수 있습니다. 입력 및 출력 데이터는 입력 매개변수 및 반환된 값 섹션에서 자세히 다룹니다.

다음 단계

상태 및 관찰 페이지에서 작업 상태 및 작업 진행률을 모니터링하는 방법을 자세히 알아봅니다.