Google은 흑인 공동체를 위한 인종 간 평등을 진전시키기 위해 노력하고 있습니다. Google에서 어떤 노력을 하고 있는지 확인하세요.

Firebase JobDispatcher에서 WorkManager로 이전

WorkManager는 Android에서 지연 가능한 백그라운드 작업을 예약하고 실행하기 위한 라이브러리이며 Firebase JobDispatcher를 대체하여 사용하도록 권장됩니다. 다음 가이드는 Firebase JobDispatcher 구현을 WorkManager로 이전하는 과정을 안내합니다.

Gradle 설정

WorkManager 라이브러리를 Android 프로젝트로 가져오려면 앱의 build.gradle 파일에 다음 종속 항목을 추가합니다.

    dependencies {
      def work_version = "2.3.4"

        // (Java only)
        implementation "androidx.work:work-runtime:$work_version"

        // Kotlin + coroutines
        implementation "androidx.work:work-runtime-ktx:$work_version"

        // optional - RxJava2 support
        implementation "androidx.work:work-rxjava2:$work_version"

        // optional - GCMNetworkManager support
        implementation "androidx.work:work-gcm:$work_version"

        // optional - Test helpers
        androidTestImplementation "androidx.work:work-testing:$work_version"
      }
    

JobService에서 Worker로

FirebaseJobDispatcher는 실행되어야 하는 작업을 정의하기 위한 진입점으로 JobService의 서브클래스를 사용합니다. JobService를 직접 사용하거나 SimpleJobService를 사용할 수도 있습니다.

JobService는 다음과 유사합니다.

Kotlin

    import com.firebase.jobdispatcher.JobParameters
    import com.firebase.jobdispatcher.JobService

    class MyJobService : JobService() {
        override fun onStartJob(job: JobParameters): Boolean {
            // Do some work here
            return false // Answers the question: "Is there still work going on?"
        }
        override fun onStopJob(job: JobParameters): Boolean {
            return false // Answers the question: "Should this job be retried?"
        }
    }
    

자바

    import com.firebase.jobdispatcher.JobParameters;
    import com.firebase.jobdispatcher.JobService;

    public class MyJobService extends JobService {
        @Override
        public boolean onStartJob(JobParameters job) {
            // Do some work here

            return false; // Answers the question: "Is there still work going on?"
        }

        @Override
        public boolean onStopJob(JobParameters job) {
            return false; // Answers the question: "Should this job be retried?"
        }
    }
    

SimpleJobService를 사용한다면 재정의된 onRunJob()이 있으며, 이는 @JobResult int 유형을 반환합니다.

주요 차이점은 JobService를 직접 사용하면 onStartJob()이 기본 스레드에서 호출되며 작업을 백그라운드 스레드로 오프로드하는 것이 앱의 책임이라는 점입니다. 반면에 SimpleJobService를 사용하면 백그라운드 스레드에서 작업을 실행할 책임이 이 서비스에 있습니다.

WorkManager에도 유사한 개념이 있습니다. WorkManager의 기본 작업 단위는 ListenableWorker입니다. Worker, RxWorkerCoroutineWorker(Kotlin 코루틴 사용 시)와 같은 작업자의 다른 유용한 하위유형도 있습니다.

JobService가 ListenableWorker에 매핑됨

JobService를 직접 사용한다면 이 클래스가 매핑되는 작업자는 ListenableWorker입니다. SimpleJobService를 사용한다면 대신 Worker를 사용해야 합니다.

위의 예(MyJobService)를 사용하여 ListenableWorker로 변환할 수 있는 방법을 살펴보겠습니다.

Kotlin

    import android.content.Context
    import androidx.work.ListenableWorker
    import androidx.work.ListenableWorker.Result
    import androidx.work.WorkerParameters
    import com.google.common.util.concurrent.ListenableFuture

    class MyWorker(appContext: Context, params: WorkerParameters) :
        ListenableWorker(appContext, params) {

        override fun startWork(): ListenableFuture<ListenableWorker.Result> {
            // Do your work here.
            TODO("Return a ListenableFuture<Result>")
        }

        override fun onStopped() {
            // Cleanup because you are being stopped.
        }
    }
    

자바

    import android.content.Context;
    import androidx.work.ListenableWorker;
    import androidx.work.ListenableWorker.Result;
    import androidx.work.WorkerParameters;
    import com.google.common.util.concurrent.ListenableFuture;

    class MyWorker extends ListenableWorker {

      public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) {
        super(appContext, params);
      }

      @Override
      public ListenableFuture<ListenableWorker.Result> startWork() {
        // Do your work here.
        Data input = getInputData();

        // Return a ListenableFuture<>
      }

      @Override
      public void onStopped() {
        // Cleanup because you are being stopped.
      }
    }
    

WorkManager의 기본 작업 단위는 ListenableWorker입니다. JobService.onStartJob()과 마찬가지로, startWork()는 기본 스레드에서 호출됩니다. 여기서 MyWorkerListenableWorker를 구현하고 비동기식으로 작업 완료 신호를 보내는 데 사용되는 ListenableFuture의 인스턴스를 반환합니다. 이 시점에 고유한 스레딩 전략을 선택해야 합니다.

여기서 ListenableFuture는 최종적으로 Result.success(), Result.success(Data outputData), Result.retry(), Result.failure() 또는 Result.failure(Data outputData) 중 하나일 수 있는 ListenableWorker.Result 유형을 반환합니다. 자세한 내용은 ListenableWorker.Result의 참조 페이지를 확인하세요.

onStopped()는 제약 조건이 더 이상 충족되지 않기 때문에(예를 들어 네트워크를 더 이상 사용할 수 없기 때문에) 또는 WorkManager.cancel…() 메서드가 호출되었기 때문에 ListenableWorker가 중지되어야 한다는 신호를 보내기 위해 호출됩니다. 또한 어떤 이유로든 OS에서 작업을 종료하기로 결정하면 onStopped()가 호출될 수도 있습니다.

SimpleJobService가 Worker에 매핑됨

SimpleJobService 사용 시 위의 작업자는 다음과 같습니다.

Kotlin

    import android.content.Context;
    import androidx.work.Data;
    import androidx.work.ListenableWorker.Result;
    import androidx.work.Worker;
    import androidx.work.WorkerParameters;

    class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
        override fun doWork(): Result {
            TODO("Return a Result")
        }

        override fun onStopped() {
            super.onStopped()
            TODO("Cleanup, because you are being stopped")
        }
    }
    

자바

    import android.content.Context;
    import androidx.work.Data;
    import androidx.work.ListenableWorker.Result;
    import androidx.work.Worker;
    import androidx.work.WorkerParameters;

    class MyWorker extends Worker {

      public MyWorker(@NonNull Context appContext, @NonNull WorkerParameters params) {
        super(appContext, params);
      }

      @Override
      public ListenableWorker.Result doWork() {
        // Do your work here.
        Data input = getInputData();

        // Return a ListenableWorker.Result
        Data outputData = new Data.Builder()
            .putString(“Key”, “value”)
            .build();
        return Result.success(outputData);
      }

      @Override
      public void onStopped() {
        // Cleanup because you are being stopped.
      }
    }
    

여기서 doWork()ListenableWorker.Result의 인스턴스를 반환하여 비동기식으로 작업 완료 신호를 보냅니다. 이 메서드는 백그라운드 스레드에서 작업을 예약하는 SimpleJobService와 유사합니다.

JobBuilder가 WorkRequet에 매핑됨

FirebaseJobBuilder는 Job.Builder를 사용하여 Job 메타데이터를 나타냅니다. WorkManager는 WorkRequest를 사용하여 이 역할을 이행합니다.

WorkManager에는 OneTimeWorkRequestPeriodicWorkRequest와 같은 두 가지 유형의 WorkRequest가 있습니다.

현재 Job.Builder.setRecurring(true)를 사용한다면 새 PeriodicWorkRequest를 생성해야 합니다. 그렇지 않으면 OneTimeWorkRequest를 사용해야 합니다.

다음과 같이 FirebaseJobDispatcher를 사용하여 복잡한 Job을 예약하는 방법을 살펴보겠습니다.

Kotlin

    val input:Bundle = Bundle().apply {
        putString("some_key", "some_value")
    }

    val job = dispatcher.newJobBuilder()
        // the JobService that will be called
        .setService(MyService::class.java)
        // uniquely identifies the job
        .setTag("my-unique-tag")
        // one-off job
        .setRecurring(false)
        // don't persist past a device reboot
        .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
        // start between 0 and 60 seconds from now
        .setTrigger(Trigger.executionWindow(0, 60))
        // don't overwrite an existing job with the same tag
        .setReplaceCurrent(false)
        // retry with exponential backoff
        .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)

        .setConstraints(
            // only run on an unmetered network
            Constraint.ON_UNMETERED_NETWORK,
            // // only run when the device is charging
            Constraint.DEVICE_CHARGING
        )
        .setExtras(input)
        .build()

    dispatcher.mustSchedule(job)
    

자바

    Bundle input = new Bundle();
    input.putString("some_key", "some_value");

    Job myJob = dispatcher.newJobBuilder()
        // the JobService that will be called
        .setService(MyJobService.class)
        // uniquely identifies the job
        .setTag("my-unique-tag")
        // one-off job
        .setRecurring(false)
        // don't persist past a device reboot
        .setLifetime(Lifetime.UNTIL_NEXT_BOOT)
        // start between 0 and 60 seconds from now
        .setTrigger(Trigger.executionWindow(0, 60))
        // don't overwrite an existing job with the same tag
        .setReplaceCurrent(false)
        // retry with exponential backoff
        .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
        // constraints that need to be satisfied for the job to run
        .setConstraints(
            // only run on an unmetered network
            Constraint.ON_UNMETERED_NETWORK,
            // only run when the device is charging
            Constraint.DEVICE_CHARGING
        )
        .setExtras(input)
        .build();

    dispatcher.mustSchedule(myJob);
    

WorkManager와 동일한 목표를 달성하려면 다음과 같이 해야 합니다.

  • Worker의 입력으로 사용될 수 있는 입력 데이터를 빌드합니다.
  • 위에서 FirebaseJobDispatcher에서 정의한 것과 유사한 제약 조건 및 입력 데이터로 WorkRequest를 빌드합니다.
  • WorkRequest를 큐에 추가합니다.

Worker의 입력 설정

FirebaseJobDispatcherBundle을 사용하여 JobService로 입력 데이터를 보냅니다. WorkManager는 대신 Data를 사용합니다. 따라서 다음과 같이 됩니다.

Kotlin

    import androidx.work.workDataOf
    val data = workDataOf("some_key" to "some_val")
    

자바

    import androidx.work.Data;
    Data input = new Data.Builder()
        .putString("some_key", "some_value")
        .build();
    

Worker의 제약 조건 설정

FirebaseJobDispatcherJob.Builder.setConstaints(...)를 사용하여 작업의 제약 조건을 설정합니다. WorkManager는 대신 Constraints를 사용합니다.

Kotlin

    import androidx.work.*

    val constraints: Constraints = Constraints.Builder().apply {
        setRequiredNetworkType(NetworkType.CONNECTED)
        setRequiresCharging(true)
    }.build()
    

자바

    import androidx.work.Constraints;
    import androidx.work.Constraints.Builder;
    import androidx.work.NetworkType;

    Constraints constraints = new Constraints.Builder()
        // The Worker needs Network connectivity
        .setRequiredNetworkType(NetworkType.CONNECTED)
        // Needs the device to be charging
        .setRequiresCharging(true)
        .build();
    

WorkRequest(OneTime 또는 Periodic) 만들기

OneTimeWorkRequestPeriodicWorkRequest를 생성하려면 OneTimeWorkRequest.BuilderPeriodicWorkRequest.Builder를 사용해야 합니다.

위의 Job과 유사한 OneTimeWorkRequest를 생성하려면 다음과 같이 해야 합니다.

Kotlin

    import androidx.work.*
    import java.util.concurrent.TimeUnit

    val constraints: Constraints = TODO("Define constraints as above")
    val request: OneTimeWorkRequest =
         // Tell which work to execute
         OneTimeWorkRequestBuilder<MyWorker>()
             // Sets the input data for the ListenableWorker
            .setInputData(input)
            // If you want to delay the start of work by 60 seconds
            .setInitialDelay(60, TimeUnit.SECONDS)
            // Set a backoff criteria to be used when retry-ing
            .setBackoffCriteria(BackoffPolicy.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS)
            // Set additional constraints
            .setConstraints(constraints)
            .build()
    

자바

    import androidx.work.BackoffCriteria;
    import androidx.work.Constraints;
    import androidx.work.Constraints.Builder;
    import androidx.work.NetworkType;
    import androidx.work.OneTimeWorkRequest;
    import androidx.work.OneTimeWorkRequest.Builder;
    import androidx.work.Data;

    // Define constraints (as above)
    Constraints constraints = ...
    OneTimeWorkRequest request =
        // Tell which work to execute
        new OneTimeWorkRequest.Builder(MyWorker.class)
            // Sets the input data for the ListenableWorker
            .setInputData(inputData)
            // If you want to delay the start of work by 60 seconds
            .setInitialDelay(60, TimeUnit.SECONDS)
            // Set a backoff criteria to be used when retry-ing
            .setBackoffCriteria(BackoffCriteria.EXPONENTIAL, 30000, TimeUnit.MILLISECONDS)
            // Set additional constraints
            .setConstraints(constraints)
            .build();
    

여기서 중요한 차이점은 WorkManager의 작업이 기기 재부팅 전체에 걸쳐 항상 자동으로 유지된다는 것입니다.

PeriodicWorkRequest를 생성하려면 다음과 같이 하세요.

Kotlin

    val constraints: Constraints = TODO("Define constraints as above")
    val request: PeriodicWorkRequest =
    PeriodicWorkRequestBuilder<MyWorker>(15, TimeUnit.MINUTES)
        // Sets the input data for the ListenableWorker
        .setInputData(input)
        // Other setters
        .build()
    

자바

    import androidx.work.BackoffCriteria;
    import androidx.work.Constraints;
    import androidx.work.Constraints.Builder;
    import androidx.work.NetworkType;
    import androidx.work.PeriodicWorkRequest;
    import androidx.work.PeriodicWorkRequest.Builder;
    import androidx.work.Data;

    // Define constraints (as above)
    Constraints constraints = ...

    PeriodicWorkRequest request =
        // Executes MyWorker every 15 minutes
        new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES)
            // Sets the input data for the ListenableWorker
            .setInputData(input)
            . // other setters (as above)
            .build();
    

작업 예약

WorkerWorkRequest를 정의했으므로 이제 작업을 예약할 준비가 되었습니다.

FirebaseJobDispatcher로 정의된 모든 Job에는 Job고유하게 식별하는 데 사용된 tag가 있습니다. 또한 애플리케이션에서 이 Job의 인스턴스가 setReplaceCurrent를 호출하여 Job의 기존 사본을 대체해야 하는지를 스케줄러에 알리는 방법도 제공했습니다.

Kotlin

    val job = dispatcher.newJobBuilder()
        // the JobService that will be called
        .setService(MyService::class.java)
        // uniquely identifies the job
        .setTag("my-unique-tag")
        // don't overwrite an existing job with the same tag
        .setRecurring(false)
        // Other setters...
        .build()
    

자바

    Job myJob = dispatcher.newJobBuilder()
        // the JobService that will be called
        .setService(MyJobService.class)
        // uniquely identifies the job
        .setTag("my-unique-tag")
        // don't overwrite an existing job with the same tag
        .setReplaceCurrent(false)
        // other setters
        // ...

    dispatcher.mustSchedule(myJob);
    

WorkManager를 사용할 때 enqueueUniqueWork()enqueueUniquePeriodicWork() API(각각 OneTimeWorkRequestPeriodicWorkRequest 사용 시)를 사용하여 동일한 결과를 얻을 수 있습니다. 자세한 내용은 WorkManager.enqueueUniqueWork()WorkManager.enqueueUniquePeriodicWork()의 참조 페이지를 확인하세요.

이 작업은 다음과 유사합니다.

Kotlin

    import androidx.work.*

    val request: OneTimeWorkRequest = TODO("A WorkRequest")
    WorkManager.getInstance(myContext)
        .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, request)
    

자바

    import androidx.work.ExistingWorkPolicy;
    import androidx.work.OneTimeWorkRequest;
    import androidx.work.WorkManager;

    OneTimeWorkRequest workRequest = // a WorkRequest;
    WorkManager.getInstance(myContext)
        // Use ExistingWorkPolicy.REPLACE to cancel and delete any existing pending
        // (uncompleted) work with the same unique name. Then, insert the newly-specified
        // work.
        .enqueueUniqueWork("my-unique-name", ExistingWorkPolicy.KEEP, workRequest);
    

작업 취소

FirebaseJobDispatcher 사용 시 다음을 통해 작업을 취소할 수 있습니다.

Kotlin

    dispatcher.cancel("my-unique-tag")
    

자바

    dispatcher.cancel("my-unique-tag");
    

WorkManager를 사용하는 경우 다음을 사용할 수 있습니다.

Kotlin

    import androidx.work.WorkManager
    WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name")
    

자바

    import androidx.work.WorkManager;
    WorkManager.getInstance(myContext).cancelUniqueWork("my-unique-name");
    

WorkManager 초기화

WorkManager는 일반적으로 ContentProvider 또는 Application.onCreate()를 사용하여 앱당 한 번씩 초기화되어야 합니다.

WorkManager는 대개 ContentProvider를 사용하여 자체적으로 초기화됩니다. 그러나 스레드 풀의 크기 및 지정된 시간에 예약될 수 있는 작업자 수와 관련하여 기본값에 약간의 미묘한 차이가 있습니다. 따라서 WorkManager를 맞춤설정해야 할 수도 있습니다.

일반적으로 WorkManager.initialize()를 사용하여 맞춤설정합니다. 이를 통해 Worker를 실행하는 데 사용되는 백그라운드 ExecutorWorkers를 구성하는 데 사용되는 WorkerFactory를 맞춤설정할 수 있습니다. (WorkerFactory는 종속 항목 주입의 컨텍스트에서 유용합니다.) WorkManager의 자동 초기화를 중지했는지 확인하려면 이 메서드에 관한 문서를 읽어보세요.

자세한 내용은 initialize()Configuration.Builder에 관한 문서를 참조하세요.