Share
Embed
[단축본] 컴포즈 내부로 이해하는 최적화 비법
Ji Sungbin
April 29, 2023
Transcript
68 slides
-
/PEF4ZTUFN $PNQPTF6* 4OBQTIPU4ZTUFN 8SBQVQ 📖ݾର $PNQPTF3VOUJNF
$PNQPTF6* +FUQBDL$PNQPTF -
@Composable fun UiNodes() { Column { repeat(3) { index ->
Text(text = "My index is $index") } } } Column Text Text Text -
@Composable fun UiNodes() { Column { repeat(3) { index ->
Text(text = "My index is $index") } } } Ui Node Ui Node Ui Node Ui Node -
/** * ஹನૉ ݽٚ ؘఠܳ ೞҊ ҙܻೞח ېझੑפ. * Gap
Buffer ܐҳઑ৬ ےؒ ঘࣁझ۽ ҳഅعҊ, ղࠗ ؘఠח Array<Any?>ী ؾפ. */ internal class SlotTable : CompositionData, Iterable<CompositionGroup> -
Gap Buffer: ࢎਊೡ ҕрٜਸ ܻ ഛࠁ೧ فҊ, ೧ ҕрীࢲ সਸ
೯ೞח ܐ ҳઑ. ਤఃೖ٣ইীࢲח زੌೠ ਤ Ӕী ۞झఠ݂ػ ബਯੋ ੑ ߂ ઁ সਸ ೲਊೞח ز ߓৌ ۄҊ ೞҊ णפ. -
Gap Buffer: زੌೠ ਤ Ӕী ۞झఠ݂ػ ബਯੋ ੑ ߂ ઁ
সਸ ೲਊೞח ز ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ cursorܳ աఋն -
Gap Buffer: زੌೠ ਤ Ӕী ۞झఠ݂ػ ബਯੋ ੑ ߂ ઁ
সਸ ೲਊೞח ز ߓৌ 1, ୡӝ Gap ࢤࢿ: O(N) [(v)_, _, _, _, _, _, _, _, _, _] // _ח Gap, (v)ח അ cursorܳ աఋն 2. 0ߣ૩ ੋؙझী Hi ੑ [(v)H, i, _, _, _, _, _, _, _, _] // ݽف Gapী ੑغ۽ ୶о Gap ࢤࢿ ਃೞ ঋই O(1)ী ৮ܐؽ -
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ਵ۽ ߸҃ೞח Ѫਵ۽ ҳഅؾפ) -
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)݅ী ৮ܐؽ -
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 ߣ૩ਤ -
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…” ߣ૩ਤ ߣ૩ਤ -
/** * 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) } -
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 -
@Composable fun Main() { Column { Text(text = "Column Data")
} } Column Text “Column Data” Gap Gap Gap Gap Gap Gap -
@Composable fun Main() { Column { Text(text = "Column Data")
} } “Column Data” Gap Gap Gap Gap Gap Gap Group Group -
“Column Data” Gap Gap Gap Gap Gap Gap Group Group
3FTUBSUBCMF(SPVQ 3FQMBDFBCMF(SPVQ .PWBCMF(SPVQ @Composable fun Main() { Column { Text(text = "Column Data") } } -
// RestartableGroup: ܻஹನ࣌ ੌযզ ࣻ ח ஹನ࠶ // ਤ۽ ࢤࢿػ.
߹ب ২࣌ হݶ ݽٚ ஹನ࠶ী // ࢤࢿغח ӒܛҊ, ܻஹನ࣌ ೞח ߑߨਸ оܰ. // ݽٚ RestartableGroup ೧ ߧਤ ݅ఀ // ܻஹನ࣌ झ(RecomposeScope)ܳ ഋࢿೠ. @Composable fun RestartableContainer() { Text(text = "SungbinLand") } RestartableGroup “SungbinLand” Gap Gap Gap Gap Gap Gap RestartableGroup 3FTUBSUBCMF$POUBJOFS 3FDPNQPTF4DPQF 5FYU 3FDPNQPTF4DPQF -
// 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 -
/** * ஹನ࠶ ߓػ ਤо ׳ۄݶ ਤ ݫݽઁ࣌ 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 -
!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) } -
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 -
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 -
@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 -
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 */ -
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] [re-composition] setContent recomposition setContent recomposition Text recomposition Text recomposition റ زੌ ۽Ӓ ߈ࠂ */ -
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] [re-composition] setContent recomposition Text recomposition Text recomposition റ زੌ ۽Ӓ ߈ࠂ Button recomposition */ -
EPOVUIPMFTLJQQJOH // ஹੌ @Composable fun TextWrapper(text: String) { Text(text
= text) } // ஹੌ റ (೨ब ࠗ࠙݅ द) fun TextWrapper(composer: Composer, text: String) { // ղࠗীࢲ addRecomposeScope() ۽ ܻஹನ࣌ झܳ ୶оೞҊ composer.startRestartGroup(-199242123) Text(text = text) composer.endRestartGroup() } -
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 -
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 -
Composition Layout Draw @Composable fun Main() { Text( modifier =
Modifier .clickable { getOne() }, text = "Hello, World!" ) } fun getOne() = 1 -
@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 -
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> -
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 -
RestartableGroup Function0<Int> Gap Gap Composition Layout Draw “Hello, World!” Text
Int “Hello, World!” ۨইਓઁডઑѤ DPOTUSBJOUT ҅ ठܶప࠶ч҅ JOWPLF 6*ܻҳ୷ -
@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 -
Composition Layout Draw Composition DPOTUSBJOUT ࠗݽઁডઑѤ֢ @Stable interface BoxWithConstraintsScope :
BoxScope { val constraints: Constraints val minWidth: Dp val maxWidth: Dp val minHeight: Dp val maxHeight: Dp } -
Composition Layout Draw ןୡӝച @Composable fun LazyLayout() @Composable fun LazyRow()
@Composable fun LazyColumn() -B[Z-BZPVUചݶীաఋաӝ߄۽ী-BZPVU%SBX೯ -
4OBQTIPU SomeGroup 1 Snapshot Gap Gap Gap Gap Gap
Gap 4OBQTIPU 2 Snapshot -
4OBQTIPU SomeGroup 1 Snapshot Gap Gap Gap Gap Gap
4OBQTIPU 2 Snapshot 4OBQTIPU 3 Snapshot -
/4OBQTIPU SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2
Snapshot 3 Snapshot 5 Snapshot N Snapshot 7 Snapshot -
SomeGroup 1 Snapshot 4 Snapshot 6 Snapshot 2 Snapshot 3
Snapshot 5 Snapshot N Snapshot 7 Snapshot 4OBQTIPU3FBE 4OBQTIPU8SJUF -
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 -
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 -
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 -
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 -
↟ೣԋࠁݶજਸܐ 4. Wrap-up -BNCEB0QUJNJ[JOH -JWF-JUFSBM $PNQJMFS.FUSJDT
4OBQTIPU4ZTUFNXJUI.7$$ #BTFMJOF1SPGJMFT $PNQPTBCMF(SPVQT 4UBCJMJUZ4ZTUFN -
хࢎפ ஹನૉ ղࠗ ٘ח cs.android.comীࢲ оઉ৳णפ. ۨઃప࣌ীח షझীࢲ ઁҕೠ
షझಕझо ਊغয णפ. QnA🙋