애플리케이 션을 백그라운드에서 동작시키기 위한 방법
소개
애플리케이션을 개발하다보면 대용량 파일 업로드/다운로드 주기적인 데이터 fetch 작업 등 애플리케이션에서 백그라운드로 동작을 해줘야하는 상황들이 있다. 이러한 백그라운드 프로세스 작업들의 경우 어떤 식으로 실행할 수 있으며 어떻게 유지할 수 있는지 iOS/Android 각각의 환경에서 알아본 내용이다.
안드로이드에서의 백그라운드 작업
안드로이드에서의 지속적인 작업은 WorkManager를 통해 처리가능하다. 대부분의 백그라운드 처리가 지속적 작업을 통해 수행되기 때문에 WorkManager는 일반적으로 백그라운드 처리를 위한 주요 권장 API로 사용된다.
지속적인 작업의 유형
WorkManager는 세 가지 유형의 지속적인 작업을 처리한다.
- 즉시(Immediate): 즉시 시작해서 곧 완료해야 하는 작업
- 오래 걸리는 작업(Long Running): 10분 이상 소요되는 작업
- 예약 작업(Deferrable): 나중에 시작되거나 주기적으로 실행되는 예약된 작업
아래 그림은 다양한 유형의 지속적 작업이 서로 어떻게 연관되어 있는지 간략히 설명한다.
지속적인 작업 유형(https://developer.android.com/develop/background-work/background-tasks/persistent)
마찬가지로, 다음 표에서는 다양한 유형의 작업을 간략히 설명한다.
유형 | 주기 | 접근방법 |
---|---|---|
즉각적인 | 일회성 | OneTimeWorkRequest , Worker 사용, 즉시 처리해야할 경우 OneTimeWorkRequest 에서 setExpedited() 호출 |
오래 걸리는 작업 | 한번 또는 주기적으로 | WorkRequest , Worker 사용, 알림 처리를 위해서는 setForeground() 호출 |
예약 작업 | 한번 또는 주기적으로 | PeriodicWorkRequest , Worker 사용 |
WorkManager를 설정하는 방법에 대한 자세한 내용은 WorkRequest 정의 가이드에서 확인할 수 있다.
WorkManager 기능
WorkManager는 더 간단하고 일관된 API를 제공하는 것 외에도 여러 가지 주요 이점을 제공한다.
작업 제약
작업 제약 조건을 사용하여 작업을 실행하기 위한 최적의 조건을 선언적으로 정의한다. 예를 들어, 장치가 무제한 네트워크에 있을 때, 장치가 유휴 상태일 때 또는 베터리가 충분할 때만 실행한다.
강력한 스케줄링
WorkManager를 사용하면 유연한 스케줄링창을 사용하여 작업을 한 번 또는 반복적으로 실행하도록 스케줄링 할 수 있다. 작업에는 태그를 지정하고 이름을 지정할 수도 있으므로 고유하고 대체 가능한 작업을 스케줄링하고 작업 그룹을 함께 모니터링하거나 취소할 수 있다.
예약된 작업은 내부적으로 관리되는 SQLite 데이터베이스에 저장되고 WorkManager는 장치가 재부팅되어도 이 작업을 지속하고 다시 예약되도록 보장한다.
또한 WorkManager는 Doze 모드와 같은 절전 기능과 모범 사례를 준수하므로 이에 대해 걱정할 필요가 없다.
신속한 작업
WorkManager를 사용하면 백그라운드에서 실행하기 위해 즉각적인 작업을 예약할 수 있다. 사용자에게 중요하고 몇 분 안에 완료되는 작업에는 Expedited 작업을 사용해야한다.
유연한 재시도 정책
작업이 실패하는 경우도 있을 것이다. WorkManager는 구성 가능한 지수 백오프 정책을 포함하여 유연한 재시도 정책을 제공한다.
작업 체이닝
복잡한 작업의 경우 직관적인 인터페이스를 사용하여 개별 작업을 연결하여 어떤 작업이 순차적으로 실행되고 어떤 작업이 병렬로 실행될지 제어할 수 있다.
- Kotlin
- Java
val continuation = WorkManager.getInstance(context)
.beginUniqueWork(
Constants.IMAGE_MANIPULATION_WORK_NAME,
ExistingWorkPolicy.REPLACE,
OneTimeWorkRequest.from(CleanupWorker::class.java)
).then(OneTimeWorkRequest.from(WaterColorFilterWorker::class.java))
.then(OneTimeWorkRequest.from(GrayScaleFilterWorker::class.java))
.then(OneTimeWorkRequest.from(BlurEffectFilterWorker::class.java))
.then(
if (save) {
workRequest<SaveImageToGalleryWorker>(tag = Constants.TAG_OUTPUT)
} else /* upload */ {
workRequest<UploadWorker>(tag = Constants.TAG_OUTPUT)
}
)
WorkManager.getInstance(...)
.beginWith(Arrays.asList(workA, workB))
.then(workC)
.enqueue();
각 작업 태스크에 대해 해당 작업에 대한 입력 및 출력 데이터를 정의 할 수 있다. 작업을 함께 연결할때 WorkManager는 자동으로 한 작업 목록에서 다음 작업 목록으로 출력 데이터를 전달한다.
내장된 상호 스레딩 운용
WorkManager는 Coroutines과 RxJava와 완벽하게 통합되며, 사용자의 비동기 API를 플러그인할 수 있는 유연성을 제공한다.
코루틴과 WorkManager는 서로 다른 사용 사례에 권장되지만 상호 배타적이지는 않다. WorkManager를 통해 예약된 작업 내에서 코루틴을 사용할 수 있다.
WorkManager 시작하기
WorkManager를 사용하려면 먼저 라이브러리를 Android 프로젝트로 가져와야한다.
앱 build.gradle
파일에 다음 종속성을 추가하자.
- Groovy
- Kotlin
dependencies {
def work_version = "2.9.1"
// (Java only)
implementation "androidx.work:work-runtime:$work_version"
// Kotlin + coroutines
implementation "androidx.work:work-runtime-ktx:$work_version"
// optional - RxJava2 support
implementation "androidx.work:work-rxjava2:$work_version"
// optional - GCMNetworkManager support
implementation "androidx.work:work-gcm:$work_version"
// optional - Test helpers
androidTestImplementation "androidx.work:work-testing:$work_version"
// optional - Multiprocess support
implementation "androidx.work:work-multiprocess:$work_version"
}
dependencies {
val work_version = "2.9.1"
// (Java only)
implementation("androidx.work:work-runtime:$work_version")
// Kotlin + coroutines
implementation("androidx.work:work-runtime-ktx:$work_version")
// optional - RxJava2 support
implementation("androidx.work:work-rxjava2:$work_version")
// optional - GCMNetworkManager support
implementation("androidx.work:work-gcm:$work_version")
// optional - Test helpers
androidTestImplementation("androidx.work:work-testing:$work_version")
// optional - Multiprocess support
implementation("androidx.work:work-multiprocess:$work_version")
}
종속성을 추가하고 Gradle 프로젝트를 동기화한 후 실행할 작업을 정의한다.
베타, 알파, 릴리스 후보 버전을 포함한 최신 버전의 WorkManager는 항상 WorkManager 릴리스 페이지 에서 찾을 수 있다.
작업 정의하기
작업은 Worker
클래스를 사용하여 정의된다. doWork()
메서드는 WorkManager가 제공하는 백그라운드 스레드에서 비동기적으로 실행된다.
WorkManager가 실행할 작업을 만들려면 Worker
클래스를 확장하고 doWork()
메서드를 재정의한다. 예를 들어 이미지를 업로드하는 Worker
를 만들려면 다음과 같이 적용할 수 있다.
- Kotlin
- Java
class UploadWorker(appContext: Context, workerParams: WorkerParameters):
Worker(appContext, workerParams) {
override fun doWork(): Result {
// Do the work here--in this case, upload the images.
uploadImages()
// Indicate whether the work finished successfully with the Result
return Result.success()
}
}
public class UploadWorker extends Worker {
public UploadWorker(
@NonNull Context context,
@NonNull WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
// Do the work here--in this case, upload the images.
uploadImages();
// Indicate whether the work finished successfully with the Result
return Result.success();
}
}
doWork()
를 통해 반환된 Result
내용은 WorkManager 서비스에 작업이 성공했는지 여부를 알려주고, 실패한 경우 작업을 다시 시도할지 여부를 알려준다.
Result.success()
: 작업이 성공적으로 완료됨Result.failure()
: 작업이 실패함Result.retry()
: 작업이 실패하였으며, 재시도 정책에 따라 다른 시간에 다시시도해야 함을 알림
작업 요청 생성
작업이 정의되면 WorkManager 서비스로 예약해야 실행할 수 있다. WorkManager는 작업 예약 방법에 많은 유연성을 제공한다. 일정 시간 간격동안 주기적으로 실행되도록 예약하거나 한 번만 실행되도록 예약할 수 있다.
작업을 어떻게 예약하든 항상 WorkRequest
를 사용한다. Worker
가 작업 단위를 정의한다면, WorkRequest
(및 하위 클래스)는 실행 방법과 시기를 정의한다. 간단한 경우에는 아래와 같이 OneTimeWorkRequest
를 사용할 수 있다.
- Kotlin
- Java
val uploadWorkRequest: WorkRequest =
OneTimeWorkRequestBuilder<UploadWorker>()
.build()
WorkRequest uploadWorkRequest =
new OneTimeWorkRequest.Builder(UploadWorker.class)
.build();
시스템에 WorkRequest 전달
마지막으로, WorkRequest
를 WorkManager
로 enqueue()
를 사용하여 제출해야 한다.
- Kotlin
- Java
WorkManager
.getInstance(myContext)
.enqueue(uploadWorkRequest)
WorkManager
.getInstance(myContext)
.enqueue(uploadWorkRequest);
워커가 실행되는 정확한 시간은 사용자가 정의한 WorkRequest
및 시스템 최적화에 사용되는 제약 조건에 따라 달라진다. WorkManager는 이러한 제약 조건하에서 최상의 동작을 제공하도록 설계되었다.
iOS에서의 백그라운드 작업
안드로이드와 비교했을때, iOS에서는 백그라운드 작업에 많은 제한을 두고 있다. iOS 13 이전 버전에서는 백그라운드 프로세스로 동작하는 경우를 고려하지 않아, 많은 개발자들이 여러 방법으로 앱이 현재 "