๋ณธ ๊ฒ์๊ธ์ Coroutine ๊ฐ๋
์ Android ๋ด์์ ์ฌ์ฉ๋๋ ๊ฒ์ ์ ์ ๋ก ์์ฑ๋์๋ค.
#1 Coroutine์ด ์ฐ์ธ LiveData ์์ - ์ ํต์ ์ธ ๋ฐฉ๋ฒ
#1์ LiveData ๊ฐ ํ ๋น์ Coroutines๊ฐ ์ฐ์ธ ์ํ ํ๋ก์ ํธ๋ค.
#1-1 build.gradle.kts (Module)
plugins {
...
}
android {
...
}
dependencies {
...
// Coroutines
val coroutinesVersion = "1.7.3"
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
val lifecycleVersion = "2.6.2"
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion")
// LiveData
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion")
}
Coroutines, ViewModel, LiveData ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ถ๊ฐํ๋ค.
#1-2 User.kt ๋ฐ์ดํฐ ํด๋์ค
// package com.example.livedatabuilder.model
data class User(val id: Int, val name: String)
2๊ฐ์ ํ๋กํผํฐ๋ฅผ ๊ฐ์ง data class๋ฅผ ๋ง๋ค์๋ค.
#1-3 UserRepository.kt
// package com.example.livedatabuilder.model
import kotlinx.coroutines.delay
class UserRepository {
suspend fun getUsers(): List<User> {
delay(8000)
val users: List<User> = listOf(
User(1, "Park Bom"),
User(2, "Park Sandara"),
User(3, "CL"),
User(4, "Gong Minji")
)
return users
}
}
Repository ์ญํ ์ ํ ํด๋์ค๋ ๋ง๋ ๋ค. ์ด ํด๋์ค์ getUsers() ๋ฉ์๋๋ ๋ฐฉ๊ธ ๋ง๋ค์๋ data class ์ธ์คํด์ค์ ๋ฐฐ์ด์ ๋ฐํํ๋ค. Repository์์ ๊ฐ์ ๊บผ๋ด์ค๋ ๋ฐ์ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ delay() ํจ์๋ก ํํํ๋ค.
#1-4 MainActivityViewModel.kt
// package com.example.livedatabuilder
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.livedatabuilder.model.User
import com.example.livedatabuilder.model.UserRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class MainActivityViewModel : ViewModel() {
private var usersRepository = UserRepository()
var users: MutableLiveData<List<User>?> = MutableLiveData()
fun getUsers() {
viewModelScope.launch {
var result: List<User>? = null
withContext(Dispatchers.IO) {
result = usersRepository.getUsers()
}
users.value = result
}
}
}
ViewModel๊ณผ ๊ทธ ํ๋กํผํฐ๋ก LiveData๋ฅผ ๋ง๋ ๋ค. LiveData์ ๊ฐ์ ํ ๋นํด์ฃผ๊ธฐ ์ํด์, viewModelScope์์ Repository.getUsers()๋ฅผ ํธ์ถํ๋ค. Dispatchers.IO ์ค๋ ๋(์ด ๋งํฌ์ #3-2 ์ฐธ์กฐ)๋ ์ฃผ๋ก ์
/์ถ๋ ฅ์ ๋ด๋นํ๋ ์ค๋ ๋๋ค. Repository์ ์ญํ ๋ํ ๋ฐ์ดํฐ ์
/์ถ๋ ฅ์ด๋ฏ๋ก withContext() ํจ์๋ฅผ ํตํด์ ์ค๋ ๋๋ฅผ ์์ ์ ํํด getUsers()๋ฅผ ์ํ์์ผฐ๋ค.
#1-5 MainActivity.kt
// package com.example.livedatabuilder
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
class MainActivity : AppCompatActivity() {
private lateinit var mainActivityViewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
mainActivityViewModel.getUsers()
mainActivityViewModel.users.observe(this, Observer { myUsers ->
myUsers?.forEach {
Log.i("interfacer_han", "name is ${it.name}")
}
})
}
}
/* Log ์ถ๋ ฅ ๊ฒฐ๊ณผ
name is Park Bom
name is Park Sandara
name is CL
name is Gong Minji
*/
Activity์์ LiveData์ ๊ฐ์ ํ ๋นํ๋ ์์
ํ ํด๋น LiveData๋ฅผ observeํ๋ค. ์ด์ 8์ด ํ์ Coroutines ์ฝ๋๊ฐ ์๋ฃ๋๋ฉด Log ๋ฉ์์ง๊ฐ ๋ฐ ๊ฒ์ด๋ค.
#2 Coroutine์ด ์ฐ์ธ LiveData ์์ - LiveData Builder ์ด์ฉ
#2-1 ๊ฐ์
์๋ช ์ฃผ๊ธฐ ์ธ์ ๊ตฌ์ฑ์์๋ก Kotlin ์ฝ๋ฃจํด ์ฌ์ฉ | Android ๊ฐ๋ฐ์ | Android Developers
์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ์๋ช ์ฃผ๊ธฐ ์ธ์ ๊ตฌ์ฑ์์๋ก Kotlin ์ฝ๋ฃจํด ์ฌ์ฉ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. Kotlin ์ฝ
developer.android.com
#1์ ์ฝ๋๋ฅผ ์์ ํด ๋ฉ๋ชจ๋ฆฌ ๋์์ ์ฝ๋๋ ๋ ๋ค ์ค์ผ ์ ์๋ ๋ฐฉ๋ฒ์ด ์๋๋ฐ, LiveData Builder๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค. lifecycle-livedata-ktx๊ฐ 2.4.0 ๋ฒ์ ์ด์์ด๋ฉด LiveData Builder๋ฅผ ์ฌ์ฉํ ์ ์๋ค. #1์์ ์ด๋ฏธ ํด๋น ๋ฒ์ ์ด์์ lifecycle-livedata-ktx๋ฅผ ๋ชจ๋ ์์ค build.gradle.kts์์ ์ถ๊ฐํ์ผ๋ฏ๋ก build.gradle.kts๋ ์์ ํ์ง ์์๋ ๋๋ค.
#2-2 MainActivityViewModel.kt์์ LiveData์ LiveDataBuilder ์ฌ์ฉ
// package com.example.livedatabuilder
import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import com.example.livedatabuilder.model.UserRepository
import kotlinx.coroutines.Dispatchers
class MainActivityViewModel : ViewModel() {
private var usersRepository = UserRepository()
var users = liveData(Dispatchers.IO) {
val result = usersRepository.getUsers()
emit(result) // LiveData์ ์๋ก์ด ๊ฐ์ ๋ฐํํ๊ณ ์ด๋ฅผ ๊ตฌ๋
(LiveData.observe())ํ๊ณ ์๋ ๊ด์ฐฐ์(Observer)๋ค์๊ฒ ์๋ฆผ
}
/*
var users: MutableLiveData<List<User>?> = MutableLiveData()
fun getUsers() {
viewModelScope.launch {
var result: List<User>? = null
withContext(Dispatchers.IO) {
result = usersRepository.getUsers()
}
users.value = result
}
}
*/
}
LiveData๋ LiveData๊ฐ ๋ค์ด๊ฐ ์๋ LifecycleOwner ์ธํฐํ์ด์ค์ ์ํฅ์ ๋ฐ์ผ๋ฉฐ ์๋ํ๋ค (์ด ๋งํฌ์ #2-3 ์ฐธ์กฐ). LifecycleOwner์ ์ํ์ ๋ฐ๋ผ LiveData ๊ฐ์ '๊ฐฑ์ ํ ํ์ ์์
'์ ํ ์ง ์ํ ์ง๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ด๋ค. ํ์ง๋ง, #1-4์์ LiveData๋ viewModelScope ์ฆ, ViewModel์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๋ฐ๋ฅด๋ ์ฝ๋ฃจํด ์์ญ์ ์ํด ์คํ๋๋ค. ๋ฐ๋ฉด, LiveData Builder๋ LiveData๊ฐ ํ์ฑํ๋๋ฉด ์คํ์ ์์ํ๊ณ , ๋นํ์ฑํ๋๋ฉด ์๋์ผ๋ก ์ทจ์๋๋ ์ฝ๋ฃจํด ์์ญ์ ๋ง๋ ๋ค. ๋ฐ๋ผ์ LiveData๊ฐ์ ๊ฐฑ์ ํ๋ ๋ฐ์ ๊ด๋ จ๋ Coroutines์ ๋์/์ทจ์๋ฅผ ViewModel์ด ์๋, LiveData๊ฐ ์ฃผ์ฒด์ ์ผ๋ก ํ๋จํ๊ฒ ๋๋ค. ์ด๋ ์ค๋์์ ์๋ฐฉํ๋ฉด์ ๋์์ ์ปดํจํ
์์ ์ ์ฝ์ผ๋ก๋ ์ด์ด์ง๋ค.
์ถ๊ฐ๋ก, ์ฝ๋ ๋ํ ๋ ๊ฐ๊ฒฐํด์ก๋ค. ์ด ์ฅ์ ์ ViewModel์์ ๋ฟ๋ง ์๋๋ผ Activity์์๋ ๊ทธ๋ ๋ค. ์๋ ์ฝ๋๋ฅผ ๋ณด์.
#2-3 MainActivity.kt์์ getUsers() ์ญ์
// package com.example.livedatabuilder
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
class MainActivity : AppCompatActivity() {
private lateinit var mainActivityViewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainActivityViewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
// mainActivityViewModel.getUsers()
mainActivityViewModel.users.observe(this, Observer { myUsers ->
myUsers?.forEach {
Log.i("interfacer_han", "name is ${it.name}")
}
})
}
}
/* Log ์ถ๋ ฅ ๊ฒฐ๊ณผ
name is Park Bom
name is Park Sandara
name is CL
name is Gong Minji
*/
#1-5์์ LiveData๋ฅผ observeํ๊ธฐ ์ ์ ์ด๊น๊ฐ์ ํ ๋นํ๋ ์์
์ ํ ํ์๊ฐ ์์ด์ก๋ค. ์๋ํ๋ฉด LiveData๊ฐ ํ์ฑํ๋๋ฉด ์ฆ, LiveData๊ฐ ๋ด๊ธด Activity(์ฌ๊ธฐ์ MainActivity)์ LifecycleOwner์ ์ํ๊ฐ ํ์ฑํ ์ํ๋ฉด, LiveData Builder๊ฐ ์์์ ์ผ๋ก ์ํ๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ด๋ค.
#3 ์ฃผ์์
"๊ทธ๋ ๋ค๋ฉด, ViewModelScope.launch { ... }๋ฅผ liveData { ... }๋ก ๋ชฝ๋ ๋ค ๋ฐ๊ฟ๋ฒ๋ฆฌ๋ฉด ์ข์ ๊ฒ ์๋๊ฐ?"๋ผ๋ ์๊ฐ์ด ๋ค์๋ค๋ฉด LiveData Builder๋ฅผ ์๋ชป ์ดํดํ๊ณ ์๋ ๊ฒ์ด๋ค. liveData { ... }๋ฅผ ๋ ์ฐ์ํ, ์ ๊ทธ๋ ์ด๋ ๋ฒ์ ์ ViewModelScope๋ผ๊ณ ์๊ฐํ๋ฉด ์ ๋๋ค. ์ด ๋์ ์๋ก ๊ทธ ์ฉ๋๊ฐ ๋ค๋ฅด๋ค.
ViewModelScope๋ ์ด ๊ฒ์๊ธ์์ ๋ณด๋ฏ CoroutineScope๋ฅผ ์์์ ์ผ๋ก ViewModel์ ์๋ช ์ฃผ๊ธฐ์ ์ข ์๋๊ฒ ๋ง๋๋ ๊ฒ์ ๋ชฉ์ ์ ๋๋ค. ๋ฐ๋ฉด, LiveData Builder๋ ์ด๊ธฐํ์ Coroutine์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ LiveData๋ฅผ ๊ฐํธํ๊ณ ๋ฉ๋ชจ๋ฆฌํจ์จ์ ์ผ๋ก ์ด๊ธฐํํ๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค. ๋ณธ ๊ฒ์๊ธ์ ์ด๊ธฐํ์ Coroutine์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ LiveData๋ฅผ ์ด๊ธฐํํ ๋์ ํ์ ํ์ฌ ViewModelScope ์ธ์ ๋ ์ข์ ๋ฐฉ๋ฒ์ด ์๋ค๋ ๊ฒ์ ์๋ ค์ฃผ๋ ๊ธ์ด์ง, ViewModelScope๋ฅผ LiveData Builder๋ก ๋์ฒดํ ์ ์์์ ๋ณด์ด๋ ๊ธ์ด ์๋๋ค.
๋ณธ ๊ฒ์๊ธ์ ์ฐ์ธ LiveData Builder๋ฅผ ์ดํด๋ณด๋ฉด ๋ทฐ๋ชจ๋ธ(#2-2)์ emit()์ด, ์กํฐ๋นํฐ(#2-3)์ mainActivityViewModel.getUsers()๋ฅผ ๋์ฒดํ๊ฒ ๋ง๋ค์๊ณ , ViewModelScope๋ณด๋ค ๋ฉ๋ชจ๋ฆฌ์ ์ผ๋ก ์ด์ ์ด ์๋ CoroutineScope์ธ liveData { ... }๋ฅผ ์ฌ์ฉํ๊ฒ ๋ง๋ค์๋ค. ์ด๊ฒ ์ ๋ถ๋ค. ์ด ์ญํ ์ ๋์ด liveData { ... } ์์ ํด๋น LiveData์ ์ด๊ธฐํ ์ธ ์ด๋ค ๋ค๋ฅธ ์ฝ๋๋ฅผ ๋ฃ์ผ๋ฉด ์ปดํ์ผ์ ๋ ์ง ๋ชฐ๋ผ๋ ์๋ฌ๊ฐ ๋ ์ฌ์ง๊ฐ ์๊ธฐ๊ณ ๋ง๋ค. ์๋, ์คํ๋ ค ์ปดํ์ผ์ ๋๋๊น ์๋ฌ๊ฐ ์๊ฒผ์ ๋ ๋ฌธ์ ์ ์ ์ฐพ๊ธฐ๊ฐ ๋ ์ด๋ ต๋ค. ์ ๋ฆฌํ์๋ฉด, ์ด๊ธฐํ์ Courotine์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ LiveData ์ธ์๋ ์ ๋ถ ViewModelScope๋ฅผ ์ด๋ค๊ณ ์๊ฐํ์.
#4 ์์ฝ
์ด๊ธฐํ์ Coroutine์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์ LiveData๋ฅผ ๊ฐํธํ๊ณ ๋ฉ๋ชจ๋ฆฌํจ์จ์ ์ผ๋ก ์ด๊ธฐํํ๋ค.
#5 ์์ฑ๋ ์ฑ
android-practice/coroutines/LiveDataBuilder at master ยท Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] Room - Entity, DAO, Database (0) | 2024.02.24 |
---|---|
์ฐธ[Android] Room - ๊ธฐ์ด, INSERT์ DELETE ์ฐ์ต (0) | 2024.02.23 |
[Kotlin] Coroutines - LifecycleScope (0) | 2024.02.21 |
[Android] RecyclerView - Adapter์ ์ธ์(Argument) ์ ๋ฌ (0) | 2024.02.03 |
[Android] RecyclerView - ๊ธฐ์ด (0) | 2024.02.02 |