버그 잡이

android 알람 앱 시간 설정 #Calendar #LocalDateTime #joda-time 본문

안드로이드

android 알람 앱 시간 설정 #Calendar #LocalDateTime #joda-time

버그잡이 2020. 4. 30. 17:34

알람 설정시간 < 현재 시간

 

알람 앱에서 내가 선택한 시간이 현재 시간보다 이전시간이면 다음날로 알람 설정이 되는 것을 볼 수 있다.

 

"이 로직은 어떻게 짜는 것일까?"

 

 

"DAY_OF_YEAR"

 

 

: 365일 중 오늘의 몇번째 날인지 알려준다.

 

 

다음날로 알람 설정을 하기 위해서는 기본적으로 다음과 같은 로직이 떠오를 것이다.

 

 

if(알람 설정시간 < 현재 시간){

    현재 날짜 += 1

}

 

 

그런데 이렇게 했을 경우 문제는 월 마지막 날인 경우 32일에 알람이 설정된다.

이를 막기 위해서는 월마다 예외처리를 해줘야하는데 월 마지막날은 또 각기 다르기 때문에 예외처리 하기에 복잡하다.

 

DAY_OF_YEAR을 활용하면 위의 문제를 해결할 수 있다.

366일 되는 경우만 예외처리를 해주면 되기 때문이다.

 

 

if(알람 설정시간 < 현재 시간){

   if(DAY_OF_MONTH < 365){

    DAY_OF_MONTH += 1

   }

   else{

     DAY_OF_MONTH = 1

     YEAR += 1

   }

}

 

 

*실제 구현 코드

 var year: Int = calendar.get(Calendar.YEAR)
 val currentHour: Int = calendar.get(Calendar.HOUR_OF_DAY)
 var alarmDay: Int = calendar.get(Calendar.DAY_OF_YEAR)

 if (currentHour > hour) {
     if (alarmDay < 356) {
         alarmDay += 1
     } else {
         alarmDay = 1
         year += 1
     }
 }

 

*Calendar 사용에 대한 구체적인 방법은 아래 블로그를 참고

https://black-jin0427.tistory.com/18

 

 

 

 

"Calendar가 아닌 LocalDateTime"

 

 

지금 글을 쓰면서 의문이 들었는데 왜 Calendar 클래스를 사용했나?

날짜 시간 정보를 나타내는 클래스는 Date도 있고, java8부터 추가된 LocalDateTime도 있다.

 

각각의 클래스들에 대해서 찾아보면 기존 Date, Calendar 클래스에 문제가 많이 있다고 한다.

그래서 그 개선방안으로 나온 것이 LocalDateTime이라고 한다.

 

Date, Calendar의 문제점

 

1. 불변객체가 아니다.

 

- set 메서드를 호출해서 날짜를 지정할 수 있다. 이는 calendar, date 객체가 여러 객체에서 공유되면 한 곳에서 바꾼 값이 다른 곳에 영향을 미치는 부작용이 생길 수 있다.

 

 

2. int 상수 필드의 남용

 

- 이로 인해 엉뚱한 상수가 들어가도 이를 컴파일 시점에서 확인할 수 없다.

 

 

3. 헷갈리는 월 지정

 

- date, calendar는 1월을 0으로 시작한다. 

- calendar.set(1582, 10 , 4); 이와 같이 정하면 10월이 아니라 9월로 셋팅된다.

 

 

4. 오류에 둔감한 시간대 ID 지정

 

- 시간대의 아이디를 'Asia/Seoul' 대신 'Seoul/Asia'로 잘못 지정한 경우 이를 오류로 반환하지 않기 때문에 버그를 찾기 쉽지 않다.

 

등등...

 

구체적인 사항은 아래 블로그를 참고하자!

* https://d2.naver.com/helloworld/645609

 

 

 

LocalDateTime을 사용해야하는 이유.

 

- 위와 같은 문제점들을 해결하고자 나온 클래스이다.

 

- 그리고 놀랍게도 LocalDateTime에는 내일 날짜를 가져오는 메서드가 있다...

 

var setDateTime = LocalDate.now().atTime(hour, min).apply {
    if (this < LocalDateTime.now()){
        plusDays(1)
    }
}

 

위와 같이 보다 깔끔한 코드가 작성가능하다.

 

 

 

 

하지만...

 

LocalDateTime 은 알람매니저와 함께 사용하기에는 부적합한 부분이 있다.

 

1. alarmManager에 datetime을 입력하기 위해서는 milliseconds 단위로 가져와야한다.

 

 - 물론 아래와 같이 변경가능하지만 변경하기 위한 코드를 작성하면 calendar를 쓰는 것보다 코드가 더 복잡한 느낌이다.

 

import java.time;

LocalDateTime ldt = LocalDateTime.of(2018, Month.DECEMBER, 20, 12, 10);
// or just parse it from string:
LocalDateTime ldt2 = LocalDateTime.parse("2018-12-20T12:10:00");

// after that, you can get millis for your zone this way:
ZonedDateTime zdt = ldt.atZone(ZoneId.of("America/Los_Angeles"));
long currentTimeMillis = zdt.toInstant().toEpochMilli();

//... your code
alarm.setRepeating(AlarmManager.RTC_WAKEUP, currentTimeMillis, 30*1000, pintent);

https://stackoverflow.com/questions/51098254/how-to-set-alarmmanager-for-specific-date-hour-minute-and-second

 

 

 

2. API 26이상부터 적용가능하다.

 

- 그 이전 버전에 대한 처리를 어떻게 할것인가?

- date, calendar를 다시 쓰기는 뭔가 자존심 상한다.

- joda-time 라이브러리와 같은 대안이 있다고 한다(아직 써보지는 않았다.)

 

구글 공식 문서 예제에서도 alarmManager를 셋팅할때 LocalDateTime이 아닌 Calendar를 사용하고 있는 것도 이러한 이유들 때문이지 않을까 싶다. 코드가 너무 지저분해지니...

(물론 LocalDateTime과 joda-time 라이브러리를 사용하는 것이 보다 안전한 코딩일 것이다.)

 

 

 

 

 

결론

 

- 정확한 날짜, 시간 정보를 위해서는 LocalDateTime을 활용하자!

- API 26 이하인 경우에는 joda-time 라이브러리와 같은 대안을 고려하자.

 

 

 

 

 

 

 

반응형
Comments