Share
Embed
Jetpack Compose A to Z (short)
Jetpack Compose A to Z 네이버/화해 강연 자료 (40분)
Ji Sungbin
February 23, 2023
Transcript
144 slides
-
$PNQPTFࣗѐ $PNQPTFҳࢿਃࣗ )PXJUXPSLT $PNQJMFS%FCVHHJOH
#FTU1SBDUJDF 8SBQVQ 📖ݾର -
/** * Composable য֢ప࣌ ஹನૉ ೣࣻীࢲ ࣻਵ۽ ࢎਊغݴ ѱ 3о
ೡਸ ыणפ. * * 1. ݵ١ࢿ ࠁ * 2. ਤ ݫݽઁ࣌ ഝࢿച * 3. ؘఠ ઁҕ ژח ஹನ࠶ ߑ */ @MustBeDocumented @Retention(AnnotationRetention.BINARY) @Target( AnnotationTarget.FUNCTION, AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.PROPERTY_GETTER ) annotation class Composable -
// э input ਸ ߉ ஹನ࠶ਸ ݵ١ࢿ ࠁਵ۽ ೦࢚ प೯ਸ
Ѥցګ. // ݵ١ࢿ: э ো Ѿҗח ೦࢚ زੌೞח ࢿ @Composable fun Idempotent() { // ݅ড UI јन ਃೞݶ... Text(text = "ࢿ࠼ے٘") // ஹನ࠶ ೠ ߣ݅ प೯ػ. Text(text = System.currentTimeMillis().toString()) // ஹನ࠶ ೦࢚ प೯ػ. } -
// ஹನ࠶ ۠ఋীࢲ Ѿػ ࢶࣽਤী ݏѱ ߽۳۽ प೯ؽਵ۽ ஹನ࠶ प೯
ࣽࢲী ઓೞח Ѥ ০ ঋ. // ٮۄࢲ ݵ١ࢿਸ ࠁೞח Ѫ ਃೞ. 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 оמࢿ } -
// ஹನ࠶ ೣࣻ दӒפ৬ call-site, ӒܻҊ प೯ػ ࣽࢲܳ ӝળਵ۽ Ҋਬೠ
key о ࢤࢿغҊ, // ஹನૉח ೧ key ܳ ӝળਵ۽ ݽٚ ؘఠܳ ೞҊ ҙܻೠ. // э key ܳ о ஹನ࠶ э input ਵ۽ प೯ ػݶ ӝઓী प೯ػ Ѿҗܳ Ӓ۽ ࢎਊೠ. // -> ݵ١ࢿҗ ਤ ݫݽઁ࣌ Ѿҗ // ਤ ݫݽઁ࣌: э ਤীࢲ э input ਵ۽ प೯غݶ ೦࢚ э output աৢ ѪۄҊ оೞҊ, // чਸ நयೞҊ ࢎਊ ೞח ӝࣿ @Composable fun PositionMemoization() { repeat(5) { Text(text = "Bye, world!") // э call-site ݅, ஹನ࠶ ؘఠо ܲ ҕрী ؽ } } -
/** * @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!") } -
/** * ஹನૉ ݽٚ ؘఠܳ ೞҊ ҙܻೞח ېझ ੑפ. *
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 Text “Column Data” Gap Gap Gap Gap Text “Loading…” "ODIPS۽߄۽Ӕ -
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 -
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” -
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 !$PNQPTBCMFݵ١ࢿࠁ ܻஹನ࣌ࢤۚ4NBSU3FDPNQPTJUJPO -
var isLoading = true @Composable fun Main() { Column {
Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Emit Column Text Text -
var isLoading = true @Composable fun Main() { Column {
Text(text = "Column Data") if (isLoading) { Text(text = "Loading...") } } } Materializing -
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…” -
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…” -
@Composable fun TextWrapper() { Text(text = "SungbinLand") } dependencies {
// annotation processor XXX implementation "androidx.compose.foundation:foundation:1.3.0-alpha01" } -
@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") } -
/** * উ ࢚కח ѱ 3о ઑѤਸ ٮܵפ. * *
- ч ߸҃عਸ ҃ ஹನ࠶ীѱ ঌ۰ઉঠ פ. (, [State] ۽ زظঠ ೣ) * - ੋझఢझ৬ അ ੋझఢझо زੌ೧ঠ פ. * - ݽٚ ҕѐ ٜ٘ب উ ࢚కৈঠ פ. * * 3о ઑѤਸ ٮܲݶ ஹನૉח ೧ ٘о উੋ ࢚కۄҊ ঈೞҊ, * ೧ ٘ ч ߄Շ ঋওݶ ࢎਊػ ஹನ࠶ ܻஹನ࣌ਸ ࢤۚפ. * * @see Immutable * @see Stable */ @Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) annotation class StableMarker -
/** * ࢤࢿػ റ۽ ݽٚ ҕѐੋ ٘о ߸ೞ ঋחח
Ѫਸ աఋղݴ ېझী ਊؼ ࣻ णפ. * * ࢤࢿ റ ч ߸҃غ ঋਵ۽ উ ߣ૩ ӏੋ * "ч ߸҃عਸ ҃ ஹನ࠶ীѱ ঌ۰ઉঠ פ." о ޖदؾפ. */ @Target(AnnotationTarget.CLASS) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Immutable fun main() { val list = mutableListOf(1) list.add(2) // ੋझఢझח زੌೞ݅ ч ߄Չ ࣻ णפ. // @Immutable ۞ೠ ߸҃ب ೲਊೞ ঋणפ. } -
/** * ч ߸҃ؼ ࣻ ח ࢚కݴ, ਊ ࢚ী ٮۄ
ডрঀ ೡ ׳ۄפ. * * ఋੑী ਊػݶ ୶о ೡ হ [StableMarker] ೡਸ Ӓ۽ оઉцפ. * * ೣࣻա ۽ಌ౭ী ਊػݶ [StableMarker] ೡী ୶оغח ೡ ࢤӤפ. * э input ী যࢲח ೦࢚ زੌೠ output ਸ ٜ݅যղݴ(ࣽࣻ ೣࣻ), ೣࣻ ҃ ੋٜ द ݽف উੋ ࢚కۄח Ѫਸ ডࣘפ. * ݅ড input زੌೞݶ output ژೠ زੌೡ Ѫӝ ٸޙী ܻஹನ࣌ਸ झఈೞѱ ؾפ. */ @Target( AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY ) @Retention(AnnotationRetention.BINARY) @StableMarker annotation class Stable -
// উࢿ ػ. @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()) } -
// উࢿ ػ. 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()) } -
public interface MutableList<E> : List<E>, MutableCollection<E> @Composable fun ListDisplay(texts: List<String>
= mutableListOf()) { Text(text = texts.joinToString()) } -
@Immutable class ImmutableListWrapper(val value: List<String>) @Composable fun ListDisplay(texts: ImmutableListWrapper) {
Text(text = texts.value.joinToString()) } -
/** * ஹੌ दী ݽٚ ېझٜ উࢿਸ زਵ۽ ୶ۿפ. *
* @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) ېझউࢿ୶ۿ j j j j -
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<String>? = 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 } } -
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<String>? = 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 } } -
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<String>? = 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 } } -
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<String>? = 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 } } -
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<String>? = 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 } } -
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<String>? = 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 } } -
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<String>? = 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 } } -
private val liveLiteralCache = HashMap<String, MutableState<Any?>>() @InternalComposeApi @ComposeCompilerApi fun <T>
liveLiteral(key: String, value: T): State<T> { return liveLiteralCache.getOrPut(key) { mutableStateOf(value) } as State<T> } -
@InternalComposeApi fun updateLiveLiteralValue(key: String, value: Any?) { var needToUpdate =
true val stateObj = liveLiteralCache.getOrPut(key) { needToUpdate = false mutableStateOf(value) } if (needToUpdate) { stateObj.value = value } } -
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 } -
// @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<String>? = 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 } } -
/** * 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") } -
$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) } } -
@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 -
@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 -
// 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 -
🎨 🖼️ 🖌️ 🧰 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 -
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContent { Text(text = "SungbinLand") } } } -
// ஹನૉ ۠ఋҗ UI োѾ fun ComponentActivity.setContent( parent: CompositionContext?
= null, content: @Composable () -> Unit ) { ComposeView(this).apply { setParentCompositionContext(parent) setContent(content) setOwners() setContentView(this, DefaultActivityContentLayoutParams) } } -
// ComposeView#setContent ۽ ೣࣻо प೯ؽ private fun ensureCompositionCreated() {
setContent(/* Recomposer о ٜ݅যҊ ੑؾפ */) { content() } } -
/** * ܻஹನ࣌ਸ ࣻ೯ೞҊ ೞա ࢚ ஹನ࠶ী সؘܳ ਊೞӝ ਤೠ
झா۞ ੑפ. */ class Recomposer( effectCoroutineContext: CoroutineContext ) : CompositionContext() /** * Handler ߔ ژח Choreographer গפݫ࣌ ۨ ױ҅ * ݢ بېೞח ױ҅ীࢲ ٣झಁܳ ࣻ೯ೞח CoroutineDispatcher ੑפ. */ class AndroidUiDispatcher private constructor( val choreographer: Choreographer, private val handler: Handler ) : CoroutineDispatcher() -
/** * ܻஹನ࣌ਸ ࣻ೯ೞҊ ೞա ࢚ ஹನ࠶ী সؘܳ ਊೞӝ ਤೠ
झா۞ ੑפ. */ class Recomposer( effectCoroutineContext: CoroutineContext ) : CompositionContext() /** * Handler ߔ ژח Choreographer গפݫ࣌ ۨ ױ҅ * ݢ بېೞח ױ҅ীࢲ ٣झಁܳ ࣻ೯ೞח CoroutineDispatcher ੑפ. */ class AndroidUiDispatcher private constructor( val choreographer: Choreographer, private val handler: Handler ) : CoroutineDispatcher() -
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> val toRecompose
= mutableListOf<RecomposeScope>() while (shouldKeepRecomposing) { awaitWorkAvailable() parentFrameClock.withFrameNanos { frameTime -> try { toRecompose.fastForEach { composition -> performRecompose(composition) } } finally { toRecompose.clear() } } } } -
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) } } } -
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) } } } -
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> val toRecompose
= mutableListOf<RecomposeScope>() // ܻஹನ࣌ ਃೠ RecomposeScope о ӟ. (ח җ ࢤۚ) while (shouldKeepRecomposing) { // shouldKeepRecomposing: Recomposer о ܻஹನ࣌ ਃਸ х೧ঠ ೞחܳ աఋմ. awaitWorkAvailable() // ࢜۽ ܻஹನ࣌ ਃ ਸ ٸ ө suspend ࢚క۽ ӝܽ. parentFrameClock.withFrameNanos { frameTime -> // Choreographer#postFrameCallback ਸ ഐೠ. try { toRecompose.fastForEach { composition -> // RecomposeScope ܳ ࣽഥೞݴ performRecompose(composition) // ܻஹನ࣌ ೯! } } finally { toRecompose.clear() } } } } -
private suspend fun awaitWorkAvailable() { suspendCancellableCoroutine { co -> if
(hasSchedulingWork) { // ӝੋ ܻஹನ࣌ ਃ ݶ ߄۽ resume ೞҊ co.resume(Unit) } else { // ӝੋ ܻஹನ࣌ ਃ হݶ workContinuation ਵ۽ CancellableContinuation റ suspend ਬ workContinuation = co } } } -
private fun deriveStateLocked(): CancellableContinuation<Unit>? { // ࠁܨ ੋ স: ܻஹನ࣌
ਃ ١١ ஹನૉ ۠ఋ ղࠗীࢲ ೯غח ৈ۞ স ਃ val newState = when { /* അ ࠁܨ ੋ স ৈࠗী ٮۄ ࢜۽ ࢚క ҅ */ } return if (newState == State.PendingWork) { // ࠁܨ ੋ স ݶ workContinuation.also { // workContinuation ߈ജ ߂ workContinuation ୡӝച workContinuation = null } } else null } -
private suspend fun recompositionRunner(block: suspend CoroutineScope.(parentFrameClock: MonotonicFrameClock) -> Unit) {
val parentFrameClock = coroutineContext.monotonicFrameClock withContext(broadcastFrameClock) { Snapshot.registerApplyObserver { _, _ -> deriveStateLocked()?.resume(Unit) } coroutineScope { block(parentFrameClock) } } } -
suspend fun runRecomposeAndApplyChanges() = recompositionRunner { parentFrameClock -> val toRecompose
= mutableListOf<RecomposeScope>() 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) } } } -
// ComposeView#setContent ۽ ೣࣻо प೯ؽ private fun ensureCompositionCreated() {
setContent(/* Recomposer о ٜ݅যҊ ੑؾפ */) { content() } } -
// 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<Composer, Int, Unit> realFn(composer, 1) } Root Group Composable Gap Gap Gap Gap Gap Gap -
CVJMENFUSJDT // ஹੌ۞о хೠ ஹನ࠶ ೣٜࣻ ࠁܳ աఋմ. kotlinOptions {
freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" + "${rootProject.file(".").absolutePath}/report/compose-metrics" ] } -
<ݽٕݺ>@<࠽٘ఋੑ>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 } -
CVJMESFQPSUT // ஹੌ۞о хೠ ஹನ࠶ ೣٜࣻ ࢎਊੋ ېझ৬ ੋী ೠ
উࢿ ࠁܳ աఋմ. kotlinOptions { freeCompilerArgs += [ "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" + "${rootProject.file(".").absolutePath}/report/compose-reports" ] } -
<ݽٕݺ>@<࠽٘ఋੑ>DMBTTFTUYU /* ——— classes ——— */ sealed class UiState {
object Loading : UiState() object Done : UiState() data class Exception(val throwable: Throwable) : UiState() } /* ——— reports ——— */ stable class Loading { <runtime stability> = Stable } stable class Done { <runtime stability> = Stable } unstable class Exception { unstable val throwable: Throwable <runtime stability> = Unstable } -
<ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTDTW /* ——— composables ——— */ @Composable fun DisplayText(text: String
= "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { Text(text = texts.joinToString()) } /* ——— reports ——— */ -
<ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @Composable fun DisplayText(text: String
= "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { 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<String> ) -
<ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @Composable fun DisplayText(text: String
= "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { 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<String> ) -
<ݽٕݺ>@<࠽٘ఋੑ>DPNQPTBCMFTUYU /* ——— composables ——— */ @NoLiveLiterals // new @Composable
fun DisplayText(text: String = "Hi") { Text(text = text) } @Composable fun DisplayTexts(texts: List<String>) { 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<String> ) -
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] setContent recomposition Text recomposition [re-composition] setContent 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] setContent recomposition Text recomposition Button recomposition [re-composition] Text recomposition റ زੌ ۽Ӓ ߈ࠂ */ -
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() } -
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 -
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) } -
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<String> ) { Text(modifier = modifier, text = text()) } -
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() } } } -
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() } } } -
!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) } -
!/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 ) } -
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") } -
#BTFMJOFQSPGJMFT .BDSPCFODINBSLݽٕࢸ ٘ࢿ CBTFMJOFQSPGJMFTࢤࢿ @ExperimentalBaselineProfilesApi @RunWith(AndroidJUnit4::class) class BaselineProfileGenerator {
@get:Rule val baselineProfileRule = BaselineProfileRule() @Test fun startup() = baselineProfileRule.collectBaselineProfile( packageName = AppPackageName ) { pressHome() startActivityAndWait() } } -
#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() } } } -
#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 ஹੌ } // … } -
#BTFMJOFQSPGJMFT ܖӂೠഝࢿച 3VO adb pull CBTFMJOFQSPGUYUਵ۽ੌݺ߸҃റTSDNBJOਵ۽ز ਊറ࠺Ү ਊೡݽٕীઓࢿ୶о dependencies {
implementation("androidx.profileinstaller:profileinstaller:1.2.0-beta01") } -
хࢎפ ஹನૉ ղࠗ ٜ٘ cs.android.com ীࢲ оઉ৳णפ. ۨઃప࣌ীח షझীࢲ
ઁҕೠ షझಕझо ਊغয णפ. QnA🙋