Skip to content

Latest commit

ย 

History

History
816 lines (723 loc) ยท 36 KB

Kotlin Anko.md

File metadata and controls

816 lines (723 loc) ยท 36 KB

Kotlin Anko

๋ฐœํ‘œ์ž : ์†ก์ง„์šฐ

๋ฐœํ‘œ์ผ์ž : 2018-10-24()

๋ฐœํ‘œ์ฃผ์ œ : Kotlin Anko

0. ์ฐธ๊ณ ๋ฌธ์„œ

Anko๋ž€?

  • Kotlin์œผ๋กœ ์ž‘์„ฑ๋œ DSL ( Domain-specific-Language) ์ž…๋‹ˆ๋‹ค.
  • ๊ธฐ์กด์— Android์—์„œ๋Š” View๋ฅผ ๋””์ž์ธ ํ• ๋•Œ XML Layout์„ ํ†ตํ•ด์„œ ํ‘œํ˜„ํ•˜์˜€์Šต๋‹ˆ๋‹ค.
  • Anko์˜ ๊ณต์‹์ ์œผ๋กœ ๋ฐœํ‘œ๋œ ์ž๋ฃŒ์—๋Š” Android ๊ฐœ๋ฐœ์„ ์ข€๋” ์‰ฝ๊ณ  ๋น ๋ฅด๊ฒŒ ๊ฐœ๋ฐœํ• ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ์†Œ๊ฐœ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ธฐ์กด์˜ XML ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉด Java์ฝ”๋“œ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ์ž‘์—…์„ ํ•˜๊ฒŒ ๋˜๋ฉด์„œ CPU๋‚˜ ๋ฐฐํ„ฐ๋ฆฌ ์†Œ๋ชจ๋ฅผ ํ•˜๊ณ  ์ฝ”๋“œ์˜ ์žฌํ™œ์šฉ์ด ๋ถˆํŽธํ•˜๋‹ค๋Š” ๋‹จ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • Anko๋Š” ์ด๋Ÿฐ ๋‹จ์ ์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ๊ฐ€๋…์„ฑ์€ ๋†’์ด๊ณ  XML ํŒŒ์‹ฑ์— ๋”ฐ๋ฅธ ์˜ค๋ฒ„ํ—ค๋“œ๋ฅผ ์—†์•จ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Anko์˜ ์ข…๋ฅ˜

  • Anko Commons
  • Anko Loayouts
  • Anko SQLite
  • Anko Coroutines

Build ์ถ”๊ฐ€

  • ์šฐ์„  Anko ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด Gradle์ถ”๊ฐ€ ์ž‘์—…์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. 2018 - 05 - 17 ์—์„œ๋Š” '0.10.5'๋ฒ„์ „๊นŒ์ง€ ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
dependencies {
    implementation "org.jetbrains.anko:anko:$anko_version"
}

Anko Commons

์˜์กด์„ฑ

  • Anko Commons์€ ์•ˆ๋“œ๋กœ์ด๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž‘์„ฑํ•  ๋•Œ ์ผ๋ฐ˜์ ์œผ๋กœ ์ž์ฃผ ๊ตฌํ˜„ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • Anko Commons์„ ์‚ฌ์šฉํ•˜์—ฌ๋ฉด ์ด๋ฅผ ์‚ฌ์šฉํ•  ๋ชจ๋“ˆ์˜ ๋นŒ๋“œ์Šคํฌ๋ฆฝํŠธ์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
// build.gradle

android {
    ...
}

dependencies {

    // Anko Commons ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    implementation "org.jetbrains.anko:anko-commons:0.10.2"

    ...
}
  • ์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋œ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ํ•„์š”์— ๋”ฐ๋ผ anko-appcompat-v7-commons ํ˜น์€ anko=support-v4-commons์„ ๋นŒ๋“œ์Šคํฌ๋ฆฝํŠธ ๋‚ด ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
// build.gradle

android {
    ...
}

dependencies {

    // Anko Commons ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    implementation "org.jetbrains.anko:anko-commons:0.10.2"

    // appcompat-v7์šฉ Anko Commons ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    implementation "org.jetbrains.anko:anko-appcompat-v7-commons:0.10.2"

    // support-v4์šฉ Anko Commons ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    implementation "org.jetbrains.anko:anko-support-v4-commons:0.10.2"
}

ํ† ์ŠคํŠธ ํ‘œ์‹œํ•˜๊ธฐ

  • toast() ๋ฐ toastLong() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ† ์ŠคํŠธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ฐ„ํŽธํ•˜๊ฒŒ ํ‘œ์‹œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ† ์ŠคํŠธ๋ฅผ ํ‘œ์‹œํ•˜๋ ค๋ฉด Context ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, ์ด ํด๋ž˜์Šค ํ˜น์€ ์ด๋ฅผ ์ƒ์†ํ•˜๋Š” ํด๋ž˜์Šค(์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ)๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋‹ค์Œ ์ฝ”๋“œ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
// Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_SHORT).show()
toast("Hello, Kotlin!")

// ๋‹ค์Œ ์ฝ”๋“œ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
// Toast.makeText(Context, R.string.hello, Toast.LENGTH_SHORT).show()
toast(R.string.hello)

// ๋‹ค์Œ ์ฝ”๋“œ์™€ ๋™์ผํ•œ ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
// Toast.makeText(Context, "Hello, Kotlin!", Toast.LENGTH_LONG).show()
longToast("Hello, Kotlin!)

๋‹ค์ด์–ผ๋กœ๊ทธ ์ƒ์„ฑ ๋ฐ ํ‘œ์‹œํ•˜๊ธฐ

  • alert() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด AlertDialog๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ† ์ŠคํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ Context ํด๋ž˜์Šค ํ˜น์€ ์ด๋ฅผ ์ƒ์†ํ•˜๋Š” ํด๋ž˜์Šค(์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ) ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋‹ค์ด์–ผ๋กœ๊ทธ์˜ ์ œ๋ชฉ๊ณผ ๋ณธ๋ฌธ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
alert(title = "Message", message = "Let's learn Kotlin!") {

    // AlertDialog.Builder.setPositiveButton()์— ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค.
    positiveButton("Yes") {
        // ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
        toast("Yay!")
    }

    // AlertDialog.Builder.setNegativeButton()์— ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค.
    negativeButton("No") {
        // ๋ฒ„ํŠผ์„ ํด๋ฆญํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
        longToast("No way...")
    }
}.show()
  • ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๊ฐ€ ์•„๋‹Œ ์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ(android.support.v7.app.AlertDialog)๋ฅผ ์ƒ์„ฑํ•˜๋ ค๋ฉด,
  • anko-appcompat-v7-commons์„ ์˜์กด์„ฑ์— ์ถ”๊ฐ€ํ•œ ํ›„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด Appcompat์„ ํ•จ๊ป˜ ์ธ์ž์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
// import ๋ฌธ์ด ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค.
import org.jetbrains.anko.appcompat.v7.Appcompat

// Appcompat์„ ์ธ์ž์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
alert(Appcompat, title = "Message", message = "Let's learn Kotlin!") {
    ...
}.show()
  • ์—ฌ๋Ÿฌ ํ•ญ๋ชฉ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•˜๋„๋ก ํ•  ๋–„ ์‚ฌ์šฉํ•˜๋Š” ๋ฆฌ์ŠคํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š” selector() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋‹ค์ด์–ผ๋กœ๊ทธ์— ํ‘œ์‹œํ•  ๋ชฉ๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
val cities = listOf("Seoul", "Tokyo", "Mountain View", "Singapore")

// ๋ฆฌ์ŠคํŠธ ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
selector(title = "Select City", items = cities) { dig, selection -> 
    
    // ํ•ญ๋ชฉ์„ ์„ ํƒํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
    toast("You selected ${cities[selection]}!")
}
  • ์ž‘์—…์˜ ์ง„ํ–‰ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ํ”„๋กœ๊ทธ๋ ˆ์Šค ๋‹ค์ด์–ผ๋กœ๊ทธ๋Š”progressDialog()์™€ indeterminateProgressDialog() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • progressDialog() ํ•จ์ˆ˜๋Š” ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œ ์ƒํƒœ์™€ ๊ฐ™์ด ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•ด์•ผ ํ•˜๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • indeterminateProgressDialog() ํ•จ์ˆ˜๋Š” ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
// ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•˜๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
val pd = progressDialog(title = "File Download", message = "Downloading...")

// ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
pd.show()

// ์ง„ํ–‰๋ฅ ์„ 50์œผ๋กœ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
pd.progress = 50

// ์ง„ํ–‰๋ฅ ์„ ํ‘œ์‹œํ•˜์ง€ ์•Š๋Š” ๋‹ค์ด์–ผ๋กœ๊ทธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.
indenterminateProgressDialog(message = "Please wait...").show()

์ธํ…ํŠธ ์ƒ์„ฑ ๋ฐ ์‚ฌ์šฉํ•˜๊ธฐ

  • ์ธํ…ํŠธ๋Š” ์ปดํฌ๋„ŒํŠธ ๊ฐ„์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•  ๋•Œ์—๋„ ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ฃผ๋กœ ์•กํ‹ฐ๋น„ํ‹ฐ๋‚˜ ์„œ๋น„์Šค๋ฅผ ์‹คํ–‰ํ•˜๋Š” ์šฉ๋„๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ์ธํ…ํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์ด ์ธํ…ํŠธ๋Š” ๋Œ€์ƒ ์ปดํฌ๋„ŒํŠธ์— ๋Œ€ํ•œ ์ •๋ณด์™€ ๊ธฐํƒ€ ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
// DetailActivity ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ ๋Œ€์ƒ ์ปดํฌ๋„ŒํŠธ๋กœ ์ง€์ •ํ•˜๋Š” ์ธํ…ํŠธ
val intent = Intent(this, DetailActivity::class.java)

// DetailActivity๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
startActivity(intent)
  • ์ด ์ธํ…ํŠธ์— ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒฝ์šฐ ์ธํ…ํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
val intent = Intent(this, DetailActivity::class.java)

// ์ธํ…ํŠธ์— ๋ถ€๊ฐ€์ •๋ณด๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
intent.putExtra("id", 150L)
intent.putExtra("title", "Awesome item")

// ์ธํ…ํŠธ์— ํ”Œ๋ž˜๊ทธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
intent.setFlag(Intent.FLAG_ACTIVITY_NO_HISTORY)
  • intentFor() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ๊ฐ„์†Œํ•œ ํ˜„ํƒœ๋กœ ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๋Š” ์ธํ…ํŠธ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
val intent = intentFor<DetailActivity>(
    // ๋ถ€๊ฐ€ ์ •๋ณด๋ฅผ Pair ํ˜•ํƒœ๋กœ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    "id" to 150L, "title" to "Awesome item")

    // ์ธํ…ํŠธ ํ”Œ๋ž˜๊ทธ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
    .noHistory()
  • ์ธํ…ํŠธ์— ํ”Œ๋ž˜๊ทธ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, startActivity() ํ•จ์ˆ˜๋‚˜ startService() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ธํ…ํŠธ ์ƒ์„ฑ๊ณผ ์ปดํฌ๋„ŒํŠธ ํ˜ธ์ถœ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋“ค ํ•จ์ˆ˜๋Š” ๋ชจ๋‘ Context ํด๋ž˜์Šค๋ฅผ ํ•„์š”๋กœ ํ•˜๋ฏ€๋กœ, ์ด ํด๋ž˜์Šค ํ˜น์€ ์ด๋ฅผ ์ƒ์†ํ•˜๋Š” ํด๋ž˜์Šค(์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ) ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// ๋ถ€๊ฐ€์ •๋ณด ์—†์ด DetailActivity๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
startActivity<DetailActivity>()

// ๋ถ€๊ฐ€์ •๋ณด๋ฅผ ํฌํ•จํ•˜์—ฌ DetailActivity๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
startActivity<DetailActivity>("id" to 150L, "title" to "Awesome item")

// ๋ถ€๊ฐ€์ •๋ณด ์—†์ด DataSyncService๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
startService<DataSyncService>()

// ๋ถ€๊ฐ€์ •๋ณด๋ฅผ ํฌํ•จํ•˜์—ฌ DataSyncService๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
startService<DataSyncService>("id" to 1000L)
  • ์ด ์™ธ์—๋„, ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํŠน์ • ์ž‘์—…์„ ๋ฐ”๋กœ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
// ์ „ํ™”๋ฅผ ๊ฑฐ๋Š” ์ธํ…ํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
makeCall(number = "01012345678")

// ๋ฌธ์ž๋ฉ”์‹œ์ง€๋ฅผ ๋ฐœ์†กํ•˜๋Š” ์ธํ…ํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
sendSMS(number = "01012345678", text = "Hello, Kotlin!")

// ์›น ํŽ˜์ด์ง€๋ฅผ ์—ฌ๋Š” ์ธํ…ํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
browse(url = "https://google.com")

// ์ด๋ฉ”์ผ์„ ๋ฐœ์†กํ•˜๋Š” ์ธํ…ํŠธ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
email(email = "[email protected]", subject = "Hello, Taeho Kim", text = "How are you?")

๋กœ๊ทธ ๋ฉ”์‹œ์ง€ ๊ธฐ๋กํ•˜๊ธฐ

  • ์•ˆ๋“œ๋กœ์ด๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋กœ๊ทธ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•˜๋ ค๋ฉด android.util.Log ํด๋ž˜์Šค์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๋กœ๊ทธ๋ฅผ ๊ธฐ๋กํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ๋งค๋ฒˆ ํƒœ๊ทธ๋ฅผ ํ•จ๊ป˜ ์ž…๋ ฅํ•ด์•ผ ํ•˜๋ฏ€๋กœ ๋‹ค์†Œ ๋ถˆํŽธํ•ฉ๋‹ˆ๋‹ค.
  • Anko ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” AnkoLogger๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ›จ์”ฌ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • AnkoLogger์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด android.util.Log ํด๋ž˜์Šค์˜ ๋กœ๊ทธ ๊ธฐ๋ก ๋ฉ”์„œ๋“œ์— ๋Œ€์‘ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
android.util.Log AnkoLogger
v() verbose()
d() debug()
i() info()
w() warn()
e() error()
wtf() wtf()
  • AnkoLogger๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ด๋ฅผ ์‚ฌ์šฉํ•  ํด๋ž˜์Šค์—์„œ AnkoLogger ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • AnkoLogger ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ ์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ์˜ ์‚ฌ์šฉ ์˜ˆ๋Š” ๋‹ค์Œ ์ฝ”๋“œ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ถœ๋ ฅํ•  ๋ฉ”์‹œ์ง€์˜ ํƒ€์ž…์œผ๋กœ String๋งŒ ํ—ˆ์šฉํ•˜๋Š” android.util.Log ํด๋ž˜์Šค์™€ ๋‹ฌ๋ฆฌ ๋ชจ๋“  ํƒ€์ž…์„ ํ—ˆ์šฉํ•˜๋Š” ๋ชจ์Šต์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// AnkoLogger ์ธํ„ฐํŒจ์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค.
class MainActivity: AppCompatActivity(), AnkoLogger {

    fun doSomething() {
        // Log.INFO ๋ ˆ๋ฒจ๋กœ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
        info("doSomething() called")
    }

    fun doSomethingWithParameter(number: Int) {
        // Log.DEBUG ๋ ˆ๋ฒจ๋กœ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
        // String ํƒ€์ž…์ด ์•„๋‹Œ ์ธ์ž๋Š” ํ•ด๋‹น ์ธ์ž์˜ toString() ํ•จ์ˆ˜ ๋ณ€ํ™˜๊ฐ’์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค.
        debug(number)
    }
    ...
}
  • AnkoLogger์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ ๋ฉ”์‹œ์ง€๋ฅผ ๊ธฐ๋กํ•˜๋Š” ๊ฒฝ์šฐ, ๋กœ๊ทธ ํƒœ๊ทธ๋กœ ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ํด๋ž˜์Šค์˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์•ž์˜ ์˜ˆ์ œ์—์„œ๋Š” "MainActivity"๋ฅผ ๋กœ๊ทธ ํƒœ๊ทธ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋กœ๊ทธ ํƒœ๊ทธ๋ฅผ ๋ฐ”๊พธ๊ณ  ์‹ถ๋‹ค๋ฉด loggerTag ํ”„๋กœํผํ‹ฐ๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
class MainActivity: AppCompatActivity(), AnkoLogger {
    // ์ด ํด๋ž˜์Šค ๋‚ด์—์„œ ์ถœ๋ ฅ๋˜๋Š” ๋กœ๊ทธ ํƒœ๊ทธ๋ฅผ "Main"์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    override val loggerTag: String
        get() = "Main"

    ...
}

๋‹จ์œ„ ๋ณ€ํ™˜ํ•˜๊ธฐ

  • ์•ˆ๋“œ๋กœ์ด๋“œ๋Š” ๋‹ค์–‘ํ•œ ๊ธฐ๊ธฐ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ํ”ฝ์…€(px) ๋‹จ์œ„ ๋Œ€์‹  dip(ํ˜น์€ dp; device independent pixels)๋‚˜ sp(scale independent pixels)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • dp๋‚˜ sp ๋‹จ์œ„๋Š” ๊ฐ ๋‹จ๋ง๊ธฐ์˜ ํ™”๋ฉด ํฌ๊ธฐ๋‚˜ ๋ฐ€๋„์— ๋”ฐ๋ผ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ํฌ๊ธฐ๋ฅผ ์ผ์ • ๋น„์œจ๋กœ ์กฐ์ •ํ•˜๋ฏ€๋กœ, ๋‹ค์–‘ํ•œ ํ™”๋ฉด ํฌ๊ธฐ๋‚˜ ๋ฐ€๋„๋ฅผ ๊ฐ€์ง„ ๋‹จ๋ง๊ธฐ์— ๋Œ€์‘ํ•˜๋Š” UI๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ปค์Šคํ…€ ๋ทฐ ๋‚ด๋ทฐ์™€ ๊ฐ™์ด ๋ทฐ์— ํ‘œ์‹œ๋˜๋Š” ์š”์†Œ์˜ ํฌ๊ธฐ๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋‹ค๋ฃจ๋Š” ๊ฒฝ์šฐ dp๋‚˜ sp๋‹จ์œ„๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ๋ณต์žกํ•œ ๊ณผ์ •์„ ๊ฑฐ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
class MainActivity : AppCompatActivity() {
    fun doSomething() {
        // 100dp๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        val dpInPixel = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP, 100f, resources.displayMetrics)

        // 16sp๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        val spInPixel = TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_SP, 16f, resources.displayMetrics)
    }
    ...
}
  • Anko์—์„œ ์ œ๊ณตํ•˜๋Š” dip() ๋ฐ sp() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๋Ÿฌํ•œ ๋‹จ์œ„๋ฅผ ๋งค์šฐ ๊ฐ„๋‹จํžˆ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹จ์œ„๋ฅผ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ๋‹จ๋ง๊ธฐ์˜ ํ™”๋ฉด ์ •๋ณด๋ฅผ ๋‹ด๊ณ  ์žˆ๋Š” DisplayMetrics ๊ฐ์ฒด๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ, ์ด ํ•จ์ˆ˜๋“ค์€ ๋‹จ๋ง๊ธฐ ํ™”๋ฉด ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” ํด๋ž˜์Šค์ธ Context๋ฅผ ์ƒ์†ํ•œ ํด๋ž˜์Šค ํ˜น์€ ์ปค์Šคํ…€ ๋ทฐ ํด๋ž˜์Šค ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • dip() ํ•จ์ˆ˜ ๋ฐ sp() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•ž์˜ ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ‘œํ˜„ํ•œ ๋ชจ์Šต์ž…๋‹ˆ๋‹ค.
  • TypedValue.applyDimension() ๋ฉ”์„œ๋“œ๋Š” Float ํ˜• ์ธ์ž๋งŒ ์ง€์›ํ–ˆ์ง€๋งŒ, dip() ๋ฐ sp() ํ•จ์ˆ˜๋Š” Int ํ˜• ์ธ์ž๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
// 100dp๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
val dpInPixel = dip(100)

// 16sp๋ฅผ ํ”ฝ์…€ ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
val spInPixel = sp(16)
  • ๋ฐ˜๋Œ€๋กœ, ํ”ฝ์…€ ๋‹จ์œ„๋ฅผ dp๋‚˜ sp ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜๋„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ๊ฐ px2dip(), px2sp() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
// 300px๋ฅผ dp ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
val pxInDip = px2dip(300)

// 80px๋ฅผ sp ๋‹จ์œ„๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
val pxInSp = px2sp(80)

๊ธฐํƒ€

  • ์—ฌ๋Ÿฌ ๋‹จ๋ง๊ธฐ ํ™˜๊ฒฝ์„ ์ง€์›ํ•˜๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€, ๋‹จ๋ง๊ธฐ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ UI๋ฅผ ๋ณด์—ฌ์ฃผ๋„๋ก ๊ตฌํ˜„ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์Šต๋‹ˆ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ, configuration() ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ๋‹จ๋ง๊ธฐ ํ™˜๊ฒฝ์ผ ๋•Œ๋งŒ ์‹คํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„ ๋‹จ๋ง๊ธฐ ํ™˜๊ฒฝ ์ข…๋ฅ˜
density ํ™”๋ฉด ๋ฐ€๋„
language ์‹œ์Šคํ…œ ์–ธ์–ด
long ํ™”๋ฉด ๊ธธ์ด
nightMode ์•ผ๊ฐ„๋ชจ๋“œ ์—ฌ๋ถ€
orientation ํ™”๋ฉด ๋ฐฉํ–ฅ
rightToLeft RTL(Right-to-Left)๋ ˆ์ด์•„์›ƒ ์—ฌ๋ถ€
screenSize ํ™”๋ฉด ํฌ๊ธฐ
smallestWidth ํ™”๋ฉด์˜ ๊ฐ€์žฅ ์ž‘์€ ๋ณ€์˜ ๊ธธ์ด
uiMode UI ๋ชจ๋“œ(์ผ๋ฐ˜, TV, ์ฐจ๋Ÿ‰, ์‹œ๊ณ„, VR ๋“ฑ)
  • configuration() ๋˜ํ•œ ๋‹จ๋ง๊ธฐ ํ™˜๊ฒฝ์— ์ ‘๊ทผํ•ด์•ผ ํ•˜๋ฏ€๋กœ ์ด ์ •๋ณด์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋Š” Context ํด๋ž˜์Šค ํ˜น์€ ์ด๋ฅผ ์ƒ์†ํ•œ ํด๋ž˜์Šค(์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ)์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
class MainActivity : AppCompatActivity() {
    fun doSomething() {
        configuration(orientation = Orientation.PORTRAIT) {
            // ๋‹จ๋ง๊ธฐ๊ฐ€ ์„ธ๋กœ ๋ฐฉ๋Ÿ‰์ผ ๋•Œ ์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
            ...
        }
        configuration(orientation = Orientation.LANDSCAPE, language = "ko") {
            // ๋‹จ๋ง๊ธฐ๊ฐ€ ๊ฐ€๋กœ ๋ฐฉํ–ฅ์ด๋ฉด์„œ ์‹œ์Šคํ…œ ์–ธ์–ด๊ฐ€ ํ•œ๊ตญ์–ด๋กœ ์„ค์ •๋˜์–ด ์žˆ์„ ๋•Œ
            // ์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
            ...
        }
    }
    ...
}
  • ๋‹จ์ˆœํžˆ ๋‹จ๋ง๊ธฐ์˜ OS ๋ฒ„์ „์— ๋”ฐ๋ผ ๋ถ„๊ธฐ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” ๊ฒฝ์šฐ doFromSdk()์™€ doIfSdk()๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
doFromSdk(Build.VERSION_CODES.0) {
    // ์•ˆ๋“œ๋กœ์ด๋“œ 8.0 ์ด์ƒ ๊ธฐ๊ธฐ์—์„œ ์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    ...
}

doIfSdk(Build.VERSION_CODES.N) {
    // ์•ˆ๋“œ๋กœ์ด๋“œ 7.0 ๊ธฐ๊ธฐ์—์„œ๋งŒ ์ˆ˜ํ–‰ํ•  ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
    ...
}

Anko Layouts

  • ์•ˆ๋“œ๋กœ์ด๋“œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ž‘์„ฑํ•  ๋•Œ, ๋Œ€๋ถ€๋ถ„ XML ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • ์†Œ์Šค ์ฝ”๋“œ(Java ํ˜น์€ Kotlin)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ XML ๋ ˆ์ด์•„์›ƒ์— ๋น„ํ•ด ๋ณต์žกํ•˜๊ณ  ๊นŒ๋‹ค๋ผ๋กœ์›Œ ๋Œ€๋‹ค์ˆ˜์˜ ์‚ฌ๋žŒ๋“ค์ด ์„ ํ˜ธํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ XML๋กœ ์ž‘์„ฑ๋œ ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ด ํŒŒ์ผ์— ์ •์˜๋œ ๋ทฐ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ์ž‘์—…์„ ๋จผ์ € ์ˆ˜ํ–‰ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋•Œ๋ฌธ์— ์†Œ์Šค ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•œ ๊ฒฝ์šฐ์— ๋น„ํ•ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ฑ๋Šฅ์ด ์ €ํ•˜๋˜๊ณ , ํŒŒ์‹ฑ ๊ณผ์ •์—์„œ ์ž์›์ด ๋” ํ•„์š”ํ•œ ๋งŒํผ ๋ฐฐํ„ฐ๋ฆฌ๋„ ๋” ๋งŽ์ด ์†Œ๋ชจํ•ฉ๋‹ˆ๋‹ค.
  • Anko Layouts๋Š” ์†Œ์Šค ์ฝ”๋“œ๋กœ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•  ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์—ฌ๋Ÿฌ ํ•จ์ˆ˜๋“ค์„ ์ œ๊ณตํ•˜๋ฉฐ, ์•„๋ฃฐ ์‚ฌ์šฉํ•˜๋ฉด XML ๋ ˆ์ด์•„์›ƒ์„ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ํŽธ๋ฆฌํ•˜๊ฒŒ ์†Œ์Šค์ฝ”๋“œ๋กœ๋„ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Anko Layouts์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ด๋ฅผ ์‚ฌ์šฉํ•  ๋ชจ๋“ˆ์˜ ๋นŒ๋“œ์Šคํฌ๋ฆฝํŠธ์— ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๋˜๋ฉฐ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ minSdkVersion์— ๋”ฐ๋ผ ์‚ฌ์šฉํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ minSdkVersion์— ๋Œ€์‘ํ•˜๋Š” Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
minSdkVersion Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
15์ด์ƒ 19๋ฏธ๋งŒ anko-sdk15
19์ด์ƒ 21๋ฏธ๋งŒ anko-sdk19
21์ด์ƒ 23๋ฏธ๋งŒ anko-sdk21
23์ด์ƒ 25๋ฏธ๋งŒ anko-sdk23
25์ด์ƒ anko-sdk25
android {
    defaultConfig {
        // minSdkVersion์ด 15๋กœ ์„ค์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.
        minSdkVersion 15
        targetSdkVersion 27
        ...
    }
    ...
}

dependencies {
    // minSdkVersion์— ๋งž์ถ”์–ด Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    compile "org.jetbrains.anko:anko-sdk15:0.10.2"
}
  • ์„œํ”„ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ํฌํ•จ๋œ ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์‚ฌ์šฉํ•˜๋Š” ๋ทฐ๊ฐ€ ํฌํ•จ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€์‘ํ•˜๋Š” Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์˜์กด์„ฑ์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • ๊ฐ ์„œํ”„ํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๋Œ€์‘ํ•˜๋Š” Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
appcompat-v7 anko-appcompat-v7
cardview anko-cardview-v7
design anko-design
gridlayout anko-gridlayout-v7
recyclerview-v7 anko-recyclerview-v7
support-v4 anko-support-v4
  • ๋‹ค์Œ์€ ์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ์ด์— ๋Œ€์‘ํ•˜๋Š” Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์˜์กด์„ฑ์œผ๋กœ ์ถ”๊ฐ€ํ•œ ์˜ˆ์ž…๋‹ˆ๋‹ค.
android {
    ...
}

dependencies {
    // appcompat-v7 ์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€
    implementation "com.android.support:appcompat-v7:27.0.1"

    // appcompat-v7์šฉ Anko Layouts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
    implementation "org.jetbrains.anko:anko-appcompat-v7:0.10.2"
}

DSL๋กœ ํ™”๋ฉด ๊ตฌ์„ฑํ•˜๊ธฐ

  • Anko Layouts์„ ์‚ฌ์šฉํ•˜๋ฉด ์†Œ์Šค ์ฝ”๋“œ์—์„œ ํ™”๋ฉด์„ DSL(Domain Specific Language) ํ˜•ํƒœ๋กœ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค์Œ์€ DSL์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฐ„๋‹จํ•œ ์˜ˆ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. XML ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ •์˜ํ•  ๋•Œ๋ณด๋‹ค ๋” ๊ฐ„๋‹จํ•˜๊ฒŒ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
verticalLayout {
    padding = dip(12)

    textView("Enter Login Credentials")

    editText {
        hint = "E-mail"
    }

    editText {
        hint = "Password"
    }

    button("Submit")
}
  • ์•ž์˜ ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•œ verticalLayout(), textView(), editText(), button()์€ Anko Layout์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋กœ, ๋ทฐ ํ˜น์€ ๋‹ค๋ฅธ ๋ทฐ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ์„ ์ƒ์„ฑํ•˜๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์Œ์€ ์—ฌ๊ธฐ์—์„œ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜ ์ค‘ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํ•จ์ˆ˜ ๋ช‡ ๊ฐœ์˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.
ํ•จ์ˆ˜ ์ƒ์„ฑํ•˜๋Š” ๋ทฐ ๋น„๊ณ 
button() android.widget.Button
checkBox() android.widget.CheckBox
editText() android-widget.EditText
frameLayout() android-widget.FrameLayout
imageView() android-widget-ImageView
linearLayout() android.widget.LinearLayout
radioButton() android.widget.RadioButton
relativeLayout() android-widget.RelativeLayout
switch() android-widget.Switch ์„œํฌํŠธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๋ทฐ๋Š” switchCompat() ์‚ฌ์šฉ
verticalLayout() android-widget-LinearLayout orientation ๊ฐ’์œผ๋กœ LinearLayout.VERTICAL์„ ๊ฐ–๋Š” LinearLayout
webView() android-webkit.WebView
  • XML ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์— XML๋กœ ๊ตฌ์„ฑํ•œ ๋ ˆ์ด์•„์›ƒ์„ ์ €์žฅํ•˜๋“ฏ์ด, DSL๋กœ ๊ตฌ์„ฑํ•œ ๋ทฐ๋Š” AnkoComponent ํด๋ž˜์Šค๋ฅผ ์ปจํ…Œ์ด๋„ˆ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • AnkoComponent์—๋Š” ์ •์˜๋˜์–ด ์žˆ๋Š” ํ™”๋ฉด์„ ํ‘œ์‹œํ•  ๋Œ€์ƒ ์ปดํฌ๋„ŒํŠธ์˜ ์ •๋ณด๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.
  • ๋‹ค์Œ์€ MainActivity ์•กํ‹ฐ๋น„ํ‹ฐ์— ํ‘œ์‹œํ•  ๋ทฐ์˜ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๋Š” AnkoComponent์˜ ์ฝ”๋“œ ์˜ˆ์‹œ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.
class MainActivityUI : AnkoComponent<MainActivity> {

    override fun createView(ui: AnkoContext<MainActivity>) = ui.apply {
        vertivalLayout {
            // LinearLayout์˜ padding์„ 12dp๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
            padding = dip(12)

            // TextView๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
            textView("Enter Login Credentials")

            // EditText๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ , ํžŒํŠธ ๋ฌธ์ž์—ด์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
            editText {
                hint = "E-mail"
            }

            editText {
                hint = "password"
            }

            // ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
            button("Submit")
        }
    }.view
}
  • ์ถ”๊ฐ€๋กœ, ์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ๋Š” AnkoComponent ์—†์ด ์ง์ ‘ ์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด์—์„œ DSL์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋‹ค์Œ์€ ์•ž์˜ ์ฝ”๋“œ์™€ ๋™์ผํ•œ ๋ ˆ์ด์•„์›ƒ์„ AnkoComponent ์—†์ด ๊ตฌ์„ฑํ•˜๋Š” ์˜ˆ์ž…๋‹ˆ๋‹ค. ์ด ๋ฐฉ์‹์œผ๋กœ ํ™”๋ฉด์„ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ setContentView()๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.
class MainActivity: AppcompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // setContentView()๊ฐ€ ์—†์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.
        verticalLayout {
            padding = dip(12)

            textView("Enter Login Credentials")

            editText {
                hint = "E-mail"
            }

            editText {
                hint = "Password"
            }

            button("Submit")
        }
    }
}

ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ์‚ฌ์šฉํ•˜๊ธฐ

  • ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ Anko Layouts์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์œ„ํ•œ AnkoComponent๋ฅผ ๋งŒ๋“ค๊ณ , onCreateView()์—์„œ createView()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜์—ฌ ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ํ™”๋ฉด์œผ๋กœ ์‚ฌ์šฉํ•  ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
  • createView()๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ ค๋ฉด AnkoContext ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ๋งŒ๋“ค์–ด ์ธ์ž๋กœ ์ „๋‹ฌํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
class MainFragment : Fragment()  {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?{
        // AnkoComponent.createView() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋ทฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
        return  MainFragmentUI().createView(AnkoContext.create(context, this))
    }
}

// ํ”„๋ž˜๊ทธ๋จผํŠธ๋ฅผ ์œ„ํ•œ AnkoComponent๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
class MainFragmentUI : AnkoComponent<MainFragment>  {
    override fun createView(ui: AnkoContext<MainFragment>)  = ui.apply  {
        verticalLayout {

            textView("Enter Login Credentials")

            editText {
                hint = "E-mail"
            }

            editText {
                hint = "Password"
            }

            button("Submit")
        }
    }.view
}

Anko Support Plugin

  • Anko Support Plugin์€ Anko์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ€๊ฐ€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” IDE ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค.

  • ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋ ค๋ฉด ์ฝ”ํ‹€๋ฆฐ IDE ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์„ค์น˜ํ•˜๋Š” ๊ณผ์ •๊ณผ ๋™์ผํ•˜๊ฒŒ ์ง„ํ–‰ํ•˜๋ฉด ๋˜๋ฉฐ, ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฒ€์ƒ‰ ๋‹ค์ด์–ผ๋กœ๊ทธ์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด 'Anko Support'๋ฅผ ์„ ํƒํ•˜์—ฌ ์„ค์น˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  • Anko Support Plugin์—์„œ๋Š” AnkoComponent๋กœ ์ž‘์„ฑํ•œ ํ™”๋ฉด์ด ์–ด๋–ป๊ฒŒ ํ‘œ์‹œ๋˜๋Š”์ง€ ๋ฏธ๋ฆฌ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ๋ ˆ์ด์•„์›ƒ ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

  • ๋ ˆ์ด์•„์›ƒ ํ”„๋ฆฌ๋ทฐ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด, ๋จผ์ € ํ”„๋ฆฌ๋ทฐ ๊ธฐ๋Šฅ์œผ๋กœ ํ™•์ธํ•˜๊ณ  ์‹ถ์€ AnkoComponent๊ฐ€ ๊ตฌํ˜€๋˜์–ด ์žˆ๋Š” ํŒŒ์ผ์„ ์—ฐ ํ›„ AnkoComponent์˜ ๊ตฌํ˜„๋ถ€ ๋‚ด๋ถ€ ์•„๋ฌด๊ณณ์— ์ปค์„œ๋ฅผ ๋‘ก๋‹ˆ๋‹ค.

  • ๊ทธ ๋‹ค์Œ, [View > Tools Windows > Anko Layout Preview]๋ฅผ ์„ ํƒํ•˜์—ฌ ๋ ˆ์ด์ด์›ƒ ํ”„๋ฆฌ๋ทฐ ์ฐฝ์„ ๋„์›๋‹ˆ๋‹ค.

  • ๋ ˆ์ด์•„์›ƒ ํ”„๋ฆฌ๋ทฐ ์ฐฝ์€ XML ๋ ˆ์ด์•„์›ƒ ํ”„๋ฆฌ๋ทฐ ์ฐฝ๊ณผ ๊ฑฐ์˜ ์œ ์‚ฌํ•œ ํ˜•ํƒœ๋กœ ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์•ž์—์„œ ์„ ํƒํ•œ AnkoComponent์˜ ๋ ˆ์ด์•„์›ƒ ํ”„๋ฆฌ๋ทฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋ฉฐ, ํ™”๋ฉด์ด ํ‘œ์‹œ๋˜์ง€ ์•Š๊ฑฐ๋‚˜ ๋ฐ”๋€ ๋‚ด์šฉ์ด ๋ฐ˜์˜๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด ํ”„๋กœ์ ํŠธ๋ฅผ ๋‹ค์‹œ ๋นŒ๋“œํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Anko SQLite

  • Android ์ปค์„œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ SQLite ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ๊ฒƒ์€ ํž˜๋“ญ๋‹ˆ๋‹ค.
  • ์ฟผ๋ฆฌ์˜ ๊ฒฐ๊ณผ ํ–‰์„ ๊ตฌ๋ฌธ ๋ถ„์„ํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ ์ƒ์šฉ๊ตฌ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์ด๋ฅผ ์—ด๊ฑฐ ๋œ ๋ชจ๋“  ๋ฆฌ์†Œ์Šค๋ฅผ ์ ์ ˆํ•˜๊ฒŒ ๋‹ซ์œผ๋ ค๋ฉด ์…€ ์ˆ˜์—†๋Š” try..finally ๋ธ”๋ก์œผ๋กœ ๋ฌถ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • Anko๋Š” SQLite databases์™€ ํ•จ๊ป˜ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Using Anko SQLite in your project

  • build.gradle์˜ dependency์— anko-sqlite๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
dependencies {
    implementation "org.jetbrains.anko:anko-sqlite:$anko_version"
}

Accessing the database

  • SQLiteOpenHelper๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ์ผ๋ฐ˜์ ์œผ๋กœ getReadableDatabase () ๋˜๋Š” getWritableDatabase ()๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ๋Š” ์‹ค์ œ๋กœ ํ”„๋กœ๋•์…˜ ์ฝ”๋“œ์—์„œ ๋™์ผํ•˜์ง€๋งŒ ์ˆ˜์‹  ๋œ SQLiteDatabase์—์„œ close () ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ์–ด๋”˜๊ฐ€์— ๋„์šฐ๋ฏธ ํด๋ž˜์Šค๋ฅผ ์บ์‹œํ•ด์•ผํ•˜๋ฉฐ ์—ฌ๋Ÿฌ ์Šค๋ ˆ๋“œ์—์„œ์ด ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ ๋™์‹œ ์•ก์„ธ์Šค๋ฅผ ์ธ์‹ํ•˜๊ณ  ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • ์ด ๋ชจ๋“  ๊ฒƒ์€ ๊ฝค ํž˜๋“ญ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•ˆ๋“œ๋กœ์ด๋“œ ๊ฐœ๋ฐœ์ž๋Š” ๋””ํดํŠธ SQLite API์— ์—ด์ค‘ํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹  ORM๊ณผ ๊ฐ™์€ ๊ฐ’ ๋น„์‹ผ ๋ž˜ํผ๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.
  • Anko๋Š” ๊ธฐ๋ณธ ํด๋ž˜์Šค๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ๋Œ€์ฒดํ•˜๋Š” ManagedSQLiteOpenHelper ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
class MyDatabaseOpenHelper(ctx: Context) : ManagedSQLiteOpenHelper(ctx, "MyDatabase", null, 1) {
    companion object {
        private var instance: MyDatabaseOpenHelper? = null

        @Synchronized
        fun getInstance(ctx: Context): MyDatabaseOpenHelper {
            if (instance == null) {
                instance = MyDatabaseOpenHelper(ctx.getApplicationContext())
            }
            return instance!!
        }
    }

    override fun onCreate(db: SQLiteDatabase) {
        // Here you create tables
        db.createTable("Customer", true, 
                    "id" to INTEGER + PRIMARY_KEY + UNIQUE,
                    "name" to TEXT,
                    "photo" to BLOB)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        // Here you can upgrade tables, as usual
        db.dropTable("User", true)
    }
}

// Access property for Context
val Context.database: MyDatabaseOpenHelper
    get() = MyDatabaseOpenHelper.getInstance(getApplicationContext())
  • try ๋ธ”๋ก์— ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ๋Œ€์‹  ์ด์ œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
database.use {
    // 'this' is a SQLiteDatabase instance
}
  • {} ์•ˆ์— ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ ํ•œ ํ›„์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์™„์ „ํžˆ ๋‹ซํž™๋‹ˆ๋‹ค.

  • ๋น„๋™๊ธฐ ํ˜ธ์ถœ ์˜ˆ์ œ :

class SomeActivity : Activity() {
    private fun loadAsync() {
        async(UI) {
            val result = bg { 
                database.use { ... }
            }
            loadComplete(result)
        }
    }
}
  • ์•„๋ž˜ ์–ธ๊ธ‰๋œ ๋ฉ”์„œ๋“œ๋“ค๊ณผ ๋ชจ๋“  ๋ฉ”์„œ๋“œ๋ฅผ์€ SQLiteException์— throw ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • Anko๊ฐ€ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ฒ˜๋Ÿผ ๊ฐ€์žฅํ•˜๋Š” ๊ฒƒ์€ ๋ฌด๋ฆฌ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์ง์ ‘ ์ฒ˜๋ฆฌํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

Creating and dropping tables

  • Anko์™€ ํ•จ๊ป˜๋ผ๋ฉด ์‰ฝ๊ฒŒ ์ƒˆ๋กœ์šด ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค๊ณ  ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ตฌ๋ฌธ์€ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.
database.use {
    createTable("Customer", true,
    "id" to INTEGER + PRIMARY_KEY + UNIQUE, 
    "name" to TEXT,
    "photo" to BLOB)
}
  • SQLite์—์„œ๋Š” NULL, INTEGER, REAL, TEXT, BLOB ์ด 5๊ฐœ์˜ ๋ฉ”์ธ ํƒ€์ž…๋“ค์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ๊ฐ ์—ด์—๋Š” PRIMATY KEY ๋˜๋Š” UNIQUE์™€ ๊ฐ™์€ ์ˆ˜์ •์ž๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ˆ˜์ •์ž๋Š” ๊ธฐ๋ณธ ์œ ํ˜• ์ด๋ฆ„์— "์ถ”๊ฐ€"์™€ ํ•จ๊ป˜ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ํ…Œ์ด๋ธ”์„ ์ง€์šธ ๋•Œ์—๋Š” dropTable ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
dropTable("User", true)

Inserting data

  • ์ผ๋ฐ˜์ ์œผ๋กœ ํ…Œ์ด๋ธ”์— ํ–‰์„ ์‚ฝ์ž…ํ•˜๋ ค๋ฉด ContentValues โ€‹โ€‹์ธ์Šคํ„ด์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ๊ทธ ์˜ˆ์ž…๋‹ˆ๋‹ค.
val values = ContentValues()
values.put("id", 5)
values.put("name", "John Smith")
values.put("email", "[email protected]")
db.insert("User", null, values)
  • Anko๋Š” insert() ํ•จ์ˆ˜์˜ ์ธ์ž๋กœ ๊ฐ’์„ ์ง์ ‘ ์ „๋‹ฌํ•˜์—ฌ ์ด๋Ÿฌํ•œ ์ฝ”๋“œ์˜ ๊ฑฐ์น˜๋ž˜์„ ์ œ๊ฑฐ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
// Where db is an SQLiteDatabase
// eg: val db = database.writeableDatabase
db.insert("User", 
    "id" to 42,
    "name" to "John",
    "email" to "[email protected]"
)
  • database.use๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด
database.use {
    insert("User",
        "id" to 42,
        "name" to "John",
        "email" to "[email protected]"
    )
}

Querying data

  • Anko๋Š” ํŽธ๋ฆฌํ•œ ์ฟผ๋ฆฌ ๋นŒ๋”๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. db.select (tableName, vararg columns)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. db๋Š” SQLiteDatabase์˜ ์ธ์Šคํ„ด์Šค์ž…๋‹ˆ๋‹ค.
ํ•จ์ˆ˜ ๊ธฐ๋Šฅ
column(String) ๊ฒ€์ƒ‰์–ด ์„ ํƒํ•˜๋Š” ์—ด ์ถ”๊ฐ€ํ•˜๊ธฐ
distinct(Boolean) ๊ณ ์œ ํ•œ ์ฟผ๋ฆฌ
whereArgs(String) ์›์‹œ ๋ฌธ์ž์—ด where ์ฟผ๋ฆฌ๋ฅผ ์ง€์ •ํ•˜๊ธฐ
whereArgs(String, args) (์ค‘์š”) ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌwhere ์ฟผ๋ฆฌ๋ฅผ ์ง€์ •ํ•˜๊ธฐ
whereSimple(String, args) ? '๋งˆํฌ ์ธ์ž๋ฅผ ๊ฐ€์ง„where` ์ฟผ๋ฆฌ๋ฅผ ์ง€์ •ํ•˜๊ธฐ
orderBy(String, [ASC/DESC]) ์ด ์—ด์˜ ์ˆœ์„œ
groupBy(String) ์ด ์—ด์˜ ๊ทธ๋ฃนํ™”ํ•˜๊ธฐ
limit(count: Int) ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ํ–‰ ์ˆ˜ ์ œํ•œํ•˜๊ธฐ
limit(offset: Int, count: Int) ์˜คํ”„์…‹์ด์žˆ๋Š” ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ ํ–‰ ์ˆ˜ ์ œํ•œํ•˜๊ธฐ
having(String) ์›์‹œ 'having'ํ‘œํ˜„์‹ ์ง€์ •ํ•˜๊ธฐ
having(String, args) (์ค‘์š”) ์ธ์ž๋กœ `having' ํ‘œํ˜„์‹์„ ์ง€์ •ํ•˜๊ธฐ
  • (์ค‘์š”) ๋กœ ํ‘œ์‹œ๋œ ํ•จ์ˆ˜๋Š” ํŠน๋ณ„ํ•œ ๋ฐฉ๋ฒ•์œผ๋กœ ์ธ์ž๋ฅผ ๊ตฌ๋ฌธ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ์ˆœ์„œ๋กœ๋“  ๊ฐ’์„ ์ œ๊ณตํ•˜๊ณ  ์›ํ™œํ•œ escaping์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
db.select("User", "name")
    .whereArgs("(_id >  {userId}) and (name = {userName})",
        "userName" to "John", 
        "userId" to 42)
  • ์—ฌ๊ธฐ์—์„œ {userId} ๋ถ€๋ถ„์€ 42, {userName}์€ 'John'์œผ๋กœ ๋ฐ”๋€๋‹ˆ๋‹ค. ํ˜•์‹์ด ์ˆซ์ž๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ (Int, Float ๋“ฑ) ๋˜๋Š” Boolean ๊ฐ’์ด๋ฉด escaped๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ํ˜•์‹์˜ ๊ฒฝ์šฐ toString() ํ‘œํ˜„์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
  • whereSimple ํ•จ์ˆ˜๋Š” String ์ž๋ฃŒํ˜•์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ SQLiteDatavase์˜ query() ์™€ ๊ฐ™์€ ์ผ์„ ํ•ฉ๋‹ˆ๋‹ค. (์งˆ๋ฌธ ํ‘œ์‹œ ?๋Š” ์ž๋ฃŒํ˜•์˜ ์‹ค์ œ ๊ฐ’๊ณผ ๋Œ€์ฒด๋ฉ๋‹ˆ๋‹ค.)
  • ์ด ์ฟผ๋ฆฌ๋ฅผ ์–ด๋–ป๊ฒŒ ์‹คํ–‰ํ• ๊นŒ์š”? exec() ํ•จ์ˆ˜๋ฅผ ์”๋‹ˆ๋‹ค. Cursor. () -> T ํ˜•์‹์˜ ํ™•์žฅ ๊ธฐ๋Šฅ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์‹ ํ•œ ํ™•์žฅ ๊ธฐ๋Šฅ์„ ์‹œ์ž‘ํ•œ ๋‹ค์Œ Cursor๋ฅผ ๋‹ซ์•„ ์Šค์Šค๋กœ ์ˆ˜ํ–‰ํ•  ํ•„์š”๊ฐ€ ์—†๋„๋กํ•ฉ๋‹ˆ๋‹ค.
db.select("User", "email").exec {
    // Doing some stuff with emails
}

Parsing query results

  • ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” Cursor๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๊ทธ๊ฒƒ์„ ์ •๊ทœ ํด๋ž˜์Šค๋กœ ์–ด๋–ป๊ฒŒ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?
  • Anko๋Š” parseSingle, parseOpt ๋ฐ parseList ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜์—ฌ ํ›จ์”ฌ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌ ํ•  ์ˆ˜ โ€‹โ€‹์žˆ์Šต๋‹ˆ๋‹ค.
Method Description
parseSingle(rowParser): T Parse exactly one row
parseOpt(rowParser): T? Parse zero or one row
parseList(rowParser): List Parse zero or more rows
  • ์ˆ˜์‹  ๋œ Cursor๊ฐ€ ๋‘˜ ์ด์ƒ์˜ ํ–‰์„ ํฌํ•จํ•˜๋ฉด parseSingle () ๋ฐ parseOpt ()๊ฐ€ ์˜ˆ์™ธ๋ฅผ throwํ•ฉ๋‹ˆ๋‹ค.
  • rowParser ๋ž€ ๋ฌด์—‡์ผ๊นŒ์š”? ๊ฐ ํ•จ์ˆ˜๋Š” RowParser์™€ MapRowParser์ธ ๋‘ ๊ฐ€์ง€ ์œ ํ˜•์˜ ํŒŒ์„œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
interface RowParser<T> {
    fun parseRow(columns: Array<Any>): T
}

interface MapRowParser<T> {
    fun parseRow(columns: Map<String, Any>): T
}
  • ๋งค์šฐ ํšจ์œจ์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด RowParser๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค (ํ•˜์ง€๋งŒ ๊ฐ ์—ด์˜ ์ธ๋ฑ์Šค๋ฅผ ์•Œ์•„์•ผํ•ฉ๋‹ˆ๋‹ค).

  • parseRow๋Š” Any์˜ ํƒ€์ž…์„ ๋ฐ›์•„๋“ค์ž…๋‹ˆ๋‹ค (Any ํ˜•์€ Long, Double, String ๋˜๋Š” ByteArray ์ด์™ธ์˜ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์Œ).

  • ๋ฐ˜๋ฉด์— MapRowParser๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ด ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์—ฌ ํ–‰ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Anko๋Š” ์ด๋ฏธ ๊ฐ„๋‹จํ•œ ๋‹จ์ผ ์—ด ํ–‰์— ๋Œ€ํ•œ ํŒŒ์„œ๋ฅผ ๋ณด์œ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

    • ShortParser
    • IntParser
    • LongParser
    • FloatParser
    • DoubleParser
    • StringParser
    • BlobParser
  • ๋˜ํ•œ ํด๋ž˜์Šค ์ƒ์„ฑ์ž์—์„œ ํ–‰ ํŒŒ์„œ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค.

class Person(val firstName: String, val lastName: String, val age: Int)
  • ํŒŒ์„œ๋Š” ๊ฐ„๋‹จํ•ด์ง‘๋‹ˆ๋‹ค.
val rowParser = classParser<Person>()
  • ํ˜„์žฌ๋กœ์„œ๋Š” ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์— ์„ ํƒ์  ๋งค๊ฐœ ๋ณ€์ˆ˜๊ฐ€์žˆ๋Š” ๊ฒฝ์šฐ Anko๋Š” ์ด๋Ÿฌํ•œ ํŒŒ์„œ ์ž‘์„ฑ์„ ์ง€์›ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ ์ƒ์„ฑ์ž๋Š” Java Reflection์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜ธ์ถœ๋˜๋ฏ€๋กœ ์ปค๋‹ค๋ž€ ๋ฐ์ดํ„ฐ ์„ธํŠธ์˜ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž ์ •์˜ RowParser๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๊ฒƒ์ด ๋” ํ•ฉ๋ฆฌ์ ์ž…๋‹ˆ๋‹ค.
  • Anko db.select () ๋นŒ๋”๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” parseSingle, parseOpt ๋˜๋Š” parseList๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ณ  ์ ์ ˆํ•œ ํŒŒ์„œ๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Custom row parsers

  • ์˜ˆ๋ฅผ ๋“ค์–ด, ์—ด (Int, String, String)์— ๋Œ€ํ•ด ์ƒˆ ํŒŒ์„œ๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค. ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
class MyRowParser : RowParser<Triple<Int, String, String>> {
    override fun parseRow(columns: Array<Any>): Triple<Int, String, String> {
        return Triple(columns[0] as Int, columns[1] as String, columns[2] as String)
    }
}
  • ์ž, ์ด์ œ ์ฝ”๋“œ์— 3๊ฐ€์ง€ ๋ช…์‹œ์  ์บ์ŠคํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. rowParser ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ œ๊ฑฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.
val parser = rowParser { id: Int, name: String, email: String ->
    Triple(id, name, email)
}
  • ์ด๊ฒŒ ๋‹ค ์ž…๋‹ˆ๋‹ค. rowParser๋Š” ๋ชจ๋“  ์บ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์›ํ•˜๋Š”๋Œ€๋กœ ๋žŒ๋‹ค ๋งค๊ฐœ ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Cursor streams

  • Anko๋Š” SQLite Cursor์— ๊ธฐ๋Šฅ์ ์œผ๋กœ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
  • cursor.asSequence () ๋˜๋Š” cursor.asMapSequence () ํ™•์žฅ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ผ๋ จ์˜ ํ–‰์„ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ์ปค์„œ๋ฅผ ๋‹ซ๋Š” ๊ฒƒ์„ ์žŠ์ง€ ๋งˆ์„ธ์š” :)

Updating values

  • ์‚ฌ์šฉ์ž ์ค‘ ํ•œ ๋ช…์—๊ฒŒ ์ƒˆ๋กœ์šด ์ด๋ฆ„์„ ์ค๋‹ˆ๋‹ค.
update("User", "name" to "Alice")
    .where("_id = {userId}", "userId" to 42)
    .exec()
  • ๋˜ํ•œ ์ „ํ†ต์ ์ธ ๋ฐฉ์‹์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๋ ค๋Š” ๊ฒฝ์šฐ update์—๋Š” whereSimple () ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
update("User", "name" to "Alice")
    .`whereSimple`("_id = ?", 42)
    .exec()

Delete Data

  • ํ–‰์„ ์‚ญ์ œ ํ•ด ๋ด…์‹œ๋‹ค (delete ๋ฉ”์†Œ๋“œ์—๋Š” whereSimple () ๋ฉ”์†Œ๋“œ๊ฐ€ ์—†์œผ๋ฉฐ, ๋Œ€์‹  ์ธ์ˆ˜์— ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค).
val numRowsDeleted = delete("User", "_id = {userID}", "userID" to 37)

Transaction

  • transaction ()์ด๋ผ๋Š” ํŠน๋ณ„ํ•œ ํ•จ์ˆ˜๊ฐ€ ์žˆ๋Š”๋ฐ, ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ์‚ฐ์„ ํ•˜๋‚˜์˜ SQLite ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๋ฌถ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
transaction {
    // Your transaction code
}
  • {} ๋ธ”๋ก ๋‚ด์— ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด ํŠธ๋žœ์žญ์…˜์€ ์„ฑ๊ณต์œผ๋กœ ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.
  • ์–ด๋–ค ์ด์œ ๋กœ ํŠธ๋žœ์žญ์…˜์„ ์ค‘๋‹จํ•˜๋ ค๋ฉด TransactionAbortException์„ throw ํ•˜์„ธ์š”. ์ด ๊ฒฝ์šฐ์—๋Š”์ด ์˜ˆ์™ธ๋ฅผ ์ง์ ‘ ์ฒ˜๋ฆฌ ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

Anko Coroutines

Using Anko Coroutines in your project

  • build.gradle์˜ dependency์— anko-coroutines๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
dependencies {
    implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
}

Listener helpers

asReference()

  • ๋น„๋™๊ธฐ API๊ฐ€ ์ทจ์†Œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š์œผ๋ฉด ์ฝ”๋ฃจํ‹ด์ด ๋ฌด๊ธฐํ•œ ์ •์ง€ ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ฝ”๋ฃจํ‹ด์€ ์บก์ฒ˜ ๋œ ๊ฐ์ฒด์— ๋Œ€ํ•œ ๊ฐ•๋ ฅํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด์œ ํ•˜๋ฏ€๋กœ Activity ๋˜๋Š” Fragment ์ธ์Šคํ„ด์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ์บก์ฒ˜ํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ์—๋Š” ์ง์ ‘ ์บก์ฒ˜ ๋Œ€์‹  asReference ()๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
suspend fun getData(): Data { ... }

class MyActivity : Activity() {
    fun loadAndShowData() {
		// Ref<T> uses the WeakReference under the hood
		val ref: Ref<MyActivity> = this.asReference()
	
		async(UI) {
		    val data = getData()
				
		    // Use ref() instead of this@MyActivity
		    ref().showData(data)
		}
    }

    fun showData(data: Data) { ... }
}

bg()

  • bg ()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์Šค๋ ˆ๋“œ์—์„œ ์ฝ”๋“œ๋ฅผ ์‰ฝ๊ฒŒ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
fun getData(): Data { ... }
fun showData(data: Data) { ... }

async(UI) {
    val data: Deferred<Data> = bg {
		// Runs in background
		getData()
    }

    // This code is executed on the UI thread
    showData(data.await())
}