Remember 함수는 컴포저블이 리컴포지션이 일어날 때 데이터를 저장하고 다시 사용할 수 있도록 하는 함수다.
쉽게 말하면 컴포즈에게 리컴포지션이 일어났을 때 잃어버리거나 리셋하지 않고 데이터를 계속해서 트래킹하라고 말해주는 것이다.
remember 내부 구조 설명글 https://medium.com/@joseph.1hach/everything-you-need-to-know-about-remember-in-android-jetpack-compose-3fb14356365
/**
* Remember the value produced by [calculation]. [calculation] will only be evaluated during the composition.
* Recomposition will always return the value produced by composition.
*/
@Composable
inline fun <T> remember(crossinline calculation: @DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
remember의 내부를 들여다보면 @DisallowComposableCalls 어노테이션을 가지는 람다를 받고 있다.
해당 어노테이션은 Text()와 같은 컴포저블 함수가 오지못하도록 막는 역할을 한다. 컴포저블 함수를 remember하는 것은 예상치 못한 동작이나 리컴포지션을 무한반복하는 에러가 발생할 가능성이 있기에 막혀있다.
remember함수는 currentComposer.cache(false, calculation)을 리턴하는데 해당 함수는 파라미터로 받은 calculation의 결과값을 캐싱해서 값을 제공해준다.
Composer은 컴포즈 프레임워크가 컴포지션과 리컴포지션 프로세스를 관리할 때 사용하는 internal object이다. 실제로 @Composable이 붙은 컴포저블 함수를 컴파일해보면 파라미터로 Composer와 key가 추가된다.
cache는 Composer의 extension으로
/**
* A Compose compiler plugin API. DO NOT call directly.
*
* Cache, that is remember, a value in the composition data of a composition. This is used to
* implement [remember] and used by the compiler plugin to generate more efficient calls to
* [remember] when it determines these optimizations are safe.
*/
@ComposeCompilerApi
inline fun <T> Composer.cache(invalid: Boolean, block: @DisallowComposableCalls () -> T): T {
@Suppress("UNCHECKED_CAST")
return rememberedValue().let {
if (invalid || it === Composer.Empty) {
val value = block()
updateRememberedValue(value)
value
} else it
} as T
}
두가지 파라미터를 가지는데 invalid는 캐시된 값이 invalid되면 처음에 제공한 calculation을 통해 값이 갱신해야한다. 만약 값이 변경되지 않거나 state가 변경되지 않는다면 recomposition을 할 필요가 없다. 두번째는 제네릭 T를 반환하는 람다를 가지고있다.
rememberedValue()는 이전 컴포지션에서 저장된 값을 반환한다.
if (invalid || it === Composer.Empty) { ... } else it 은 캐시된 값이 invalid한지, 빈값인지를 검사한다. true일 경우 새로운 값을 계산해야할 필요가 있다고 판단된다. 위에서 전달한 calculation을 통해 값을 새로 받아오고 해당 값은 updateRememberedValue(value)함수를 통해서 캐시 값을 갱신하고 새로운 값을 리턴한다. 만약 위 컨디션검사에서 false가 나오면 기존에 가지고 있던 값을 리턴한다.
remember함수는 아래와 같이 여러 키를 받을 수 있는데
/**
* Remember the value returned by [calculation] if [key1], [key2] and [key3] are equal to the
* previous composition, otherwise produce and remember a new value by calling [calculation].
*/
@Composable
inline fun <T> remember(
key1: Any?,
key2: Any?,
key3: Any?,
crossinline calculation: @DisallowComposableCalls () -> T
): T {
return currentComposer.cache(
currentComposer.changed(key1) or
currentComposer.changed(key2) or
currentComposer.changed(key3),
calculation
)
}
key는 컴포지션이 리컴포즈하고 메모리에 저장된 값을 갱신해야할지 판단하는 중요한 값으로 사용된다.
이때 메모리에 어떻게 값이 저장되는지는 Gap Buffer, Positional Memorize 이렇게 두가지가 사용된다.
자세한 내용은 글을 읽는 것보단 Android Developers의 유튜브를 참고하는 것이 더 도움이 된다.