<이전글>
[안드로이드 코드랩] 안드로이드 Room-DB만들기, Entity개념, DAO
<코드랩 과정6: Room데이터베이스와 코루틴> Android Kotlin 기초 | 학습 과정 | Android Developers Android Kotlin 기초 Android Kotlin 기초 교육 과정은 Google Developers 교육팀에서 만들었습니다. 이..
devforyou.tistory.com
<코드랩>
# 시작하며
저번글에서는 Room을 사용하기 위한 설정을 끝냈다면, 이번에는 기능적인 요소를 완성시켰다. 아직 코드랩에서 요구하는 100% 기능은 아니다.
일반적으로 DB를 사용하는 작업과 네트워크를 사용하는 작업은 시간이 오래걸리기 때문에, 메인쓰레드에서 해당 작업을 진행하게 된다면, UI가 작동하지 않기 때문에 좋은 사용자 경험을 할 수 없음과 동시에 앱이 죽게 될 수 도 있다. 그럭히 때문에 다중 쓰레드 또는 코루틴을 이용해서 작업해야한다. 이번 챕터에서는 코루틴을 사용했다.
# DAO function을 suspend로 바꿔주기
@Dao
interface SleepDatabaseDao {
@Insert
suspend fun insert(night: SleepNight)
@Update
suspend fun update(night: SleepNight)
@Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
suspend fun get(key: Long): SleepNight?
@Query("DELETE FROM daily_sleep_quality_table")
suspend fun clear()
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
suspend fun getTonight(): SleepNight?
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
fun getAllNights(): LiveData<List<SleepNight>>
}
suspend는 코루틴 스코프 안에서 쓰이는 함수이다. getAllNights를 제외한 작업들은 코루틴 스코프 안에서 호출되기 때문에 suspend함수로 바꿔준다.
특이하게 getAllNights함수는 suspend로 바꿔주지 않아도 됐다.
코드랩에서는 위와같은 설명이 있었다. LiveData를 반환하는 쿼리문에 대해서는 이미 룸은 백그라운드 쓰레드에서 작업하고 있기 때문이라고 한다. 그렇기 때문에 DB가 update될 경우 즉 DB의 내용이 바뀔 경우 매번 getAll을 수행할 필요 없이, 디비가 최신상태를 유지하도록 해준다.
# start,stop구현하기 - ViewModel
start버튼을 누르면 현재시점을 시작으로하는 엔티티가 하나 생성되면서 DB에 저장 될 수 있도록 해야한다.
private var tonight = MutableLiveData<SleepNight?>()
tonight변수를 통해서 현재의 night을 관찰할 수 있도록 한다.
init {
Log.d("ViewModel","SleepTrackerViewModel Created!")
initializeTonight()
}
private fun initializeTonight() {
viewModelScope.launch {
tonight.value = getTonightFromDatabase()
}
}
private suspend fun getTonightFromDatabase(): SleepNight?{
var night = database.getTonight()
if(night?.endTimeMilli != night?.startTimeMilli){
night = null
}
return night
}
db에서 현재 night이 있는지를 알아낸다
이제 start버튼을 눌렀을때 새로운 SleepNight엔티티를 만들기 위한 이벤트처리를 해주도록 하자.
fun onStartTracking() {
viewModelScope.launch {
val newNight = SleepNight()
insert(newNight)
tonight.value = getTonightFromDatabase()
}
}
private suspend fun insert(night: SleepNight) {
database.insert(night)
}
코루틴을 이용해서 insert를 수행하도록 한다. 그리고 data-binding을 통해서 onClick메서드를 붙여주자. data-binding이 은근 편한거 같다.
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"
위와 같은 방법으로 onStop, onClear를 구현해준다.
fun onStopTracking() {
viewModelScope.launch {
val oldNight = tonight.value ?: return@launch
oldNight.endTimeMilli = System.currentTimeMillis()
update(oldNight)
}
}
private suspend fun update(night:SleepNight) {
database.update(night)
}
fun onClear() {
viewModelScope.launch {
clear()
tonight.value = null
}
}
private suspend fun clear(){
database.clear()
}
# 모든 데이터 불러오기
보통 리사이클러뷰를 사용해서 관리하겠지만, 지금 코드랩 순서로는 리사이클러뷰가 뒷 단원에 있어서 Transformaitions을 통해 받아온 모든 텍스트를 재조합해서 TextView에 한번에 그려주는 형태이다.
맨 처음 설명한 거와 같이 LiveData로 DB에서 반환을 하게 된다면, 따로 호출해줄 필요 없이 latest data로 업데이트 된다.
private val nights = database.getAllNights()
로 nights를 받아와 준다.
val nightsString = Transformations.map(nights) { nights ->
formatNights(nights, application.resources)
}
제공해준 소스에서 nigths를 알맞게 정리해서 텍스트로 출력해준다.
android:text="@{sleepTrackerViewModel.nightsString}"
# 마치며
다음 코드랩에서는 평점 기능을 추가하는 기능을 구현한다.