[Android] Performance Testing XML vs Programmatically vs Jetpack Compose

๐Ÿ’ก UI ์ž‘์—…์„ XML๊ณผ Code, Jetpack Compose ์ง„ํ–‰ํ–ˆ์„ ๋•Œ์˜ ์ฐจ์ด๋ฅผ ๋น„๊ตํ•˜๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

 

๊ฐœ์š”

  • XML๊ณผ Programmatically ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ฐจ์ด์ ์ด ๊ถ๊ธˆํ•˜๋˜ ์ค‘ ์ง์ ‘ ํ…Œ์ŠคํŠธํ•œ ๋ธ”๋กœ๊ทธ๋ฅผ ๋ฐœ๊ฒฌํ•˜์—ฌ ํ•™์Šตํ•˜๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

Performance comparison: building Android Layout with XML vs By Code vs Jetpack Compose

 

Performance comparison: building Android Layout with XML vs By Code vs Jetpack Compose

The time required to create the activity layout in XML vs by code programmatically vs jetpack compose

mustafa-basim.medium.com

  • ์œ„ ํฌ์ŠคํŒ…์—์„œ๋Š” XML๋กœ UI๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ๊ณผ Kotlin Code ์ƒ์—์„œ UI๋ฅผ ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์˜ ์ฐจ์ด์ ์„ ํŒŒ์•…ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ Jetpack Compose์˜ ๋“ฑ์žฅ์œผ๋กœ 3๊ฐ€์ง€ ๊ฒฝ์šฐ์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค !
  • ์œ„ ํฌ์ŠคํŒ…์€ 2020๋…„ ๊ฒŒ์‹œ๋ฌผ๋กœ, Jetpack Compose๊ฐ€ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ์—ˆ์œผ๋ฏ€๋กœ ํ˜„์žฌ๋Š” Jetpack Compose์˜ ํ…Œ์ŠคํŠธ ์‹œ๊ฐ„์ด ๋”์šฑ ๋‹จ์ถ•๋˜์—ˆ์„ ๊ฐ€๋Šฅ์„ฑ์ด ํฝ๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์„ฑ๋Šฅ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

๋น„๊ต ๋Œ€์ƒ

XML ๋ ˆ์ด์•„์›ƒ

  • ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ, UI ์š”์†Œ๋ฅผ XML ํŒŒ์ผ์—์„œ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me" />

</LinearLayout>

์ฝ”๋“œ๋กœ UI ์ž‘์„ฑ

  • ์ฝ”ํ‹€๋ฆฐ ์ฝ”๋“œ์—์„œ View ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๊ณ  ๋ ˆ์ด์•„์›ƒ์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
val linearLayout = LinearLayout(this).apply {
    orientation = LinearLayout.VERTICAL
    layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.MATCH_PARENT,
        LinearLayout.LayoutParams.MATCH_PARENT
    )
}

val textView = TextView(this).apply {
    text = "Hello World!"
    layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT
    )
}

val button = Button(this).apply {
    text = "Click Me"
    layoutParams = LinearLayout.LayoutParams(
        LinearLayout.LayoutParams.WRAP_CONTENT,
        LinearLayout.LayoutParams.WRAP_CONTENT
    )
}

linearLayout.addView(textView)
linearLayout.addView(button)
setContentView(linearLayout)

Jetpack Compose

  • ์„ ์–ธ์  ๋ฐฉ์‹์œผ๋กœ Composable Function์œผ๋กœ UI๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
@Composable
fun Greeting() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        Text(text = "Hello World!")
        Button(onClick = { /* Do something */ }) {
            Text("Click Me")
        }
    }
}

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Greeting()
        }
    }
}

ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

  • ์ด 2๊ฐ€์ง€ ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์•ฑ์˜ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„

  • ๊ฐ ์•ฑ์„ ์‹คํ–‰ํ•˜๊ณ  ์ฒซ ํ™”๋ฉด์ด ๋กœ๋“œ๋˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค.

๋ ˆ์ด์•„์›ƒ ๋ Œ๋”๋ง ์‹œ๊ฐ„

  • UI๋ฅผ ๋ Œ๋”๋ง ํ•˜๋Š” ๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„์„ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ

  • Android Studio์—์„œ ๋™์ผํ•œ ๊ธฐ๊ธฐ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Compose ๋ฒ„์ „์„ ์ตœ์‹  ๋ฒ„์ „์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Wi-Fi๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด CPU๊ฐ€ ๋Š๋ ค์ง€๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ๋ชจ๋“  Wi-Fi์™€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์„œ๋น„์Šค๋ฅผ ๋„๊ณ  ํ…Œ์ŠคํŠธ๋ฅผ ์ง„ํ–‰ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฐ˜๋ณต ํ…Œ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ ๋ฐฉ์‹๋ณ„๋กœ ์—ฌ๋Ÿฌ ๋ณ€ ๋ฐ˜๋ณตํ•ด์„œ ํ‰๊ท  ๊ฐ’์„ ๋„์ถœํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Logcat์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ ์•ฑ์˜ ์‹œ์ž‘๊ณผ onResume()์—์„œ ์‹œ๊ฐ„์„ ์ธก์ •ํ•ฉ๋‹ˆ๋‹ค.
override fun onResume() {
    super.onResume()
    val end = System.currentTimeMillis()
    val result = end - start
}

ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ

  • ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ€๋ฆฌ์ดˆ(ms) ๋‹จ์œ„๋กœ ์ธก์ •ํ•˜์—ฌ ์ •๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.
๋ฐฉ๋ฒ• ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„(ms) ๋ ˆ์ด์•„์›ƒ ๋ Œ๋”๋ง ์‹œ๊ฐ„(ms)
XML ๋ ˆ์•„์ด์›ƒ ํ‰๊ท  ์•ฝ 250ms ํ‰๊ท  ์•ฝ 300ms
์ฝ”๋“œ ์ž‘์„ฑ ํ‰๊ท  ์•ฝ 220ms ํ‰๊ท  ์•ฝ 280ms
Jetpack Compose ํ‰๊ท  ์•ฝ 300ms ํ‰๊ท  ์•ฝ 350ms

์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„

  • ์ฝ”๋“œ๋กœ UI๋ฅผ ์ž‘์„ฑํ•œ ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๋น ๋ฅธ ๋กœ๋”ฉ ์‹œ๊ฐ„์„ ๋ณด์˜€์Šต๋‹ˆ๋‹ค.
  • XML ๋ ˆ์ด์•„์›ƒ์ด ๊ทธ ๋’ค๋ฅผ ๋”ฐ๋ฅด๊ณ , Jetpack Compose๊ฐ€ ๊ฐ€์žฅ ๋Š๋ ธ์Šต๋‹ˆ๋‹ค.
  • Jetpack Compose์˜ ๊ฒฝ์šฐ ์„ ์–ธ์  ๋ฐฉ์‹๊ณผ ๋งŽ์€ ๋ฆฌ์†Œ์Šค๋ฅผ ์š”๊ตฌํ•˜๋Š” ์ดˆ๊ธฐ ์„ค์ •์œผ๋กœ ์ธํ•ด ๋กœ๋”ฉ ์‹œ๊ฐ„์ด ๊ธธ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ ˆ์ด์•„์›ƒ ๋ Œ๋”๋ง ์‹œ๊ฐ„

  • ์ฝ”๋“œ๋ฅผ UI๋กœ ์ž‘์„ฑํ•œ ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๋น ๋ฅด๊ฒŒ ๋ Œ๋”๋ง ๋˜์—ˆ์œผ๋ฉฐ, XML๋ณด๋‹ค ์กฐ๊ธˆ ๋” ๋น ๋ฅธ ๋ Œ๋”๋ง์„ ๋ณด์—ฌ์คฌ์Šต๋‹ˆ๋‹ค.
  • Jetpack Compose๋Š” ํ˜„์žฌ ๋ Œ๋”๋ง ์‹œ๊ฐ„์ด ๊ฐ€์žฅ ๊ธธ์—ˆ์ง€๋งŒ ์ตœ์‹  ๋ฒ„์ „์—์„œ๋Š” ๋งŽ์€ ์ตœ์ ํ™”๊ฐ€ ์ด๋ฃจ์–ด์ง€๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์•„ ํŠน์ • ์ƒํ™ฉ์—์„œ๋Š” ๋น ๋ฅธ ๋ Œ๋”๋ง์„ ๋ณด์ธ ๊ฒฝ์šฐ๋„ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

  • Jetpack Compose๋Š” ์ดˆ๊ธฐ ๋กœ๋”ฉ ์‹œ๊ฐ„๊ณผ ๋ Œ๋”๋ง ์‹œ๊ฐ„์ด ๊ธธ์ง€๋งŒ ์„ ์–ธ์  UI ์ž‘์„ฑ์˜ ์žฅ์ ๊ณผ ๋ณต์žกํ•œ UI๋ฅผ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ์žฅ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
    • ๋˜ํ•œ ์ตœ์‹  ๋ฒ„์ „์ผ ์ˆ˜๋ก ์ตœ์ ํ™”๊ฐ€ ์ด๋ฃจ์–ด์ง€๋ฏ€๋กœ ์ ์  ์žฅ์ ์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ฝ”๋“œ ์ž‘์„ฑ ๋ฐฉ์‹์ด XML ๋ ˆ์ด์•„์›ƒ ๋ณด๋‹ค ๋น ๋ฅธ ๋กœ๋”ฉ๊ณผ ๋ Œ๋”๋ง ์‹œ๊ฐ„์„ ๋ณด์—ฌ์ฃผ์—ˆ์ง€๋งŒ ๊ทธ ์ฐจ์ด๋Š” ์ž‘์œผ๋ฉฐ, ์ฝ”๋“œ ์ž‘์„ฑ ๋ฐฉ์‹์ด ๋” ๋ณต์žกํ•œ ๊ฒƒ์„ ๊ณ ๋ คํ•˜๋ฉด XML ๋ ˆ์ด์•„์›ƒ ๋ฐฉ์‹์ด ๋” ์ข‹์€ ๋ฐฉ๋ฒ•์ด ๋  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ”„๋กœ์ ํŠธ์˜ ํ•„์š”์— ๋”ฐ๋ผ ์ตœ์ ์˜ ์„ ํƒ์„ ์ง„ํ–‰ํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค !!