Q. Service 내에서 Thread를 사용해야 한다면 그냥 일반적인 코드에서 Thread를 사용하는 것이 무엇이 다를까?
Thread의 문제: 안드로이드 컴포넌트가 아니므로 독자적인 생명주기도 없고 Main Thread가 아니기 때문에 앱을 나가면 프로세스가 유지되지 않는다. 만약에 OOM Killer(메모리 부족에 의한 특정 프로세스 종료)에 의해서 프로세스가 종료되면 다시 재시작 될 것이라는 보장도 없다.
Service 내부 동작: 안드로이드 4대 컴포넌트 중 하나로서 독자적인 생명주기를 가지고 있고 Main Thread에서 동작하기 때문에 사용자가 앱을 나가도 프로세스가 유지된다. 만약 강제로 프로세스가 죽을 경우 다시 살아날 수도 있다.
백그라운드 작업에 사용, ui thread에서 사용하나 앱이 느려지므로 별도의 스레드 처리 필요
시작: startService, 종료: stopService, 실행 중 종료 : stopSelf
ex) 음악재생, 파일 입출력, 네트워크 트랜잭션.. etc
백그라운드, 바인더, 포그라운드 3가지 유형
서비스는 잘못 사용할 확률이 높아 스레드 처리를 직접하지 않기위해
IntentService가 등장
Thread 처리할 때 동시에는 안되며 하나씩 작업
API 30 에서 부터 IntentService가 deprecated되어 JobIntentService로 대체됨
백그라운드에서 작업을 실행하면 RAM 및 배터리와 같은 제한된 리소스가 소비됨에 따라서 사용자 환경이 저하될 수 있다
WorkManager 는 모든 OS 백그라운드 실행 제한을 고려하여 백그라운드 실행에 권장되는 솔루션이다
사용 예시
사용자가 현재 보고있는 UI를 빠르게 변경하는 작업이나 결제 진행 등 즉시 처리해야 하는 작업은 ForgroundService를 사용하거나 ThreadPool, Rx등을 사용
참조 : Modern background execution in Android
WorkManager 클래스 생성 -> WorkManager Request 생성 -> Equeue Request (워크매니저 실행 요청) -> 상태 업데이트 얻기(작업 대기 및 수신)
WorkManager를 이용해 작업을 등록하고 실행하려면 아래의 클래스를 이용합니다.
- Worker: 작업 내용을 가지는 추상 클래스
- WorkRequest: 작업 의뢰 내용으로 이를 상속한
- OneTimeWorkRequest, PeriodicWorkRequest 두 개의 클래스를 사용
- Constraints: WorkRequst의 제약 조건 명시
Worker 클래스를 생성한다
doWork() 매서드로 백그라운드 스레드에서 동기식으로 실행됨
class SimpleWorker : Worker() {
override fun doWork(): WorkerResult {
// 처리해야할 작업 명시
//return은 SUCCESS, FAILURE, RETRY가 있다.
return WorkerResult.SUCCESS
}
}
경우에 따라 작업을 수행하는 코드를 작성..
// 한번만 수행
val workRequest = OneTimeWorkRequestBuilder<SimpleWorker>().build()
//15분마다 작업 실행
val workRequest = PeriodicWorkRequestBuilder<SimpleWorker>(15, TimeUnit.MINUTES).build()
val workManager = WorkManager.getInstance()
workManager?.enqueue(workRequest)
constraints로 표시한다
//배터리 충전중일때 실행(예시)
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
val requestConstraint = OneTimeWorkRequestBuilder<SimpleWorker>()
.setConstraints(constraints)
.build()
val workManager = WorkManager.getInstance()
workManager?.enqueue(requestConstraint)
A 작업 이후 B 작업을 해야한다면, beginWith과 then 사용하여 표시함
val workA = OneTimeWorkRequestBuilder<AWorker>().build()
val workB = OneTimeWorkRequestBuilder<BWorker>().build()
WorkManager.getInstance()?.apply{
beginWith(workA).then(workB).enqueue()
}
더 자세한 정보 참고
https://dongsik93.github.io/til/2020/05/15/til-jetpack-workmanager/
WorkerParameters > getExtra처럼 데이터를 전달 받는데 사용
class ScheduledWorker(appContext: Context, workerParams: WorkerParameters) :
Worker(appContext, workerParams) {
override fun doWork(): Result {
// Get Notification Data
val title = inputData.getString(EXTRA_TITLE).toString()
val message = inputData.getString(EXTRA_BODY).toString()
// FCM 전송(FCM 전송하는 Utils 클래스 생성)
NotificationUtil(applicationContext).showNotification(
title,
message
)
//성공
return Result.success()
}
}
foreground, background, doze mode일 경우 모두 10분전에 알람 받을 수 있도록 설정
private fun workManagerAlarm(
scheduledTime : String, title : String, body : String
) {
//workMAnager 에 넘겨줄 파라미터 값
val data =
workDataOf(
EXTRA_BODY to body, EXTRA_TITLE to title
)
//지정한 시간의(schedudleTIme) 10분 이전의 타임스탬프 계산
val timeDiff =
scheduledTime.toLong() - System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(10)
//파라미터와 지연 시간 설정
val workRequest = OneTimeWorkRequestBuilder<ScheduledWorker>().setInputData(data)
.setInitialDelay(timeDiff, TimeUnit.MILLISECONDS).build()
//워크매니저 생성
val workManager = WorkManager.getInstance(applicationContext)
//워크매니저 실행
workManager.enqueue(workRequest)
}]
서비스 직접 실행은 되도록 피해야하며, JobIntentService를 사용 가능함 (10분 지연)
이외 WorkManager 로 사용 -> 간단하고 짧은 작업 / 긴 작업(foreground로) 등 사용
이외의 Background Task
또한 WorkManager은 지연이 될 수 있기 때문에, 정확한 시간에 동작해야하는 작업은 AlarmManager을 사용하는 것이 좋습니다.
관련 내용 : https://developer.android.com/topic/libraries/architecture/workmanager/how-to/define-work#expedited
출처 : youtube.com/watch?v=ZqhrZ8_3jlg&t=125
https://youngest-programming.tistory.com/361