WorkManager 고급 주체

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>) 메서드로 여러 체인을 결합하여 더 복잡한 시퀀스를 만들 수 있습니다. 예를 들어 다음과 같은 시퀀스를 실행한다고 가정합니다.

그림 1. 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();

이 경우 WorkManagerworkA를 실행한 후에 workB를 실행합니다. 또한 workC 후에 workD를 실행합니다. WorkManagerworkBworkD를 모두 완료한 후에 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를 자세히 알아보려면 다음 추가 리소스를 참고하세요.

샘플