ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Compose] hiltViewModel을 써서 composable끼리 viewModel을 공유해보자!
    Android📱 2022. 9. 20. 18:28

    * 안드로이드를 공부하고 있는 학생의 공부 글입니다. 틀린 내용이 있다면 꼭 알려주시면 좋겠습니다. 혹시나 글을 보시는 분들은 너무 믿지는 마세요. ㅠ

     

    요즘 Compose를 이용해서 새로운 프로젝트를 진행해보고 있는데

    아주 재미있다 ㅎㅎ(만족)

     

    일단 Compose를 사용하면서 고민을 했던 점이 몇 가지가 있었다.

     

    화면 이동부터 fragment를 쓸까말까 등등 많은 고민을 하였는데

    나는 compose는 Single Activity를 위한 기술이라는 생각도 있었고, compose도 공부할 겸 100% Compose를 사용해보고 싶었다.

    취직이 언제될지는 모르겠지만.... 언제 이런 도전을 해보겠는가...

    지금뿐이다!

     

    그러다가 마주친 문제가 viewModel의 공유였다.

     

    보통 작업을 할때는 activityViewModels를 이용해서 activity에 있는 ViewModel을 fragment가 공유해서 데이터를 공유하고,

    요청을 fragment가 이동할 때마다 하지 않아도 됐었는데

    compose는 어떻게 해야할지 고민이 되었다...

     

    하지만 역시...똑똑한 사람덜.... 이미 다 방법은 있었다.

    바로 Hilt (Hilt를 사용하고 있습니다...)에서 제공하는 hiltViewModel이라는 함수였다.

     

    일단 사용법은

     

    implementation "androidx.hilt:hilt-navigation-compose:1.0.0"

    이 친구를 추가해준다. (app gradle)

    그리고 composeable을 이동할때 사용하는 navigation에

     

    val navController = rememberNavController()
    NavHost(navController, startDestination = "ExampleRoute") {
        composable("ExampleRoute") {
            val viewModel = hiltViewModel<ExampleViewModel>()
        }
    }

    이렇게 코드를 달아주면 된다. (그전에 당연히 ViewModel을 만들어주세요)

    viewModel은 @HiltViewModel로!!

     

    그러면 composable에 viewModel을 인자로 넘겨주어 viewModel을 사용할 수 있게 된다.

     

    아주 쉽죠?

     

    그런데 내가 이글을 쓰게 된 이유는 바로 ViewModel의 공유였다.

    하지만 위처럼만 작성을 하면

    이렇게 다른 ViewModel을 사용하게 된다.

     

    그래서 뷰모델을 공유해주기 위해서는 

    val navController = rememberNavController()
    NavHost(navController, startDestination = "Parent") {
        navigation(startDestination = "InnerRouteA", route = "Parent") {
            composable("InnerRouteA") {
                val viewModel = hiltViewModel<ParentViewModel>(
                    navController.getBackStackEntry("Parent")
                )
            }
            composable("InnerRouteB") {
                val viewModel = hiltViewModel<ParentViewModel>(
                    navController.getBackStackEntry("Parent")
                )
            }
        }
    }

    이렇게 hiltViewModel의 인자로 getBackStackEntry를 넘겨주어야 한다.

     

    이 코드를 보고 나는 궁금해졌다...

    이게 어떻게 가능한 것일까?!

    너무 신기하지 않은가

    그냥 navController.getBackStackEntry()만 넣어줬는데 

    알아서 뷰 모델을 생성하고 있는 걸 사용해준다 ㄷㄷ

     

    그래서 내부 코드를 한번 구경해보기로 하였다.

     

    일단 hiltViewModel을 한번 보자

     

    인자로 viewModelStoreOwner라는 객체를 받고 있고, 그건 우리가 넘겨주었던 getBackStackEntry였다.

    그러면 getBackStackEntry를 가보자!!

    한번 슬쩍 보면 backQueue라는 변수에 ArrayDeque로 NavBackStackEntry가 들어가 있고, 여기서 가장 끝에 있는 NavBackStackEntry를 찾고 내가 인자로 넘겨주었던 rout와 비교를 해서 꺼내는 것을 알 수 있다.

     

    그리고 NavBackStackEntry객체를 보면

    NavBackStackEntry가 ViewModelStoreOwner를 상속받은 객체이기 때문에 ViewModelStoreOwner를 넘겨달라고 했던 hiltViewModel이 NavBackStackEntry를 받을 수 있었다는 것을 알 수 있었다.

     

    그렇다면 ViewModelStoreOwner는 뭐길래 내가 쓰고 싶은 ViewModel의 존재 여부를 확인할 수 있는 것일까?

    ViewModelStoreOwner에 가보면 설명이 설명이 나와있는데 ViewModelStore라는 이야기가 나온다.

    ViewModelStore는 ViewModel을 Map 형태로 저장을 하는 객체이다.

    그리고 getViewModelStore는 ViewModelStore를 return 한다.

     

    그러면 hiltViewModel은 createHiltViewModelFactory라는 함수로 viewModelStoreOwner를 던져주고,

    createHiltViewModelFactory는 HiltViewModelFactory를 이용해서 내가 원하는 viewModel을 생성하거나 찾아오게 되는 것이다.

     

    요약하면 hiltViewModel()은 백 스택에서 내가 지정한 rout와 같은 rout을 갖는 NavBackStackEntry를 받고,

    거기서 NavBackStackEntry를 이용해서 ViewModelFactory로 내가 원하는 뷰 모델을 생성하거나 가져오게 된다!

    ViewModelFactory를 따라 들어가 보면 SavedStateViewModelFactory라는 class가 있는데 아마 이 녀석으로 찾는 걸로 추측된다!

     

    아주 신기했지만 결국 누군가의 노력....

    열심히 만들어 주셨으니 잘 사용해보아야겠다!

Designed by Tistory.