본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[android] WorkManager 를 사용해보자

by 돼지왕왕돼지 2020. 8. 21.


android workmanager

https://www.bignerdranch.com/blog/doing-work-with-androids-new-work-manager/

https://developer.android.com/topic/libraries/architecture/workmanager

https://developer.android.com/reference/androidx/work/WorkManager


-

https://developer.android.com/jetpack/androidx/versions


글을 정리하는 20.07.14 시점에는 2.3.4 release 상태



-

Google 이 2018 I/O 에서 WorkManager 라는 것을 발표했다.

그 전에는 background task 를 수행하기 위해서는 여러가지 배경지식이 필요했다. API Level 이 xx 일 때는 뭘 써야하고, xx 일 때는 뭘 써야하고 등등.


WorkManager 는 JobScheduler, FirebaseJobDispatcher, AlarmManager, Service 등을 사용해서 API version 상관없이 쉽게 사용할 수 있게 해준다.



-

API 23+(M) 이면 JobScheduler 를 사용하고, 14-22 에서는 Firebase dependency 가 있어 사용할 수 있다면 JobDispatcher, 그렇지 않으면 AlarmManager + BroadcastReceiver 를 사용한다.



-

WorkManager 는 아래것들로 구성된다.

    WorkManager : work 를 argument 와 함께 받아 enqueue 시킨다.

    Worker : doWork() 를 구현해야 하며, bg thread 에서 해당 code 를 수행시킨다.

    WorkRequest : 개별 task 를 의미한다. 어떤 Worker 가 enqueue 되어야 하는지와 실행조건(constraints) 등을 명시한다. 이 녀석은 abstract 로 OneTimeWorkRequest 나 PeriodicWorkRequest 를 사용하면 된다.

    WorkStatus : WorkRequest 마다 하나씩 제공된다.



-

Worker 의 구현

class YourWorker: Worker {
  override fun WorkerResult doWork() {
    //do the work you want done on the background in here
    return WorkerResult.SUCCESS
  }
}



-

Work 를 enqueue 시키기

OneTimeWorkRequest 를 사용하면서 constraint 가 없기 떄문에 즉각적으로 work 를 수행한다.

val work: OneTimeWorkRequest  = OneTimeWorkRequest.Builder(YourWorker::class.java).build()
WorkManager.getInstance().enqueue(work)


-

아래와 같이 constraint 를 줄 수 있다.

val constraints: Constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresCharging(true) .build() val work: OneTimeWorkRequest = OneTimeWorkRequest.Builder(YourWork::class.java).setConstraints(constraints).build()



-

반복적으로 job 을 수행하고 싶다면 PeriodicWorkRequest 를 이용하면 된다.

val recurringWork: PeriodicWorkRequest = PeriodicWorkRequest.Builder(YourWorker::class.java, 3, TimeUnit.HOURS).build()
WorkManager.getInstance().enqueue(recurringWork)


-

다음과 같이 WorkRequest 를 chaining 해서 사용할 수도 있다.

beginWith then 으로 chaining 을 한 경우 한 work 가 실패할 경우 다른 work 들도 수행되지 않는다.

WorkManager.getInstance().beginWith(firstWork)
  .then(secondWork)
  .then(thirdWork)
  .then(otherWork1, otherWork2)
  .enqueue()


-

아래와 같이 Status 를 얻어올 수도 있다.

WorkManager.getInstance().getStatusById(compressionWorkRequest.getId())
getStatusById 의 결과물은 LiveData<WorkStatus> 이다.

WorkStatus 는 UUID type 의 id, Data 인 outputData, State, Set<String> 인 Tag 등을 가지고 있다.


State 는 enum 으로 BLOCKED, CANCELLED, ENQUEUED, FAILED, RUNNING, SUCCEEDED 상태를 갖는다. ( isFinished() 라는 함수도 갖는다. )



-

아래와 같이 Task 를 cancel 할 수 있다.

UUID compressionWorkId = compressionWorkRequest.getId();
WorkManager.getInstance().cancelByWorkId(compressionWorkId);



-

WorkContinuation 을 사용해서 복잡한 로직도 만들 수 있다.

WorkContinuation chain1 = WorkManager.getInstance()
    .beginWith(workA)
    .then(workB);
WorkContinuation chain2 = WorkManager.getInstance()
    .beginWith(workC)
    .then(workD);

WorkContinuation chain3 = WorkContinuation
    .combine(chain1, chain2)
    .then(workE);

chain3.enqueue();




-

beginWith 대신 beginUniqueWork 를 통해 unique 한 work sequence 를 만들 수도 있다.

unique work sequence 는 이름을 가지며, WorkManager 가 한번에 하나의 sequence 만 허락한다.

unique work sequence 를 전달할 때 WorkManager에게 어떤 처리를 할 지 명시할 수 있다.


Cancel : 기존의 sequence 를 제거하고 새로운 것으로 대체

Keep : 기존의 것을 keep 하고 새로운 것을 버린다.

Append : 기존의 것을 Run 하고 새로운 것도 Run 시킨다.


unique work sequence 는 sync 와 같이 여러번 수행하지 않아야 하는 work 를 control 하거나,

undo 와 같이 순서가 보장되어야 하는 work 들을 chaining 할 때 유용하다.



-

Tag 를 주어 논리적으로 Work 들을 groupping 할 수 있다.

OneTimeWorkRequest cacheCleanupTask = new OneTimeWorkRequest.Builder(MyCacheCleanupWorker.class)
    .setConstraints(myConstraints)
    .addTag("cleanup")
    .build();
이렇게 태그를 주면 


WorkManager.cancelAllWorkByTag(), WorkManager.getStatusesByTag() 를 통해 group 의 상태를 query 하거나 control 할 수 있다.



-

WorkRequest.Builder.setInputData() 를 통해 input 을 전달할 수 있다.

Worker 에서는 Worker.getInputData() 를 통해 해당 값을 전달받을 수 있다.

Output 을 주려면 Worker.setOutputData() 를 통해 값을 전달할 수 있다.


Data 는 아래와 같이 Builder 를 통해 만든다.

Data myData = new Data.Builder()
    .putInt(KEY_X_ARG, 42)
    .putInt(KEY_Y_ARG, 421)
    .putInt(KEY_Z_ARG, 8675309)
    .build();


Ouptut 값은 아래와 같은 방식으로 확인할 수 있다.

WorkManager.getInstance().getStatusById(mathWork.getId()) .observe(lifecycleOwner, status -> { if (status != null) { int myResult = status.getOutputData().getInt(KEY_RESULT, myDefaultValue));          // ... do something with the result ... } });



-

끝!!




댓글0