본문 바로가기
Android Jetpack Compose/Navigation, Route

Route 에서 전달된 값을 ViewModel 에서 받는 방법 # Jetpack Compose Navigaion savedStateHandle

by Developer88 2023. 4. 13.
반응형

오늘은 Route에서 넘어온 값을 ViewModel 에서 savedStateHandle 을 이용해 받아서 사용하는 방법에 대해서 정리해 보겠습니다.

 

1. Route에서 값을 넘겨줄 때

먼저 route에서 값을 넘겨주는 코드를 보고 가겠습니다.

testId를 route에 실어서 보내주는 코드인데요.

TestId는 ViewModel에서 받아서 사용할 것이므로,

TestPage에 인자로 넘겨주지 않았습니다.

 

...
composable(
    route = "test_route" + "/{testId}",
    arguments = listOf(
        navArgument("testId") {
            type = NavType.LongType
            defaultValue = -8889L
        },
    ),
) {
    TestPage()
}

 

 

 

참고로 공식문서에 나온, Route 의 Destination 간 전달할 수 있는 데이터 타입으로는 다음과 같은 것들이 있습니다.

 

 

참고로, 복잡한 데이터를 Parcelable 이나 Serializable로 넘기는 것은 하지 말라고 하고 있습니다.

복잡한 구조의 데이터를 넘기려면, 공유되는 ViewModel 이나 database에 저장해서 사용하라고 하고 있네요.

 

 

이는 공식문서에서 2번이나 언급할 정도로 반대하고 있는 내용입니다.

 

 

일반적으로 위의 예제에 있는 TestPage()에 인자로 넘겨주고 싶다면,

아래와 같이 testId를 받아온 후, TestPage(testId)와 같이 넘겨주기만 하면 되는데요.

하지만 저희는 ViewModel에서 받을 것이기 때문에 위 코드는 사용하지 않습니다.

 

val testId = backStackEntry.arguments?.getLong("testId")

 

아래에서 ViewModle 에서 어떻게 받는지 보도록 하겠습니다.

 

2. ViewModel 에서 값을 받을 때

ViewModel의 객체를 생성할 때 직접 객체를 생성하는 방법도 있지만,

Hilt를 이용할 수도 있는데요.

이 글에서는 Hilt를 이용해서 ViewModel객체를 생성하는 방법을 이용하고 있습니다.

hilt에 대해서는 아래 글을 참조해 주세요.

>> HILT 에 대해서 정리해 보겠습니다. # DI Dependency Injection

 

2-1. ViewModel 에서 SavedStateHandle 로 받기

ViewModel 에서 값을 받을 때는 SavedStateHandle을 사용해주면 되는데요.

아래는 Hilt를 이용해서 viewModel 을 생성해주었는데요.

이 savedStateHandle 을 이용해서, initBlock에서 값을 아래와 같이 받아왔습니다.

 

참고로 아래의 testId와 같이 한번 값을 받아오면,

변하지 않을 변수에 대해서는,

"?"의 타입과 함께 val 키워드로 선언해주면, immutability를 유지할 수 있어서 좋습니다.

 

@HiltViewModel
class MyViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    val testId: Long?

    init {
        testId = savedStateHandle.get<Long>("testId")
    }
}

 

 

한가지 주의할 점은, 이 값을 저장해서 사용할 때 nullable 한 타입으로 선언해야 한다는 것 입니다.

이유는 get<T>타입 함수가 Nullable 한 타입으로 들어오기 리턴해주기 때문입니다.

 

 

3. 팁

3-1. 특수문자 보낼 때는 uriEncode 와 uriDecode

다른 글에서도 언급한 적이 있는데요.

만약 fileUri 에 들어있을 법한 "/"가 포함된 String같은 것들을 route의 parameter로 보낼 경우,

이를 navigation 라이브러리가 잘못 해석할수 있기 때문에, 

encoding과 decoding을 해주어야 합니다.

 

먼저 encoding은 아래와 같이 해주고요.

 

val encodedFileUriString = Uri.encode(fileUriString)

 

decoding 은 다음과 같이 해주면 됩니다.

 

imageUriString?.let { uriString ->
    val decodedUri = Uri.decode(uriString).toUri()
    val bitmap = getBitmapFromUri(decodedUri)
    imageBitmap = bitmap.asImageBitmap()
}

 

이렇게 하면 "/"와 같이 route에 사용될 법한 문자가 들어있는 스트링도 안전하게 routing 할 수 있게 됩니다.

 

728x90

댓글