일단, 날짜 년, 월, 일, 요일에 대해선 자세히 다루지 않는다. Date라는게 참 복잡 미묘한 놈이라서. 이 녀석을 제외하면 좀 단순해지는데, 나는 항상 System.currentTimeMillis() 만 이용해왔다. 이건 뭐 다들 알겠지만, 1970년 1월 1일 자정을 기준으로 현재까지 흐른 시간을 milliseconds로 돌려준다. 대부분의 경우 이걸로 해결이 되긴 하는데, 타이머를 만들면서 좀 더 유용한 것들이 있더라. 이에 대해 잘 정리된 영문 포스팅이 있으니 참조. 나도 이 포스팅을 참고로 정리해 놓으려고 한다.
Wall Clock
우리말로 하면 벽시계다. 앞에서 말한 System.currentTimeMillis() 가 바로 그것. 현재의 날짜와 시간을 알 수 있는 값으로, Date(milliseconds: Long)를 이용해서 날짜로 변환할 수도 있다. (다만, Date나 Calendar등의 문제로 인해 Java8 이후로는 LocalDate, LocalDateTime이 제공되어 사용하도록 되어있다고 한다.) 문제는 사용자가 타임존을 바꾸거나, 시간을 변경하면 같이 변경된다는 점이다.( 프로그래밍 적으로도 setCurrentTimeMillis(long)이 존재하니까. ) 실행시간 체크등을 위해 자주 사용해 왔지만, 엄밀히 말해 거기에 쓰기 적합한 것이라고 보기 어렵다. calendar나 Alarm clock처럼 현실의 시간과 연관된 경우에 사용하는게 맞다.
안드로이드에선 시간이 변경된경우, ACTION_TIME_TICK, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED 의 Intent를 broadcast receiver에서 받아 변경시 알 수 있다.
CPU Clock
말 그대로 CPU time을 측정한다. 여기서 CPU time은 부팅 이후로 흐른 시간을 말한다. 사용하는 함수는 uptimeMillis() 이다. 이것은 부팅후 흐른 시간을 milliseconds로 알려준다. 문제는 시스템이 딥슬립 상태로 들어가면, 이것도 같이 멈춘다는 점이다. Thread.sleep(millis)나 Object.wait(millis)도 이걸 사용한다고 한다. 슬립과 상관없는 인터벌 측정용으로 적당하다. 대부분의 timestamp용도로 적당하다고 한다. 슬립에 들어가면 안되니까, 백그라운드 타이머나, 알람등에는 부적격.
그럼 딥슬립까지 측정하려면 어떻게 해야하나? elapsedRealtime() 을 사용하면 된다. 이건 슬립에 들어가서 CPU가 멈춰도 계속 작동한다. 그래서 일반적인 인터벌 측정용으로는 무조건 이거사용. AlarmManager는 슬립상태에서도 깨울 수가 있는데, 이 때 시간 설정에 사용하는게 System.currentMillis()아니면 elapsedRealtime()이다.
sleep 사용시 항상 주의해야할 점
sleep()이나 delay()는 평소에도 자주 사용되는 함수들인데, 앞에서 말했듯 uptimeMillis()를 사용한다. 그말은 CPU가 잠들면 깨어나지 않는다는 얘기다. 일반적으로 상관 없을 수도 있지만, 디바이스의 딥슬립에 상관없이 작동시키고 싶다면, AlarmManager를 사용할 수 있다. …자꾸 단 하나의 정답이 없고 돌고 도는거 같은데, AlarmManager도 문제가 있다. 재부팅시 elapsedRealtime()은 날라간다는 거다. 이경우, BOOT_COMPLETED Intent를 받아서 재부팅을 감지하고 다시 처리해주면 된다.
마무리
시간은 뭐 이정도로 정리된다. 크게 어렵진 않은데, 나는 그냥 뭉뚱그려서 System.currentTimeMillis()만 써왔다. 간단한 작업들에는 문제가 없었긴한데, 이렇게 한단계 스텝업 하는거지. 그리고 날짜에 대한건… 이것도 진짜 정리가 필요한데 별로 다루고 싶지 않다. 더러워 ㅋㅋㅋ 끝.