Skip to content

hundong2/AndroidStudy

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1 Commit
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

TsetApplication - Android ์ดˆ๋ณด์ž ๊ฐ€์ด๋“œ

Android ๊ฐœ๋ฐœ์„ ์ฒ˜์Œ ์‹œ์ž‘ํ•˜๋Š” ๋ถ„๋“ค์„ ์œ„ํ•œ ์ƒ์„ธํ•œ ๊ฐ€์ด๋“œ์ž…๋‹ˆ๋‹ค.

๋ชฉ์ฐจ

  1. ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ
  2. ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ
  3. ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋ณธ ์ง€์‹
  4. Android ์•ฑ ์‹คํ–‰ ์›๋ฆฌ
  5. UI์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ธฐ์ˆ 
  6. ์ฝ”๋“œ ์ƒ์„ธ ๋ถ„์„
  7. ์•ฑ ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ ๋ฐฉ๋ฒ•

ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

์ด ํ”„๋กœ์ ํŠธ๋Š” Android ๋ฉ€ํ‹ฐ ๋ชจ๋“ˆ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ž…๋‹ˆ๋‹ค. ์Šค๋งˆํŠธํฐ๊ณผ ์ž๋™์ฐจ(Android Automotive) ๋ชจ๋‘์—์„œ ์ž‘๋™ํ•˜๋Š” ์Œ์•… ํ”Œ๋ ˆ์ด์–ด ์•ฑ์˜ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

ํ”„๋กœ์ ํŠธ ์ •๋ณด

  • ์–ธ์–ด: Kotlin (์ฝ”ํ‹€๋ฆฐ) 2.0.21
  • ์ตœ์†Œ SDK: Android 9.0 (API 28)
  • ๋Œ€์ƒ SDK: Android 36
  • ๋นŒ๋“œ ๋„๊ตฌ: Gradle 8.13
  • UI ํ”„๋ ˆ์ž„์›Œํฌ: Material Design 3

๋ชจ๋“ˆ ๊ตฌ์„ฑ

  1. mobile: ์Šค๋งˆํŠธํฐ์šฉ ์•ฑ
  2. automotive: ์ž๋™์ฐจ์šฉ ์•ฑ
  3. shared: ๋‘ ์•ฑ์ด ๊ณต์œ ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

ํ”„๋กœ์ ํŠธ ํด๋” ๊ตฌ์กฐ

์ „์ฒด ๊ตฌ์กฐ ๊ฐœ์š”

TsetApplication/
โ”œโ”€โ”€ mobile/                    # ๐Ÿ“ฑ ์Šค๋งˆํŠธํฐ ์•ฑ ๋ชจ๋“ˆ
โ”œโ”€โ”€ automotive/                # ๐Ÿš— ์ž๋™์ฐจ ์•ฑ ๋ชจ๋“ˆ
โ”œโ”€โ”€ shared/                    # ๐Ÿ“ฆ ๊ณต์œ  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
โ”œโ”€โ”€ gradle/                    # โš™๏ธ Gradle ๋นŒ๋“œ ์„ค์ •
โ”œโ”€โ”€ build.gradle.kts           # ๐Ÿ”ง ๋ฃจํŠธ ๋นŒ๋“œ ์„ค์ •
โ”œโ”€โ”€ settings.gradle.kts        # ๐Ÿ”ง ํ”„๋กœ์ ํŠธ ์„ค์ •
โ””โ”€โ”€ README.md                  # ๐Ÿ“– ์ด ๋ฌธ์„œ

1. mobile ๋ชจ๋“ˆ ์ƒ์„ธ ๊ตฌ์กฐ

mobile/
โ”œโ”€โ”€ build.gradle.kts                          # ๋ชจ๋“ˆ ๋นŒ๋“œ ์„ค์ •
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ main/                                 # ๋ฉ”์ธ ์†Œ์Šค ์ฝ”๋“œ
โ”‚   โ”‚   โ”œโ”€โ”€ java/com/example/tsetapplication/ # Kotlin ์ฝ”๋“œ
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ MainActivity.kt               # ๋ฉ”์ธ ํ™”๋ฉด ์•กํ‹ฐ๋น„ํ‹ฐ
โ”‚   โ”‚   โ”œโ”€โ”€ res/                              # ๋ฆฌ์†Œ์Šค ํŒŒ์ผ๋“ค
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ layout/                       # ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ activity_main.xml         # ๋ฉ”์ธ ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ drawable/                     # ์ด๋ฏธ์ง€, ๋„ํ˜• ๋“ฑ
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ mipmap-*/                     # ์•ฑ ์•„์ด์ฝ˜ (ํ™”์งˆ๋ณ„)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ values/                       # ๊ฐ’ ๋ฆฌ์†Œ์Šค
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ colors.xml                # ์ƒ‰์ƒ ์ •์˜
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ strings.xml               # ๋ฌธ์ž์—ด ์ •์˜
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ themes.xml                # ํ…Œ๋งˆ ์ •์˜
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ xml/                          # ๊ธฐํƒ€ XML ์„ค์ •
โ”‚   โ”‚   โ””โ”€โ”€ AndroidManifest.xml               # ์•ฑ ์„ค์ • ํŒŒ์ผ
โ”‚   โ”œโ”€โ”€ test/                                 # ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
โ”‚   โ””โ”€โ”€ androidTest/                          # UI ํ…Œ์ŠคํŠธ
โ””โ”€โ”€ proguard-rules.pro                        # ์ฝ”๋“œ ๋‚œ๋…ํ™” ๊ทœ์น™

์ฃผ์š” ํด๋” ์„ค๋ช…

src/main/java/ - ์ฝ”ํ‹€๋ฆฐ ์†Œ์Šค ์ฝ”๋“œ
  • ์•ฑ์˜ ์‹ค์ œ ๋กœ์ง์ด ์ž‘์„ฑ๋˜๋Š” ๊ณณ
  • ํŒจํ‚ค์ง€๋ช… ํ˜•์‹: com.example.tsetapplication
  • ๋ชจ๋“  .kt ํŒŒ์ผ(์ฝ”ํ‹€๋ฆฐ ํŒŒ์ผ)์ด ์—ฌ๊ธฐ์— ์œ„์น˜
src/main/res/ - ๋ฆฌ์†Œ์Šค ํด๋”
  • layout/: XML๋กœ ์ž‘์„ฑ๋œ ํ™”๋ฉด ๋ ˆ์ด์•„์›ƒ
  • drawable/: ์ด๋ฏธ์ง€, ๋ฒกํ„ฐ ๊ทธ๋ž˜ํ”ฝ, ๋„ํ˜• ๋“ฑ
  • mipmap-*/: ์•ฑ ์•„์ด์ฝ˜ (hdpi, xhdpi ๋“ฑ ํ™”์งˆ๋ณ„ ๋ถ„๋ฅ˜)
  • values/: ์ƒ‰์ƒ, ๋ฌธ์ž์—ด, ์Šคํƒ€์ผ ๋“ฑ์˜ ๊ฐ’ ์ •์˜
  • xml/: ๋ฐฑ์—… ๊ทœ์น™, ๊ธฐํƒ€ ์„ค์ • ํŒŒ์ผ
AndroidManifest.xml - ์•ฑ ๋งค๋‹ˆํŽ˜์ŠคํŠธ
  • ์•ฑ์˜ ๊ธฐ๋ณธ ์ •๋ณด๋ฅผ ์„ ์–ธํ•˜๋Š” ํ•„์ˆ˜ ํŒŒ์ผ
  • ์•ฑ ์ด๋ฆ„, ์•„์ด์ฝ˜, ๊ถŒํ•œ, ์•กํ‹ฐ๋น„ํ‹ฐ ๋“ฑ์„ ์ •์˜

2. shared ๋ชจ๋“ˆ ์ƒ์„ธ ๊ตฌ์กฐ

shared/
โ”œโ”€โ”€ build.gradle                              # ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋นŒ๋“œ ์„ค์ •
โ”œโ”€โ”€ src/
โ”‚   โ””โ”€โ”€ main/
โ”‚       โ”œโ”€โ”€ java/com/example/tsetapplication/shared/
โ”‚       โ”‚   โ””โ”€โ”€ MyMusicService.kt             # ์Œ์•… ์„œ๋น„์Šค
โ”‚       โ”œโ”€โ”€ res/
โ”‚       โ”‚   โ””โ”€โ”€ xml/
โ”‚       โ”‚       โ””โ”€โ”€ automotive_app_desc.xml   # Automotive ์„ค์ •
โ”‚       โ””โ”€โ”€ AndroidManifest.xml               # ์„œ๋น„์Šค ์„ ์–ธ

3. Gradle ์„ค์ • ํŒŒ์ผ

build.gradle.kts (๋ฃจํŠธ)

  • ์ „์ฒด ํ”„๋กœ์ ํŠธ์˜ ๊ณตํ†ต ํ”Œ๋Ÿฌ๊ทธ์ธ ์ •์˜
  • ๋ชจ๋“  ๋ชจ๋“ˆ์— ์ ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ์„ค์ •

settings.gradle.kts

  • ํ”„๋กœ์ ํŠธ์— ํฌํ•จ๋  ๋ชจ๋“ˆ ์„ ์–ธ
  • ์ €์žฅ์†Œ(Repository) ์„ค์ •

gradle.properties

  • Gradle ๋นŒ๋“œ ์„ฑ๋Šฅ ์„ค์ •
  • JVM ๋ฉ”๋ชจ๋ฆฌ ์˜ต์…˜ ๋“ฑ

gradle/libs.versions.toml

  • ์˜์กด์„ฑ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ฒ„์ „ ๊ด€๋ฆฌ
  • ์ค‘์•™ ์ง‘์ค‘์‹ ๋ฒ„์ „ ๊ด€๋ฆฌ ํŒŒ์ผ

์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋ณธ ์ง€์‹

1. ์ฝ”ํ‹€๋ฆฐ์ด๋ž€?

์ฝ”ํ‹€๋ฆฐ(Kotlin)์€ JetBrains๊ฐ€ ๊ฐœ๋ฐœํ•œ ํ˜„๋Œ€์ ์ธ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ, Google์ด Android ๊ณต์‹ ์–ธ์–ด๋กœ ์ฑ„ํƒํ–ˆ์Šต๋‹ˆ๋‹ค.

์ฝ”ํ‹€๋ฆฐ์˜ ํŠน์ง•

  • โœ… ๊ฐ„๊ฒฐํ•จ: Java๋ณด๋‹ค ์ ์€ ์ฝ”๋“œ๋กœ ๊ฐ™์€ ๊ธฐ๋Šฅ ๊ตฌํ˜„
  • โœ… ์•ˆ์ „์„ฑ: Null ์•ˆ์ „์„ฑ์œผ๋กœ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฅ˜ ๊ฐ์†Œ
  • โœ… ์ƒํ˜ธ์šด์šฉ์„ฑ: Java ์ฝ”๋“œ์™€ 100% ํ˜ธํ™˜
  • โœ… ํ˜„๋Œ€์ : ํ•จ์ˆ˜ํ˜• ํ”„๋กœ๊ทธ๋ž˜๋ฐ, ์ฝ”๋ฃจํ‹ด ๋“ฑ ์ง€์›

2. ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋ณธ ๋ฌธ๋ฒ•

๋ณ€์ˆ˜ ์„ ์–ธ

// val: ๋ถˆ๋ณ€ ๋ณ€์ˆ˜ (๊ฐ’ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€, Java์˜ final)
val name = "Android"
val count: Int = 10

// var: ๊ฐ€๋ณ€ ๋ณ€์ˆ˜ (๊ฐ’ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)
var score = 0
score = 100  // OK

ํ•จ์ˆ˜ ์„ ์–ธ

// ๊ธฐ๋ณธ ํ˜•ํƒœ
fun greet(name: String): String {
    return "Hello, $name!"
}

// ๋‹จ์ผ ํ‘œํ˜„์‹ ํ•จ์ˆ˜
fun add(a: Int, b: Int) = a + b

// ๋ฐ˜ํ™˜๊ฐ’ ์—†๋Š” ํ•จ์ˆ˜
fun printMessage(msg: String): Unit {  // Unit์€ ์ƒ๋žต ๊ฐ€๋Šฅ
    println(msg)
}

ํด๋ž˜์Šค ์„ ์–ธ

// ๊ธฐ๋ณธ ํด๋ž˜์Šค
class Person {
    var name: String = ""
    var age: Int = 0
}

// ์ƒ์„ฑ์ž๊ฐ€ ์žˆ๋Š” ํด๋ž˜์Šค
class Student(val name: String, var grade: Int) {
    fun study() {
        println("$name is studying")
    }
}

// ํด๋ž˜์Šค ์‚ฌ์šฉ
val student = Student("John", 10)
student.study()  // "John is studying"

์ƒ์† (Inheritance)

// ๋ถ€๋ชจ ํด๋ž˜์Šค (open ํ‚ค์›Œ๋“œ ํ•„์š”)
open class Animal {
    open fun sound() {
        println("Some sound")
    }
}

// ์ž์‹ ํด๋ž˜์Šค
class Dog : Animal() {
    override fun sound() {
        println("Bark!")
    }
}

Null ์•ˆ์ „์„ฑ

// Nullable ํƒ€์ž… (? ๋ถ™์ž„)
var name: String? = null
name = "Kotlin"

// Non-null ํƒ€์ž… (? ์—†์Œ)
var age: Int = 10
// age = null  // ์ปดํŒŒ์ผ ์˜ค๋ฅ˜!

// ์•ˆ์ „ํ•œ ํ˜ธ์ถœ
val length = name?.length  // name์ด null์ด๋ฉด length๋„ null

// Elvis ์—ฐ์‚ฐ์ž
val len = name?.length ?: 0  // name์ด null์ด๋ฉด 0 ๋ฐ˜ํ™˜

3. Android์—์„œ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์ฝ”ํ‹€๋ฆฐ ๊ธฐ๋Šฅ

๋žŒ๋‹ค ํ‘œํ˜„์‹

// ๊ธฐ๋ณธ ํ˜•ํƒœ
val sum = { a: Int, b: Int -> a + b }
println(sum(3, 5))  // 8

// ๋ฆฌ์Šค๋„ˆ์—์„œ ์ž์ฃผ ์‚ฌ์šฉ
button.setOnClickListener { view ->
    // ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰
    println("Button clicked")
}

// ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ํ•˜๋‚˜์ผ ๋•Œ it ์‚ฌ์šฉ
items.forEach { item ->
    println(item)
}

// ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ
items.forEach {
    println(it)  // it์€ ํ˜„์žฌ item
}

apply, let, run, with ๋“ฑ์˜ ์Šค์ฝ”ํ”„ ํ•จ์ˆ˜

// apply: ๊ฐ์ฒด ์„ค์ •์— ์œ ์šฉ
val person = Person().apply {
    name = "John"
    age = 25
}

// let: null ์ฒดํฌ์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ
name?.let {
    println("Name is $it")
}

// with: ๊ฐ์ฒด์˜ ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
with(person) {
    println(name)
    println(age)
}

Android ์•ฑ ์‹คํ–‰ ์›๋ฆฌ

1. Android ์•ฑ์˜ 4๋Œ€ ์ปดํฌ๋„ŒํŠธ

Android ์•ฑ์€ 4๊ฐ€์ง€ ์ฃผ์š” ๊ตฌ์„ฑ ์š”์†Œ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค:

1๏ธโƒฃ Activity (์•กํ‹ฐ๋น„ํ‹ฐ)

  • ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค ํ™”๋ฉด์„ ๋‹ด๋‹น
  • ํ•˜๋‚˜์˜ Activity = ํ•˜๋‚˜์˜ ํ™”๋ฉด
  • ์˜ˆ: ๋กœ๊ทธ์ธ ํ™”๋ฉด, ๋ฉ”์ธ ํ™”๋ฉด, ์„ค์ • ํ™”๋ฉด ๋“ฑ

2๏ธโƒฃ Service (์„œ๋น„์Šค)

  • ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…์„ ๋‹ด๋‹น
  • ํ™”๋ฉด ์—†์ด ์‹คํ–‰
  • ์˜ˆ: ์Œ์•… ์žฌ์ƒ, ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ๋“ฑ

3๏ธโƒฃ Broadcast Receiver (๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฆฌ์‹œ๋ฒ„)

  • ์‹œ์Šคํ…œ ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹ 
  • ์˜ˆ: ๋ฐฐํ„ฐ๋ฆฌ ๋ถ€์กฑ ์•Œ๋ฆผ, ๋„คํŠธ์›Œํฌ ๋ณ€๊ฒฝ ๋“ฑ

4๏ธโƒฃ Content Provider (์ปจํ…์ธ  ํ”„๋กœ๋ฐ”์ด๋”)

  • ์•ฑ ๊ฐ„ ๋ฐ์ดํ„ฐ ๊ณต์œ 
  • ์˜ˆ: ์—ฐ๋ฝ์ฒ˜, ๊ฐค๋Ÿฌ๋ฆฌ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ

2. ์•ฑ ์‹คํ–‰ ํ๋ฆ„

์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ ์•„์ด์ฝ˜ ํ„ฐ์น˜
    โ†“
Android ์‹œ์Šคํ…œ์ด ์•ฑ ํ”„๋กœ์„ธ์Šค ์‹œ์ž‘
    โ†“
Application ํด๋ž˜์Šค ์ƒ์„ฑ (์•ฑ ์ „์ฒด ์ดˆ๊ธฐํ™”)
    โ†“
AndroidManifest.xml ์ฝ๊ธฐ
    โ†“
MAIN/LAUNCHER ์•กํ‹ฐ๋น„ํ‹ฐ ์ฐพ๊ธฐ
    โ†“
MainActivity ์ƒ์„ฑ ๋ฐ onCreate() ํ˜ธ์ถœ
    โ†“
๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ(activity_main.xml) ๋กœ๋“œ
    โ†“
ํ™”๋ฉด์— UI ํ‘œ์‹œ
    โ†“
์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋Œ€๊ธฐ

3. Activity ์ƒ๋ช…์ฃผ๊ธฐ (Lifecycle)

Activity๋Š” ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ ๋‹จ๊ณ„๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๋Š” ์ฝœ๋ฐฑ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค:

class MainActivity : AppCompatActivity() {

    // 1. ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ (์ตœ์ดˆ 1ํšŒ)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // ์ดˆ๊ธฐํ™” ์ž‘์—…: UI ์„ค์ •, ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™” ๋“ฑ
    }

    // 2. ํ™”๋ฉด์ด ๋ณด์ด๊ธฐ ์‹œ์ž‘ํ•  ๋•Œ
    override fun onStart() {
        super.onStart()
        // ๋ฆฌ์†Œ์Šค ์ค€๋น„
    }

    // 3. ์‚ฌ์šฉ์ž์™€ ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ
    override fun onResume() {
        super.onResume()
        // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘, ์„ผ์„œ ๋“ฑ๋ก ๋“ฑ
    }

    // 4. ์ผ์‹œ ์ •์ง€ (๋‹ค๋ฅธ ์•ฑ์ด foreground๋กœ ์˜ด)
    override fun onPause() {
        super.onPause()
        // ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ค‘์ง€, ๋ฐ์ดํ„ฐ ์ €์žฅ ๋“ฑ
    }

    // 5. ํ™”๋ฉด์ด ๋ณด์ด์ง€ ์•Š์„ ๋•Œ
    override fun onStop() {
        super.onStop()
        // ๋ฆฌ์†Œ์Šค ํ•ด์ œ
    }

    // 6. ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์™„์ „ํžˆ ์ข…๋ฃŒ๋  ๋•Œ
    override fun onDestroy() {
        super.onDestroy()
        // ์ตœ์ข… ์ •๋ฆฌ ์ž‘์—…
    }
}

์ƒ๋ช…์ฃผ๊ธฐ ํ๋ฆ„๋„

onCreate() โ†’ onStart() โ†’ onResume() [์‹คํ–‰ ์ค‘] โ†’ onPause() โ†’ onStop() โ†’ onDestroy()
             โ†‘                                                    โ†“
             โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ onRestart() โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

4. AndroidManifest.xml์˜ ์—ญํ• 

๋ชจ๋“  Android ์•ฑ์€ AndroidManifest.xml ํŒŒ์ผ์„ ๊ฐ€์ ธ์•ผ ํ•˜๋ฉฐ, ๋‹ค์Œ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.tsetapplication">

    <!-- ๊ถŒํ•œ ์„ ์–ธ -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.TsetApplication">

        <!-- ์•กํ‹ฐ๋น„ํ‹ฐ ์„ ์–ธ -->
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <!-- ์•ฑ ๋Ÿฐ์ฒ˜์— ํ‘œ์‹œํ•  ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ ์ง€์ • -->
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- ์„œ๋น„์Šค ์„ ์–ธ -->
        <service android:name=".MyMusicService" />

    </application>
</manifest>

UI์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๊ธฐ์ˆ 

1. Android UI ๊ตฌ์กฐ

Android UI๋Š” XML ๋ ˆ์ด์•„์›ƒ๊ณผ Kotlin ์ฝ”๋“œ๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค:

ํ™”๋ฉด = Layout (XML) + Activity (Kotlin)

View์™€ ViewGroup

  • View: ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๋ชจ๋“  UI ์š”์†Œ์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค

    • ์˜ˆ: TextView, Button, ImageView ๋“ฑ
  • ViewGroup: View๋“ค์„ ๋‹ด๋Š” ์ปจํ…Œ์ด๋„ˆ

    • ์˜ˆ: LinearLayout, ConstraintLayout, FrameLayout ๋“ฑ
ViewGroup (์ปจํ…Œ์ด๋„ˆ)
  โ”œโ”€โ”€ View (ํ…์ŠคํŠธ)
  โ”œโ”€โ”€ View (๋ฒ„ํŠผ)
  โ””โ”€โ”€ ViewGroup (ํ•˜์œ„ ์ปจํ…Œ์ด๋„ˆ)
        โ”œโ”€โ”€ View (์ด๋ฏธ์ง€)
        โ””โ”€โ”€ View (์ž…๋ ฅ์ฐฝ)

2. ๋ ˆ์ด์•„์›ƒ XML ์ดํ•ดํ•˜๊ธฐ

activity_main.xml ์˜ˆ์ œ ๋ถ„์„

<?xml version="1.0" encoding="utf-8"?>
<!-- ConstraintLayout: ์ œ์•ฝ ์กฐ๊ฑด์œผ๋กœ View ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ -->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main"
    android:layout_width="match_parent"    <!-- ๋ถ€๋ชจ ๋„ˆ๋น„๋งŒํผ -->
    android:layout_height="match_parent">  <!-- ๋ถ€๋ชจ ๋†’์ด๋งŒํผ -->

    <!-- TextView: ํ…์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•˜๋Š” View -->
    <TextView
        android:layout_width="wrap_content"   <!-- ๋‚ด์šฉ๋งŒํผ -->
        android:layout_height="wrap_content"  <!-- ๋‚ด์šฉ๋งŒํผ -->
        android:text="Hello World!"

        <!-- ์ œ์•ฝ ์กฐ๊ฑด: ํ™”๋ฉด ์ค‘์•™์— ๋ฐฐ์น˜ -->
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

์ฃผ์š” ์†์„ฑ ์„ค๋ช…

์†์„ฑ ์„ค๋ช… ๊ฐ€๋Šฅํ•œ ๊ฐ’
android:id View์˜ ๊ณ ์œ  ์‹๋ณ„์ž @+id/view_name
layout_width ๋„ˆ๋น„ match_parent, wrap_content, 100dp
layout_height ๋†’์ด match_parent, wrap_content, 100dp
android:text ํ…์ŠคํŠธ ๋‚ด์šฉ "๋ฌธ์ž์—ด" ๋˜๋Š” @string/name
android:textSize ๊ธ€์ž ํฌ๊ธฐ 16sp
android:textColor ๊ธ€์ž ์ƒ‰์ƒ #FF0000 ๋˜๋Š” @color/red

3. Layout ์ข…๋ฅ˜

ConstraintLayout (์ถ”์ฒœ)

  • ์œ ์—ฐํ•œ ๋ ˆ์ด์•„์›ƒ
  • ์„ฑ๋Šฅ ์ตœ์ ํ™”
  • ๋ณต์žกํ•œ UI๋„ ๊นŠ์ด ์—†์ด ๊ตฌํ˜„ ๊ฐ€๋Šฅ
<androidx.constraintlayout.widget.ConstraintLayout>
    <Button
        android:id="@+id/button1"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/button2"
        app:layout_constraintTop_toBottomOf="@id/button1"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

LinearLayout

  • ์„ธ๋กœ ๋˜๋Š” ๊ฐ€๋กœ๋กœ ์ˆœ์„œ๋Œ€๋กœ ๋ฐฐ์น˜
<LinearLayout
    android:orientation="vertical">  <!-- ์„ธ๋กœ ๋ฐฐ์น˜ -->
    <TextView android:text="์ฒซ ๋ฒˆ์งธ" />
    <TextView android:text="๋‘ ๋ฒˆ์งธ" />
    <TextView android:text="์„ธ ๋ฒˆ์งธ" />
</LinearLayout>

FrameLayout

  • View๋ฅผ ๊ฒน์ณ์„œ ๋ฐฐ์น˜
<FrameLayout>
    <ImageView />  <!-- ๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€ -->
    <TextView />   <!-- ์ด๋ฏธ์ง€ ์œ„์— ํ…์ŠคํŠธ -->
</FrameLayout>

4. Activity์—์„œ View ์ ‘๊ทผํ•˜๊ธฐ

๋ฐฉ๋ฒ• 1: findViewById (์ „ํ†ต์  ๋ฐฉ๋ฒ•)

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // XML์˜ View๋ฅผ Kotlin ์ฝ”๋“œ์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ
        val textView = findViewById<TextView>(R.id.textView)
        textView.text = "ํ…์ŠคํŠธ ๋ณ€๊ฒฝ"

        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰
            textView.text = "๋ฒ„ํŠผ์ด ํด๋ฆญ๋˜์—ˆ์Šต๋‹ˆ๋‹ค"
        }
    }
}

๋ฐฉ๋ฒ• 2: View Binding (๊ถŒ์žฅ)

// build.gradle.kts์— ์„ค์ • ํ•„์š”
android {
    buildFeatures {
        viewBinding = true
    }
}

// Activity์—์„œ ์‚ฌ์šฉ
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // View์— ์‰ฝ๊ฒŒ ์ ‘๊ทผ
        binding.textView.text = "ํ…์ŠคํŠธ ๋ณ€๊ฒฝ"
        binding.button.setOnClickListener {
            binding.textView.text = "๋ฒ„ํŠผ ํด๋ฆญ๋จ"
        }
    }
}

5. ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฒ˜๋ฆฌ

๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ

// ๋ฐฉ๋ฒ• 1: setOnClickListener
button.setOnClickListener {
    Toast.makeText(this, "๋ฒ„ํŠผ ํด๋ฆญ!", Toast.LENGTH_SHORT).show()
}

// ๋ฐฉ๋ฒ• 2: ๋žŒ๋‹ค ํ‘œํ˜„์‹์œผ๋กœ View ์ ‘๊ทผ
button.setOnClickListener { view ->
    view.isEnabled = false  // ๋ฒ„ํŠผ ๋น„ํ™œ์„ฑํ™”
}

EditText ์ž…๋ ฅ ๊ฐ์ง€

val editText = findViewById<EditText>(R.id.editText)

// ํ…์ŠคํŠธ ๋ณ€๊ฒฝ ๊ฐ์ง€
editText.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
        println("์ž…๋ ฅ๋œ ํ…์ŠคํŠธ: ${s.toString()}")
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
})

6. Toast์™€ Snackbar

Toast: ๊ฐ„๋‹จํ•œ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ

// ์งง์€ ๋ฉ”์‹œ์ง€ (2์ดˆ)
Toast.makeText(this, "์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_SHORT).show()

// ๊ธด ๋ฉ”์‹œ์ง€ (3.5์ดˆ)
Toast.makeText(this, "์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค", Toast.LENGTH_LONG).show()

Snackbar: ์•ก์…˜์ด ์žˆ๋Š” ๋ฉ”์‹œ์ง€

Snackbar.make(view, "์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", Snackbar.LENGTH_LONG)
    .setAction("์ทจ์†Œ") {
        // ์ทจ์†Œ ๋ฒ„ํŠผ ํด๋ฆญ ์‹œ ์‹คํ–‰
        println("์‚ญ์ œ ์ทจ์†Œ")
    }
    .show()

7. Intent๋กœ ํ™”๋ฉด ์ „ํ™˜

๋‹ค๋ฅธ Activity๋กœ ์ด๋™

// ๊ฐ„๋‹จํ•œ ์ด๋™
val intent = Intent(this, SecondActivity::class.java)
startActivity(intent)

// ๋ฐ์ดํ„ฐ ์ „๋‹ฌ
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("name", "John")
intent.putExtra("age", 25)
startActivity(intent)

// SecondActivity์—์„œ ๋ฐ์ดํ„ฐ ๋ฐ›๊ธฐ
class SecondActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val name = intent.getStringExtra("name")
        val age = intent.getIntExtra("age", 0)

        println("Name: $name, Age: $age")
    }
}

8. RecyclerView๋กœ ๋ฆฌ์ŠคํŠธ ํ‘œ์‹œ

RecyclerView๋Š” ๊ธด ๋ชฉ๋ก์„ ํšจ์œจ์ ์œผ๋กœ ํ‘œ์‹œํ•˜๋Š” ๋ทฐ์ž…๋‹ˆ๋‹ค.

// 1. ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค
data class Item(val title: String, val description: String)

// 2. ViewHolder
class ItemViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    val titleText: TextView = view.findViewById(R.id.titleText)
    val descText: TextView = view.findViewById(R.id.descText)
}

// 3. Adapter
class ItemAdapter(private val items: List<Item>) :
    RecyclerView.Adapter<ItemViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_layout, parent, false)
        return ItemViewHolder(view)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = items[position]
        holder.titleText.text = item.title
        holder.descText.text = item.description
    }

    override fun getItemCount() = items.size
}

// 4. Activity์—์„œ ์‚ฌ์šฉ
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.adapter = ItemAdapter(itemList)

์ฝ”๋“œ ์ƒ์„ธ ๋ถ„์„

1. MainActivity.kt ๋ถ„์„

ํŒŒ์ผ ์œ„์น˜: mobile/src/main/java/com/example/tsetapplication/MainActivity.kt

package com.example.tsetapplication

// ํ•„์š”ํ•œ ํด๋ž˜์Šค๋“ค์„ import (๊ฐ€์ ธ์˜ค๊ธฐ)
import android.os.Bundle                          // ๋ฐ์ดํ„ฐ ์ „๋‹ฌ์šฉ
import androidx.activity.enableEdgeToEdge         // ์ „์ฒด ํ™”๋ฉด ๋ชจ๋“œ
import androidx.appcompat.app.AppCompatActivity   // ๊ธฐ๋ณธ Activity ํด๋ž˜์Šค
import androidx.core.view.ViewCompat              // View ํ˜ธํ™˜์„ฑ ๋„๊ตฌ
import androidx.core.view.WindowInsetsCompat      // ์‹œ์Šคํ…œ ๋ฐ” ์ •๋ณด

// MainActivity ํด๋ž˜์Šค ์„ ์–ธ
// : AppCompatActivity() = AppCompatActivity๋ฅผ ์ƒ์†๋ฐ›์Œ
class MainActivity : AppCompatActivity() {

    // override = ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์žฌ์ •์˜
    // onCreate = Activity๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ
    override fun onCreate(savedInstanceState: Bundle?) {
        // ๋ถ€๋ชจ ํด๋ž˜์Šค์˜ onCreate ๋จผ์ € ํ˜ธ์ถœ (ํ•„์ˆ˜!)
        super.onCreate(savedInstanceState)

        // Edge-to-Edge ๋ชจ๋“œ ํ™œ์„ฑํ™”
        // ์ƒํƒœ๋ฐ”, ๋‚ด๋น„๊ฒŒ์ด์…˜๋ฐ” ๋’ค๊นŒ์ง€ ์ปจํ…์ธ  ํ‘œ์‹œ
        enableEdgeToEdge()

        // XML ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์„ ํ™”๋ฉด์— ํ‘œ์‹œ
        // R.layout.activity_main = res/layout/activity_main.xml
        setContentView(R.layout.activity_main)

        // ์‹œ์Šคํ…œ ๋ฐ”(์ƒํƒœ๋ฐ”, ๋‚ด๋น„๊ฒŒ์ด์…˜๋ฐ”) ์˜์—ญ ์ฒ˜๋ฆฌ
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            // ์‹œ์Šคํ…œ ๋ฐ”์˜ ํฌ๊ธฐ ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())

            // View์— ํŒจ๋”ฉ ์„ค์ • (์‹œ์Šคํ…œ ๋ฐ”์— ๊ฐ€๋ ค์ง€์ง€ ์•Š๋„๋ก)
            v.setPadding(
                systemBars.left,    // ์™ผ์ชฝ ํŒจ๋”ฉ
                systemBars.top,     // ์œ„์ชฝ ํŒจ๋”ฉ
                systemBars.right,   // ์˜ค๋ฅธ์ชฝ ํŒจ๋”ฉ
                systemBars.bottom   // ์•„๋ž˜์ชฝ ํŒจ๋”ฉ
            )

            // insets๋ฅผ ๋ฐ˜ํ™˜ (๋‹ค๋ฅธ ๋ฆฌ์Šค๋„ˆ์—์„œ๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋„๋ก)
            insets
        }
    }
}

์ฝ”๋“œ ์‹คํ–‰ ์ˆœ์„œ

  1. ์‚ฌ์šฉ์ž๊ฐ€ ์•ฑ ์•„์ด์ฝ˜ ํ„ฐ์น˜
  2. Android ์‹œ์Šคํ…œ์ด MainActivity ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
  3. onCreate() ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ
  4. enableEdgeToEdge() - ์ „์ฒด ํ™”๋ฉด ๋ชจ๋“œ ํ™œ์„ฑํ™”
  5. setContentView() - activity_main.xml ๋ ˆ์ด์•„์›ƒ ๋กœ๋“œ
  6. setOnApplyWindowInsetsListener() - ์‹œ์Šคํ…œ ๋ฐ” ์˜์—ญ ํŒจ๋”ฉ ์„ค์ •
  7. ํ™”๋ฉด ํ‘œ์‹œ ์™„๋ฃŒ

์ฃผ์š” ๊ฐœ๋… ์„ค๋ช…

AppCompatActivity

  • ๋ชจ๋“  Activity์˜ ๊ธฐ๋ณธ ํด๋ž˜์Šค
  • Android ์ด์ „ ๋ฒ„์ „๊ณผ์˜ ํ˜ธํ™˜์„ฑ ์ œ๊ณต
  • ActionBar, ํ…Œ๋งˆ ๋“ฑ์˜ ๊ธฐ๋Šฅ ํฌํ•จ

Bundle

  • ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์ „๋‹ฌํ•˜๋Š” ์ปจํ…Œ์ด๋„ˆ
  • Activity ์žฌ์ƒ์„ฑ ์‹œ ์ƒํƒœ ๋ณต์›์— ์‚ฌ์šฉ
  • savedInstanceState๋กœ ์ด์ „ ์ƒํƒœ ๋ฐ›์Œ

findViewById()

  • XML์— ์ •์˜๋œ View๋ฅผ Kotlin ์ฝ”๋“œ์—์„œ ์ฐพ๋Š” ๋ฉ”์„œ๋“œ
  • ID๋กœ View๋ฅผ ๊ฒ€์ƒ‰
  • ๋ฐ˜ํ™˜ ํƒ€์ž…: View (ํ˜•๋ณ€ํ™˜ ํ•„์š”)

2. activity_main.xml ๋ถ„์„

ํŒŒ์ผ ์œ„์น˜: mobile/src/main/res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- XML ์„ ์–ธ: ํŒŒ์ผ ๋ฒ„์ „๊ณผ ์ธ์ฝ”๋”ฉ ์ง€์ • -->

<!-- ConstraintLayout: ์ œ์•ฝ ์กฐ๊ฑด ๊ธฐ๋ฐ˜ ๋ ˆ์ด์•„์›ƒ -->
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"

    <!-- View์˜ ๊ณ ์œ  ID (MainActivity์—์„œ findViewById๋กœ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉ) -->
    android:id="@+id/main"

    <!-- ๋ ˆ์ด์•„์›ƒ ํฌ๊ธฐ -->
    android:layout_width="match_parent"    <!-- ๋ถ€๋ชจ ๋„ˆ๋น„๋งŒํผ (ํ™”๋ฉด ์ „์ฒด) -->
    android:layout_height="match_parent"   <!-- ๋ถ€๋ชจ ๋†’์ด๋งŒํผ (ํ™”๋ฉด ์ „์ฒด) -->

    <!-- tools ๋„ค์ž„์ŠคํŽ˜์ด์Šค: ๊ฐœ๋ฐœ ์‹œ์—๋งŒ ์‚ฌ์šฉ, ์‹ค์ œ ์•ฑ์—๋Š” ์˜ํ–ฅ ์—†์Œ -->
    tools:context=".MainActivity">

    <!-- TextView: ํ…์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•˜๋Š” View -->
    <TextView
        <!-- wrap_content: ๋‚ด์šฉ๋ฌผ ํฌ๊ธฐ๋งŒํผ -->
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        <!-- ํ‘œ์‹œํ•  ํ…์ŠคํŠธ -->
        android:text="Hello World!"

        <!-- ConstraintLayout ์ œ์•ฝ ์กฐ๊ฑด๋“ค -->
        <!-- Bottom์„ parent์˜ Bottom์— ์—ฐ๊ฒฐ -->
        app:layout_constraintBottom_toBottomOf="parent"

        <!-- End(์˜ค๋ฅธ์ชฝ)๋ฅผ parent์˜ End์— ์—ฐ๊ฒฐ -->
        app:layout_constraintEnd_toEndOf="parent"

        <!-- Start(์™ผ์ชฝ)๋ฅผ parent์˜ Start์— ์—ฐ๊ฒฐ -->
        app:layout_constraintStart_toStartOf="parent"

        <!-- Top์„ parent์˜ Top์— ์—ฐ๊ฒฐ -->
        app:layout_constraintTop_toTopOf="parent" />

        <!-- ๋„ค ๋ฐฉํ–ฅ ๋ชจ๋‘ parent ์ค‘์•™์— ์—ฐ๊ฒฐ โ†’ ํ™”๋ฉด ์ •์ค‘์•™์— ๋ฐฐ์น˜ -->

</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout ์ œ์•ฝ ์กฐ๊ฑด ์ดํ•ด

ํ™”๋ฉด (parent)
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                             โ”‚
โ”‚                             โ”‚ โ† Top
โ”‚           TextView          โ”‚ โ† ๋ชจ๋“  ๋ฐฉํ–ฅ์ด parent์™€ ์—ฐ๊ฒฐ๋˜์–ด
โ”‚         "Hello World!"      โ”‚   ํ™”๋ฉด ์ •์ค‘์•™์— ์œ„์น˜
โ”‚                             โ”‚ โ† Bottom
โ”‚                             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  Start โ†’              โ† End

3. MyMusicService.kt ๋ถ„์„

ํŒŒ์ผ ์œ„์น˜: shared/src/main/java/com/example/tsetapplication/shared/MyMusicService.kt

package com.example.tsetapplication.shared

// ํ•„์š”ํ•œ ํด๋ž˜์Šค import
import android.os.Bundle
import android.support.v4.media.MediaBrowserCompat.MediaItem
import androidx.media.MediaBrowserServiceCompat
import android.support.v4.media.session.MediaSessionCompat
import java.util.ArrayList

/**
 * MyMusicService: ์Œ์•… ์žฌ์ƒ ์„œ๋น„์Šค
 *
 * MediaBrowserServiceCompat๋ฅผ ์ƒ์†๋ฐ›์•„ ๊ตฌํ˜„
 * - Android Auto, Automotive์—์„œ ์Œ์•… ํƒ์ƒ‰ ๊ฐ€๋Šฅ
 * - ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์Œ์•… ์žฌ์ƒ
 * - ๋‹ค๋ฅธ ์•ฑ(์˜ˆ: Android Auto)์—์„œ ์ œ์–ด ๊ฐ€๋Šฅ
 */
class MyMusicService : MediaBrowserServiceCompat() {

    // MediaSession: ๋ฏธ๋””์–ด ์žฌ์ƒ ์ƒํƒœ ๊ด€๋ฆฌ
    // lateinit: ๋‚˜์ค‘์— ์ดˆ๊ธฐํ™”ํ•  ๋ณ€์ˆ˜ (onCreate์—์„œ ์ดˆ๊ธฐํ™”)
    private lateinit var session: MediaSessionCompat

    // callback: ์‚ฌ์šฉ์ž ๋™์ž‘(์žฌ์ƒ, ์ผ์‹œ์ •์ง€ ๋“ฑ)์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฐ์ฒด
    // object: ์ต๋ช… ํด๋ž˜์Šค ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    private val callback = object : MediaSessionCompat.Callback() {

        // ์žฌ์ƒ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ
        override fun onPlay() {
            // TODO: ์‹ค์ œ ์Œ์•… ์žฌ์ƒ ๋กœ์ง ๊ตฌํ˜„
        }

        // ํŠน์ • ๊ณก์œผ๋กœ ์ด๋™
        override fun onSkipToQueueItem(queueId: Long) {}

        // ํŠน์ • ์œ„์น˜๋กœ ์ด๋™ (์˜ˆ: 30์ดˆ๋กœ ์ด๋™)
        override fun onSeekTo(position: Long) {}

        // ๋ฏธ๋””์–ด ID๋กœ ์žฌ์ƒ
        override fun onPlayFromMediaId(mediaId: String?, extras: Bundle?) {}

        // ์ผ์‹œ์ •์ง€
        override fun onPause() {}

        // ์ •์ง€
        override fun onStop() {}

        // ๋‹ค์Œ ๊ณก
        override fun onSkipToNext() {}

        // ์ด์ „ ๊ณก
        override fun onSkipToPrevious() {}

        // ์‚ฌ์šฉ์ž ์ •์˜ ๋™์ž‘
        override fun onCustomAction(action: String?, extras: Bundle?) {}

        // ๊ฒ€์ƒ‰์–ด๋กœ ์žฌ์ƒ (์˜ˆ: "๋น„ํ‹€์ฆˆ ๋…ธ๋ž˜ ํ‹€์–ด์ค˜")
        override fun onPlayFromSearch(query: String?, extras: Bundle?) {}
    }

    // ์„œ๋น„์Šค๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ํ˜ธ์ถœ (์ตœ์ดˆ 1ํšŒ)
    override fun onCreate() {
        super.onCreate()

        // MediaSession ์ƒ์„ฑ
        session = MediaSessionCompat(this, "MyMusicService")

        // Session Token ์„ค์ • (๋‹ค๋ฅธ ์•ฑ์ด ์ด ์„œ๋น„์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•จ)
        sessionToken = session.sessionToken

        // Callback ๋“ฑ๋ก (์‚ฌ์šฉ์ž ๋™์ž‘ ์ฒ˜๋ฆฌ)
        session.setCallback(callback)

        // ํ”Œ๋ž˜๊ทธ ์„ค์ •
        session.setFlags(
            // ๋ฏธ๋””์–ด ๋ฒ„ํŠผ ์ฒ˜๋ฆฌ (ํ—ค๋“œ์…‹ ๋ฒ„ํŠผ ๋“ฑ)
            MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
            // ์ „์†ก ์ œ์–ด ์ฒ˜๋ฆฌ (์žฌ์ƒ, ์ผ์‹œ์ •์ง€ ๋“ฑ)
            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
        )
    }

    // ์„œ๋น„์Šค๊ฐ€ ์ข…๋ฃŒ๋  ๋•Œ ํ˜ธ์ถœ
    override fun onDestroy() {
        // MediaSession ๋ฆฌ์†Œ์Šค ํ•ด์ œ
        session.release()
    }

    /**
     * onGetRoot: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„œ๋น„์Šค์— ์—ฐ๊ฒฐํ•  ๋•Œ ํ˜ธ์ถœ
     *
     * @param clientPackageName ์—ฐ๊ฒฐ ์š”์ฒญํ•œ ์•ฑ์˜ ํŒจํ‚ค์ง€๋ช…
     * @param clientUid ํด๋ผ์ด์–ธํŠธ ์•ฑ์˜ UID
     * @param rootHints ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ œ๊ณตํ•œ ํžŒํŠธ
     * @return BrowserRoot ๊ฐ์ฒด (์—ฐ๊ฒฐ ํ—ˆ์šฉ ์‹œ) ๋˜๋Š” null (๊ฑฐ๋ถ€ ์‹œ)
     */
    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): MediaBrowserServiceCompat.BrowserRoot? {
        // "root"๋ผ๋Š” ID๋กœ ๋ฃจํŠธ ๋ฐ˜ํ™˜ (๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ ํ—ˆ์šฉ)
        // ์‹ค์ œ ์•ฑ์—์„œ๋Š” clientPackageName์„ ๊ฒ€์‚ฌํ•˜์—ฌ ๋ณด์•ˆ ๊ฐ•ํ™” ํ•„์š”
        return MediaBrowserServiceCompat.BrowserRoot("root", null)
    }

    /**
     * onLoadChildren: ๋ฏธ๋””์–ด ํ•ญ๋ชฉ ๋ชฉ๋ก์„ ์š”์ฒญํ•  ๋•Œ ํ˜ธ์ถœ
     *
     * @param parentId ๋ถ€๋ชจ ๋ฏธ๋””์–ด ํ•ญ๋ชฉ์˜ ID
     * @param result ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌํ•  ๊ฐ์ฒด
     */
    override fun onLoadChildren(
        parentId: String,
        result: Result<MutableList<MediaItem>>
    ) {
        // ๋นˆ ๋ฆฌ์ŠคํŠธ ๋ฐ˜ํ™˜ (์‹ค์ œ๋กœ๋Š” ์Œ์•… ๋ชฉ๋ก์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•จ)
        // TODO: ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋‚˜ ํŒŒ์ผ์—์„œ ์Œ์•… ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
        result.sendResult(ArrayList())
    }
}

MediaBrowserService ๊ตฌ์กฐ

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   Android Auto / ์ž๋™์ฐจ   โ”‚
โ”‚   (ํด๋ผ์ด์–ธํŠธ)            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚ ์—ฐ๊ฒฐ ์š”์ฒญ
         โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚   MyMusicService         โ”‚
โ”‚   (์„œ๋น„์Šค)               โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ onGetRoot() โ† ์—ฐ๊ฒฐ ํ—ˆ์šฉ? โ”‚
โ”‚ onLoadChildren() โ† ๋ชฉ๋ก  โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚   MediaSession           โ”‚
โ”‚   - callback             โ”‚
โ”‚   - onPlay()             โ”‚
โ”‚   - onPause()            โ”‚
โ”‚   - onSkipToNext()       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ†“
    ์‹ค์ œ ์Œ์•… ์žฌ์ƒ

4. R ํด๋ž˜์Šค๋ž€?

R ํด๋ž˜์Šค๋Š” Android๊ฐ€ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๋Š” ํด๋ž˜์Šค๋กœ, ๋ชจ๋“  ๋ฆฌ์†Œ์Šค์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ID๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

// R ํด๋ž˜์Šค ๊ตฌ์กฐ (์ž๋™ ์ƒ์„ฑ๋จ, ์ง์ ‘ ์ˆ˜์ • ๋ถˆ๊ฐ€)
class R {
    class layout {
        const val activity_main = 0x7f030001
    }

    class id {
        const val main = 0x7f070001
        const val textView = 0x7f070002
    }

    class string {
        const val app_name = 0x7f0a0001
    }

    class drawable {
        const val ic_launcher = 0x7f020001
    }
}

// ์‚ฌ์šฉ ์˜ˆ
setContentView(R.layout.activity_main)       // ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ
val view = findViewById(R.id.textView)        // View ID
val appName = getString(R.string.app_name)   // ๋ฌธ์ž์—ด ๋ฆฌ์†Œ์Šค

์•ฑ ๋นŒ๋“œ ๋ฐ ์‹คํ–‰ ๋ฐฉ๋ฒ•

1. Android Studio์—์„œ ์‹คํ–‰

  1. ํ”„๋กœ์ ํŠธ ์—ด๊ธฐ

    • Android Studio ์‹คํ–‰
    • Open an Existing Project ์„ ํƒ
    • TsetApplication ํด๋” ์„ ํƒ
  2. Gradle ๋™๊ธฐํ™”

    • ํ”„๋กœ์ ํŠธ๊ฐ€ ์—ด๋ฆฌ๋ฉด ์ž๋™์œผ๋กœ Gradle ๋™๊ธฐํ™” ์‹œ์ž‘
    • ๋˜๋Š” File โ†’ Sync Project with Gradle Files
  3. ์—๋ฎฌ๋ ˆ์ดํ„ฐ ๋˜๋Š” ์‹ค์ œ ๊ธฐ๊ธฐ ์—ฐ๊ฒฐ

    • ์—๋ฎฌ๋ ˆ์ดํ„ฐ: Tools โ†’ Device Manager โ†’ Create Device
    • ์‹ค์ œ ๊ธฐ๊ธฐ: USB ์—ฐ๊ฒฐ + ๊ฐœ๋ฐœ์ž ์˜ต์…˜ ํ™œ์„ฑํ™”
  4. ์•ฑ ์‹คํ–‰

    • ์ƒ๋‹จ ํˆด๋ฐ”์—์„œ ์‹คํ–‰ ๋ฒ„ํŠผ (โ–ถ) ํด๋ฆญ
    • ๋˜๋Š” Shift + F10 (Windows/Linux), Ctrl + R (Mac)

2. Gradle ๋ช…๋ น์–ด๋กœ ๋นŒ๋“œ

# Unix/Mac
./gradlew build

# Windows
gradlew.bat build

์ฃผ์š” Gradle ์ž‘์—… (Tasks)

# ์•ฑ ๋นŒ๋“œ (APK ์ƒ์„ฑ)
./gradlew assembleDebug          # ๋””๋ฒ„๊ทธ APK
./gradlew assembleRelease        # ๋ฆด๋ฆฌ์Šค APK

# ์•ฑ ์„ค์น˜ ๋ฐ ์‹คํ–‰
./gradlew installDebug           # ๋””๋ฒ„๊ทธ APK ์„ค์น˜
./gradlew installRelease         # ๋ฆด๋ฆฌ์Šค APK ์„ค์น˜

# ํ…Œ์ŠคํŠธ ์‹คํ–‰
./gradlew test                   # ๋‹จ์œ„ ํ…Œ์ŠคํŠธ
./gradlew connectedAndroidTest   # ๊ธฐ๊ธฐ ํ…Œ์ŠคํŠธ

# ๋นŒ๋“œ ์ •๋ฆฌ
./gradlew clean                  # ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ ์‚ญ์ œ

# ์˜์กด์„ฑ ํ™•์ธ
./gradlew dependencies           # ์˜์กด์„ฑ ํŠธ๋ฆฌ ์ถœ๋ ฅ

3. APK ํŒŒ์ผ ์œ„์น˜

๋นŒ๋“œ๊ฐ€ ์™„๋ฃŒ๋˜๋ฉด APK ํŒŒ์ผ์ด ๋‹ค์Œ ์œ„์น˜์— ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค:

mobile/build/outputs/apk/debug/mobile-debug.apk

4. ๋กœ๊ทธ ํ™•์ธ (Logcat)

Android Studio ํ•˜๋‹จ์˜ Logcat ํƒญ์—์„œ ์•ฑ ๋กœ๊ทธ๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ์ฝ”๋“œ์—์„œ ๋กœ๊ทธ ์ถœ๋ ฅ
import android.util.Log

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // ๋กœ๊ทธ ์ถœ๋ ฅ
        Log.d("MainActivity", "onCreate ํ˜ธ์ถœ๋จ")  // Debug
        Log.i("MainActivity", "์ •๋ณด ๋กœ๊ทธ")        // Info
        Log.w("MainActivity", "๊ฒฝ๊ณ  ๋กœ๊ทธ")        // Warning
        Log.e("MainActivity", "์—๋Ÿฌ ๋กœ๊ทธ")        // Error
    }
}

Logcat ํ•„ํ„ฐ:

# ์•ฑ์˜ ๋กœ๊ทธ๋งŒ ๋ณด๊ธฐ
package:com.example.tsetapplication

# ํŠน์ • ํƒœ๊ทธ๋งŒ ๋ณด๊ธฐ
tag:MainActivity

# ์—๋Ÿฌ๋งŒ ๋ณด๊ธฐ
level:ERROR

์ถ”๊ฐ€ ํ•™์Šต ์ž๋ฃŒ

๊ณต์‹ ๋ฌธ์„œ

์ถ”์ฒœ ํ•™์Šต ์ˆœ์„œ

  1. Kotlin ๊ธฐ๋ณธ ๋ฌธ๋ฒ• ํ•™์Šต
  2. Activity์™€ Lifecycle ์ดํ•ด
  3. Layout๊ณผ View ํ•™์Šต
  4. RecyclerView๋กœ ๋ฆฌ์ŠคํŠธ ๊ตฌํ˜„
  5. ViewModel๊ณผ LiveData (MVVM ํŒจํ„ด)
  6. Room ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค
  7. Retrofit์œผ๋กœ ๋„คํŠธ์›Œํฌ ํ†ต์‹ 
  8. Coroutine์œผ๋กœ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ

๋ฌธ์ œ ํ•ด๊ฒฐ (Troubleshooting)

1. Gradle ๋™๊ธฐํ™” ์‹คํŒจ

File โ†’ Invalidate Caches / Restart โ†’ Invalidate and Restart

2. R ํด๋ž˜์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ

./gradlew clean
./gradlew build

3. SDK ๋ฒ„์ „ ์˜ค๋ฅ˜

local.properties ํŒŒ์ผ ํ™•์ธ:

sdk.dir=/Users/์‚ฌ์šฉ์ž๋ช…/Library/Android/sdk

4. ์—๋ฎฌ๋ ˆ์ดํ„ฐ๊ฐ€ ๋А๋ฆผ

  • Hardware Acceleration ํ™œ์„ฑํ™” (Intel HAXM, AMD Hypervisor)
  • RAM ํ• ๋‹น๋Ÿ‰ ์ฆ๊ฐ€ (AVD Manager์—์„œ ์„ค์ •)
  • x86 ์ด๋ฏธ์ง€ ์‚ฌ์šฉ (ARM๋ณด๋‹ค ๋น ๋ฆ„)

๋งˆ์น˜๋ฉฐ

์ด ๊ฐ€์ด๋“œ๊ฐ€ Android ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•˜๋Š” ๋ฐ ๋„์›€์ด ๋˜๊ธฐ๋ฅผ ๋ฐ”๋ž๋‹ˆ๋‹ค. ๊ถ๊ธˆํ•œ ์ ์ด ์žˆ์œผ๋ฉด ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜๊ฑฐ๋‚˜, ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์งˆ๋ฌธํ•ด๋ณด์„ธ์š”!

ํ–‰๋ณตํ•œ ์ฝ”๋”ฉ ๋˜์„ธ์š”! ๐Ÿš€

About

android study repo with kotlin

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages