Upgrade to Pro — share decks privately, control downloads, hide ads and more …

컴포즈 내부로 이해하는 최적화 비법

Ji Sungbin
November 17, 2022

컴포즈 내부로 이해하는 최적화 비법

2022 성빈랜드 발표회에서 진행된 강연 자료를 공개합니다.

---

# 컴포즈 내부로 이해하는 최적화 비법

### 타이틀

안녕하세요, 저는 “컴포즈 내부로 이해하는 최적화 비법” 라는 주제로 발표할 지성빈 입니다.

### 소개

저는 안드로이드 개발을 시작한지는 약 8년정도 됐으며 덕키 프로젝트의 리드를 담당하고 있습니다.

### 목차

이번 발표는 컴포즈의 기본적인 내부 동작 원리를 살펴보면서 이를 응용하여 최적화 하는 비법들에 대해 소개하려고 합니다. 목차는 Node System, Compose UI, Snapshot System, Compose Compiler, 마지막 마무리로 준비했습니다.

### 목차로부터 알 수 있는 점

이 목차에서도 새로운 정보를 알 수 있습니다. Node System, Snapshot System 그리고 Compose Compiler 를 합친 영역을 Compose Runtime 이라고 부르고, 이 Compose Runtime 과 Compose UI 를 더하면 최종적으로 Jetpack Compose 가 됩니다.

Jetpack Compose 는 Rumtime 과 UI 가 더해진 최종 상태라는 것만 알아두시면 됩니다.

### 시작하기 전에

본격적으로 발표를 시작하기 전에, 컴포즈 버전은 1.3.0 버전을 기준으로 준비했으며 빠른 이해를 위해 이 발표에서 소개할 개념들의 세부 사항은 생략하였습니다.

### Node System

이제, Compose Runtime 의 뼈대인 Node System 을 먼저 알아보는 것으로 시작하겠습니다.

### Node - 1

간단하게 3개의 Text 를 표시하는 컴포저블이 있습니다. 이 컴포저블은 그려지기 위해 오른쪽처럼 UI Tree 로 렌더링됩니다.

### Node - 2

렌더링된 UI Tree 의 각각 노드들은 Compose Runtime 에서 Ui Node 라는 타입으로 관리됩니다. 이렇듯, 결국 모든 컴포저블은 Compose Runtime 에서 노드로 처리되고, 이러한 시스템을 Node System 이라고 부릅니다.

### Compose Runtime & Compose UI

Compose Runtime 과 Compose UI 를 쉽게 나타내자면 Compose Runtime 은 Node interface 를 나타내고, Compose UI 는 해당 노드를 실제로 구현한 Node implementation 이라고 할 수 있습니다.

### SlotTable

Compose Runtime 에서 생성되는 Node 들의 데이터는 SlotTable 에서 관리됩니다. SlotTable 은 갭 버퍼와 랜덤 액새스를 이용하여 선형 배열에 모든 데이터를 저장하고 관리하는 클래스 입니다.

### Gap Buffer

대부분 갭 버퍼에 대해 생소하실 거 같아 간단하게 Gap Buffer 를 소개하겠습니다. 텍스트 에디터를 생각해 봅시다. 입력되는 텍스트가 일반 선형 ...

Ji Sungbin

November 17, 2022
Tweet

More Decks by Ji Sungbin

Other Decks in Programming

Transcript

  1.  /PEF4ZTUFN  $PNQPTF6*  4OBQTIPU4ZTUFN  $PNQPTF$PNQJMFS  8SBQVQ

    📖ݾର $PNQPTF3VOUJNF $PNQPTF6*  +FUQBDL$PNQPTF
  2. 1. Node System  *OUSPEVDJOH/PEF  4MPU5BCMF(SPVQ  !3FBE0OMZ$PNQPTBCMF 

    $PNQPTJUJPO 3VOUJNF 3FDPNQPTJUJPO 4NBSU  %POVUIPMFTLJQQJOH
  3. @Composable fun UiNodes() { Column { repeat(3) { index ->

    Text(text = "My index is $index") } } } Column Text Text Text
  4. @Composable fun UiNodes() { Column { repeat(3) { index ->

    Text(text = "My index is $index") } } } Ui Node Ui Node Ui Node Ui Node
  5. /** * ஹನૉ੄ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೞח ௿ېझ ੑפ׮. *

    Gap Buffer ੗ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘ੉ఠח Array<Any?> ী ੷੢ؾפ׮. */ internal class SlotTable : CompositionData, Iterable<CompositionGroup>
  6. Gap Buffer: ࢎਊೡ ҕрٜਸ ޷ܻ ഛࠁ೧ فҊ, ೧׼ ҕрীࢲ ੘সਸ

    ૓೯ೞח ੗ܐ ҳઑ. ਤఃೖ٣ইীࢲח زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ ੉ۄҊ ੿੄ೞҊ ੓णפ׮.
  7. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն
  8. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gap ী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1) ী ৮ܐؽ
  9. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gap ী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1) ী ৮ܐؽ 3. 1ߣ૩ ੋؙझ੄ i ઁѢ [H, (v)_, _, _, _, _, _, _, _, _] // cursor ܳ i о ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N) ੉ ѦܻҊ // ੉റ Gap ীࢲ ч ઁѢо ૓೯غ޲۽ O(1) ݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gap ਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮)
  10. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ

    ੘সਸ ೲਊೞח ز੸ ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ࢗੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gap ী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1) ী ৮ܐؽ 3. 1ߣ૩ ੋؙझ੄ i ઁѢ [H, (v)_, _, _, _, _, _, _, _, _] // cursor ܳ i о ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N) ੉ ѦܻҊ // ੉റ Gap ীࢲ ч ઁѢо ૓೯غ޲۽ O(1) ݅ী ৮ܐؽ (ч ઁѢח ೧׼ чਸ Gap ਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮) 4. 0ߣ૩ ੋؙझ੄ H ܳ Bye ۽ ߸҃ [(v)B, y, e, _, _, _, _, _, _, _] // cursor ܳ H о ੓ח ਤ஖۽ ৤ӝӝ ਤ೧ O(N) ੉ ѦܻҊ // ੉റ ݽف Gap ীࢲ ч সؘ੉౟о ૓೯غ޲۽ O(1) ݅ী ৮ܐؽ
  11. var isLoading = false @Composable fun Main() { Column {

    // 1ߣ૩ ਤ஖ (അ੤ cursor) Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap ߣ૩ਤ஖
  12. var isLoading = true @Composable fun Main() { Column {

    // 1ߣ૩ ਤ஖ (੉੹ cursor) Text(text = "Column Data") if (isLoading) { // 2ߣ૩ ਤ஖ (അ੤ cursor) Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Text “Loading…” ߣ૩ਤ஖ ߣ૩ਤ஖
  13. /** * Gap ਸ ޖदೞҊ ౠ੿ ਤ஖੄ ੺؀੸ੋ ੋؙझܳ оܰఃח

    ېಌੑפ׮. (ےؒ ঘࣁझ ഝࢿച) * * ےؒ ঘࣁझ: ؘ੉ఠܳ ੷੢ೞח ࠶۾ਸ ೠߣী ৈ۞ ѐ ঘࣁझೞח Ѫ੉ ইפۄ ౠ੿ ਤ஖۽ ߄۽ ੽Ӕೞৈ ೠ ߣী ೞա੄ ࠶۾݅ਸ ঘࣁझೞח ߑध */ internal class Anchor(loc: Int) { internal var location = loc val valid get() = location != Int.MIN_VALUE fun toIndexFor(slots: SlotTable) = slots.anchorIndex(this) fun toIndexFor(writer: SlotWriter) = writer.anchorIndex(this) }
  14. var isLoading = true @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { // Anchor ۽ ߄۽ ੽Ӕ Text(text = "Loading...") } } } Column “Column Data” Gap Gap Gap Gap Text “Loading…” "ODIPS۽߄۽੽Ӕ Text
  15. @Composable fun Main() { Column { Text(text = "Column Data")

    } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap
  16. @Composable fun Main() { Column { Text(text = "Column Data")

    } } “Column Data” Gap Gap Gap Gap Gap Gap Group Group
  17. @Composable fun Main() { Column { Text(text = "Column Data")

    } } “Column Data” Gap Gap Gap Gap Gap Gap Group Group 3FTUBSUBCMF(SPVQ 3FQMBDFBCMF(SPVQ .PWBCMF(SPVQ
  18. // RestartableGroup: ܻஹನ૑࣌੉ ੌযզ ࣻ ੓ח ஹನ੷࠶ ઱ਤ۽ ࢤࢿػ׮. //

    ߹ب੄ ২࣌੉ হ׮ݶ ݽٚ ஹನ੷࠶ী ؀೧ ࢤࢿغח Ӓܛ੉Ҋ, // ܻஹನ૑࣌ ೞח ߑߨਸ оܰ஘׮. // ݽٚ RestartableGroup ਷ ೧׼ ߧਤ ݅ఀ // ੗୓੄ ܻஹನ૑࣌ झ௏೐(RecomposeScope)ܳ ഋࢿೠ׮. @Composable fun RestartableContainer() { Text(text = "SungbinLand") } RestartableGroup “SungbinLand” Gap Gap Gap Gap Gap Gap RestartableGroup 3FTUBSUBCMF$POUBJOFS  3FDPNQPTF4DPQF 5FYU  3FDPNQPTF4DPQF
  19. // ReplaceableGroup: ࠙ӝী ٮۄ ੤ߓ஖ ؼ ࣻ ੓ח ஹನ੷࠶ ઱ਤ۽

    ࢤࢿػ׮. // Ӓܛਸ Ү୓೧ঠ ೡ ٸ ੘ࢿػ ؘ੉ఠܳ ੿ܻೞח ߑߨਸ оܰ஘׮. @Composable fun ReplaceableContainer(isColumn: Boolean = true) { when (isColumn) { true -> Text(text = "Column") else -> Text(text = "Row") } } RestartableGroup “Column” Gap Gap Gap Gap Gap ReplaceableGroup 3FQMBDFBCMF$POUBJOFS XIFO RestartableGroup 5FYU
  20. /** * ஹನ੷࠶੉ ߓ஖ػ ਤ஖о ׳ۄ૑ݶ ਤ஖ ݫݽ੉ઁ੉࣌੄ key о

    ׳ۄઉࢲ ؘ੉ఠо ୡӝചؾפ׮. * ؘ੉ఠܳ ਬ૑ೠ ࢚క۽ ਤ஖ܳ ৤ӝӝ ਤ೧ࢶ key ஹನ੷࠶ਸ ੉ਊ೧ * ਤ஖ ݫݽ੉ઁ੉࣌ীࢲ ଵҊೡ key ܳ ૒੽ ੿੄೧ঠ ೤פ׮. * * key ஹನ੷࠶ਸ ੉ਊ೧ ߓ஖ػ ஹನ੷࠶਷ ઱ਤ۽ MovableGroup ੉ ࢤࢿؾפ׮. * MovableGroup ਷ ஹನ੷࠶੉ ਤ஖ ݫݽ੉ઁ੉࣌ key ৬ ؘ੉ఠܳ ೦࢚ ࠁઓೞݴ * ੉زೞח ߑߨਸ оܰ஝פ׮. */ @Composable inline fun <T> key( vararg keys: Any?, block: @Composable () -> T ) @Composable fun MovableContainer() { key(Any()) { Text(text = "Key") } } RestartableGroup “Key” Gap Gap Gap Gap Gap MovableGroup .PWBCMF$POUBJOFS LFZ RestartableGroup 5FYU
  21. !3FBE0OMZ$PNQPTBCMF object MaterialTheme { val colors: Colors @Composable @ReadOnlyComposable get()

    = LocalColors.current val typography: Typography @Composable @ReadOnlyComposable get() = LocalTypography.current // … } @Composable @ReadOnlyComposable fun stringResource(@StringRes id: Int): String { val resources = resources() return resources.getString(id) }
  22. var isLoading = false @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap Composition
  23. var isLoading = true @Composable fun Main() { Column {

    Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Text “Loading…” Recomposition
  24. @Composable fun Main() { var number by remember { mutableStateOf(0)

    } Button(onClick = { number++ }) { Text(text = number.toString()) } } Button Function0<Unit> Gap Gap Gap Gap Text “$number” Smart-Recomposition Gap
  25. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* ܻஹನ૑࣌੉ য٣ী ૓೯ؼөਃ? 1, setContent content ৔৉ 2. Text 3. setContent content ৔৉ + Text */
  26. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } /* [first-composition] setContent recomposition Text recomposition [re-composition] setContent recomposition Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ */
  27. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } /* [first-composition] setContent recomposition Text recomposition Button recomposition [re-composition] Text recomposition ੉റ زੌ ۽Ӓ ߈ࠂ */
  28. EPOVUIPMFTLJQQJOH // ஹ౵ੌ ੹ @Composable fun TextWrapper(text: String) { Text(text

    = text) } // ஹ౵ੌ റ (೨ब ࠗ࠙݅ ಴द) fun TextWrapper(composer: Composer, text: String) { composer.startRestartGroup(-199242123) // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ૑࣌ झ௏೐ܳ ୶оೞҊ ੓਺ Text(text = text) composer.endRestartGroup() }
  29. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Text( modifier = Modifier.clickable { number++ }, text = number.toString() ).also { println("Text recomposition") } } setContent { var number by remember { mutableStateOf(0) } println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Text RecomposeScope 

  30. EPOVUIPMFTLJQQJOH setContent { var number by remember { mutableStateOf(0) }

    println("setContent recomposition") Button(onClick = { number++ }) { Text( text = number.toString() ).also { println("Text recomposition") } }.also { println("Button recomposition") } } Text RecomposeScope 
 Button RecomposeScope
  31. Composition Layout Draw @Composable fun Main() { Text( modifier =

    Modifier .clickable { getOne() }, text = "Hello, World!" ) } fun getOne() = 1
  32. @Composable fun Main() { Text( modifier = Modifier .clickable {

    getOne() }, text = "Hello, World!" ) } fun getOne() = 1 RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Gap Gap Gap Gap
  33. Modifier.offset { IntOffset(x = 0, y = 0) } Modifier.layout

    { measurable, constraints -> val placeable = measurable.measure(constraints) layout(width = 0, height = 0) { placeable.place(x = 0, y = 0, zIndex = 1f) } } Modifier.graphicsLayer { scaleX = 1f; rotationX = 0f; clip = false shape = RectangleShape; translationX = 0f alpha = 1f; shadowElevation = 0f } SomeGroup Function0<Unit> Gap Composition Layout Draw Function0<Unit> Gap Gap Gap Gap Function0<Unit>
  34. Modifier.offset { IntOffset(x = 0, y = 0) } Modifier.layout

    { measurable, constraints -> val placeable = measurable.measure(constraints) layout(width = 0, height = 0) { placeable.place(x = 0, y = 0, zIndex = 1f) } } Modifier.graphicsLayer { scaleX = 1f; rotationX = 0f; clip = false shape = RectangleShape; translationX = 0f alpha = 1f; shadowElevation = 0f } SomeGroup Function0<Unit> Gap Composition Layout Draw Function0<Unit> Gap Gap Gap Gap Function0<Unit> Skippable
  35. RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Text

    Int “Hello, World!” ۨ੉ইਓઁডઑѤ DPOTUSBJOUT ҅࢑ ठܶప੉࠶ч҅࢑ JOWPLF  6*౟ܻҳ୷
  36. @Composable fun Main() { Text( modifier = Modifier .clickable {

    getOne() }, text = "Hello, World!” ) } fun getOne() = 1 RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Gap Gap Gap Gap
  37. Composition Layout Draw Composition DPOTUSBJOUT ࠗݽ੄ઁডઑѤ֢୹ @Stable interface BoxWithConstraintsScope :

    BoxScope { val constraints: Constraints val minWidth: Dp val maxWidth: Dp val minHeight: Dp val maxHeight: Dp }
  38. Composition Layout Draw ן਷ୡӝച @Composable fun LazyLayout() @Composable fun LazyRow()

    @Composable fun LazyColumn() -B[Z-BZPVU਷ചݶীաఋաӝ߄۽੹ী-BZPVU%SBX૓೯
  39. 4OBQTIPU  SomeGroup 1 Snapshot Gap Gap Gap Gap Gap

    4OBQTIPU 2 Snapshot 4OBQTIPU 3 Snapshot
  40. /4OBQTIPU  SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2

    Snapshot 3 Snapshot 5 Snapshot N Snapshot 7 Snapshot
  41. SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2 Snapshot 3

    Snapshot 5 Snapshot N Snapshot 7 Snapshot 4OBQTIPU3FBE 4OBQTIPU8SJUF 
  42. SomeGroup px - snapshot px4 - snapshot px2 - snapshot

    px3 - snapshot @Composable fun Main() { val px = 1.dp.px val px2 = 2.dp.px val px3 = 3.dp.px val px4 = 4.dp.px } inline val Dp.px @Composable @ReadOnlyComposable get() = with(LocalDensity.current) { toPx() } Gap Gap Gap Gap
  43. SomeGroup density - snapshot @Composable fun Main() { val density

    = LocalDensity.current val px = with(density) { 1.dp.toPx() } val px2 = with(density) { 2.dp.toPx() } val px3 = with(density) { 3.dp.toPx() } val px4 = with(density) { 4.dp.toPx() } } Gap Gap Gap Gap Gap Gap Gap
  44. DPNQPTJUJPO-PDBM0G val LocalTextSelectionColors = compositionLocalOf { DefaultTextSelectionColors } val LocalTextStyle

    = compositionLocalOf { TextStyle.Default } val LocalConfiguration = compositionLocalOf<Configuration>(neverEqualPolicy()) { noLocalProvidedFor("LocalConfiguration") } val LocalContentAlpha = compositionLocalOf { 1f } SomeGroup CompositionLocal CompositionLocal Composable CompositionLocal CompositionLocal Composable Composable Composable
  45. TUBUJD$PNQPTJUJPO-PDBM0G val LocalContext = staticCompositionLocalOf<Context> { noLocalProvidedFor("LocalContext") } val LocalClipboardManager

    = staticCompositionLocalOf<ClipboardManager> { noLocalProvidedFor("LocalClipboardManager") } val LocalDensity = staticCompositionLocalOf<Density> { noLocalProvidedFor("LocalDensity") } val LocalFocusManager = staticCompositionLocalOf<FocusManager> { noLocalProvidedFor("LocalFocusManager") } StaticCompositionLocal Composable Composable Composable Composable Composable SomeGroup Composable Composable
  46. 4UBCJMJUZ /** * উ੿ ࢚కח ௼ѱ 3о૑੄ ઑѤਸ ٮܵפ׮. *

    * - ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮. (૊, [State] ۽ ੘زظঠ ೣ) * - чী ߸҃੉ হח ҃਋ ੉੹ ੋझఢझ৬ അ੤ ੋझఢझо زੌ೧ঠ ೤פ׮. * - ݽٚ ҕѐ ೙ٜ٘ب ׮ উ੿ ࢚కৈঠ ೤פ׮. * * ੉ 3о૑ ઑѤਸ ٮܲ׮ݶ ஹನૉח ೧׼ ೙٘о উ੿੸ੋ ࢚కۄҊ ౵ঈೞҊ, * ೧׼ ೙٘੄ ч੉ ߄Շ૑ ঋও׮ݶ ࢎਊػ ஹನ੷࠶੄ ܻஹನ૑࣌ਸ ࢤۚ೤פ׮. * * @see Immutable * @see Stable */ @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) annotation class StableMarker
  47. /** * ࢤࢿػ ੉റ۽ ݽٚ ҕѐ੸ੋ ೙٘о ੺؀ ߸ೞ૑ ঋח׮ח

    Ѫਸ աఋղݴ ௿ېझী ੸ਊؼ ࣻ ੓णפ׮. * * ࢤࢿ ੉റ ч੉ ߸҃غ૑ ঋਵ޲۽ উ੿੄ ୐ ߣ૩ ӏ஗ੋ * "ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮." о ޖदؾפ׮. */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Immutable fun main() { val list = mutableListOf(1) list.add(2) // ੋझఢझח زੌೞ૑݅ ч੉ ߄Չ ࣻ ੓णפ׮. // @Immutable ਷ ੉۞ೠ ߸҃ب ೲਊೞ૑ ঋणפ׮. }
  48. /** * ч੉ ߸҃ؼ ࣻ ੓ח ࢚క੉ݴ, ੸ਊ ؀࢚ী ٮۄ

    ডрঀ ৉ೡ੉ ׳ۄ૘פ׮. * * ఋੑী ੸ਊػ׮ݶ ୶о ৉ೡ হ੉ [StableMarker] ੄ ৉ೡਸ Ӓ؀۽ оઉцפ׮. * * ೣࣻա ೐۽ಌ౭ী ੸ਊػ׮ݶ [StableMarker] ੄ ৉ೡী ୶оغח ৉ೡ੉ ࢤӤפ׮. * э਷ input ী ੓যࢲח ೦࢚ زੌೠ output ਸ ٜ݅যղݴ(ࣽࣻ ೣࣻ), ೣࣻ੄ ҃਋ ੋ੗ٜ ৉द ݽف উ੿੸ੋ ࢚కۄח Ѫਸ ডࣘ೤פ׮. * ݅ড input ੉ زੌೞ׮ݶ output ژೠ زੌೡ Ѫ੉ӝ ٸޙী ܻஹನ૑࣌ਸ झఈೞѱ ؾפ׮. */ @Target( AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY ) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Stable
  49. // উ੿ࢿ਷ ੹౵ػ׮. @Immutable interface SungbinLand class SungbinLandImpl : SungbinLand

    // ߈ജೞח SungbinLand о উ੿ ࢚క੉ӝ ٸޙী // ೣࣻী StableMarker о হযب ੉ ೣࣻח উ੿ ࢚క۽ р઱ػ׮. fun provideSungbinLand(): SungbinLand = SungbinLandImpl() // data ੋ੗੄ ఋੑ੉ উ੿ ࢚క੉ӝ ٸޙী data ੋ੗ח উ੿ ࢚కо غҊ, // ੉ ஹನ੷࠶਷ skippable ࢚కо ػ׮. @Composable fun SungbinLandDisplay(data: SungbinLand = provideSungbinLand()) { Text(text = data.toString()) }
  50. // উ੿ࢿ਷ ੹౵ػ׮. interface SungbinLand @Immutable class SungbinLandImpl : SungbinLand

    // ߈ജೞח SungbinLand ী ؀ೠ উ੿ࢿ ੿ࠁо হӝ ٸޙী ੉ ೣࣻח ࠛউ੿ ࢚కо ػ׮. fun provideSungbinLand(): SungbinLand = SungbinLandImpl() // data ੋ੗੄ ఋੑ਷ ࠛউ੿ ࢚క੉૑݅, provideSungbinLand() ೣࣻীࢲ чਸ ઁҕೞחؘ ࢎਊೞח // SungbinLandImpl ௿ېझח উ੿ ࢚క੉ӝ ٸޙী data ੋ੗੄ ӝࠄ ч੉ উ੿ ࢚కۄ // ੉ ஹನ੷࠶਷ skippable ࢚కо ػ׮. (ױ, ৈ੹൤ data ੋ੗ח ࠛউ੿ ࢚క۽ թח׮) @Composable fun SungbinLandDisplay(data: SungbinLand = provideSungbinLand()) { Text(text = data.toString()) }
  51. /** * ஹ౵ੌ द੼ী ݽٚ ௿ېझٜ੄ উ੿ࢿਸ ੗زਵ۽ ୶ۿ೤פ׮. *

    * @param parameters উ੿ࢿਸ ୶ۿೞחؘ ب਑੉ ؼ ࣻ ੓ѱ ੋ੗ٜ੄ ࠺౟݃झ௼ܳ աఋշפ׮. */ @ComposeCompilerApi @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) annotation class StabilityInferred(val parameters: Int) // ਬੌೠ ೙٘ੋ value о উ੿ ࢚క੉Ҋ ࠛ߸ೞӝ ٸޙী Name ௿ېझח উ੿ ࢚క۽ ౸ױػ׮. class Name(val value: String) // ਬੌೠ ೙٘ੋ value ੄ ఋੑ੉ ઁ֎ܼ੉ۄ ఋੑਸ ౸ױೡ ࣻ হӝী ࢎ੹ী ࠺౟݃झఊػ чਸ ӝળਵ۽ উ੿ ࢚కܳ ୶ۿೠ׮. // ೞ૑݅ ೙٘о о߸ ࢚క੉ӝ ٸޙী T ఋੑ੉ উ੿੉ৈب NameWithGeneric ௿ېझח ࠛউ੿ ࢚క۽ ౸ױػ׮. class NameWithGeneric<T>(var value: T)
  52. ↟ೣԋࠁݶજਸ੗ܐ 5. Wrap-up  -BNCEB0QUJNJ[JOH  -JWF-JUFSBM  $PNQJMFS.FUSJDT 

    4OBQTIPU4ZTUFNXJUI.7$$  #BTFMJOFQSPGJMFT  $PNQPTF(SPVQT 50%0