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

Jetpack Compose A to Z (short)

Ji Sungbin
February 23, 2023

Jetpack Compose A to Z (short)

Jetpack Compose A to Z 네이버/화해 강연 자료 (40분)

Ji Sungbin

February 23, 2023
Tweet

More Decks by Ji Sungbin

Other Decks in Programming

Transcript

  1. Jetpack Compose A to Z
    ✨૑ࢿ࠼

    View Slide

  2. 👋૑ࢿ࠼
    🏠TVOHCJO
    🏰TVOHCJOMBOE
    🐙HJUIVCDPNKJTVOHCJO

    View Slide

  3. $PNQPTFࣗѐ
    $PNQPTFҳࢿਃࣗ
    )PXJUXPSLT
    $PNQJMFS%FCVHHJOH
    #FTU1SBDUJDF
    8SBQVQ
    📖ݾର

    View Slide

  4. ↟ BMQIBӝળ
    ↟ ௏٘ࣁࠗࢎ೦ࢤۚ
    📝द੘ೞӝ੹ী

    View Slide

  5. 1. Compose ࣗѐ
    ஹನૉܳ৵ॄঠೡө

    View Slide

  6. ழझథٜ࠭ܳ݅ӝਤೠҗ੿੉ցޖӡ׮
    ܻࢎ੉௿۞࠭৬э੉೙ࣻ੸ੋஹನք౟ܳҳࢿೞחؘҗ੿੉ցޖӡ׮
    ࢚కҙܻоࣻز੉ۄपࣻೞӝऔ׮
    😵💫ӝઓ6*ѐߊ੄ޙઁ੼

    View Slide

  7. tࢤ࢑ࢿ੷ೞu

    View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. җ੿੉হ׮

    View Slide

  12. →ࢤ࢑ࢿ߂੤ࢎਊࢿૐо

    View Slide

  13. 2. Compose ҳࢿਃࣗ
    ஹನૉܳઁ؀۽੉೧ೞӝਤೠӝࠄѐ֛

    View Slide

  14. View Slide

  15. /**


    * Composable য֢ప੉࣌਷ ஹನૉ ೣࣻীࢲ ೙ࣻ੸ਵ۽ ࢎਊغݴ ௼ѱ 3о૑੄ ৉ೡਸ ыणפ׮.


    *


    * 1. ݵ١ࢿ੄ ࠁ੢


    * 2. ਤ஖ ݫݽ੉ઁ੉࣌ ഝࢿച


    * 3. ؘ੉ఠ ઁҕ ژח ஹನ੷࠶ ߑ୹


    */


    @MustBeDocumented


    @Retention(AnnotationRetention.BINARY)


    @Target(


    AnnotationTarget.FUNCTION,


    AnnotationTarget.TYPE,


    AnnotationTarget.TYPE_PARAMETER,


    AnnotationTarget.PROPERTY_GETTER


    )


    annotation class Composable


    View Slide

  16. // э਷ input ਸ ߉਷ ஹನ੷࠶ਸ ݵ١ࢿ੄ ࠁ੢ਵ۽ ೦࢚ ੤प೯ਸ Ѥցګ׮.


    // ݵ١ࢿ: э਷ ো࢑੄ Ѿҗח ೦࢚ زੌೞ׮ח ࢿ૕


    @Composable


    fun Idempotent() {


    // ݅ড UI јन੉ ೙ਃೞ׮ݶ...


    Text(text = "ࢿ࠼ے٘") // ੉ ஹನ੷࠶਷ ೠ ߣ݅ प೯ػ׮.


    Text(text = System.currentTimeMillis().toString()) // ੉ ஹನ੷࠶਷ ೦࢚ ੤प೯ػ׮.


    }


    View Slide

  17. // ஹನ੷࠶਷ ۠ఋ੐ীࢲ Ѿ੿ػ ਋ࢶࣽਤী ݏѱ ߽۳۽ प೯ؽਵ۽ ஹನ੷࠶੄ प೯ ࣽࢲী ੄ઓೞח Ѥ ০૑ ঋ׮.


    // ٮۄࢲ ݵ١ࢿਸ ࠁ੢ೞח Ѫ੉ ઺ਃೞ׮.


    lateinit var text: String


    @Composable


    fun PureFunction() {


    LoadText()


    ShowText()


    }


    @Composable


    private fun LoadText() {


    text = "Bye, world!"


    Text(text = "Load Text...")


    }


    @Composable


    private fun ShowText() {


    Text(text = text) // text is not initialized properly оמࢿ ੓਺


    }


    View Slide

  18. // ஹನ੷࠶ ೣࣻ੄ दӒפ୊৬ call-site, ӒܻҊ प೯ػ ࣽࢲܳ ӝળਵ۽ Ҋਬೠ key о ࢤࢿغҊ,


    // ஹನૉח ೧׼ key ܳ ӝળਵ۽ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೠ׮.


    // э਷ key ܳ о૓ ஹನ੷࠶੉ э਷ input ਵ۽ ੤प೯ ػ׮ݶ ӝઓী प೯ػ Ѿҗܳ Ӓ؀۽ ੤ࢎਊೠ׮.


    // -> ݵ١ࢿҗ ਤ஖ ݫݽ੉ઁ੉࣌੄ Ѿҗ


    // ਤ஖ ݫݽ੉ઁ੉࣌: э਷ ਤ஖ীࢲ э਷ input ਵ۽ प೯غݶ ೦࢚ э਷ output ੉ աৢ Ѫ੉ۄҊ о੿ೞҊ,


    // чਸ நयೞҊ ੤ࢎਊ ೞח ӝࣿ


    @Composable


    fun PositionMemoization() {


    repeat(5) {


    Text(text = "Bye, world!") // э਷ call-site ੉૑݅, ஹನ੷࠶ ؘ੉ఠо ׮ ׮ܲ ҕрী ੷੢ؽ


    }


    }


    View Slide

  19. /**


    * @Composable (input) -> output


    * ੉ ೣࣻ੄ ҃਋ input ਷ ܻࣗझ ই੉٣, output ਷ ܻࣗझ чܳ աఋշפ׮.


    *


    * ੉ ೣࣻ੄ @Composable ਷ ؘ੉ఠܳ ઁҕೞח ৉ೡਸ ೤פ׮.


    */


    @Composable


    fun stringResource(@StringRes id: Int): String {


    val resources = resources()


    return resources.getString(id)


    }


    /**


    * @Composable (input) -> Unit


    * ੉ ೣࣻ੄ ҃਋ input җ output ੉ হणפ׮.


    *


    * ੉ ೣࣻ੄ @Composable ਷ ஹನ੷࠶ਸ ߑ୹ೞח ৉ೡਸ ೤פ׮.


    */


    @Composable


    fun ByeWorld() {


    Text(text = "Bye, world!")


    }


    View Slide

  20. /**


    * ஹನૉ੄ ݽٚ ؘ੉ఠܳ ੷੢ೞҊ ҙܻೞח ௿ېझ ੑפ׮.


    * Gap Buffer ੗ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘ੉ఠח Array ী ੷੢ؾפ׮.


    */


    internal class SlotTable : CompositionData, Iterable


    View Slide

  21. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ

    View Slide

  22. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն

    View Slide

  23. Gap Buffer: زੌೠ ਤ஖ Ӕ୊ী ௿۞झఠ݂ػ ബਯ੸ੋ ࢗੑ ߂ ࢏ઁ ੘সਸ ೲਊೞח ز੸ ߓৌ


    1, ୡӝ Gap ࢤࢿ: O(N)


    [(v)_, _, _, _, _, _, _, _, _, _] // _ ח Gap, (v) ח അ੤ cursor ܳ աఋն


    2. 0ߣ૩ ੋؙझী Hi ࢗੑ


    [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gap ী ࢗੑغ޲۽ ୶о Gap ࢤࢿ੉ ೙ਃೞ૑ ঋই O(1) ী ৮ܐؽ

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. /**


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


    }

    View Slide

  29. var isLoading = true


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) { // Anchor ۽ ߄۽ ੽Ӕ


    Text(text = "Loading...")


    }


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Text
    “Loading…”
    "ODIPS۽߄۽੽Ӕ

    View Slide

  30. 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

    View Slide

  31. 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”

    View Slide

  32. 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

    View Slide

  33. 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
    !$PNQPTBCMF੄ݵ١ࢿࠁ੢
    ܻஹನ૑࣌ࢤۚ4NBSU3FDPNQPTJUJPO

    View Slide

  34. var isLoading = true


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) {


    Text(text = "Loading...")


    }


    }


    }


    Emit
    Column
    Text
    Text

    View Slide

  35. var isLoading = true


    @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    if (isLoading) {


    Text(text = "Loading...")


    }


    }


    }


    Materializing

    View Slide

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

    View Slide

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

    View Slide

  38. 3. How it works?
    ↟$PNQJMFS
    ↟3VOUJNF

    View Slide

  39. @Composable


    fun TextWrapper() {


    Text(text = "SungbinLand")


    }


    dependencies {


    // annotation processor XXX


    implementation "androidx.compose.foundation:foundation:1.3.0-alpha01"


    }

    View Slide

  40. @Composable


    fun TextWrapper() {


    Text(text = "SungbinLand")


    }


    buildFeatures {


    compose true // ஹನૉ ௏ౣܽ ஹ౵ੌ۞ ഝࢿച


    /**


    * ஹನૉ ஹ౵ੌ җ੿ਸ ௏ౣܽ ঱য੄ ஹ౵ੌ җ੿ਵ۽ ੐ߓ٘ೞৈ ୊ܻ ࣘبܳ ֫੉Ҋ, য֢ప੉࣌ ೐۽ࣁࢲח ೡ ࣻ হח ֫ਸ ࣻળ੄ ௏٘ ੽Ӕਸ ೞৈ ஹ౵ੌؾפ׮.


    * ৘ܳ ٜয, ௏ౣܽҗ ஹನૉח ݣ౭೒ۖಬਸ ؀࢚ਵ۽ ೞ޲۽ ஹ౵ੌ җ੿ীࢲ ࢤࢿغח IR ਸ അ੤ ೒ۖಬী ݏח ജ҃ਵ۽ ࣻ੿ೞҊ lowering ਸ ૓೯೤פ׮.


    */


    }


    // or… gradle.kts ܳ ࢎਊೠ׮ݶ


    import org.jetbrains.kotlin.gradle.plugin.PLUGIN_CLASSPATH_CONFIGURATION_NAME


    dependencies {


    add(PLUGIN_CLASSPATH_CONFIGURATION_NAME, "org.jetbrains.compose.compiler:compiler:$version")


    }


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. View Slide

  47. Stability System
    ௿ېझউ੿ࢿ୶ۿ
    j j j j

    View Slide

  48. /**


    * উ੿ ࢚కח ௼ѱ 3о૑੄ ઑѤਸ ٮܵפ׮.


    *


    * - ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮. (૊, [State] ۽ ੘زظঠ ೣ)


    * - ੉੹ ੋझఢझ৬ അ੤ ੋझఢझо زੌ೧ঠ ೤פ׮.


    * - ݽٚ ҕѐ ೙ٜ٘ب ׮ উ੿ ࢚కৈঠ ೤פ׮.


    *


    * ੉ 3о૑ ઑѤਸ ٮܲ׮ݶ ஹನૉח ೧׼ ೙٘о উ੿੸ੋ ࢚కۄҊ ౵ঈೞҊ,


    * ೧׼ ೙٘੄ ч੉ ߄Շ૑ ঋও׮ݶ ࢎਊػ ஹನ੷࠶੄ ܻஹನ૑࣌ਸ ࢤۚ೤פ׮.


    *


    * @see Immutable


    * @see Stable


    */


    @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)


    @Retention(AnnotationRetention.BINARY)


    annotation class StableMarker


    View Slide

  49. /**


    * ࢤࢿػ ੉റ۽ ݽٚ ҕѐ੸ੋ ೙٘о ੺؀ ߸ೞ૑ ঋח׮ח Ѫਸ աఋղݴ ௿ېझী ੸ਊؼ ࣻ ੓णפ׮.


    *


    * ࢤࢿ ੉റ ч੉ ߸҃غ૑ ঋਵ޲۽ উ੿੄ ୐ ߣ૩ ӏ஗ੋ


    * "ч੉ ߸҃عਸ ҃਋ ஹನ੷࠶ীѱ ঌ۰ઉঠ ೤פ׮." о ޖदؾפ׮.


    */


    @Target(AnnotationTarget.CLASS)


    @Retention(AnnotationRetention.BINARY)


    @StableMarker


    annotation class Immutable


    fun main() {


    val list = mutableListOf(1)


    list.add(2) // ੋझఢझח زੌೞ૑݅ ч੉ ߄Չ ࣻ ੓णפ׮.


    // @Immutable ਷ ੉۞ೠ ߸҃ب ೲਊೞ૑ ঋणפ׮.


    }


    View Slide

  50. /**


    * ч੉ ߸҃ؼ ࣻ ੓ח ࢚క੉ݴ, ੸ਊ ؀࢚ী ٮۄ ডрঀ ৉ೡ੉ ׳ۄ૘פ׮.


    *


    * ఋੑী ੸ਊػ׮ݶ ୶о ৉ೡ হ੉ [StableMarker] ੄ ৉ೡਸ Ӓ؀۽ оઉцפ׮.


    *


    * ೣࣻա ೐۽ಌ౭ী ੸ਊػ׮ݶ [StableMarker] ੄ ৉ೡী ୶оغח ৉ೡ੉ ࢤӤפ׮.


    * э਷ input ী ੓যࢲח ೦࢚ زੌೠ output ਸ ٜ݅যղݴ(ࣽࣻ ೣࣻ), ೣࣻ੄ ҃਋ ੋ੗ٜ ৉द ݽف উ੿੸ੋ ࢚కۄח Ѫਸ ডࣘ೤פ׮.


    * ݅ড input ੉ زੌೞ׮ݶ output ژೠ زੌೡ Ѫ੉ӝ ٸޙী ܻஹನ૑࣌ਸ झఈೞѱ ؾפ׮.


    */


    @Target(


    AnnotationTarget.CLASS,


    AnnotationTarget.FUNCTION,


    AnnotationTarget.PROPERTY_GETTER,


    AnnotationTarget.PROPERTY


    )


    @Retention(AnnotationRetention.BINARY)


    @StableMarker


    annotation class Stable


    View Slide

  51. // উ੿ࢿ਷ ੹౵ػ׮.


    @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())


    }

    View Slide

  52. // উ੿ࢿ਷ ੹౵ػ׮.


    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())


    }


    View Slide

  53. @Composable


    fun ListDisplay(texts: List) {


    Text(text = texts.joinToString())


    }


    View Slide

  54. public interface MutableList : List, MutableCollection


    @Composable


    fun ListDisplay(texts: List = mutableListOf()) {


    Text(text = texts.joinToString())


    }


    View Slide

  55. @Immutable


    class ImmutableListWrapper(val value: List)


    @Composable


    fun ListDisplay(texts: ImmutableListWrapper) {


    Text(text = texts.value.joinToString())


    }


    View Slide

  56. // https://github.com/Kotlin/kotlinx.collections.immutable


    @Composable


    fun ListDisplay(texts: ImmutableList) {


    Text(text = texts.joinToString())


    }


    View Slide

  57. /**


    * ஹ౵ੌ द੼ী ݽٚ ௿ېझٜ੄ উ੿ࢿਸ ੗زਵ۽ ୶ۿ೤פ׮.


    *


    * @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(var value: T)


    ௿ېझউ੿ࢿ୶ۿ
    j j j j

    View Slide

  58. LiveLiteral
    -JWF-JUFSBMഝࢿച
    j j j j

    View Slide

  59. -JWF-JUFSBM
    Stability

    View Slide

  60. fun earth() {


    print("Bye World")


    }


    View Slide

  61. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  62. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  63. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  64. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  65. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  66. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  67. fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  68. private val liveLiteralCache = HashMap>()


    @InternalComposeApi


    @ComposeCompilerApi


    fun liveLiteral(key: String, value: T): State {


    return liveLiteralCache.getOrPut(key) {


    mutableStateOf(value)


    } as State


    }


    View Slide

  69. @InternalComposeApi


    fun updateLiveLiteralValue(key: String, value: Any?) {


    var needToUpdate = true


    val stateObj = liveLiteralCache.getOrPut(key) {


    needToUpdate = false


    mutableStateOf(value)


    }


    if (needToUpdate) {


    stateObj.value = value


    }


    }


    View Slide

  70. private fun findEffectiveRecomposeScope(group: Int): RecomposeScopeImpl? {


    var current = group


    while (current > 0) {


    for (data in DataIterator(this, current)) {


    if (data is RecomposeScopeImpl) {


    return data


    }


    }


    current = groups.parentAnchor(current)


    }


    return null


    }


    View Slide

  71. // @Composable XXX


    fun earth() {


    print(LiveLiterals$EarthKt.`getString$arg-0$call-print$fun-earth`())


    }


    object LiveLiterals$EarthKt {


    var `String$arg-0$call-print$fun-earth` = "Bye World"


    var `State$String$arg-0$call-print$fun-earth`: MutableState? = null




    fun `getString$arg-0$call-print$fun-earth`(): String {


    val field = this.`String$arg-0$call-print$fun-earth`


    val state = if (field == null) {


    val tmp = liveLiteral(


    "String$arg-0$call-print$fun-earth",


    this.`String$arg-0$call-print$fun-earth`


    )


    this.`String$arg-0$call-print$fun-earth` = tmp


    tmp


    } else field


    return field.value


    }


    }


    View Slide

  72. /**


    * LiveLiteral ੉ ெઉ ੓ח ҃਋ীب ੸ਊغח ߧਤ ղীࢲ LiveLiteral ਸ ࢤࢿೞ૑ ঋب۾ ஹನૉ ஹ౵ੌ۞ী ಴दೞח ؘ ࢎਊؾפ׮.


    */


    @Target(


    AnnotationTarget.PROPERTY,


    AnnotationTarget.FUNCTION,


    AnnotationTarget.CLASS,


    AnnotationTarget.FILE


    )


    @Retention(AnnotationRetention.SOURCE)


    annotation class NoLiveLiterals


    // @file:NoLiveLiterals


    @NoLiveLiterals


    fun earth() {


    print("Bye World")


    }


    View Slide

  73. $PNQPTFS઱ੑ
    j j 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)


    }


    }


    View Slide

  74. @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 j j

    View Slide

  75. @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    }


    }


    Column
    Text
    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap

    View Slide

  76. @Composable


    fun Main() {


    Column {


    Text(text = "Column Data")


    }


    }


    “Column Data”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    Group
    Group

    View Slide

  77. // RestartableGroup: ܻஹನ૑࣌੉ ੌযզ ࣻ ੓ח ஹನ੷࠶ ઱ਤ۽ ࢤࢿػ׮.


    // ߹ب੄ ২࣌੉ হ׮ݶ ݽٚ ஹನ੷࠶ী ؀೧ ࢤࢿغח Ӓܛ੉Ҋ,


    // ܻஹನ૑࣌ ೞח ߑߨਸ оܰ஘׮.


    // ݽٚ RestartableGroup ਷ ೧׼ ߧਤ ݅ఀ


    // ੗୓੄ ܻஹನ૑࣌ झ௏೐(RecomposeScope)ܳ ഋࢿೠ׮.


    @Composable


    fun RestartableContainer() {


    Text(text = "SungbinLand")


    }


    RestartableGroup
    “SungbinLand”
    Gap
    Gap
    Gap
    Gap
    Gap
    Gap
    RestartableGroup
    3FTUBSUBCMF$POUBJOFS
    3FDPNQPTF4DPQF
    5FYU
    3FDPNQPTF4DPQF

    View Slide

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

    View Slide

  79. /**


    * ஹನ੷࠶੉ ߓ஖ػ ਤ஖о ׳ۄ૑ݶ ਤ஖ ݫݽ੉ઁ੉࣌੄ 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

    View Slide

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

    View Slide

  81. 3. How it works?
    ↟$PNQJMFS
    ↟3VOUJNF

    View Slide

  82. 🎨
    🖼️
    🖌️
    🧰
    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

    View Slide

  83. Runtime Process (full)

    View Slide

  84. Runtime Process (simply)

    View Slide

  85. class MainActivity : ComponentActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {


    super.onCreate(savedInstanceState)


    setContent {


    Text(text = "SungbinLand")


    }


    }


    }


    View Slide

  86. // ஹನૉ ۠ఋ੐җ UI ੄ োѾ


    fun ComponentActivity.setContent(


    parent: CompositionContext? = null,


    content: @Composable () -> Unit


    ) {


    ComposeView(this).apply {


    setParentCompositionContext(parent)


    setContent(content)


    setOwners()


    setContentView(this, DefaultActivityContentLayoutParams)


    }


    }


    View Slide

  87. // ComposeView#setContent ۽ ੉ ೣࣻо प೯ؽ


    private fun ensureCompositionCreated() {


    setContent(/* Recomposer о ٜ݅য૑Ҋ ઱ੑؾפ׮ */) {


    content()


    }


    }

    View Slide

  88. /**


    * ܻஹನ૑࣌ਸ ࣻ೯ೞҊ ೞա ੉࢚੄ ஹನ੷࠶ী সؘ੉౟ܳ ੸ਊೞӝ ਤೠ झா઴۞ ੑפ׮.


    */


    class Recomposer(


    effectCoroutineContext: CoroutineContext


    ) : CompositionContext()


    /**


    * Handler ௒ߔ ژח Choreographer ੄ গפݫ੉࣌ ೐ۨ੐ ױ҅ ઺


    * ݢ੷ بېೞח ױ҅ীࢲ ٣झಁ஖ܳ ࣻ೯ೞח CoroutineDispatcher ੑפ׮.


    */


    class AndroidUiDispatcher private constructor(


    val choreographer: Choreographer,


    private val handler: Handler


    ) : CoroutineDispatcher()


    View Slide

  89. /**


    * ܻஹನ૑࣌ਸ ࣻ೯ೞҊ ೞա ੉࢚੄ ஹನ੷࠶ী সؘ੉౟ܳ ੸ਊೞӝ ਤೠ झா઴۞ ੑפ׮.


    */


    class Recomposer(


    effectCoroutineContext: CoroutineContext


    ) : CompositionContext()


    /**


    * Handler ௒ߔ ژח Choreographer ੄ গפݫ੉࣌ ೐ۨ੐ ױ҅ ઺


    * ݢ੷ بېೞח ױ҅ীࢲ ٣झಁ஖ܳ ࣻ೯ೞח CoroutineDispatcher ੑפ׮.


    */


    class AndroidUiDispatcher private constructor(


    val choreographer: Choreographer,


    private val handler: Handler


    ) : CoroutineDispatcher()


    View Slide

  90. suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->


    val toRecompose = mutableListOf()


    while (shouldKeepRecomposing) {


    awaitWorkAvailable()


    parentFrameClock.withFrameNanos { frameTime ->


    try {


    toRecompose.fastForEach { composition ->


    performRecompose(composition)


    }


    } finally {


    toRecompose.clear()


    }


    }


    }


    }


    View Slide

  91. private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit) {


    // Recomposer ীࢲ ߉਷ Choreographer ੄ ېಌੋ MonotonicFrameClock ܳ оઉৡ׮.


    val parentFrameClock: MonotonicFrameClock = coroutineContext.monotonicFrameClock


    withContext(broadcastFrameClock) { // broadcastFrameClock: ஹನૉ ۠ఋ੐ীࢲ ҟ৉ਵ۽ ࢎਊغח MonotonicFrameClock ੄ ېಌ


    Snapshot.registerApplyObserver { _, _ -> // Snapshot ੄ write ੉߮౟ܳ ߉ח׮.


    deriveStateLocked()?.resume(Unit)


    }


    coroutineScope {


    block(parentFrameClock)


    }


    }


    }


    View Slide

  92. private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit) {


    // Recomposer ীࢲ ߉਷ Choreographer ੄ ېಌੋ MonotonicFrameClock ܳ оઉৡ׮.


    val parentFrameClock: MonotonicFrameClock = coroutineContext.monotonicFrameClock


    withContext(broadcastFrameClock) { // broadcastFrameClock: ஹನૉ ۠ఋ੐ীࢲ ҟ৉ਵ۽ ࢎਊغח MonotonicFrameClock ੄ ېಌ


    Snapshot.registerApplyObserver { _, _ -> // Snapshot ੄ write ੉߮౟ܳ ߉ח׮.


    deriveStateLocked()?.resume(Unit)


    }


    coroutineScope {


    block(parentFrameClock)


    }


    }


    }


    View Slide

  93. suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock ->


    val toRecompose = mutableListOf() // ܻஹನ૑࣌੉ ೙ਃೠ RecomposeScope о ׸ӟ׮. (׸ח җ੿ ࢤۚ)


    while (shouldKeepRecomposing) { // shouldKeepRecomposing: Recomposer о ܻஹನ૑࣌ ਃ୒ਸ х૑೧ঠ ೞח૑ܳ աఋմ׮.


    awaitWorkAvailable() // ࢜۽਍ ܻஹನ૑࣌ ਃ୒੉ ੓ਸ ٸ ө૑ suspend ࢚క۽ ӝ׮ܽ׮.


    parentFrameClock.withFrameNanos { frameTime -> // Choreographer#postFrameCallback ਸ ഐ୹ೠ׮.


    try {


    toRecompose.fastForEach { composition -> // RecomposeScope ܳ ࣽഥೞݴ


    performRecompose(composition) // ܻஹನ૑࣌ ૓೯!


    }


    } finally {


    toRecompose.clear()


    }


    }


    }


    }


    View Slide

  94. private suspend fun awaitWorkAvailable() {


    suspendCancellableCoroutine { co ->


    if (hasSchedulingWork) { // ؀ӝ઺ੋ ܻஹನ૑࣌ ਃ୒੉ ੓׮ݶ ߄۽ resume ೞҊ


    co.resume(Unit)


    } else {


    // ؀ӝ઺ੋ ܻஹನ૑࣌ ਃ୒੉ হ׮ݶ workContinuation ਵ۽ CancellableContinuation ੷੢ റ suspend ਬ૑


    workContinuation = co


    }


    }


    }


    View Slide

  95. private fun deriveStateLocked(): CancellableContinuation? {


    // ࠁܨ ઺ੋ ੘স: ܻஹನ૑࣌ ਃ୒ ١١ ஹನૉ ۠ఋ੐ ղࠗীࢲ ૓೯غח ৈ۞ ੘স ਃ୒


    val newState = when { /* അ੤ ࠁܨ ઺ੋ ੘স ৈࠗী ٮۄ ࢜۽਍ ࢚క ҅࢑ */ }


    return if (newState == State.PendingWork) { // ࠁܨ ઺ੋ ੘স੉ ੓׮ݶ


    workContinuation.also { // workContinuation ߈ജ ߂ workContinuation ୡӝച


    workContinuation = null


    }


    } else null


    }


    View Slide

  96. private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit) {


    val parentFrameClock = coroutineContext.monotonicFrameClock


    withContext(broadcastFrameClock) {


    Snapshot.registerApplyObserver { _, _ ->


    deriveStateLocked()?.resume(Unit)


    }


    coroutineScope {


    block(parentFrameClock)


    }


    }


    }


    View Slide

  97. 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.(parentFrameClock: MonotonicFrameClock) -> Unit) {


    val parentFrameClock = coroutineContext.monotonicFrameClock


    withContext(broadcastFrameClock) {


    Snapshot.registerApplyObserver { _, _ -> deriveStateLocked()?.resume(Unit) } // Snapshot ч ߸҃੉ ੓ਸ ٸ resume


    coroutineScope { block(parentFrameClock) }


    }


    }


    View Slide

  98. // ComposeView#setContent ۽ ੉ ೣࣻо प೯ؽ


    private fun ensureCompositionCreated() {


    setContent(/* Recomposer о ٜ݅য૑Ҋ ઱ੑؾפ׮ */) {


    content()


    }


    }

    View Slide

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

    View Slide

  100. 4. Compiler Debugging
    ↟CVJMENFUSJDT
    ↟CVJMESFQPSUT

    View Slide

  101. CVJMENFUSJDT
    // ஹ౵ੌ۞о х૑ೠ ஹನ੷࠶ ೣٜࣻ੄ ੿ࠁܳ աఋմ׮.


    kotlinOptions {


    freeCompilerArgs += [


    "-P",


    "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +


    "${rootProject.file(".").absolutePath}/report/compose-metrics"


    ]


    }

    View Slide

  102. <ݽٕݺ>@<࠽٘ఋੑ>NPEVMFKTPO
    {
    {


    "skippableComposables": 48, // skippable ࢚కੋ ஹನ੷࠶ ѐࣻ


    "restartableComposables": 67,


    "readonlyComposables": 0,


    "totalComposables": 67, // ੹୓ ஹನ੷࠶ ѐࣻ


    "restartGroups": 67,


    "totalGroups": 72,


    "staticArguments": 130,


    "certainArguments": 28,


    "knownStableArguments": 793, // উ੿ ࢚క۽ ঌ۰૓ ੋ੗ ѐࣻ


    "knownUnstableArguments": 30, // ࠛউ੿ ࢚క۽ ঌ۰૓ ੋ੗ ѐࣻ


    "unknownStableArguments": 8,


    "totalArguments": 831,


    "markedStableClasses": 0,


    "inferredStableClasses": 16, // উ੿ ࢚క۽ ୶ۿػ ௿ېझ ѐࣻ


    "inferredUnstableClasses": 2, // ࠛউ੿ ࢚క۽ ୶ۿػ ௿ېझ ѐࣻ


    "inferredUncertainClasses": 0,


    "effectivelyStableClasses": 16,


    "totalClasses": 18,


    "memoizedLambdas": 54,


    "singletonLambdas": 5,


    "singletonComposableLambdas": 8,


    "composableLambdas": 31,


    "totalLambdas": 89


    }


    View Slide

  103. CVJMESFQPSUT
    // ஹ౵ੌ۞о х૑ೠ ஹನ੷࠶ ೣٜࣻ੉ ࢎਊ઺ੋ ௿ېझ৬ ੋ੗ী ؀ೠ উ੿ࢿ ੿ࠁܳ աఋմ׮.


    kotlinOptions {


    freeCompilerArgs += [


    "-P",


    "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +


    "${rootProject.file(".").absolutePath}/report/compose-reports"


    ]


    }

    View Slide

  104. <ݽٕݺ>@<࠽٘ఋੑ>DMBTTFTUYU
    /* ——— classes ——— */


    sealed class UiState {


    object Loading : UiState()


    object Done : UiState()


    data class Exception(val throwable: Throwable) : UiState()


    }


    /* ——— reports ——— */


    stable class Loading {


    = Stable


    }


    stable class Done {


    = Stable


    }


    unstable class Exception {


    unstable val throwable: Throwable


    = Unstable


    }

    View Slide

  105. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTDTW
    /* ——— composables ——— */


    @Composable


    fun DisplayText(text: String = "Hi") {


    Text(text = text)


    }


    @Composable


    fun DisplayTexts(texts: List) {


    Text(text = texts.joinToString())


    }


    /* ——— reports ——— */


    View Slide

  106. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU
    /* ——— composables ——— */


    @Composable


    fun DisplayText(text: String = "Hi") {


    Text(text = text)


    }


    @Composable


    fun DisplayTexts(texts: List) {


    Text(text = texts.joinToString())


    }


    /* ——— reports ——— */


    restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText(


    stable text: String? = @dynamic LiveLiterals$MainActivityKt.String$param-text$fun-DisplayText()


    )


    restartable /* skippable ੉ হ਺ */ scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts(


    unstable texts: List


    )


    View Slide

  107. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU
    /* ——— composables ——— */


    @Composable


    fun DisplayText(text: String = "Hi") {


    Text(text = text)


    }


    @Composable


    fun DisplayTexts(texts: List) {


    Text(text = texts.joinToString())


    }


    /* ——— reports ——— */


    restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText(


    stable text: String? = @dynamic LiveLiterals$MainActivityKt.String$param-text$fun-DisplayText()


    )


    restartable scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts(


    unstable texts: List


    )


    View Slide

  108. <ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU
    /* ——— composables ——— */


    @NoLiveLiterals // new


    @Composable


    fun DisplayText(text: String = "Hi") {


    Text(text = text)


    }


    @Composable


    fun DisplayTexts(texts: List) {


    Text(text = texts.joinToString())


    }


    /* ——— reports ——— */


    restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun DisplayText(


    stable text: String? = @static "Hi"


    )


    restartable scheme("[androidx.compose.ui.UiComposable]") fun DisplayTexts(


    unstable texts: List


    )


    View Slide

  109. 5. Best Practice
    ↟ܻஹನ૑࣌ߧਤ઴੉ӝ
    ↟ܻஹನ૑࣌হগӝ
    ↟ࠛ೙ਃೠ࢚క୶੸ઁѢ
    ↟"05ஹ౵ੌഝࢿച

    View Slide

  110. 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


    */

    View Slide

  111. 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


    ੉റ زੌ ۽Ӓ ߈ࠂ


    */

    View Slide

  112. 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


    ੉റ زੌ ۽Ӓ ߈ࠂ


    */

    View Slide

  113. EPOVUIPMFTLJQQJOH
    // ஹ౵ੌ ੹


    @Composable


    fun TextWrapper(text: String) {


    Text(text = text)


    }


    // ஹ౵ੌ റ (೨ब ࠗ࠙݅ ಴द)


    @Composable


    fun TextWrapper(composer: Composer, text: String) {


    composer.startRestartGroup(-199242123) // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ૑࣌ झ௏೐ܳ ୶оೞҊ ੓਺


    Text(text = text)


    composer.endRestartGroup()


    }

    View Slide

  114. 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

    View Slide

  115. 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

    View Slide

  116. 4UBCJMJUZ4ZTUFN
    setContent {


    var number by remember { mutableStateOf(1) }


    TextWrapper(


    modifier = Modifier.clickable { number++ },


    text = number.toString()


    )


    }


    @Composable


    fun TextWrapper(


    modifier: Modifier = Modifier,


    text: String


    ) {


    Text(modifier = modifier, text = text)


    }


    View Slide

  117. 4UBCJMJUZ4ZTUFN
    setContent {


    var number by remember { mutableStateOf(1) }


    TextWithLambda(


    modifier = Modifier.clickable { number++ },


    text = { number.toString() }


    )


    }


    @Composable


    fun TextWithLambda(


    modifier: Modifier = Modifier,


    text: () -> String // Function0


    ) {


    Text(modifier = modifier, text = text())


    }


    View Slide

  118. 5. Best Practice
    ↟ܻஹನ૑࣌ߧਤ઴੉ӝ
    ↟ܻஹನ૑࣌হগӝ
    ↟ࠛ೙ਃೠ࢚క୶੸ઁѢ
    ↟"05ஹ౵ੌഝࢿച

    View Slide

  119. NPWBCMF$POUFOU0G
    val content = remember<@Composable () -> Unit> {


    {


    repeat(2) {


    Box(modifier = Modifier.size(100.dp).background(color = Color.Green))


    }


    }


    }


    Column {


    Button(onClick = { isRow = !isRow }) {


    Text(text = "Switch")


    }


    if (isRow) {


    Row { content() }


    } else {


    Column { content() }


    }


    }

    View Slide

  120. NPWBCMF$POUFOU0G
    val content = remember {


    movableContentOf { // movableContentWithReceiverOf


    repeat(2) {


    Box(modifier = Modifier.size(100.dp).background(color = Color.Green))


    }


    }


    }


    Column {


    Button(onClick = { isRow = !isRow }) {


    Text(text = "Switch")


    }


    if (isRow) {


    Row { content() }


    } else {


    Column { content() }


    }


    }

    View Slide

  121. !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)


    }

    View Slide

  122. !/PO3FTUBSUBCMF$PNQPTBCMF
    @Composable


    @NonRestartableComposable


    fun DisposableEffect(key1: Any?, effect: DisposableEffectScope.() -> DisposableEffectResult) {


    remember(key = key1) { DisposableEffectImpl(effect) }


    }


    @Composable


    @NonRestartableComposable


    fun Image(


    modifier: Modifier = Modifier, colorFilter: ColorFilter? = null,


    imageVector: ImageVector, contentDescription: String?, alpha: Float = DefaultAlpha,


    alignment: Alignment = Alignment.Center, contentScale: ContentScale = ContentScale.Fit


    ) {


    Image(


    modifier = modifier, alignment = alignment,


    contentScale = contentScale, alpha = alpha, colorFilter = colorFilter,


    painter = rememberVectorPainter(imageVector), contentDescription = contentDescription


    )


    }

    View Slide

  123. 5. Best Practice
    ↟ܻஹನ૑࣌ߧਤ઴੉ӝ
    ↟ܻஹನ૑࣌হগӝ
    ↟ࠛ೙ਃೠ࢚క୶੸ઁѢ
    ↟"05ஹ౵ੌഝࢿച

    View Slide

  124. TUBUJD$PNQPTJUJPO-PDBM0G
    val LocalContext = staticCompositionLocalOf {


    noLocalProvidedFor("LocalContext")


    }


    val LocalClipboardManager = staticCompositionLocalOf {


    noLocalProvidedFor("LocalClipboardManager")


    }


    val LocalDensity = staticCompositionLocalOf {


    noLocalProvidedFor("LocalDensity")


    }


    val LocalFocusManager = staticCompositionLocalOf {


    noLocalProvidedFor("LocalFocusManager")


    }

    View Slide

  125. !/P-JWF-JUFSBMT
    @file:NoLiveLiterals


    @NoLiveLiterals

    View Slide

  126. 5. Best Practice
    ↟ܻஹನ૑࣌ߧਤ઴੉ӝ
    ↟ܻஹನ૑࣌হগӝ
    ↟ࠛ೙ਃೠ࢚క୶੸ઁѢ
    ↟"05ஹ౵ੌഝࢿച

    View Slide

  127. #BTFMJOFQSPGJMFT
    #BTFMJOFQSPGJMFT਷ݠन௏٘ী؀ೠ઺ਃೠ҃۽ܳࢎ੹ஹ౵ੌೞӝਤ೧

    ࢸ஖઺ী"OESPJE۠ఋ੐ "35
    ীࢲࢎਊೞח"1,ীನೣػ௿ېझ߂ݫࢲ٘ݾ۾ੑפ׮
    IUUQTEFWFMPQFSBOESPJEDPNUPQJDQFSGPSNBODFCBTFMJOFQSPGJMFT

    View Slide

  128. #BTFMJOFQSPGJMFT
    .BDSPCFODINBSLݽٕࢸ੿

    View Slide

  129. #BTFMJOFQSPGJMFT
    .BDSPCFODINBSLݽٕࢸ੿
    ௏٘੘ࢿ CBTFMJOFQSPGJMFTࢤࢿ

    @ExperimentalBaselineProfilesApi


    @RunWith(AndroidJUnit4::class)


    class BaselineProfileGenerator {


    @get:Rule


    val baselineProfileRule = BaselineProfileRule()


    @Test


    fun startup() = baselineProfileRule.collectBaselineProfile(


    packageName = AppPackageName


    ) {


    pressHome()


    startActivityAndWait()


    }


    }


    View Slide

  130. #BTFMJOFQSPGJMFT
    .BDSPCFODINBSLݽٕࢸ੿
    ௏٘੘ࢿ CBTFMJOFQSPGJMFT੸ਊ੹റஏ੿

    @RunWith(AndroidJUnit4::class)


    class BaselineProfileBenchmark {


    @get:Rule


    val benchmarkRule = MacrobenchmarkRule()


    // …


    private fun startup(compilationMode: CompilationMode) {


    benchmarkRule.measureRepeated(


    packageName = AppPackageName,


    metrics = listOf(StartupTimingMetric()),


    iterations = 10,


    startupMode = StartupMode.COLD,


    compilationMode = compilationMode


    ) {


    pressHome()


    startActivityAndWait()


    }


    }


    }


    View Slide

  131. #BTFMJOFQSPGJMFT
    .BDSPCFODINBSLݽٕࢸ੿
    ௏٘੘ࢿ CBTFMJOFQSPGJMFT੸ਊ੹റஏ੿

    @RunWith(AndroidJUnit4::class)


    class BaselineProfileBenchmark {


    @get:Rule


    val benchmarkRule = MacrobenchmarkRule()


    @Test


    fun startupNoCompilation() {


    startup(compilationMode = CompilationMode.None()) // JIT ஹ౵ੌ


    }


    @Test


    fun startupBaselineProfile() {


    startup(compilationMode = CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require)) // AOT ஹ౵ੌ


    }


    // …


    }


    View Slide

  132. #BTFMJOFQSPGJMFT
    .BDSPCFODINBSLݽٕࢸ੿
    ௏٘੘ࢿ
    #VJME7BSJBOUTࢸ੿

    View Slide

  133. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച

    View Slide

  134. #BTFMJOFQSPGJMFT
    adb root

    View Slide

  135. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO

    View Slide

  136. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO
    adb pull

    View Slide

  137. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO
    adb pull


    CBTFMJOFQSPGUYUਵ۽౵ੌݺ߸҃റTSDNBJOਵ۽੉ز

    View Slide

  138. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO
    adb pull


    CBTFMJOFQSPGUYUਵ۽౵ੌݺ߸҃റTSDNBJOਵ۽੉ز
    ੸ਊ੹റ࠺Ү

    View Slide

  139. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO
    adb pull


    CBTFMJOFQSPGUYUਵ۽౵ੌݺ߸҃റTSDNBJOਵ۽੉ز
    ੸ਊ੹റ࠺Ү

    View Slide

  140. #BTFMJOFQSPGJMFT
    ܖ౟ӂೠഝࢿച
    3VO
    adb pull


    CBTFMJOFQSPGUYUਵ۽౵ੌݺ߸҃റTSDNBJOਵ۽੉ز
    ੸ਊ੹റ࠺Ү
    ੸ਊೡݽٕী੄ઓࢿ୶о
    dependencies {


    implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01")


    }


    View Slide

  141. 6. Wrap-up
    ↟ة݀ߡ੹ҙܻ୓҅द੘

    View Slide

  142. 6. Wrap-up
    ↟नӏ"1*DPMMFDU"T4UBUF8JUI-JGFDZDMF

    View Slide

  143. 6. Wrap-up
    ↟ஹನૉղࠗખ؊੗ࣁ൤ࠁӝ
    IUUQTTVOHCJODPNQPTFEJWF

    View Slide

  144. хࢎ೤פ׮
    ஹನૉ ղࠗ ௏ٜ٘਷ cs.android.com ীࢲ оઉ৳णפ׮.


    ੉ ೐ۨઃప੉࣌ীח షझ౱ীࢲ ઁҕೠ షझಕ੉झо ੸ਊغয ੓णפ׮.


    QnA🙋

    View Slide