Android Kotlin WorkManager는 어디에 사용되며 어떻게 사용되나요?
- WorkManager가 필요한 이유는 무엇입니까?
- 다른 백그라운드 프로세스와의 차이점
- WorkManager는 어디에서 사용할 수 있나요?
- 워크매니저 사용법
WorkManager는 Android의 Jetpack 백그라운드 처리를 지원하기 위해 Google에서 만든 요소입니다.
이것은 최신 기술이므로 많은 문제와 버그가 수정되었습니다.
현재 대부분의 Android 백그라운드 작업은 WorkManager로 수행할 수 있다고 가정하는 것이 안전합니다.
WorkManager가 필요한 이유는 무엇입니까?
먼저 Android에서 백그라운드로 이동한 앱
자동으로 꺼지는 이유 알 필요가있다
안드로이드 메모리 사용량
Android 커널은 Linux 커널을 기반으로 개발되었습니다.
가장 큰 차이점은 스왑 공간없다는 것을
스왑 공간이란 무엇입니까?
- RAM이 가득 찼을 때 사용
- 메모리의 비활성 데이터를 스왑 공간으로 이동
- 스왑 공간은 RAM 용량 증가를 대신할 수 없습니다.
- 액세스 시간이 느린 하드 드라이브에 스왑 공간이 상주하기 때문입니다.
- 액세스 시간이 느린 하드 드라이브에 스왑 공간이 상주하기 때문입니다.
Android(기본적으로)에는 스왑 공간이 없기 때문입니다.
OOM 킬러프로세스를 강제 종료합니다.
OOM 킬러는 보이는 상태그리고 소비된 메모리 양에 따라 프로세스를 종료하여
메모리를 해제하는 데 사용됩니다.
각 프로세스는 자신의 oom_adj 점수가 있습니다.
이 점수는 앱의 상태(포그라운드, 백그라운드, 서비스 등)에 따라 결정됩니다.
그리고 OOM 킬러는 다음 조건으로 프로세스를 종료합니다.
여유 공간이 x보다 작으면 y보다 큰 oom_adj로 프로세스를 정리합니다!
그러므로
oom_adj 값이 높을수록 OOM 킬러로 더 쉽게 정리할 수 있습니다.
다시 말해서,
앱에서 사용하는 메모리가 적을수록 프로세스 종료 위험이 낮아집니다.
두 번째로 이해해야 할 것은
신청 상태에 대한 이해.
가장 중요한 것은 앱입니다 백그라운드에서 계속 진행하고 싶다면
서비스 구성 요소.사용되어야한다.
서비스란 무엇입니까?
사용자 인터페이스를 제공하지 않고 백그라운드에서 장기 실행 작업을 실행할 수 있는 앱 구성 요소입니다.
서비스를 이용하는 이유는 다음과 같습니다.
- 프로세스에 긴 작업이 있음을 시스템에 알립니다.
- 서비스 이용 시 합리적인 oom_adj 점수 부여
- 서비스를 사용하면 별도의 프로세스에서 작업을 수행할 수 있습니다.
- 서비스는 Android 앱의 네 가지 주요 구성 요소 중 하나입니다.
여기에 문제가 있습니다
빨리 “별도의 프로세스에서 작업 수행”하는 것입니다.
별도의 프로세스에서 작업을 수행한다는 것은 배터리 소모가 엄청납니다!
!
!
따라서 구글도즈 모드단계적으로 도입되고 추가 개발되었습니다.
(= 백그라운드 작업을 크게 제한)
Doze Mod 공식 설명 링크
https://developer.android.com/training/monitoring-device-state/doze-standby?hl=en
구체적으로 앱마켓에 등록할 수 있는 앱에 대해 다음과 같이 제한을 추가하였습니다.
- 2018년 8월 : 새로 게시된 앱은 API 26(Oreo 8.0) 이상이어야 합니다.
- 2018년 11월 : 기존 앱도 API 26(Oreo 8.0) 이상이어야 함
- 2019년부터 : 매년 targetSdkVersion 요구 사항이 개선됩니다.
매년 Android는 새 버전을 출시하며 모든 앱은 이 API 레벨 이상을 대상으로 해야 합니다.
그래서 교체용으로 나온 서비스 작업 API보지 못하다
서로 다른 백그라운드 프로세스의 차이점
작업 스케줄러 의 모습
ComponentName service = new ComponentName(this, MyJobService.class);
JobScheduler mJobScheduler = (JobScheduler)getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(jobId, serviceComponent)
.setRequiredNetworkType(jobInfoNetworkType)
.setRequiresCharging(false)
.setRequiresDeviceIdle(false)
.setExtras(extras).build();
mJobScheduler.schedule(jobInfo);
작업 스케줄러시작된 작업을 예약합니다.
적시에 시스템 MyJobService그리고 발사 onStartJob() 안에 있는 것을 실행합니다.
하지만 작업 스케줄러API21 이상에서만 사용할 수 있으며
그 후 버그가 너무 많습니다.
작업 디스패처로 대체된다
하지만 작업 디스패처 또한 제대로 작동하지 않는 장치가 있기 때문에
~ 후에 JobIntentService로 대체된다
하지만 JobIntentService 또한 원하는 시간에 정확히 작동하지 않는 문제가 있었기 때문에
작업 관리자나타날거야.
WorkManager의 등장
위 내용을 보면 개발자는 안드로이드 버전과 휴대폰 기종 등입니다.
여러 가지를 고려하면 백그라운드 서비스를 만드는 문제에 직면하게 될 것입니다.
이러한 문제를 해결하기 위해 수행된 작업 작업 관리자보지마.
작업 관리자다음과 같은 속성이 있습니다.
- 구현을 단순화하기 위해 시스템 기반 백그라운드 처리 API를 제공합니다.
- 이 API를 사용하면 앱이 포그라운드에서 실행되지 않는 경우에도 백그라운드에서 작업을 실행할 수 있습니다.
- 이 API를 사용하면 앱이 포그라운드에서 실행되지 않는 경우에도 백그라운드에서 작업을 실행할 수 있습니다.
- 이를 통해 개발자는 백그라운드 처리에 대해 걱정할 필요가 없습니다.
- 따라서 대부분의 Service, JobIntentService 등은 서비스 기능 교환 가능하다
- 단기 및 장기 작업 모두 가능합니다.
- 네트워크 상태, 배터리 상태 등에 의해 트리거될 수 있습니다.
- 전경에서도 사용 가능
- FCM과 함께 사용할 수 있습니다.
작업 관리자다음과 같은 구성 요소가 있습니다
- 작업 관리자
- 처리할 작업을 자체 대기열에 배치 및 관리
- 싱글톤으로 구현되었으므로 getInstance()를 사용하여 WorkManager의 인스턴스를 가져옵니다.
- 노동자
- 추상 클래스이며 처리할 백그라운드 작업의 처리 코드는 이 클래스를 상속하고 doWork() 메서드를 재정의하여 작성합니다.
- 일하다()
- 작업 완료 결과에 따라 작업자 클래스에 정의된 열거형 결과다음 값 중 하나를 반환해야 합니다.
- SUCCESS, FAILURE, RETRY의 세 가지 값이 있으며 반환된 값에 따라 WorkManager는 작업을 완료할지, 재시도할지 또는 오류로 정의하고 중지할지를 결정합니다.
- 작업 완료 결과에 따라 작업자 클래스에 정의된 열거형 결과다음 값 중 하나를 반환해야 합니다.
- 일하다()
- 추상 클래스이며 처리할 백그라운드 작업의 처리 코드는 이 클래스를 상속하고 doWork() 메서드를 재정의하여 작성합니다.
- 작업 요청
- WorkManager를 통해 실제로 요청되는 개별 작업입니다.
- 여기에는 다음과 같은 이 작업을 처리하는 방법에 대한 정보가 포함되어 있습니다.
예를 들어, 수행할 작업, 작업 반복 여부, 작업의 실행 조건 및 제한 사항입니다. - 반복 여부에 따라 onTimeWorkRequest와 PeriodicWorkRequest로 나뉩니다.
- onTimeWorkRequest
- 반복되지 않는 작업, 즉 한 번만 수행되는 작업에 대한 요청을 나타내는 클래스입니다.
- 반복되지 않는 작업, 즉 한 번만 수행되는 작업에 대한 요청을 나타내는 클래스입니다.
- 정기 작업 요청
- 작업을 여러 번 실행해야 하는 요구 사항을 나타내는 클래스입니다.
- 작업을 여러 번 실행해야 하는 요구 사항을 나타내는 클래스입니다.
- WorkManager를 통해 실제로 요청되는 개별 작업입니다.
- 작업 조건
- WorkRequest의 ID와 해당 WorkRequest의 현재 상태를 포함하는 클래스입니다.
- 이 WorkState의 상태 정보를 사용하여 요청한 작업의 현재 상태를 확인할 수 있습니다.
- 6가지 상태: ENQUEUED, RUNNING, SUCCEEDED, FAILED, BLOCKED, CANCLLED
- WorkRequest의 ID와 해당 WorkRequest의 현재 상태를 포함하는 클래스입니다.
WorkManager 사용
작업자 정의
무언가를 업로드하는 작업자가 있다고 가정합니다.
작업에서 수행할 모든 작업 일하다() 그것에 정의
class UploadWorker(context:Context, params:WorkerParameters):Worker(context, params) {
companion object{
const val KEY_WORKER = "key_worker"
}
// Worker에서 실시할 작업을 doWork() 안에서 정의
override fun doWork(): Result {
try{
// 넘겨 받은 데이터를 가져온다
val count = inputData.getInt(MainActivity.KEY_COUNT_VALUE, 0)
for (i in 0 until count){
Log.d("UploadWorker", "Uploading $i")
}
val time = SimpleDateFormat("dd/M/yyyy hh:mm:ss")
val currentData = time.format(Date())
// worker가 끝난 이후 반환할 데이터를 정의
val outPutData = Data.Builder()
.putString(KEY_WORKER, currentData)
.build()
// 데이터를 반환함과 동시에 success 처리
return Result.success(outPutData)
}catch (e:Exception){
// worker 결과를 fail 처리
return Result.failure()
}
}
}
MainActivity에서 WorkManager를 정의하고 Work를 실행합니다.
노동자
한 번만 사용된 OneTimeWorkRequest
지정된 간격으로 반복되는 PeriodicWorkRequest
두 종류가 있습니다.
다음을 수행할 수도 있습니다.
- Bundle과 유사한 방식으로 원하는 데이터를 작업자에게 전달할 수 있습니다.
- 작업이 끝나면 결과와 결과 값을 함께 반환할 수 있습니다.
- Worker와 liveData를 결합하여 Work의 결과를 관찰할 수 있습니다.
- 여러 작업을 연결하여 순차적으로 실행하거나 그룹화하여 동시에 처리할 수 있습니다.
class MainActivity : AppCompatActivity() {
companion object{
val KEY_COUNT_VALUE = "key_count"
}
private lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// 일반적인 Worker 실행
binding.button1.setOnClickListener {
setOnOneTimeWorkRequest()
}
// 정해진 기간마다 반복하는 Worker 실행
binding.button2.setOnClickListener {
setPeriodicWorkRequest()
}
}
private fun setOnOneTimeWorkRequest(){
val workManager = WorkManager.getInstance(applicationContext)
// Worker에서 사용할 수 있는 데이터를 넣을 수 있음
val data = Data.Builder().putInt(KEY_COUNT_VALUE, 125)
.build()
// Constraints.Builder()를 통해 실행 제약을 설정할 수 있음
val constraints = Constraints.Builder()
// 네트워크가 연결되어있는 상태일 때
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
// OneTimeWorkRequest 생성
val uploadRequest = OneTimeWorkRequest.Builder(UploadWorker::class.java)
// 정의한 제약을 설정
.setConstraints(constraints)
// 사용할 데이터를 넣는다
.setInputData(data)
.build()
// 다수의 OneTimeWorkRequest를 생성
val filteringRequest = OneTimeWorkRequest.Builder(FilteringWorker::class.java)
.build()
val compressingRequest = OneTimeWorkRequest.Builder(CompressingWorker::class.java)
.build()
val downloadingWorker = OneTimeWorkRequest.Builder(DownloadingWorker::class.java)
.build()
// list로 묶거나
val paralleWorks = mutableListOf<OneTimeWorkRequest>()
paralleWorks.add(downloadingWorker)
paralleWorks.add(filteringRequest)
// then을 사용해서 하나씩 선형으로 실행하게 가능함
workManager.beginWith(paralleWorks).then(compressingRequest).then(uploadRequest)
.enqueue() // enqueue로 Work를 실행
// workManager와 liveData를 묶어서 worker의 상태를 observe로 확인 가능
workManager.getWorkInfoByIdLiveData(uploadRequest.id)
.observe(this, Observer {
binding.textView.text = it.state.name
// work의 처리가 끝났을 때
if (it.state.isFinished){
// outputData로 worker가 끝난 이후 결과를 받을 수 있음
// 단, 해당 worker에서 Result에 값을 넣어서 반환해야함
val data = it.outputData
val message = data.getString(UploadWorker.KEY_WORKER)
Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT).show()
}
})
}
// 설정한 주기로 반복하는 worker 정의
private fun setPeriodicWorkRequest(){
val periodicWorkRequest = PeriodicWorkRequest
.Builder(DownloadingWorker::class.java, 15, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(applicationContext).enqueue(periodicWorkRequest)
}
}