[안드로이드스튜디오_문화][WorkManager]

기말 지하기포·2023년 11월 29일
0

#WorkManager Android Developer 공식문서
=>https://developer.android.com/topic/libraries/architecture/workmanager?hl=ko
=>https://developer.android.com/topic/libraries/architecture/workmanager/basics?hl=ko

구글에서 알려주는 백그라운 처리 방법

-링크 : https://developer.android.com/guide/background?hl=ko

-백그라운드 작업은 즉시실행 , 지연실행 , 정시실행 이 세가지 카테고리로 분류한다. 그렇다면 각각의 카테고리는 어느 때에 실행되어야 하는 것일까?

-아래의 테스트를 통과하면서 남는 카테고리를 찾으면 된다.

-[1.사용자가 애플리케이션을 사용하는 동안 작업이 완료되어야 하는가?] : 이 경우는 즉시실행이 된다. 이 즉시실행은 코루틴을 사용하여 백그라운드 작업을 실행하면 된다.

-[2.작업이 정확한 시간에 실행되어야 하는가?] : 이 경우 정시 실행으로 넘어간다. 이 정시실행은 AlarmManager를 사용해서 백그라운드 작업을 실행하면 된다.

-[3.정확한 시간에 실행하지 않아도 되는 가?] : 이 경우 지연 실행으로 넘어간다. 이 지연실행을 WorkManager를 통해서 안드로이드의 백그라운드 작업을 실행한다. 우리는 이 WorkManager를 통해서 안드로이드에서 백그라운드 작업을 어떻게 실행하는지 공부 할 것이다.

WorkManager 정의

-WorkManager는 라이브러리로서 비동기 작업을 스케쥴링하고 실행하며, 앱이 종료되더라도 시스템의 백그라운드에서 해당 앱을 실행 할 수 있게 해준다. 실행 할 작업을 doWork()를 구현해서 넣어주면 된다.

-WorkManager에서는 4가지의 Worker 유형을 제공해준다. [1.Worker][2.CoroutineWorker] [3.RxWorker][4.ListenableWorker] 이렇게 총 4가지의 유형을 제공해주는데, Kotlin 사용자에게는 CoroutineWorker를 권장한다고 공식문서에 나와있으므로 WorkManer를 앞으로는 CoroutineWorker라고 생각해도 될 듯 하다.

CoroutineWorker

-안드로이드의 WorkManager 라이브러리에서 제공하는 클래스로 , 비동기 작업을 코루틴을 사용하여 수행 할 수 있게 해주는 ListenableWorker의 하위 클래스이다. 해당 클래스는 Coroutine+WorkManager를 결합하여 백그라운드 작업을 쉽고 효율적으로 처리 할 수 있게 도와준다.

-CoroutineWorker의 전체 코드는 아래와 같으며 주석처리된 부분에 설명도 나와있으므로 참고하면 좋을 듯 함.

-CoroutineWorker는 기본적으로 Dispatchers.Default라는 코루틴 디스패처에서 작업을 실행하기 때문에 CPU를 많이 사용하는 작업에 최적화된 스레드 풀에서 작업을 수행한다. 또한 doWork() 함수 내부에서 withContext()를 사용해서 다른 디스패처에서 작업을 수행 할 것을 권장하고 있다. 이는 coroutineContext가 @Deprecated 되었다는 메시지와 함께 구글에서 권장하고 있는 방법이다. doWork()를 override하여 작업 로직을 구현하면 되고 작업이 성공적으로 완료된다면 ListenableWorker.Result 타입의 결과를 반환한다. doWork()는 비동기 작업을 수행하기 위한 함수로서 suspend Function이다.

-setProgress() 함수를 사용하면 작업의 진행 상태를 업데이트 할 수 있다. setProgress() 함수를 통해 전달되는 Data 객체는 작업의 진행 상태에 대한 정보를 담고 있으며 , 이 정보는 앱의 UI 컴포넌트와 함게 공유 될 수 있어서 시각적으로 사용자에게 작업의 진행상태에 대한 정보를 알려 줄 수 있다.

public abstract class CoroutineWorker(
    appContext: Context,
    params: WorkerParameters
) : ListenableWorker(appContext, params) {

    internal val job = Job()
    internal val future: SettableFuture<Result> = SettableFuture.create()

    init {
        future.addListener(
            Runnable {
                if (future.isCancelled) {
                    job.cancel()
                }
            },
            taskExecutor.serialTaskExecutor
        )
    }

    /**
     * The coroutine context on which [doWork] will run. By default, this is [Dispatchers.Default].
     */
    @Deprecated(message = "use withContext(...) inside doWork() instead.")
    public open val coroutineContext: CoroutineDispatcher = Dispatchers.Default

    @Suppress("DEPRECATION")
    public final override fun startWork(): ListenableFuture<Result> {
        val coroutineScope = CoroutineScope(coroutineContext + job)
        coroutineScope.launch {
            try {
                val result = doWork()
                future.set(result)
            } catch (t: Throwable) {
                future.setException(t)
            }
        }
        return future
    }

    /**
     * A suspending method to do your work.
     * <p>
     * To specify which [CoroutineDispatcher] your work should run on, use `withContext()`
     * within `doWork()`.
     * If there is no other dispatcher declared, [Dispatchers.Default] will be used.
     * <p>
     * A CoroutineWorker is given a maximum of ten minutes to finish its execution and return a
     * [ListenableWorker.Result].  After this time has expired, the worker will be signalled to
     * stop.
     *
     * @return The [ListenableWorker.Result] of the result of the background work; note that
     * dependent work will not execute if you return [ListenableWorker.Result.failure]
     */
    public abstract suspend fun doWork(): Result

    /**
     * @return The [ForegroundInfo] instance if the [WorkRequest] is marked as expedited.
     *
     * @throws [IllegalStateException] when not overridden. Override this method when the
     * corresponding [WorkRequest] is marked expedited.
     */
    public open suspend fun getForegroundInfo(): ForegroundInfo {
        throw IllegalStateException("Not implemented")
    }

    /**
     * Updates the progress for the [CoroutineWorker]. This is a suspending function unlike the
     * [setProgressAsync] API which returns a [ListenableFuture].
     *
     * @param data The progress [Data]
     */
    public suspend fun setProgress(data: Data) {
        setProgressAsync(data).await()
    }

    /**
     * Makes the [CoroutineWorker] run in the context of a foreground [android.app.Service]. This
     * is a suspending function unlike the [setForegroundAsync] API which returns a
     * [ListenableFuture].
     *
     * Calling [setForeground] will throw an [IllegalStateException] if the process is subject to
     * foreground service restrictions. Consider using  [WorkRequest.Builder.setExpedited]
     * and [getForegroundInfo] instead.
     *
     * @param foregroundInfo The [ForegroundInfo]
     */
    public suspend fun setForeground(foregroundInfo: ForegroundInfo) {
        setForegroundAsync(foregroundInfo).await()
    }

    @Suppress("DEPRECATION")
    public final override fun getForegroundInfoAsync(): ListenableFuture<ForegroundInfo> {
        val job = Job()
        val scope = CoroutineScope(coroutineContext + job)
        val jobFuture = JobListenableFuture<ForegroundInfo>(job)
        scope.launch {
            jobFuture.complete(getForegroundInfo())
        }
        return jobFuture
    }

    public final override fun onStopped() {
        super.onStopped()
        future.cancel(false)
    }
}

CoroutineWorker 사용과정

1. 종속성 추가하기

-아래와 같이 모듈 수준의 build.gradle에 WorkManager 종속성을 추가해준다.

implementation ("androidx.work:work-runtime-ktx:${WorkManager_Verision}")

2. CoroutineWorker()를 상속받을 클래스 만들기 + parameter 설정해주기

-CoroutineWorker()는 parameter로 Context와 WorkerParameters를 받기 때문에 이와 같은 설정과 함께 상속을 해줘야 한다.

class PhotoCompressionWorker(
    private val appContext: Context,
    private val params: WorkerParameters
): CoroutineWorker(appContext, params)

3. doWork() 메서드를 오버라이딩 해주기 + doWork()에 필요한 입력데이터를 WorkerParameters에 전달해준다.

-doWork() 함수는 작업을 실행하려고 할 때 실행될 함수이며 , doWork()는 오버라이딩 해야된다. 그리고 해당 코드 블록 내부에서 params를 사용해서 작업을 실행 할 코드를 작성하면 된다.

-doWork() 함수내에서 작업을 하는데 있어서 필요한 데이터는 WorkerParameters에 들어있는 정보를 가져와야 한다. (WorkerParameters에 정보를 넣어주는 것은 RequestBuilder에서 보내준것이다.)

-doWork() 함수내에서 params를 이용새서 WorkRequest의 객체의 정보를 가져와서 작업하는데 사용 할 수 있다.

-params.inputData.[getString() , getLong() 등등의 get자료형()]을 활용해서 받아 올 수 있다.

override suspend fun doWork() : Result {




}


-doWork()의 작업에 필요한 입력 데이터를 보내줘야 한다. 이는 두가지의 방법을 가지고 있다. 첫째, 단일 실행 작업을 요청하느냐 둘째, 주기적으로 반복되는 작업을 요청하느냐 이렇게 두가지의 경우 중 어느 경우를 선택하느냐에 따라서 코드 작성하는 방법이 다르다.

-단일 실행 작업 요청

  • setInputData : setInputData는 작업을 실행하는데 필요한 입력 데이터를 정의한다.
  • workDataOf() : workDataOf()는 parameter 키-값 형태로 구성되고 , 작업 실행에 필요한 입력 데이터를 PhotoCompressionWorker의 params에 전달한다.
  • .build() : WorkRequest 객체 생성해서 request 변수에 저장한다.
OneTimeWorkRequestBuilder<CoroutineWorker를 상속받은 클래스>()
	.setInputData( workDataOf() )
    .build() : WorkRequest 객체 생성

-위에서 만든것을 request 변수에 저장했다는 가정하에 최종적으로 workManager의 객체의 enqueue 메서드를 활용해서 요청된 작업을 큐에 추가하여 작업이 실행 될 수 있는 조건이 만족되었을 때 큐에 있는 작업을 시작 할 수 있게 해주어야 한다.

workManager.enqueue(request)

-주기적으로 반복되는 작업 요청

  • setInputData : setInputData는 작업을 실행하는데 필요한 입력 데이터를 정의한다.
  • workDataOf() : workDataOf()는 parameter 키-값 형태로 구성되고 , 작업 실행에 필요한 입력 데이터를 PhotoCompressionWorker의 params에 전달한다.
  • .build() : WorkRequest 객체 생성해서 request 변수에 저장한다.
PeriodicWorkRequestBuilder<CoroutineWorker를 상속받은 클래스>(interval 숫자 , TimeUnit.종류(ex:HOUR , MINUTES , DAYS..))
	.setInputData( workDataOf() )
    .build() : WorkRequest 객체 생성

-위에서 만든것을 request 변수에 저장했다는 가정하에 최종적으로 workManager의 객체의 enqueue 메서드를 활용해서 요청된 작업을 큐에 추가하여 작업이 실행 될 수 있는 조건이 만족되었을 때 큐에 있는 작업을 시작 할 수 있게 해주어야 한다.

workManager.enqueue(request)

-

후...압축해도 예쁘네...

profile
포기하지 말기

0개의 댓글