ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Jetpack Glance: 위젯에 최신 데이터 전달하기
    개발/개발 지식 공유 2025. 10. 8. 20:29

    Jetpack Glance Widget을 사용하여 할 일 목록(Todo List) 위젯을 홈 화면에 만들었습니다.

     

    저는 앱에서 데이터를 수정했을 때 자동으로 최신 데이터가 위젯에도 반영되기를 원했습니다.

    하지만 제대로 된 방식으로 데이터를 제공하지 않는다면 위젯이 업뎃되지 않는 문제가 발생할 수 있습니다.

     

    성공적으로 위젯을 업데이트 하는 방법을 공유하려고 합니다.

     

     

     

    위젯에 State 데이터 제공하기

    일단 위젯에 데이터를 제공하는 가장 좋은 방법은 Flow<T>.collectAsState() 메서드를 사용하는 것입니다.

    class TodoWidget : GlanceAppWidget() {
        override suspend fun provideGlance(context: Context, id: GlanceId) {
            provideContent {
                GlanceTheme {
                    TodoWidgetContent(context)
                }
            }
        }
    
        @Composable
        private fun TodoWidgetContent(context: Context) {
            val repository = remember { (context.applicationContext as TodoApplication).repository }
    
            // getAllTodos()는 Kotlin Room을 사용하며, Flow<List<Todo>> 를 반환하는 함수입니다.
            val todos by repository.getAllTodos().collectAsState(initial = emptyList())
            
            todos.forEach { todo ->
                TodoItem(todo = todo)
                Spacer(modifier = GlanceModifier.size(8.dp))
            }
        }
    }

     

    GlanceAppWidget은 @Composable을 통해 화면을 구성합니다.

    @Composable의 리컴포지션은 관찰 가능한 상태 변경을 통해서만 가능합니다.

    그러므로 State가 아닌 다른 방식으로 데이터를 아무리 가져와봐야 위젯은 업데이트 되지 않습니다.

    데이터가 변경되어야 하는 변수는 collectAsState()를 통해 가져올 수 있도록 합시다.

     

    만약 여러분이 Flow를 매끄럽게 처리해주는 Room 같은 데이터 소스를 사용하지 않는다면, 아래와 같이 편법을 써야 합니다.

    https://stackoverflow.com/questions/77088363/android-jetpack-glance-1-0-0-problems-updating-widget

    GlanceAppWidget 내부적으로 사용하는 GlanceStateDefinition 과 DataStore 를 override하여, plain object를 flow에 담아 emit하는 방식입니다.

    어찌 되었든 GlanceAppWidget이 지원하는 방식으로 데이터를 업뎃하려면 이렇게 State에 담아내야 합니다.

     

     

     

     

    명시적으로 위젯 업데이트하기

    그리고 하나 더, 저같은 경우에는 애플리케이션에서 홈 화면으로 나가거나, 백그라운드로 전환될 때마다 위젯을 업데이트 하고자 했습니다.

    명시적으로 위젯을 업데이트 하려면 GlanceAppWidget.update() 함수를 사용하면 됩니다.

     

    저는 이를 애플리케이션 레벨의 onStop 라이프사이클에 추가하여 실행되도록 했습니다.

    class TodoApplication : Application(), DefaultLifecycleObserver {
        override fun onCreate() {
            super<Application>.onCreate()
            // ...
    
            // Observe app lifecycle to trigger widget updates
            ProcessLifecycleOwner.get().lifecycle.addObserver(this)
        }
    
        override fun onStop(owner: LifecycleOwner) {
            super.onStop(owner)
            owner.lifecycleScope.launch {
                TodoWidget().updateAll(applicationContext)
            }
        }
    }

     

    주목해야 할 점은 ProcessLifecycleOwner의 사용입니다.

    만약 Activity의 onStop 에서 lifecycleScope.launch 로 코드를 실행하면, 코루틴에 추가된 작업이 실행되기 전에 Activity가 destroy 될 경우 코루틴 작업이 취소되어 버리는 상황이 발생합니다.

     

    onStop 라이프사이클에서 suspend fun 코드의 실행을 최대한 보장하려면 ProcessLifecycle을 이용하도록 합시다.

    (물론 프로세스가 완전히 종료되어 버리는 상황에서는 실행이 보장되지 않을 수 있습니다)

     

     

     

    참고

    GlanceAppWidget: https://developer.android.com/develop/ui/compose/glance/glance-app-widget

    Compose 상태: https://developer.android.com/develop/ui/compose/state?hl=ko

    ProcessLifecycleOwner: https://developer.android.com/reference/androidx/lifecycle/ProcessLifecycleOwner

     

    댓글

Copyright 2022. ProdYou All rights reserved.