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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Ji Sungbin 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 를 소개하겠습니다. 텍스트 에디터를 생각해 봅시다. 입력되는 텍스트가 일반 선형 ...

Avatar for Ji Sungbin

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