ViewModel에서 코루틴 활용하기
// UserViewModel.kt
// context를 활용해야 하는 경우에 AndroidViewModel을 필수로 쓰자
class UserViewModel(application: Application): AndroidViewModel(application) {
// UserViewModel은 repository와 UI를 연결시키는 커뮤니케이션 센터와 같은 역할을 하게된다.
private val readAllDate: LiveData<List<User>>
private val repository: UserRepository
init {
val userDao = UserDatabase.getDatabase(application).userDao()
repository = UserRepository(userDao)
readAllDate = repository.realAllData
}
fun addUser(user: User) {
viewModelScope.launch(Dispatchers.IO) {
// ViewModel에서 코루틴을 활용하기 위한 ViewModelScope
// Dispatcher.IO는 이 코드는 백그라운드 스레드에서 돌리겠다는 뜻이다.
// 메인스레드에서 데이터베이스 작업을 하면 매우 나쁜 환경이다!
repository.addUser(user)
}
}
}
// UserRepository.kt
// Room의 구성요소는 아니지만, 클린 코드와 아키텍쳐를 위해 이와 같이 코드를 분리해 사용하면 매우 좋다~
class UserRepository(private val userDao: UserDao) {
val realAllData: LiveData<List<User>> = userDao.readAllData()
suspend fun addUser(user: User) {
userDao.addUser(user)
}
}
// UserDao.kt
@Dao
interface UserDao {
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun addUser(user: User) // suspend를 붙인 이유는 코루틴 때문에
@Query("SELECT * FROM user_table ORDER BY id ASC")
fun readAllData(): LiveData<List<User>>
}
@Volatile
이 어노테이션을 활용하면 다른 스레드에서 활용되어도 즉각적으로 보이게 해준다. 변수 선언시 이 어노테이션이 지정되었을 때 값을 메인 메모리에 적재하기 때문이다.
@Volatile
private var INSTANCE: UserDatabase? = null
synchronized()
이것을 활용하면 여러 스레드에서 동시에 액세스해도 안전하게 접근이 가능하다. 다음과 같이 중복 생성을 방지하기 위해서 사용이 가능하다. room 이야기긴 한데, 여러 개의 인스턴스가 작동하게 되면 퍼포먼스에 있어 굉장히 고비용이기 때문이다.
companion object {
// 데이터베이스는 클래스에 하나의 인스턴스를 가지니까 싱글톤으로 만들자
@Volatile
private var INSTANCE: UserDatabase? = null
fun getDatabase(context: Context): UserDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) { // 지금 우리 데이터베이스가 준비되었을 때, 즉 존재할 때
return tempInstance
}
synchronized(this) { // 지금 우리 데이터베이스가 null로 준비되지 않았을 때
val instance = Room.databaseBuilder(
context.applicationContext,
UserDatabase::class.java,
"user_database"
).build()
INSTANCE = instance
return instance
}
}
}
Room