구현해야할 메소드는 onClick 메소드 하나 뿐인것을 알 수 있습니다. onClick 메소드는 너무나 간단하게 로그를 찍는 코드밖에 없습니다. 하지만 꽤나 복잡한 과정을 거치게 되죠. 분명 로그만 찍는데 부가적으로 기록해야하는 코드가 길다는 것이죠.
혹은 우리가 자주 사용하는 Thread를 Runnable을 통해서 객체 생성과 동시에 동작시키려면 이렇게 코드를 짜야합니다.
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread run");
}
}).start();
람다를 사용한 방식
만약 위의 코드들에 람다표현식을 적용한다면 이렇게 바뀝니다.
button.setOnClickListener(v->Log.e(TAG,"클릭 발생"));
그리고 Thread를 동작시키는 코드는 아래와 같게됩니다.
new Thread(()->System.out.println("Thread run")).start();
몇줄이 단지 한줄로 바뀌게 되지요. 이렇게 람다를 적용하면 이렇게 간단한 코드로 바뀌게 됩니다. 즉, 가독성이 정말 높아지게 됩니다. 여기서 우리는 단지 하나의 익명 클래스를 구현했지만, 그것이 여러개가 된다면 가독성이 매우 떨어지게 됩니다.
반면 익명 함수이기 때문에 재사용이 불가능하고, 디버깅이 어려운 단점이 있습니다.
람다 표현식 문법
이제는 람다를 사용하는 방법에는 어떤 방법이 있는지 알아보도록 합시다. 이때 람다를 사용할 수 있는 경우는 Overriding해야할 메소드가 하나여야합니다. 두개 이상의 메소드를 구현해야할때에는 사용할 수가 없습니다.
1. 구현부가 한줄인 경우
() -> System.out.println(x)
2. 인자가 있는 경우
() -> {System.out.println("lambda")};
3. 인자가 있는 경우
인자가 2개 이상인 경우에는 괄호로 묶어야합니다. 그리고 타입을 명시적으로 지정해줄 수도 있습니다.
x -> {System.out.println(x)};
(x) -> {System.out.println(x)};
(x,y) -> {System.out.println(x+","+y)};
(int x, int y) -> {System.out.println(x+","+y)};
4. 반환값이 있을 경우
(int x) -> { return x*x };
함수형 프로그래밍
람다를 사용하는 가장 주된 이유는 함수형 프로그래밍을 해줄 수 있게 만들어주기 때문입니다. 여기서 함수형 프로그래밍이라는 것은 순수 함수, 메소드로 문제를 해결하는 프로그래밍 페러다임입니다. 자바 8 이전에는 어떻게(how) 문제를 해결하는 것에 초점을 두지만, 8 이후에는 무엇(what)을 할 것인가에 대해서도 지원하게 됩니다.
예를 들어 for을 우리가 구현하지 않고 미리 정의된 forEach를 사용하는 방식입니다. 이렇게 되면 코드를 짧게 유지하여 유지 보수하기가 매우 쉬워집니다.
함수형 프로그래밍을 도와주는 몇몇 기법에는 지금 소개한 람다 표현식과 StreamAPI 등이 있습니다.
여기서 Calendar 객체를 현재시간으로 미리 설정해두고, set 메소드로 timePicker에서 설정된 시간과 분으로 설정시키는 것입니다. 이때 시간과 분을 TimePicker에서 얻어오려면 @RequiresApi(api = Build.VERSION_CODES.M) 를 메소드위에 추가시켜야합니다.
이제 AlarmManager를 가져옵니다. Intent는 수신자 클래스를 전달하게 됩니다. 여기서는 AlarmReceiver라는 수신자이며 밑에서 이 Receiver를 구현하게 될겁니다. 그리고 PendingIntent를 얻어와서 setRepeating 메소드로 정확한 시간에 알람을 설정시켜줍니다.
여기서 setRepeating메소드는 알람을 반복시키는 메소드입니다. 여기서는 AlarmManager.INTERVAL_DAY를 사용하였고, 이것은 매일 알람이 울릴것을 명시해준것입니다.
굳이 정확한 시간에 알람을 울리지 않을 경우에는 setInexactRepeating 메소드를 사용할 수도 있습니다. 실제로 안드로이드 개발문서에는 이 메소드를 사용하라고 권고하고 있네요.
이제 알람이 발생되면 AlarmReceiver의 onReceive 메소드가 호출되게 됩니다. 이때 아까 우리가 MainActivity에서 넘긴 PendingIntent를 requestCode를 통해서 얻어올 수가 있습니다. 이 requestCode는 알람을 설정했던 것과 같은 코드를 사용해야합니다. 여기서는 1입니다.
오레오(Oreo)버전 이상부터는 NotificationChannel을 명시해주어야합니다. 그 코드도 적용되어 있습니다.
대충 완성하게 되면 아래와 같은 모습입니다. 앱 디자인은 개나 줘버린 모습이지만 구현이 제대로 되었나 확인만 하는 용도입니다.
그리고 원하는 시간에 알람을 저장시켜줍니다. 그래서 9시 15분에 알람을 설정합니다. 그리고 그 시간에 울리는 지 잠깐 기다려주겠습니다. 이때 9시 14분이었고, 1분만 기다리면 됩니다.
다음은 알람이 발생되어 Notification이 발생된 화면입니다.
분명 9시 15분이었는데, 9시 16분에 알람이 발생되었군요. 정확한 시간에 알람이 딱 울리지는 않는 것 같습니다. 제 생각에는 setRepeating이 setInexactRepeating메소드보다 더 정확한 시간에 발생시켜주는 것으로 확인이 됩니다.
알람 취소
그리고 알람을 취소하려면 아래와 같이 코드를 추가하면 되는데요. 위의 코드는 없지만 만약 알람 취소버튼을 추가하고 그 버튼이 눌리면 alarmManager의 cancel 메소드를 이용하면 됩니다.