[Android] Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

이도연·2024년 3월 11일
0

trouble shooting

목록 보기
12/12

UI 스레드에서 데이터베이스 액세스를 시도했을 때 발생하는 일반적인 에러이다.
안드로이드에서는 메인 스레드(또는 UI 스레드)에서 오랫동안 실행되는 작업을 수행하는 것을 피하기를 권장한다. 메인 스레드가 블록되면 사용자 인터페이스가 응답하지 않는 것처럼 느껴질 수 있기 때문이다.

문제

fragment 에서 lifecycleScope 를 사용하여 Room DB 에 접근하던 중 다음과 같은 오류가 발생했다.

viewLifecycleOwner.lifecycleScope.launch {
            menu.findItem(R.id.action_hide_completed_tasks).isChecked =
                viewModel.preferenceFlow.first().hideCompleted
        }

Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

아래와 같이 변경했더니

 viewLifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
            val preferences = viewModel.preferenceFlow.first().hideCompleted
            withContext(Dispatchers.Main) {
                menu.findItem(R.id.action_hide_completed_tasks).isChecked =
                    preferences
            }
        }

로그 에러는 찍히지 않으나, UI 가 실행되지 않았다. (체크박스에 체크가 안됨.)
DB 는 viewModel 에서 관리하고 있었는데, 바보같이 Fragment lifecycleScope 쓰레드는 변경하려고 하니 벌어진 실수였다.

해결

fun onTaskCheckedChanged(task: Task, isChecked: Boolean) = viewModelScope.launch {
        taskDao.update(task.copy(completed = isChecked))
    }
fun onTaskCheckedChanged(task: Task, isChecked: Boolean) = viewModelScope.launch {
        withContext(Dispatchers.IO) {
            taskDao.update(task.copy(completed = isChecked))
        }
    }

코루틴은 비동기 프로그래밍을 지원하기 위한 Kotlin의 기능으로, 메인 스레드를 차단하지 않고도 비동기 작업을 처리할 수 있다. 코루틴은 viewModelScope.launch 함수를 사용하여 시작된다.
이 함수는 ViewModel 의 생명주기와 함께 관리되는 코루틴을 시작한다.

또한, 코루틴을 사용하여 백그라운드 스레드에서 작업을 수행하려면 withContext 함수를 사용하여 작업을 지정된 스레드 컨텍스트로 전환해야 한다. 이 경우 Dispatchers.IO를 사용하여 IO 스레드에서 데이터베이스 작업을 실행합니다.

위와 같이 코드를 변경함으로써 데이터베이스 액세스 작업을 메인 스레드가 아닌 백그라운드 스레드에서 안전하게 수행할 수 있다. 이렇게 하면 앱이 응답성을 유지하면서 데이터베이스 작업을 처리할 수 있게 된다.

0개의 댓글