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())
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 ... } });
-
끝!!
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
#1 안드로이드 취약점 분석 및 환경 소개 - 안드로이드 모바일 앱 모의해킹 (0) | 2020.11.17 |
---|---|
[android] AsyncLayoutInflater tutorial (0) | 2020.08.22 |
[android] MultiDex 에 대한 이야기 (0) | 2020.08.20 |
[android] Define data using entities - Room 에 대해 알아보자 (0) | 2020.08.19 |
[android] Accessing data using Room DAOs - Room 에 대해 알아보자 (0) | 2020.08.18 |
댓글