위와 같은 상황에서 작업을 해야만 할 때 workmanager를 사용하는 것이 가장 좋은 것 같다.
백그라운드에서도 돌아가게 하려는 것에 가장 큰 문제는 안드로이드의 버전이 상승하면서 컨트롤 할 수 있는 부분이 점점 제약이 생기거나 변했다. 백그라운드에서 계속 무언가 서비스를 한다는 것은 당연하게도 지속적으로 클라이언트의 배터리 사용량을 증가시키는 또 클라이언트의 자원을 사용해야하는 경우이다. 이에 대해 안드로이드에서는 workmanager를 통해서 이러한 것들을 관리하기를 원하는 것 같았다.
workmanager를 사용하면서 정말 고통을 받았다.
구현을 하면서 workmanager에 대해 알아야 할 점 혹은 오류가 나는 것에 대한 해결책을 적는다.
큐는 선입선출이다. 먼저 들어간 녀석이 먼저 나오게 되어있다.
이에 따라 만약 이 전에 우리가 큐에다 집어 넣은 녀석이 있다면 지금 막 집어 넣어도 당장 그게 실행되지 않는다. 이 점이 상당히 중요하다.
우리가 워크매니저가 잘 되는 지 테스트할 때 앱의 첫 진입점에 코드를 작성하고,
실수로 20분 주기로 맞췄다고 가정하자.
00:00 첫 실행
00:16 우린 주기가 15분인 줄 알고 에러가 났다 싶어 코드를 확인하니 주기가 20분으로 되있음을 확인하고 15분으로 다시 수정하고 다시 앱을 실행시킨다.
00:20 워크매니저가 동작을 했다. 우린 주기를 00:16에 수정하고 그 15분 후인 31분에 다시 워크매니저가 동작을 해야할텐데? 뭔가 이상함을 느낀다.
이러한 일이 계속 발생했기에 내가 워크매니저가 계속 오작동을 일으킨다고 생각했던 것 같기도하다..아무튼 이러한 일을 방지하기 위해선
val workManager = WorkManager.getInstance(this) // WorkManager 인스턴스를 가져옵니다.
val uniqueWorkName = "my_unique_recurring_work_name" // 작업의 고유 이름을 정의합니다.
// 이미 큐에 동일한 이름의 작업이 있는지 확인합니다.
val workQuery = workManager.getWorkInfosForUniqueWork(uniqueWorkName)//특정 이름의 작업들가져오기
val workInfoList = workQuery.get()//리스트 형태로 가져오기
// 큐가 비어있다 => 워크 매니저 실행
if (workInfoList.isEmpty()) {
처럼 워크매니저의 큐가 지금 비어있나..안 비어있나.. 확인을 하고 그에 따라서 큐를 비우고 실행하거나 하는 방식을 도입했다.
이것보다 훨씬 좋은 방식이 있을 지 모르지만 나는 이렇게 구현을 했다.
workmanager는 주기적으로 백그라운드에서 작업을 수행함으로 너무 짧은 주기로 실행하면 안된다. 3일동안 이걸 몰라서 테스트가 왜 정상적으로 안되나 삽질을 했다. 무조건 주기는 15분 이상을 지킬 것. 아니면 오류가 난다
사실 이 방법들은 내가 막 넣어보다가 해결이 되었기에 전혀 필요없는 것 일 수도 있다.
아마 이 부분을 한 10일은 고쳤다.
아무튼 추천하는 방법들은
1.앱을 꾹 눌러 설정 - 저장공간 - 데이터 삭제 | 혹은 앱 삭제 후 재설치
=> 앱의 워크매니저의 주기가 꼬였을 때!
2.앱을 꾹 눌러 설정 - 백그라운드 작업에 대한 제약 없음으로 바꾸기(코드로도 화이트리스트에 등록하는 방법이 있는 걸로 안다.)
3.권한들을 다 넣어보자.
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
분명 쓸데없는 권한들이 많을 것이다.
일단 나의 앱에서 사용하기 위한 권한들이 대부분인데 나는 이 상태에서 정상적으로 워크매니저가 동작했다.
알림이 정상적으로 오지 않는 순간이 발생한다면
테스트기기가 안드로이드13이상이면 권한을 쥐어줘야한다.
앱 설치 시 12이하는 기본적으로 알림을 허용하면서 앱이 설치된다. 그러나 13부터는 그게 아니라 알림에 대한 코드를 작성하거나 사용자에게 직접 설정창에 들어가 알림을 허용하라고 지시해야한다.(이 자체가 앱이 유도리가 없겠지만..)
implementation "androidx.work:work-runtime-ktx:2.7.1" // kotlin
현재 23.08.19 기준 최신 버전은 2.8.1이다.
해당 부분으로 코드를 작성했어도 잘 돌아갔기에 이 버전을 사용했다.
본인 같은 경우 Application()을 상속 받아서 그 곳에 워크매니저가 작동하도록 했다.
첫 화면에다 사용해도 정상 작동은 되었지만 앱이 액티비티를 방문할 때마다 작동이 되었기에 application을 상속 받는 위치에 코드를 작성했다.
1.workmanager를 사용할 변수를 만들고 WorkManager 인스턴스를 가져온다.
val workManager = WorkManager.getInstance(this)
2.workmanager에 대한 제약을 설정한다.
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
서버에 지속적으로 데이터를 보내야 하기에 네트워크 제약을 걸었다.
3.처리할 클래스와 작업할 주기를 설정한다. 이 때 위에 제시한 제약 조건을 함께 걸 수 있다.
val workRequest = PeriodicWorkRequest.Builder(
LocationNotificationWorker::class.java, 15, TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
4.작업을 큐에 넣어 실행할 수 있도록 해준다.
workManager.enqueueUniquePeriodicWork(workRequest)
class LocationNotificationWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result {
Log.d("테스트", "==================================================")
Log.d(TAG, "doWork() started")
return try {
val location = fetchCurrentLocation()
if (location != null) {
Result.success()
}
else {
Result.failure()
}
} catch (e: Exception) {
}
}
class 클래스명(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
override fun doWork(): Result{
}
}
상속 받아야할 부분들을 상속받고 이 부분에서 내가 무엇을 할 지 정의하면 된다..
정상적으로 15분마다 작동을 하는 중이다!