Android 공부/Android Library Study

Android Worker - 매일 알람 만들기

0. WorkManager를 어떨 때 사용해야 할까?

 최근 '담다' 앱에서 WorkManager를 도입했다. '담다'앱은 시간표 앱이기에 유틸리티의 느낌이 강하게 띄고 있다. 그래서, 앱의 큰 장점이 있다면, 별도의 서버를 호출하지 않고, 앱의 많은 기능이 유연하게 돌아가서 클라이언트의 기능에만 집중할 수 있다는 장점을 갖고 있다. 또한, 대부분 WorkManager를 사용하는 이유는 안드로이드의 백그라운드 정책을 유연하게 대응하기 위해서이다. 도즈모드, 서비스 제한, 앱버킷, 배터리 세이버등의 제한 정책 속에서 안전하게 백그라운드 작업을 하기 위해서는 WorkManager가 가장 제격이기 때문이다.

 

여튼, 그래서 WorkManager를 도입해서 '담다' 앱에서는 어떤 것을 얻었을까?

- 특정 시간, 사용자가 앱을 사용할 수 있도록 쉽게 알람을 보낼 수 있다.

- 특정 시간이 좀 지났지만, 사용자에게 알람을 보내는 것을 실패하더라도, 알람을 안전하게 보낼 수 있다.

- 특정 시간이 좀 지났지만, 사용자의 디바이스 환경을 고려해서 알람을 무조건 보낼 수 있다.

- 큰 작업이 있더라도, 백그라운드 환경 속에서 안정적으로 기능을 사용할 수 있다.

- 네트워크 통신이 있더라도, 백그라운드 환경 속에서 안정적으로 기능을 사용할 수 있다.

 

* 쉽게, 특정 시간 쯤, 사용자의 디바이스 환경을 고려해서, 네트워크와 같은 큰 백그라운드 작업을 할 수 있다.

* 가장 큰 장점은 백그라운드의 다양한 제한 사항 속에서 백그라운드 작업을 위임할 수 있다는 점이다.

 

1. 구체적인 시나리오

 시간표 기능 외에, 스터디 타이머를 본격적으로 활용할 수 있는 시간을 보통 수업이 끝나고, 집에 가는 5시~6시 정도로 생각하고 있습니다. 하지만, 정각에 메시지를 주기 보다는 가변적으로 시간을 조절해서 메시지를 전달하려고 합니다. 또한, 해당 시간 이전에 하루 공부 할당량을 채웠는 지 검사를 해서 가변적으로 메시지를 전달하려고 합니다. 

 - 가변적인 시간에 메시지를 전달하려고 한다.

 - 하루 공부량을 데이터베이스에서 확인한 후, 가변적인 메시지를 전달하려고 한다.

 - 백그라운드의 제한 사항 속에서 네트워크 작업알림 메시지성공적으로 보내고 싶다.

 유틸리티 어플리케이션 특성상, 대부분은 local에서 작업을 하고 있어서, 인터넷 통신보다는 데이터베이스 통신 후에, 얻어온 결과 값을 주기적으로 메시지를 전달하려고 합니다.

 

2. 개발자 가이드

구글 디벨롭퍼 : 
 URL : https://developer.android.com/jetpack/androidx/releases/work
 내용: 개념을 파악하기에는 디벨롭퍼보다 좋은 것은 없습니다.

 

네이버 엔지니어링 :

 URL : (WorkManager 상세 리뷰)

 내용: 2번 들었지만, 발표가 유쾌하시고, 현실적인 것에 비교해서 이해가 쏙쏙 된 강의였다.

 

WokrManager에 관하여:

URL : http://dktfrmaster.blogspot.com/2018/06/workmanager.html (장범석님의 개발일지)

내용 : 메모리에 대한 이해도와 함께, WorkManager를 간결하게 정리해서 알려줍니다.

3. 코드

StudyScheduler

하루에 공부를 얼마나 했는 지, 보여주는 코드이다.

doWork()에서는 작업을 실행하는 로직이 담겨져있다. 그리고, 얼마나 공부했는지 네트워크에서 데이터를 가져온다. getTodayStudy()이라는 메소드를 호출하는데, 이 때 네트워크 통신이 정상적으로 이루어지는 것을 체크하기 위해서 try/catch로 감싸서 작업을 처리했고, 성공시에는 notification을 보여주면서 success 작업을 떨어뜨리도록 했습니다.

 그리고, 실패했을 때는, failure 작업을 떨어뜨려서 한 번 더 실행하도록 했습니다.

class StudyScheduler(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) {
    private val sharedPrefUtils = SharedPrefUtils()
    override fun doWork(): Result {
    	try{
        	val todayStudyMin = getTodayStudyTime()
        	makeNotification(todayStudyMin)
        	return Result.success()
        }catch(exception:Exception){
        	return Result.failure()
        }
    }

    private fun makeNotification(min: Int) {
        val notificationUtils = NotificationUtils()
        notificationUtils.sendOneNotification(applicationContext,
                applicationContext.getString(R.string.all_text_study_worker_title),
                applicationContext.getString(R.string.all_text_study_worker_content, min), false,
                PendingIntent.getActivity(applicationContext,
                        4,
                        Intent(applicationContext, GoalActivity::class.java), PendingIntent.FLAG_CANCEL_CURRENT))
    }

    private fun getTodayStudyTime(): Int {
        return requestAccessNetworkDB()
    }
}

GoalActivity

private fun doWorkManager() {
        val calendar = Calendar.getInstance()
        val delay = calculateDelay(calendar)
        val periodicWorkRequest = PeriodicWorkRequestBuilder<StudyScheduler>(24, TimeUnit.HOURS)
                .setInitialDelay(delay, TimeUnit.SECONDS)
                .addTag("GOAL")
                .build()
        WorkManager
                .getInstance(this)
                .enqueue(periodicWorkRequest)
}
private fun calculateDelay(startCalendar: Calendar): Long {
        val todayCalendar = Calendar.getInstance()
        todayCalendar.set(Calendar.MINUTE, 31)
        todayCalendar.set(Calendar.HOUR, 6)
        todayCalendar.set(Calendar.AM_PM, Calendar.PM)
        todayCalendar.add(Calendar.DAY_OF_YEAR, 0)

        var delay = (todayCalendar.time.time - startCalendar.time.time) / 1000
        Log.d("delay", delay.toString())
        return if (delay < 0) {
            todayCalendar.add(Calendar.DAY_OF_YEAR, 1)
            (todayCalendar.time.time - startCalendar.time.time) / 1000
        } else {
            delay
        }
}