ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Architecture Components #7 - WorkManager
    Android/AAC 2019. 8. 6. 22:19

    WorkManager는 특정 작업을 유예(Defer) 처리, 비동기 처리를 목적으로 만들어 졌다.

    유예 처리라는 것은 특정 시점, 특정 상황에서 작업을 처리함을 이야기 한다.

     

    JobSchduler 혹은 FirebaseJobDispatcher, AlarmManager 같은 클래스를 이용해 처리했었는데 WorkManager는 위의 클래스와 다른 방식이 아니라 통합하기 위한 라이브러리이다.

     

    즉, WorkdManager를 이용하면 상황에 따라 위의 3가지중 하나를 반환한다.

    결국 특정 작업의 유예, 비동기 처리를 조금 더 쉽게 작성하기 위한 라이브러리 이다.

     

    JobSchduler는 API Level 21에 추가되었고 FirebaseJobDispatcher는 API Level 9에서 추가가되었지만 기기의 Google Play Service가 설치되어 있어야 한다는 조건이 있다.


    Gradle Dependency

     

    implementation 'androidx.work:work-runtime:2.1.0'


    Worker

    Worker는 추상클래스로서 상속 받아서 수행할 작업을 작성한다.

    생성자의 매개변수중 두번째 변수가 WorkerParameters인데 이 객체로 각종의 정보를 흭득하고 Worker가 등록되면 고유의 ID 값이 부여되는데 이를 확인 할 수 있다.

    실제 수해애해야할 작업은 doWork() 함수에 정의. 리턴 값이 Result인데 작업이 어떻게 진행되었는지를 명시한다.

    SUCCESS, RETRY, FAILURE 중 하나의 값을 지정하며 SUCCESS, FAILURE는 작업이 종료된 것이며 RETRY 하면 다시 작업이 등록된다.

    class MyWorker(
    	context: Context, 
        workerParams: WorkerParameters
    ) : Worker(context, workerParams) {
    
        override fun doWork(): Result {
            Log.d("dong", "Process START!")
            Thread.sleep(10000L)
            Log.d("dong", "Process STOP!")
            return Result.success()
        }
    
    }

    Constraints

    WorkRequest에 등록하는 작업의 조건을 명시하는 객체이며 Constaints.Builder에 의해 생성된다.

     

    • 기기 충전 상태
    • 기기 베터리 상태
    • 기기 네트워크 상태
    • 기기 IDEL 여부
    • 기기 저장소 상태

    ( 제약 조건이 명시되지 않으면 대부분의 경우 등록 즉시 실행된다. )

    val myConstraints = Constraints.Builder()
        .setRequiresCharging(true)
    	.build()

    WorkRequest

    작업에 대한 요청정보이다. 이곳에 수행될 작업의 클래스를 등록하고 작업 조건을 등록한다

    OneTimeWorkRequest, PeriodicWorkRequest 두개의 클래스를 제공한다.

    OneTimeWorkRequest.Builder, PeriodicWorkRequest.Builder에 의해 생성한다.

    val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
        .setConstraints(myConstraints)
        .build()

    WorkManager

    WorkRequest를 큐에 등록, 취소하는 역할을 한다.

    val workManager = WorkManager.getInstance(this)
    
    val operation = workManager.enqueue(workRequest)

    WorkStatus

    작업의 진행 상태를 나타내며 이 정보를 이용해 UI 갱신 혹은 실행중인 작업인지에 대한 판단을 할 수 있다.

    이객체에 Request 식별 정보(id) 와 Tag 정보 그리고 상태를 표현하는 enum ( ENQUEUED, RUNNING, SUCCESSED,FAILED,BLOCKED, CHNCELLED) 정보가 있다.

    결과가 SUCCESS, FAILED, CANCELLED 인 경우가 작업이 종료된 것으로 판단한다.

    workManager.state.observe(this, Observer<Operation.State> {
        Log.d("dong", it.toString())
    })

    Tagged work

    Tag는 개발자가 WorkRequest에 추가하는 일종의 문자열 정보이다.

    하나의 WorkRequest는 자동으로 부여되는 id 값으로 식별되지만 Tag는 여러 WorkRequest에 동일 문자열로 지정하는 것이 가능하다.

    여러 request를 group으로 묶을 때 유용하게 사용할 수 있고, 여러 request가 있는 경우 tag 이름으로 한꺼번에 취소가 가능하다

    val workRequest = OneTimeWorkRequest.Builder(MyWorker::class.java)
    	.setConstraints(myConstraints)
    	.addTag("someTask")
    	.build()

    cancelAllWorkByTag 함수로 작업이 취소가 가능하며 getStatusedByTag()함수로 Work의 상태인 WorkStatus를 목록으로 획득

    workManager.cancelAllWorkByTag("someTask")

    반복실행

    PeriodicWorkRequest를 이용한다

    객체를 Builder로 생성할 떄 반복 주기를 지정하면 된다.

     

    val reriodicWorkRequest = PeriodicWorkRequest.Builder(MyWorker::class.java, 5, TimeUnit.SECONDS)
    	.setConstraints(myConstraints)
    	.build()

    Chained tasks

    작업이 여러개 있는 경우네는 각각의 작업이 순서가 있을 수 있다. 즉 A 작업이 먼저 수행되고 이 후 B작업이 수행되어야 하는 경우이다.

    이런 작업의 순서는 WorkManager에게 beginWith()와 then() 함수를 이용하여 작업의 순서를 지정할수도 있고 WorkContinuation 클래스를 이용하여 순서를 명시할 수도 있다.

    workManager
    	.beginWith(workA)
    	.then(workB)
    	.then(workC)
    	.enqueue()

    workA ->workB -> workC 순으로 작업한다.

    workManager
    	.beginWith(listOf(workA, workA2, workA3))
    	.then(workB)
    	.then(listOf(workC, workC2))
    	.enqueue()

    workA1, workA2, workA3가 모두 진행되고 그 후에 workB가 진행되며 그 이후에 workC1, workC2가 진행된다.


    WorkContinuation

    복잡한 조건은 WorkContinuation을 이용한다.

    A->B가 진행이 되고 이와 별도로 C->D가 진행되어야 하며 B와 D가 모두 끝난 후 E가 싱행되어야 하는 상황

    val chain1 = workManager
    	.beginWith(workA)
    	.then(workB)
    	
    val chain2 = workManager
    	.beginWith(workC)
    	.then(workD)
    	
    val chain3 = WorkContinuation
    	.combine(listOf(chain1, chain2))
    	.then(workE)
    	
    chain3.enqueue()

    데이터 전달

     

    work에 데이터 넘기기

            val myData = Data.Builder()
                .putInt("X_ARG", 1)
                .putInt("Y_ARG", 2)
                .putInt("Z_ARG", 3)
                .build()
    
            val work = OneTimeWorkRequest.Builder(MyWorker::class.java)
                .setInputData(myData)
                .build()
    
            val workManager = WorkManager.getInstance(this)
    
            workManager.enqueue(work)

    Work에서 데이터 받기

    class MyWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
    
        override fun doWork(): Result {
            val x = inputData.getInt("X_ARG", 0)
            val y = inputData.getInt("Y_ARG", 0)
            val z = inputData.getInt("Z_ARG", 0)
            val result = "$x. $y. $z"
            val output = Data.Builder().putString("RESULT", result).build()
            return Result.success(output)
        }
    
    }

    Work에서 넘어온 데이터 획득

    workManager.getWorkInfoByIdLiveData(work.id).observe(this, Observer {
    	Log.d("dong", it.outputData.getString("RESULT"))
    })

     

    댓글

Designed by Tistory.