WorkManager를 사용하면 정교한 작업 요청을 쉽게 설정하고 예약할 수 있습니다. 다음과 같은 시나리오에 API를 사용할 수 있습니다.
- 지정된 순서로 실행되는 체인으로 연결된 시퀀스 작업
- 앱에서 이름이 동일한 두 시퀀스를 시작하는 경우 발생하는 상황과 관련된 규칙을 포함하는 고유 명명 시퀀스
- 값을 전달하고 반환하는 작업(체인으로 연결된 작업이 포함되며 여기서 각 작업이 체인 내의 다음 작업에 인수를 전달하는 경우)
체인으로 연결된 작업
앱이 다수의 작업을 특정 순서로 실행해야 하는 경우가 있습니다.
WorkManager
를 사용하면 여러 작업과 실행 순서를 지정하는 작업 시퀀스를 만들고 큐에 추가할 수 있습니다.
예를 들어 앱에 workA
, workB
, workC
세 가지의 OneTimeWorkRequest
객체가 있고 이 순서로 작업을 실행해야 한다고 가정하겠습니다. 작업을 큐에 추가하려면 WorkManager.beginWith(OneTimeWorkRequest)
메서드를 사용하여 시퀀스를 만들고 첫 번째 OneTimeWorkRequest
객체를 전달합니다. 그러면 이 메서드가 작업 시퀀스를 정의하는 WorkContinuation
객체를 반환합니다. 그런 다음 WorkContinuation.then(OneTimeWorkRequest)
을 포함하여 나머지 OneTimeWorkRequest
객체를 순서대로 추가하고 마지막으로 WorkContinuation.enqueue()
를 사용하여 전체 시퀀스를 큐에 추가합니다.
Kotlin
WorkManager.getInstance(myContext) .beginWith(workA) // Note: WorkManager.beginWith() returns a // WorkContinuation object; the following calls are // to WorkContinuation methods .then(workB) // FYI, then() returns a new WorkContinuation instance .then(workC) .enqueue()
자바
WorkManager.getInstance(myContext) .beginWith(workA) // Note: WorkManager.beginWith() returns a // WorkContinuation object; the following calls are // to WorkContinuation methods .then(workB) // FYI, then() returns a new WorkContinuation instance .then(workC) .enqueue();
WorkManager
가 각 작업에 지정된 제약조건에 따라 요청 순서대로 작업을 실행합니다. 임의의 작업이 Result.failure()
를 반환하면 전체 시퀀스가 종료됩니다.
beginWith(List<OneTimeWorkRequest>)
호출과 then(List<OneTimeWorkRequest>)
호출 중 하나에 여러 OneTimeWorkRequest
객체를 전달할 수도 있습니다. 단일 메서드 호출에 다수의 OneTimeWorkRequest
객체를 전달하면 WorkManager
가 모든 작업을 (동시에) 실행한 후에 시퀀스의 나머지 객체를 실행합니다. 예:
Kotlin
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue()
자바
WorkManager.getInstance(myContext) // First, run all the A tasks (in parallel): .beginWith(Arrays.asList(workA1, workA2, workA3)) // ...when all A tasks are finished, run the single B task: .then(workB) // ...then run the C tasks (in parallel): .then(Arrays.asList(workC1, workC2)) .enqueue();
WorkContinuation.combine(List<OneTimeWorkRequest>)
메서드로 여러 체인을 결합하여 더 복잡한 시퀀스를 만들 수 있습니다. 예를 들어 다음과 같은 시퀀스를 실행한다고 가정합니다.
WorkContinuation
을 사용하여 복잡한 체인으로 연결된 작업을 설정할 수 있습니다.
이 시퀀스를 설정하려면 별개의 두 체인을 만든 다음 결합하여 제3의 체인을 만듭니다.
Kotlin
val chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB) val chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD) val chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE) chain3.enqueue()
자바
WorkContinuation chain1 = WorkManager.getInstance(myContext) .beginWith(workA) .then(workB); WorkContinuation chain2 = WorkManager.getInstance(myContext) .beginWith(workC) .then(workD); WorkContinuation chain3 = WorkContinuation .combine(Arrays.asList(chain1, chain2)) .then(workE); chain3.enqueue();
이 경우 WorkManager
가 workA
를 실행한 후에 workB
를 실행합니다. 또한 workC
후에 workD
를 실행합니다. WorkManager
는 workB
와 workD
를 모두 완료한 후에 workE
를 실행합니다.
WorkContinuation
메서드에는 특정 상황별 축약을 제공하는 다양한 변형이 있습니다. 자세한 내용은 WorkContinuation
문서를 참고하세요.
고유 작업 시퀀스
beginWith(OneTimeWorkRequest)
대신 beginUniqueWork(String, ExistingWorkPolicy, OneTimeWorkRequest)
호출로 시퀀스를 시작해 고유 작업 시퀀스를 만들 수 있습니다.
고유 작업 시퀀스마다 이름이 있으며, WorkManager
는 한 번에 특정 이름의 작업 시퀀스 하나만 허용합니다. 새 고유 작업 시퀀스를 만들 때는 같은 이름의 미완료 시퀀스가 이미 있는 경우 WorkManager
에서 어떻게 처리할지 지정합니다.
- 기존 시퀀스를 취소하고 새 시퀀스로 교체(REPLACE)
- 기존 시퀀스를 유지(KEEP)하고 새 요청 무시
- 새 시퀀스를 기존 시퀀스에 추가(APPEND)하여 기존 시퀀스의 마지막 작업을 완료한 후에 새 시퀀스의 첫 번째 작업 실행
고유 작업 시퀀스는 여러 번 큐에 추가해서는 안 되는 작업이 있을 때 유용할 수 있습니다. 예를 들어 앱이 네트워크에 데이터를 동기화해야 하는 경우, 이름이 'sync'인 시퀀스를 큐에 추가하고 동일한 이름을 가진 기존 시퀀스가 있는 경우 새 작업을 무시하도록 지정할 수 있습니다. 고유 작업 시퀀스는 긴 작업 체인을 점진적으로 구축해야 하는 경우에도 유용할 수 있습니다. 사용자가 긴 체인으로 연결된 작업을 실행취소할 수 있는 사진 편집 앱을 예로 들어 보겠습니다. 각 실행취소 작업에 시간이 어느 정도 걸릴 수는 있지만 올바른 순서로 실행취소를 처리해야 합니다. 이런 경우 앱에서 'undo' 체인을 만들고 필요에 따라 체인에 각 실행취소 작업을 추가할 수 있습니다.
입력 매개변수 및 반환된 값
작업에 인수를 전달하고 작업에서 결과가 반환되도록 하면 보다 유연하게 활용할 수 있습니다. 전달된 값과 반환된 값은 키-값 쌍입니다. 작업에 인수를 전달하려면 WorkRequest.Builder.setInputData(Data)
메서드를 호출한 후에 WorkRequest
객체를 만듭니다. 이 메서드는 Data
객체를 사용하고, 이 객체는 Data.Builder
를 사용하여 만듭니다. Worker
클래스는 Worker.getInputData()
를 호출하여 이 인수에 액세스할 수 있습니다. 반환 값을 출력하려면 작업의 Result
에 포함해야 합니다(예: Result.success(Data)
반환).
작업의 WorkInfo
를 검토하여 출력을 얻을 수 있습니다.
예를 들어 시간이 오래 걸리는 계산을 처리하는 Worker
클래스가 있다고 가정할 때 Worker
클래스는 다음 코드와 비슷합니다.
Kotlin
// Define the parameter keys: const val KEY_X_ARG = "X" const val KEY_Y_ARG = "Y" const val KEY_Z_ARG = "Z" // ...and the result key: const val KEY_RESULT = "result" // Define the Worker class: class MathWorker(context : Context, params : WorkerParameters) : Worker(context, params) { override fun doWork(): Result { val x = inputData.getInt(KEY_X_ARG, 0) val y = inputData.getInt(KEY_Y_ARG, 0) val z = inputData.getInt(KEY_Z_ARG, 0) // ...do the math... val result = myLongCalculation(x, y, z); //...set the output, and we're done! val output: Data = workDataOf(KEY_RESULT to result) return Result.success(output) } }
자바
// Define the Worker class: public class MathWorker extends Worker { // Define the parameter keys: public static final String KEY_X_ARG = "X"; public static final String KEY_Y_ARG = "Y"; public static final String KEY_Z_ARG = "Z"; // ...and the result key: public static final String KEY_RESULT = "result"; public MathWorker( @NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @Override public Result doWork() { // Fetch the arguments (and specify default values): int x = getInputData().getInt(KEY_X_ARG, 0); int y = getInputData().getInt(KEY_Y_ARG, 0); int z = getInputData().getInt(KEY_Z_ARG, 0); // ...do the math... int result = myLongCalculation(x, y, z); //...set the output, and we're done! Data output = new Data.Builder() .putInt(KEY_RESULT, result) .build(); return Result.success(output); } }
작업을 만들고 인수를 전달하려면 다음과 같은 코드를 사용합니다.
Kotlin
val myData: Data = workDataOf("KEY_X_ARG" to 42, "KEY_Y_ARG" to 421, "KEY_Z_ARG" to 8675309) // ...then create and enqueue a OneTimeWorkRequest that uses those arguments val mathWork = OneTimeWorkRequestBuilder<MathWorker>() .setInputData(myData) .build() WorkManager.getInstance(myContext).enqueue(mathWork)
자바
// Create the Data object: Data myData = new Data.Builder() // We need to pass three integers: X, Y, and Z .putInt(KEY_X_ARG, 42) .putInt(KEY_Y_ARG, 421) .putInt(KEY_Z_ARG, 8675309) // ... and build the actual Data object: .build(); // ...then create and enqueue a OneTimeWorkRequest that uses those arguments OneTimeWorkRequest mathWork = new OneTimeWorkRequest.Builder(MathWorker.class) .setInputData(myData) .build(); WorkManager.getInstance(myContext).enqueue(mathWork);
반환된 값은 작업의 WorkInfo
에서 사용할 수 있습니다.
Kotlin
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.id) .observe(this, Observer { info -> if (info != null && info.state.isFinished) { val myResult = info.outputData.getInt(KEY_RESULT, myDefaultValue) // ... do something with the result ... } })
자바
WorkManager.getInstance(myContext).getWorkInfoByIdLiveData(mathWork.getId()) .observe(lifecycleOwner, info -> { if (info != null && info.getState().isFinished()) { int myResult = info.getOutputData().getInt(KEY_RESULT, myDefaultValue)); // ... do something with the result ... } });
작업을 체인으로 연결하면 한 작업의 출력을 체인 내 다음 작업의 입력으로 사용할 수 있습니다. 단일 OneTimeWorkRequest
뒤에 또 다른 단일 OneTimeWorkRequest
가 나오는 단순 체인의 경우 첫 작업은 Result.success(Data)
를 호출하여 결과를 반환하고 다음 작업은 getInputData()
를 호출하여 이 결과를 가져옵니다. 더 복잡한 체인의 경우(예: 여러 작업 모두가 이후 따라오는 단일 작업에 출력을 보내기 때문에) OneTimeWorkRequest.Builder
에서 InputMerger
를 정의하여 서로 다른 작업이 같은 키를 포함한 출력을 반환할 때 어떻게 처리할지 지정할 수 있습니다.
추가 리소스
WorkManager를 자세히 알아보려면 다음 추가 리소스를 참고하세요.
샘플
- WorkManager 샘플 앱
- Sunflower: Android Jetpack을 사용한 Android 개발 권장사항을 보여주는 정원 가꾸기 앱