Slide 1

Slide 1 text

Jetpack Compose: How it works? ✨૑ࢿ࠼

Slide 2

Slide 2 text

How it works? ↟$PNQJMFS ↟ 3VOUJNF

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

/** * Composable য֢ప੉࣌਷ ஹನૉ ೣࣻীࢲ ೙ࣻ੸ਵ۽ ࢎਊغݴ ௼ѱ 3о૑੄ ৉ೡਸ ыणפ׮. * * 1. ݵ١ࢿ੄ ࠁ੢ * 2. ਤ஖ ݫݽ੉ઁ੉࣌ ഝࢿച * 3. ؘ੉ఠ ઁҕ ژח ஹನ੷࠶ ߑ୹ */ @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target( AnnotationTarget.FUNCTION, AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.PROPERTY_GETTER ) annotation class Composable

Slide 5

Slide 5 text

// э਷ inputਸ ߉਷ ஹನ੷࠶ਸ ݵ١ࢿ੄ ࠁ੢ਵ۽ ೦࢚ ੤प೯ਸ Ѥցګ׮. // ݵ١ࢿ: э਷ ো࢑੄ Ѿҗח ೦࢚ زੌೞ׮ח ࢿ૕ @Composable fun Idempotent() { // ݅ড UI јन੉ ೙ਃೞ׮ݶ... Text(text = "ࢿ࠼ے٘") // ੉ ஹನ੷࠶਷ ೠ ߣ݅ प೯ػ׮. Text(text = System.currentTimeMillis().toString()) // ੉ ஹನ੷࠶਷ ೦࢚ ੤प೯ػ׮. }

Slide 6

Slide 6 text

// ஹನ੷࠶਷ ۠ఋ੐ীࢲ Ѿ੿ػ ਋ࢶࣽਤী ݏѱ ߽۳۽ प೯ؽਵ۽ ஹನ੷࠶੄ प೯ ࣽࢲী ੄ઓೞח Ѥ ০૑ ঋ׮. // ٮۄࢲ ݵ١ࢿਸ ࠁ੢ೞח Ѫ੉ ઺ਃೞ׮. lateinit var text: String @Composable @Composable fun PureFunction() { fun LoadText() { LoadText() text = "Bye, world!" ShowText() Text(text = "Load Text...") } } @Composable fun ShowText() { Text(text = text) // text is not initialized properly оמࢿ ੓਺ }

Slide 7

Slide 7 text

// ஹನ੷࠶ ೣࣻ੄ दӒפ୊৬ call-site, ӒܻҊ प೯ػ ࣽࢲܳ ӝળਵ۽ Ҋਬೠ keyо ࢤࢿغҊ, // ஹನૉח ೧׼ keyܳ ӝળਵ۽ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೠ׮. // э਷ keyܳ о૓ ஹನ੷࠶੉ э਷ inputਵ۽ ੤प೯ ػ׮ݶ ӝઓী प೯ػ Ѿҗܳ Ӓ؀۽ ੤ࢎਊೠ׮. // -> ݵ١ࢿҗ ਤ஖ ݫݽ੉ઁ੉࣌੄ Ѿҗ // ਤ஖ ݫݽ੉ઁ੉࣌: э਷ ਤ஖ীࢲ э਷ inputਵ۽ प೯غݶ ೦࢚ э਷ output੉ աৢ Ѫ੉ۄҊ о੿ೞҊ, // чਸ நयೞҊ ੤ࢎਊ ೞח ӝࣿ @Composable fun PositionMemoization() { repeat(5) { Text(text = "Bye, world!") // э਷ call-site ੉૑݅, ஹನ੷࠶ ؘ੉ఠо ׮ ׮ܲ ҕрী ੷੢ؽ } }

Slide 8

Slide 8 text

/** * @Composable (input) -> output * ੉ ೣࣻ੄ ҃਋ input਷ ܻࣗझ ই੉٣, output਷ ܻࣗझ чܳ աఋշפ׮. * * ੉ ೣࣻ੄ @Composable਷ ؘ੉ఠܳ ઁҕೞח ৉ೡਸ ೤פ׮. */ @Composable fun stringResource(@StringRes id: Int): String { val resources = resources() return resources.getString(id) }

Slide 9

Slide 9 text

/** * @Composable (input) -> Unit * ੉ ೣࣻ੄ ҃਋ inputҗ output੉ হणפ׮. * * ੉ ೣࣻ੄ @Composable਷ ஹನ੷࠶ਸ ߑ୹ೞח ৉ೡਸ ೤פ׮. */ @Composable fun ByeWorld() { Text(text = "Bye, world!") }

Slide 10

Slide 10 text

/** * ஹನૉ੄ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೞח ௿ېझੑפ׮. * Gap Buffer ੗ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘ੉ఠח Arrayী ੷੢ؾפ׮. */ internal class SlotTable : CompositionData, Iterable

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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ਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ׮)

Slide 15

Slide 15 text

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)݅ী ৮ܐؽ

Slide 16

Slide 16 text

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 ߣ૩ਤ஖

Slide 17

Slide 17 text

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…” ߣ૩ਤ஖ ߣ૩ਤ஖

Slide 18

Slide 18 text

/** * 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) }

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

var isColumn = true @Composable fun Main() { if (isColumn) { Column { Text(text = "Column Data") } } else { Row { repeat(10) { index -> Text(text = "Row Data - $index") } } } } Column Text “Column Data” Gap Gap Gap Gap Gap Gap

Slide 21

Slide 21 text

var isColumn = false @Composable fun Main() { if (isColumn) { Column { Text(text = "Column Data") } } else { Row { repeat(10) { index -> Text(text = "Row Data - $index") } } } } Row Text “Row Data - 0” Text “Row Data - 1” Text Text Text “Row Data - 2” “Row Data - 3”

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

var isLoading = true @Composable fun Main() { Column { Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Emit Column Text Text

Slide 25

Slide 25 text

var isLoading = true @Composable fun Main() { Column { Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Materializing

Slide 26

Slide 26 text

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…”

Slide 27

Slide 27 text

val isLoading = mutableStateOf(false) // Snapshot System isLoading.value = true @Composable fun Main() { Column { Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Column Text “Column Data” Gap Gap Gap Gap Text “Loading…”

Slide 28

Slide 28 text

How it works? ↟$PNQJMFS ↟ 3VOUJNF

Slide 29

Slide 29 text

@Composable fun TextWrapper() { Text(text = "SungbinLand") } dependencies { // annotation processor XXX implementation "androidx.compose.foundation:foundation:1.3.0-alpha01" }

Slide 30

Slide 30 text

@Composable fun TextWrapper() { Text(text = "SungbinLand") } buildFeatures { compose true // ஹನૉ ௏ౣܽ ஹ౵ੌ۞ ഝࢿച /* * ஹನૉ ஹ౵ੌ җ੿ਸ ௏ౣܽ ঱য੄ ஹ౵ੌ җ੿ਵ۽ ੐ߓ٘ೞৈ ୊ܻ ࣘبܳ ֫੉Ҋ, * য֢ప੉࣌ ೐۽ࣁࢲח ೡ ࣻ হח ֫ਸ ࣻળ੄ ௏٘ ੽Ӕਸ ೞৈ ஹ౵ੌؾפ׮. * ৘ܳ ٜয, ௏ౣܽҗ ஹನૉח ݣ౭೒ۖಬਸ ؀࢚ਵ۽ ೞ޲۽ ஹ౵ੌ җ੿ীࢲ ࢤࢿغח IRਸ * അ੤ ೒ۖಬী ݏח ജ҃ਵ۽ ࣻ੿ೞҊ loweringਸ ૓೯೤פ׮. */ } // or… dependencies { "kotlinCompilerPluginClasspath"("org.jetbrains.compose.compiler:compiler:$version") }

Slide 31

Slide 31 text

௏ౣܽஹ౵ੌ۞ߡ੹୓௼ ௏٘੿੸࠙ࢳ߂ӝઓ҃Ҋরઁ ஹನૉ۠ఋ੐ߡ੹୓௼ *3ࢤࢿ ௿ېझউ੿ࢿ୶ۿ -JWF-JUFSBMഝࢿച ۈ׮୭੸ച $PNQPTFS઱ੑ ࠺Ү੹౵ഝࢿച %FGBVMU"SHVNFOUT੤ҳഅ ஹನ੷࠶Ӓܛࢤࢿ ,MJC EFDPZࢤࢿ

Slide 32

Slide 32 text

௏ౣܽஹ౵ੌ۞ߡ੹୓௼ ௏٘੿੸࠙ࢳ߂ӝઓ҃Ҋরઁ ஹನૉ۠ఋ੐ߡ੹୓௼ *3ࢤࢿ ௿ېझউ੿ࢿ୶ۿ -JWF-JUFSBMഝࢿച ۈ׮୭੸ച $PNQPTFS઱ੑ ࠺Ү੹౵ഝࢿച %FGBVMU"SHVNFOUT੤ҳഅ ஹನ੷࠶Ӓܛࢤࢿ ,MJC EFDPZࢤࢿ

Slide 33

Slide 33 text

௏٘੿੸࠙ࢳ߂ӝઓ҃Ҋরઁ j j

Slide 34

Slide 34 text

௏٘੿੸࠙ࢳ߂ӝઓ҃Ҋরઁ j j

Slide 35

Slide 35 text

௏٘੿੸࠙ࢳ߂ӝઓ҃Ҋরઁ j j

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

$PNQPTFS઱ੑ j j // ஹ౵ੌ ੹ @Composable fun TextWrapper() { Text(text = "SungbinLand") } // ஹ౵ੌ റ, Composer: ஹನૉ ۠ఋ੐੉ ஹನ੷࠶җ ࢚ഐ ੘ਊ ೡ ࣻ ੓ѱ ب৬઱ח ੋఠಕ੉झ @Composable fun TextWrapper(composer: Composer, changed: Int) { composer.startRestartGroup(-137853230) composer.sourceInformation("C(TextWrapper)22@513L26:MainActivity.kt#dmu2em") if (changed == 0 && composer.skipping) { composer.skipToGroupEnd() } else { Text(text = "SungbinLand") } composer.endRestartGroup()?.updateScope { composer, _ -> TextWrapper(composer, changed or 1) } }

Slide 38

Slide 38 text

@Composable fun TextWrapper(composer: Composer, changed: Int) { composer.startRestartGroup(-137853230) composer.sourceInformation("C(TextWrapper)22@513L26:MainActivity.kt#dmu2em") if (changed == 0 && composer.skipping) { composer.skipToGroupEnd() } else { Text(text = "SungbinLand") } composer.endRestartGroup()?.updateScope { composer, _ -> TextWrapper(composer, changed or 1) } } Group ஹನ੷࠶Ӓܛࢤࢿ j j

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

// 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

Slide 43

Slide 43 text

/** * ஹನ੷࠶੉ ߓ஖ػ ਤ஖о ׳ۄ૑ݶ ਤ஖ ݫݽ੉ઁ੉࣌੄ keyо ׳ۄઉࢲ ؘ੉ఠо ୡӝചؾפ׮. * ؘ੉ఠܳ ਬ૑ೠ ࢚క۽ ਤ஖ܳ ৤ӝӝ ਤ೧ࢶ key ஹನ੷࠶ਸ ੉ਊ೧ * ਤ஖ ݫݽ੉ઁ੉࣌ীࢲ ଵҊೡ keyܳ ૒੽ ੿੄೧ঠ ೤פ׮. * * key ஹನ੷࠶ਸ ੉ਊ೧ ߓ஖ػ ஹನ੷࠶਷ ઱ਤ۽ MovableGroup੉ ࢤࢿؾפ׮. * MovableGroup਷ ஹನ੷࠶੉ ਤ஖ ݫݽ੉ઁ੉࣌ key৬ ؘ੉ఠܳ ೦࢚ ࠁઓೞݴ * ੉زೞח ߑߨਸ оܰ஝פ׮. */ @Composable inline fun 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

Slide 44

Slide 44 text

↟3FTUBSUBCMF(SPVQ ↟3FQMBDFBCMF(SPVQ ↟.PWBCMF(SPVQ ஹನ੷࠶Ӓܛࢤࢿ j j

Slide 45

Slide 45 text

How it works? ↟$PNQJMFS ↟3VOUJNF

Slide 46

Slide 46 text

🎨 🖼️ 🖌️ 🧰 Material ݠఠܻ঴٣੗ੋदझమ 5FYU #VUUPO #PUUPN4IFFU $IJQ "MFSU%JBMPH *DPOT 4VSGBDF Foundation о੢ӝୡ੸ੋ٣੗ੋ -B[Z-JTU $PMVNO $BOWBT 4IBQF (FTUVSFT 0WFSTDSPMM&GGFDU UI ஹನૉ6*5PPMLJU੄ӝࠄࢸ҅ -BZPVU %JBMPH $PNQPTF7JFX"EBQUFS 1SFWJFX"DUJWJUJZ Runtime ஹನૉউ٘۽੉٘োѾ *OWBMJEBUJPO 4OBQTIPU4ZTUFN 4MPU5BCMF -JWF-JUFSBM

Slide 47

Slide 47 text

Runtime Process (full)

Slide 48

Slide 48 text

Runtime Process (simply)

Slide 49

Slide 49 text

class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text(text = "SungbinLand") } } }

Slide 50

Slide 50 text

// ஹನૉ ۠ఋ੐җ UI੄ োѾ fun ComponentActivity.setContent( parent: CompositionContext? = null, content: @Composable () -> Unit ) { ComposeView(this).apply { setParentCompositionContext(parent) setContent(content) setOwners() setContentView(this, DefaultActivityContentLayoutParams) } }

Slide 51

Slide 51 text

// ComposeView#setContent۽ ੉ ೣࣻо प೯ؽ private fun ensureCompositionCreated() { setContent(/* Recomposerо ٜ݅য૑Ҋ ઱ੑؾפ׮ */) { content() } }

Slide 52

Slide 52 text

/** * ܻஹನ૑࣌ਸ ࣻ೯ೞҊ ೞա ੉࢚੄ ஹನ੷࠶ী সؘ੉౟ܳ ੸ਊೞӝ ਤೠ झா઴۞ ੑפ׮. */ class Recomposer( effectCoroutineContext: CoroutineContext ) : CompositionContext() /** * Handler ௒ߔ ژח Choreographer੄ গפݫ੉࣌ ೐ۨ੐ ױ҅ ઺ * ݢ੷ بېೞח ױ҅ীࢲ ٣झಁ஖ܳ ࣻ೯ೞח CoroutineDispatcher ੑפ׮. */ class AndroidUiDispatcher private constructor( val choreographer: Choreographer, private val handler: Handler ) : CoroutineDispatcher()

Slide 53

Slide 53 text

/** * ܻஹನ૑࣌ਸ ࣻ೯ೞҊ ೞա ੉࢚੄ ஹನ੷࠶ী সؘ੉౟ܳ ੸ਊೞӝ ਤೠ झா઴۞ ੑפ׮. */ class Recomposer( effectCoroutineContext: CoroutineContext ) : CompositionContext() /** * Handler ௒ߔ ژח Choreographer੄ গפݫ੉࣌ ೐ۨ੐ ױ҅ ઺ * ݢ੷ بېೞח ױ҅ীࢲ ٣झಁ஖ܳ ࣻ೯ೞח CoroutineDispatcher ੑפ׮. */ class AndroidUiDispatcher private constructor( val choreographer: Choreographer, private val handler: Handler ) : CoroutineDispatcher()

Slide 54

Slide 54 text

suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> val toRecompose = mutableListOf() while (shouldKeepRecomposing) { awaitWorkAvailable() parentFrameClock.withFrameNanos { frameTime -> try { toRecompose.fastForEach { composition -> performRecompose(composition) } } finally { toRecompose.clear() } } } }

Slide 55

Slide 55 text

private suspend fun recompositionRunner( block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit ) { // Recomposerীࢲ ߉਷ Choreographer੄ ېಌੋ MonotonicFrameClockਸ оઉৡ׮. val parentFrameClock: MonotonicFrameClock = coroutineContext.monotonicFrameClock // broadcastFrameClock: ஹನૉ ۠ఋ੐ীࢲ ҟ৉ਵ۽ ࢎਊغח MonotonicFrameClock੄ ېಌ withContext(broadcastFrameClock) { Snapshot.registerApplyObserver { _, _ -> // State੄ write ੉߮౟ܳ ߉ח׮. deriveStateLocked()?.resume(Unit) } coroutineScope { block(parentFrameClock) } } }

Slide 56

Slide 56 text

private suspend fun recompositionRunner( block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit ) { // Recomposerীࢲ ߉਷ Choreographer੄ ېಌੋ MonotonicFrameClockਸ оઉৡ׮. val parentFrameClock: MonotonicFrameClock = coroutineContext.monotonicFrameClock // broadcastFrameClock: ஹನૉ ۠ఋ੐ীࢲ ҟ৉ਵ۽ ࢎਊغח MonotonicFrameClock੄ ېಌ withContext(broadcastFrameClock) { Snapshot.registerApplyObserver { _, _ -> // State੄ write ੉߮౟ܳ ߉ח׮. deriveStateLocked()?.resume(Unit) } coroutineScope { block(parentFrameClock) } } }

Slide 57

Slide 57 text

suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> // ܻஹನ૑࣌੉ ೙ਃೠ RecomposeScopeо ׸ӟ׮. (׸ח җ੿ ࢤۚ) val toRecompose = mutableListOf() // shouldKeepRecomposing: Recomposerо ܻஹನ૑࣌ ਃ୒ਸ х૑೧ঠ ೞח૑ܳ աఋմ׮. while (shouldKeepRecomposing) { awaitWorkAvailable() // ࢜۽਍ ܻஹನ૑࣌ ਃ୒੉ ੓ਸ ٸ ө૑ suspend ࢚క۽ ӝ׮ܽ׮. parentFrameClock.withFrameNanos { frameTime -> // Choreographer#postFrameCallbackਸ ഐ୹ೠ׮. try { toRecompose.fastForEach { composition -> // RecomposeScopeܳ ࣽഥೞݴ performRecompose(composition) // ܻஹನ૑࣌ ૓೯! } } finally { toRecompose.clear() } } } }

Slide 58

Slide 58 text

private suspend fun awaitWorkAvailable() { suspendCancellableCoroutine { co -> if (hasSchedulingWork) { // ؀ӝ઺ੋ ܻஹನ૑࣌ ਃ୒੉ ੓׮ݶ ߄۽ resume ೞҊ co.resume(Unit) } else { // ؀ӝ઺ੋ ܻஹನ૑࣌ ਃ୒੉ হ׮ݶ // workContinuationਵ۽ CancellableContinuation ੷੢ റ suspend ਬ૑ workContinuation = co } } }

Slide 59

Slide 59 text

private fun deriveStateLocked(): CancellableContinuation? { // ࠁܨ ઺ੋ ੘স: ܻஹನ૑࣌ ਃ୒ ١١ ஹನૉ ۠ఋ੐ ղࠗীࢲ ૓೯غח ৈ۞ ੘স ਃ୒ val newState = when { /* അ੤ ࠁܨ ઺ੋ ੘স ৈࠗী ٮۄ ࢜۽਍ ࢚క ҅࢑ */ } return if (newState == State.PendingWork) { // ࠁܨ ઺ੋ ੘স੉ ੓׮ݶ workContinuation.also { // workContinuation ߈ജ ߂ workContinuation ୡӝച workContinuation = null } } else null }

Slide 60

Slide 60 text

private suspend fun recompositionRunner( block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit ) { val parentFrameClock = coroutineContext.monotonicFrameClock withContext(broadcastFrameClock) { Snapshot.registerApplyObserver { _, _ -> deriveStateLocked()?.resume(Unit) } coroutineScope { block(parentFrameClock) } } }

Slide 61

Slide 61 text

suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> val toRecompose = mutableListOf() while (shouldKeepRecomposing) { awaitWorkAvailable() // ܻஹನ૑࣌ ਃ୒੉ ੓ਸ ٸ ө૑ suspend parentFrameClock.withFrameNanos { frameTime -> // ׮਺ ೐ۨ੐ীࢲ try { toRecompose.fastForEach { composition -> performRecompose(composition) } // Recomposition! } finally { toRecompose.clear() } } } } private suspend fun recompositionRunner(block: suspend CoroutineScope.(MonotonicFrameClock) -> Unit) { val parentFrameClock = coroutineContext.monotonicFrameClock withContext(broadcastFrameClock) { Snapshot.registerApplyObserver { _, _ -> deriveStateLocked()?.resume(Unit) } // State ч ߸҃੉ ੓ਸ ٸ resume coroutineScope { block(parentFrameClock) } } }

Slide 62

Slide 62 text

// ComposeView#setContent۽ ੉ ೣࣻо प೯ؽ private fun ensureCompositionCreated() { setContent(/* Recomposerо ٜ݅য૑Ҋ ઱ੑؾפ׮ */) { content() } }

Slide 63

Slide 63 text

// this: Composer private fun doCompose(content: @Composable () -> Unit) { startRoot() // ठܶ ప੉࠶ ୡӝച startGroup() // ஹನ੷࠶੉ ٜযт Ӓܛ ࢤࢿ invokeComposable(this, content) endGroup() endRoot() } // performRecomposeب ѾҴ ੉ ೣࣻܳ प೯ೠ׮. internal actual fun invokeComposable( composer: Composer, composable: @Composable () -> Unit ) { val realFn = composable as Function2 realFn(composer, 1) } Root Group Composable Gap Gap Gap Gap Gap Gap

Slide 64

Slide 64 text

хࢎ೤פ׮ ஹನૉ ղࠗ ௏٘ח cs.android.comীࢲ оઉ৳णפ׮. ੉ ೐ۨઃప੉࣌ীח షझ౱ীࢲ ઁҕೠ షझಕ੉झо ੸ਊغয ੓णפ׮. QnA🙋