코드랩 Room 사용하기2
저번에 생성한 Room Database를 가지고 ViewModel과 LiveData, Corutine을 사용해서 화면에 수면기록을 띄워준다.
1. SleepTrackerViewModel 만들기 SleepTrackerViewModel은 아까 만들어둔 SleepDatabaseDao를 사용해야한다. 그리고 SleepDatabaseDao를 사용하기 위해서는 database를 생성하기 위한 application도 필요하다.
dao를 만들때 application을 넣기 때문에, viewModelFactory에 dao하나만 넣어줘도 되긴하지만, SleepTrackerViewModel에서 formatNight을 해줄때 들어가는 인자에 application이 필요하다.
그러므로 두개의 인수(dao,application)를 가지는 ViewModelFactory를 만들어줘야한다.
SleepTrackerViewModelFactory
class SleepTrackerViewModelFactory(
private val dataSource: SleepDatabaseDao,
private val application: Application) : ViewModelProvider.Factory {
@Suppress("unchecked_cast") //warning 을 위한 어노테이션
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
return SleepTrackerViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}}
그리고 ViewModel을 만들어준다. SleepTrackerFragment
class SleepTrackerFragment : Fragment() {
lateinit var viewModel : SleepTrackerViewModel
lateinit var viewModelFactory : SleepTrackerViewModelFactory
/**
* Called when the Fragment is ready to display content to the screen. * * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality. */ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Get a reference to the binding object and inflate the fragment views.
val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
inflater, R.layout.fragment_sleep_tracker, container, false)
val application = requireNotNull(this.activity).application //database를 생성하기 위한 application val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao //Room db에 접근하기 위한 dao
viewModelFactory = SleepTrackerViewModelFactory(dataSource,application)
viewModel = ViewModelProvider(this,viewModelFactory).get(SleepTrackerViewModel::class.java)
binding.viewModel = viewModel
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
}
application에 값을 넣어주고, SleepDatabase.getInstance에 application을 넣어 sleepDatabaseDao를 가져온다. 두가지를 만들어둔 SleepTrackerViewModelFactory에 넣어서 viewModelFactory를 만들고, viewModel을 만들어준다.
viewModel 바인딩을 하기 위해 xml layout 태그 안에 viewModel을 선언해주고, binding.viewModel과 binding.lifecycleOwner를 초기화해주면 viewModel 사용 준비 끝
2. ViewModel에서 Room 사용하기
여기서 코루틴이라는것을 사용한다.
코루틴(Coroutine)이란? 앱 내에서 잠재적으로 오래 실행되는 비동기 작업을 처리하는 방법이다.
작업이 완료 될 때까지 기다려야하는 파일 읽기 or 데이터베이스 호출 수행 등 전체 앱의 실행 방해하거나 차단할 수 있다.
주 스레드를 차단하지 않고 방지하는 패턴은 콜백을 사용하는것이 있지만, 콜백보다 더 높은 수준의 유지 보수성과 유연성을 가지는 코루틴을 사용한다.
별도의 스레드에서 일시중단 -> 비동기 만듦 => UI스레드의 진행을 차단하거나 방해하지 않음 !
코루틴 사용하기 getAllNights()는 LiveData를 반환하는 특정 쿼리에 대한 백그라운드 스레드를 사용하기 때문에 이를 제외한 SleepDatabaseDao 메소드에 suspend 키워드를 붙여준다. SleepDatabaseDao
@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") //id가 key인 객체를 하나 가져옴
suspend fun get(Key:Long):SleepNight? //null처리
@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>>
}
SleepTrackerViewModel SleepTrackerViewModel에서, 버튼이 눌릴때마다 바뀔 tonight 변수를 선언하고
private var _tonight = MutableLiveData<SleepNight?>()
초기화해준다.
init {
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
}
viewModelScope.launch{ }를 통해 코루틴을 생성하고, 실행을 디스패처에 할당한다.
그 다음 suspend를 붙여 database에서 getTonight 이라는 함수를 호출한다.
START, STOP, CLEAR버튼을 눌렀을때도 똑같이 진행한다.
fun onStart(){
viewModelScope.launch {
val newNight = SleepNight()
insert(newNight)
}
}
private suspend fun insert(night:SleepNight){
database.insert(night)
}
fun onStop(){
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()
}
마지막으로 전체 데이터 표시
//SleepTrackerFragment에 띄워줄 nightsprivate val nights = database.getAllNights()
val nightsString = Transformations.map(nights) {nights->
//표시될 format 바꿔줌
formatNights(nights,application.resources)
}
참고
코드랩:https://developer.android.com/codelabs/kotlin-android-training-coroutines-and-room#0
Comments