D-day 앱을 한번 만들어보다가 헷갈리는 부분이 있었는데 해결하게 되어 글을 남겨본다.
만들고자 했던 앱의 주요 기능/조건은 다음과 같다.
- 사용자가 특정 날짜를 선택하면 오늘과의 일 수 차이를 계산하여 위젯에 띄워준다
- 액티비티에서 값(선택한 날짜)을 변경하면 위젯에 즉시 반영되어야 한다
- 하루가 지나거나 시간을 수정하면 자동으로 D-day가 바뀌어야한다
위젯에 띄워주는 것은 몇 번의 검색으로도 쉽게 이해할 수 있었고 만들기도 쉬웠지만
액티비티에서 값을 변경했을 시 즉시 반영되는 부분은 잘 되지 않았다.
우선 위젯의 생명주기를 이해해야한다. 오버라이드 된 함수들에 로그만 띄워도 어느 정도 이해할 수 있다.
onEnabled : 위젯이 처음 생성될 때 호출된다.
onDisabled : 위젯이 화면에서 삭제될 때 호출된다.
onUpdate : 위젯 xml 파일 내의 android:updatePeriodMillis 값에 따라 주기적으로 호출된다. 위젯을 갱신하는 함수를 여기 넣으면 된다.(주기는 최소 30분으로 제한)
onReceive : 브로드캐스트가 왔을 때 호출된다.
액티비티에서 날짜 값을 변경하거나 하루가 지나면 브로드캐스트를 날리게 만들었다.
아래는 액티비티에서 위젯으로 브로드캐스트 날리는 예시이다
Intent intent = new Intent(MainActivity.this, MainAppWidget.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
MainActivity.this.sendBroadcast(intent);
액티비티에서 날린 브로드캐스트를 받으려면 브로드캐스트 리시버 역할을 하는 onReceive에 값을 변경해주는 코드를 넣어야 했다.
그런데 onReceive 에서는 위젯을 갱신하지 않는다. 로그로 확인해보니 값이 정상적으로 변경이 되어도 위젯 상에는 반영이 안되었다.
30분 주기로 실행되는 onUpdate가 한번 실행되고 나서야 값이 변경이 되었다.
해결 방법은 이렇다. onReceive 에서 브로드캐스트를 받으면, 값 계산을 해 준 다음(이 경우에는 오늘 날짜에서 선택한 날짜를 뺀다)
onReceive 내에서 onUpdate 를 호출해주면 되는 것이다.
onReceive 의 인자와 onUpdate의 인자는 다르기 때문에 onReceive 내에서 다음과 같이 초기화가 필요하다.
아래 코드는 onReceive 내에서 브로드캐스트를 받고 값을 모두 수정한 다음에 써주는 게 좋다.
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName testWidget = new ComponentName(context.getPackageName(), [위젯클래스이름].class.getName());
int[] widgetIds = appWidgetManager.getAppWidgetIds(testWidget);
if(action.equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE)){
if(widgetIds != null && widgetIds.length >0) {
this.onUpdate(context, AppWidgetManager.getInstance(context), widgetIds);
}
}
위 부분을 써주지 않으면 앞서 말한 것처럼 값은 바뀌나 위젯에 즉시 적용이 되지 않는다.
onUpdate의 주기를 30분 미만으로 조정해서 하는 방법도 있다고 들었지만 그렇게 자주 업데이트할 필요가 없다면 위 방법이 적절할 것이다.