개발관련일지

android compose stable 학습정리 본문

개발기록/안드로이드

android compose stable 학습정리

BEECHANGBOT 2023. 7. 24. 20:13

컴포즈에서 @stable 어노테이션을 봤는데 뭔지 몰라서 알아보게됨

 

리컴포지션이랑 관련이 있따.

리컴포지션은 인풋값이 변경 될떄 컴포저블 함수를 다시 호출 하는 과정인데 stable(안정성)을 이용해서 런타임중에 파라미터중에 어느것도 업데이트 되지 않았다라는 것을 확신을 하거나 변경을 확인한 이력이 있는 경우의 변경된게 없을 경우 스킵을 한다. 컴포즈의 상태가 변경 될 떄 컴포즈내에 상태객체가 읽히는 모든 지점위의 가장 가까운 재시작가능한 함수를 찾는다(리컴포지션을 최소화하기위함). 로우,컬럼,박스 같은 기본 컴포저블들은 인라인함수로 동작 -> 컴파일 함수로 되어있지 않기 때문에 리컴포지션 시작지점이 없다. 부모로 부터 자식노드까지 변경된 파라미터가 있는경우 재생성된다.  

 

이렇게 컴포저블의 파라미터가 변경되지 않았거나 값이 변경될 일이 없는 경우를 스킵하는 시스템을 스마트 리컴포지션이라한다. 이 시스템에서 Stable이 영향을 미친다. 

컴포저블이 이미 컴포지션 내에 있으면서 아래의 조건이 성립 되는 경우에 인풋값이 변하지 않았을 때 스킵된다. 번역이 애매하긴한데 해석해보면 아래인거 같고 틀릴수도있다.

  • The result of equals for two instances will forever be the same for the same two instances. ( 두 인스턴스의 equals 결과가 동일할 경우)
  • If a public property of the type changes, Composition will be notified.(상태가 변경될때마다 변경사항이 컴포지션에 알려져야함)
  • All public property types are also stable.(프로퍼티의 타입(프로퍼티들이 원시타입, 스트링 , 람다)또한 안정적이여야함)

컴포즈는 타입이 안정적하다고 판단 될때 만 안정적으로 간주를하고 interface는 비안정적으로 간주되고 구현이 불변성을 가질 수 있는 경우에는 컴포즈가 안정적임을 추론할 수 없지만 @stable어노테이션을 이용해서 강제로 컴포즈 컴파일러에게 안정적이라고 알릴 수 있다. 

즉 안정성을 추론이 불가능 할 경우 어노테이션을 이용해서 강제적으로 안정적으로 알리는거다. 물론 제대로 사용하지 않은 경우는 예상과 다른 동작이 발생 할 수 있고 반드시 필수는 아니다. 

 

정리를 해보면 @Immutable , @stable 은 명시적으로 안정성이 있다고 나타낼 수 있는거다. 

컴포즈 런타임은 두개의 어노테이션을 제공하고 어노테이션들이 컴포즈 컴파일러가 런타임중 안정적인 타입만 확인 후 리컴포지션 스킵핑이 가능하도록한다.(파라미터의 인풋값이 변하지 않는한 결과는 안바뀜)

@Immutable은 생성 된 후에 모든 프로퍼티들이 불변상태(변하지않음)를 클래스에 적용 -> 변하지않으니 컴포저블이 변화를 감지해야하는거에서 무시됨 

@Stable은 값이 mutable하지만 컴포즈 런타임에 변경사항이 알려짐 -> 최신내용이 변화가 없을 경우 스킵가능

 

stable하도록 하는 방법들

1. var -> val  

2. collection class(list , set, map)은 불안정하다고 판단 (선언 후 내부 데이터가 변할 수 있으므로) 

2-1. key로 wrapping에서 타입확인 

@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            key(movie.id) { // Unique ID for this movie
                MovieOverview(movie)
            }
        }
    }
}

2-2 kotlinx.collections.immutable 컬렉션 라이브러리를 사용해서 wrapping 

 

위의 두 어노테이션을 잘 못 사용하면 컴포즈 컴파일러에서 예상 못한 결과가 나와서 런타임 에러 발생 할 수 있고 원하는대로 동작 안할 수 도 있다.

 

내가 처음 보게 된 @Stable 코드가 샘플앱인 Jetsnack이다.

/**
 * Responsible for holding state related to [JetsnackApp] and containing UI-related logic.
 */
@Stable
class JetsnackAppState(
    val scaffoldState: ScaffoldState,
    val navController: NavHostController,
    private val snackbarManager: SnackbarManager,
    private val resources: Resources,
    coroutineScope: CoroutineScope
)

위의 클래스는 앱의 주요 상태들을 관리하는 클래스고 해당 프로퍼티들의 상태들이 변경되면 ui관련 로직들도 변해야할테니 관련 컴포저블의 상태를 컴포즈에 알려줘서 안정성을 확보하기 위함이 아닐까 보편적인 방식으로 추측해본다. 

 

 

참고

https://medium.com/androiddevelopers/jetpack-compose-stability-explained-79c10db270c8 

https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md#stable-types

https://sungbin.land/a-deep-dive-into-jetpack-compose-stability-38b5b109da71

https://developer.android.com/jetpack/compose/lifecycle

https://developer.android.com/jetpack/compose/performance/stability

https://stackoverflow.com/questions/68575936/what-do-the-stable-and-immutable-annotations-mean-in-jetpack-compose