0

I'm getting the "Cannot access database on the main thread since it may potentially lock the UI for a long period of time." error, but from what I understand I'm launching a new coroutine to insert the data to the database. What am I doing wrong?

RadioActivity:

        val finishButton : Button = findViewById(R.id.radioFinishButton)
        finishButton.setOnClickListener {
            val radioName = findViewById<EditText>(R.id.radioName)
            val radioUri = findViewById<EditText>(R.id.radioUri)
            val replyIntent = Intent()

            when {
                TextUtils.isEmpty(radioName.text) -> radioName.error = "Radio name is required!"
                TextUtils.isEmpty(radioUri.text) -> radioUri.error = "Radio URL is required!"
                else -> {
                    replyIntent.putExtra(EXTRA_REPLY_NAME, radioName.text.toString())
                    replyIntent.putExtra(EXTRA_REPLY_URI, radioUri.text.toString())
                    setResult(Activity.RESULT_OK, replyIntent)
                    finish()
                }
            }
        }

RadioFragment:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val getResult = registerForActivityResult(
            ActivityResultContracts.StartActivityForResult()
        ){
            var name = ""
            var uri = ""
            if(it.resultCode == Activity.RESULT_OK){
                it.data?.getStringExtra(RadioActivity.EXTRA_REPLY_NAME)?.let{ reply ->
                    name = reply
                }
                it.data?.getStringExtra(RadioActivity.EXTRA_REPLY_URI)?.let{ reply ->
                    uri = reply
                }
                Toast.makeText(context, "Name = ${name}, uri = ${uri}", Toast.LENGTH_LONG).show()
                val radio = Radio(5, name, uri)
                radioViewModel.insert(radio)
            }else{
                Toast.makeText(context, "Error while saving", Toast.LENGTH_LONG).show()
            }
        }

        val recyclerView = view.findViewById<RecyclerView>(R.id.radioRecyclerView)
        val adapter = RadioListAdapter()
        recyclerView.adapter = adapter
        recyclerView.layoutManager = LinearLayoutManager(context)

        radioViewModel.radioList.observe(viewLifecycleOwner) {
            it.let{
                adapter.submitList(it)
            }
        }
    }

RadioRepository:

val radioList: Flow<List<Radio>> = radioDao.getAll()
    @Suppress("RedundantSuspendModifier")
    @WorkerThread
    suspend fun insert(radio: Radio) {
        radioDao.insert(radio)
    }

RadioModel:

    fun insert(radio: Radio) = viewModelScope.launch {
        repository.insert(radio)
    }
  • Hi Kuba, please check the answers of this question https://stackoverflow.com/q/44167111/15262615 – Hussien Fahmy Jan 11 '22 at 20:25
  • Could you please provide code for `RadioViewModel`. I guess you are launching a coroutine there, right? – Sergio Jan 11 '22 at 20:31
  • `from what I understand I'm launching a new coroutine` - from what I see there is no coroutine at all in posted code. you should call `insert` inside some scope or some other `Thread` – snachmsm Jan 11 '22 at 20:40
  • Sorry about that, I've included the part where I run `insert` method inside ViewModel scope – Kuba Szczukin Jan 11 '22 at 20:51

2 Answers2

1

Try to specify the dispatcher on your viewmodelScope

fun insert(radio: Radio) = viewModelScope.launch(Dispatchers.IO) {
    repository.insert(radio)
}

if it doesn't work, try to switch context by using withContext

fun insert(radio: Radio) = viewModelScope.launch {
    withContext(Dispatchers.IO) {
        repository.insert(radio)
    }
}
0

Try to switch context in RadioRepository:

suspend fun insert(radio: Radio) = withContext(Dispatchers.IO) {
    radioDao.insert(radio)
}

You are using @Suppress("RedundantSuspendModifier") annotation, which suppresses the RedundantSuspendModifier error. This error means your insert function is non suspend and will be running in thread, which invoked it.

withContext(Dispatchers.IO) switches the context of coroutine, making insert function to run in background(worker) thread.

Sergio
  • 27,326
  • 8
  • 128
  • 149
  • That was it, thank you. I was sure using @WorkerThread annotation would be enough for it to work – Kuba Szczukin Jan 11 '22 at 21:06
  • No, `@WorkerThread` annotation doesn't make function to run on a worker thread, it only denotes that method should only be called on a worker thread. – Sergio Jan 11 '22 at 21:11