Giter VIP home page Giter VIP logo

android-note's People

Watchers

MoonkiKim(김문기) avatar

android-note's Issues

compose under the hood - donut hole skipping

Donut hole skipping

리컴포지션의 특징으로, 특정 컴포지션이 리컴포지션 될때, 해당 컴포지션의 모든 서브 컴포지션이 리컴포지션 되는게 아니고, 관련이 없는 것들은 리컴포지션에서 제외되는 현상. 중간에 있는 컴포지션들은 리컴포지션에서 빠지는게 마치 도넛같아서 이름을 이렇게 부름.

Recomposition

리컴포지션은 변화가 있을때 컴포저블 함수를 다시 호출하는것이다. 해당 컴포저블 함수의 인풋에 변화가 있을때 발생한다. 새로운 인풋을 기반으로 리컴포즈 할때, 변화가 있을만한 함수나 람다들만 다시 부르고 나머지는 스킵한다. 파라미터의 변화가 없는 함수와 람다를 리컴포즈 하지 않음으써 컴포즈는 효과적으로 동작한다.

컴포져블 함수의 입력이나 상태가 변화할때, 해당 함수가 다시 호출되어 최신 변화가 반영되는걸 이해하는건 Jetpack Compose의 동작을 이해하는데 엄청나게 중요하다.

기본적인 Composable을 이해하기 위해 예시를 보자

@Composable
fun MyComponent() {
    val counter by remember { mutableStateOf(0) }
    CustomText(
        text = "Counter: $counter",
        modifier = Modifier
            .clickable {
                counter++
            },
    )
}

@Composable
fun CustomText(
    text: String,
    modifier: Modifier,
) {
    Text(
        text = text,
        modifier = modifier.padding(32.dp),
        style = TextStyle(
            fontSize = 20.sp,
            textDecoration = TextDecoration.Underline,
            fontFamily = FontFamily.Monospace
        )
    )
}

MyComponent 컴포저블 함수를 만들고, 그 안에서 counter 값을 담기위해 state object를 초기화 시켜준다. counter 값은 Text 컴포저블 함수에 의해서 보여지고, Text를 누를때마다 counter값이 증가한다. 여기서 주의깊게 볼 점은 어떤 컴포져블 함수들이 재실행되는지이다. 이해를 돕기위해 로그를 추가해준다. 초기 컴포지션은 제외하고 리컴포지션때만 로그가 찍히게 하기 위해 아래 예시처럼 로그를 만든다.

class Ref(var value: Int)

// Note the inline function below which ensures that this function is essentially
// copied at the call site to ensure that its logging only recompositions from the
// original call site.
@Composable
inline fun LogCompositions(tag: String, msg: String) {
    if (BuildConfig.DEBUG) {
        val ref = remember { Ref(0) }
        SideEffect { ref.value++ }
        Log.d(tag, "Compositions: $msg ${ref.value}")
    }
}
@Composable
fun MyComponent() {
    val counter by remember { mutableStateOf(0) }

+   LogCompositions("JetpackCompose.app", "MyComposable function")

    CustomText(
        text = "Counter: $counter",
        modifier = Modifier
            .clickable {
                counter++
            },
    )
}

@Composable
fun CustomText(
    text: String,
    modifier: Modifier = Modifier,
) {
+   LogCompositions("JetpackCompose.app", "CustomText function")

    Text(
        text = text,
        modifier = modifier.padding(32.dp),
        style = TextStyle(
            fontSize = 20.sp,
            textDecoration = TextDecoration.Underline,
            fontFamily = FontFamily.Monospace
        )
    )
}

예시를 실행시켜보면, MyComponent, CustomText counter 값이 변할 때 마다 리컴포지션 되는걸 알 수 있다. 조금 수정한 다음 예시를 보자.

@Composable
fun MyComponent() {
    val counter by remember { mutableStateOf(0) }

    LogCompositions("JetpackCompose.app", "MyComposable function")

+   Button(onClick = { counter++ }) {
+       LogCompositions("JetpackCompose.app", "Button")
        CustomText(
            text = "Counter: $counter",
-            modifier = Modifier
-                .clickable {
-                    counter++
-                },
        )
+   }
}

조금 변경한 예시를 보면, MyComponent 는 리컴포지션되지 않았음을 알 수 있다.

Recomposition Scope (리컴포지션 범위)

위 사진에서 두개의 람다 스코프를 볼 수 있다. 하나는 MyComponent 함수, 다른 하나는 CustomText 함수이다. CustomText는 MyCompoent의 람다 스코프 안에 있다. counter 값이 변경되면, 두 범위 모두 영향을 받는데, 그건 CustomText는 counter 값을 사용하는 텍스트 파라미터가 변경되었기 때문이고, MyComponet 는 해당 람다 범위가 counter 값을 캡처하고 있기 때문이다.

위 사진에서 보면, Button이랑 CustomText만 리컴포지션 된다. MyComponent에서 counter 값을 초기화 하긴하지만 값을 사용하는 곳이 없고, 바로 위 부모 범위가 아니기 때문에 MyComponent는 리컴포지션 되지 않는다. (그럼 값을 사용하지 않아도 바로 위 컴포지션은 무조건 리컴포즈 된다는 뜻인가?)

진정한 도넛

@Composable
fun MyComponent() {
    val counter by remember { mutableStateOf(0) }

    LogCompositions("JetpackCompose.app", "MyComposable function")

+   val readingCounter = counter
+   CustomButton(onClick = { counter++ }) {
        LogCompositions("JetpackCompose.app", "CustomButton scope")
        CustomText(
            text = "Counter: $counter",
            modifier = Modifier
                .clickable {
                    counter++
                },
        )
    }
}

@Composable
fun CustomButton(
    onClick: () -> Unit,
    content: @Composable () -> Unit
) {
    LogCompositions("JetpackCompose.app", "CustomButton function")
    Button(onClick = onClick, modifier = Modifier.padding(16.dp)) {
        LogCompositions("JetpackCompose.app", "Button function")
        content()
    }
}

위 결과를 보면, CustomButton function 로그와, Button function 로그가 모두 스킵된걸 볼 수 있다. 우리는 이미 이전 예제에서 mutable state object를 읽지 않거나, 인풋이 변경되지 않았으면 Compose는 리컴포즈를 스킵하는걸 알 수 있었다. 이 예시도 같은걸 결과를 보여준다. 하지만 좀 더 특별한 동작이 해당 예시에선 숨겨져 있다. 그건 특정 컴포저블의 부모와 자식 컴포저블이 리컴포지션 되는 동안 해당 컴포저블은 리컴포지션을 안할 수 있다는 것이다. 다른 뷰 시스템체계에서는 보통 부모가 변경되면 전체 자식 들이 모두 재실행되는데 컴포즈에선 영향받는부분이 없다면 중간부분이 생략 될 수 있다.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.