3

My Coroutine is running on the main thread, which i've specified in my Coroutine context:

class ClickPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs), CoroutineScope, View.OnClickListener {

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main

override fun onClick(v: View?) {
    when (key){
        "logout" -> {
            CoroutineScope(coroutineContext).launch {
                CustomApplication.database?.clearAllTables()
                Log.d("MapFragment", "Cleared Tables")
            }
            if (Profile.getCurrentProfile() != null) LoginManager.getInstance().logOut()
            FirebaseAuth.getInstance().signOut()
            val intent = Intent(context, MainActivity::class.java)
            context.startActivity(intent)
        }
    }
}

But I'm still getting this error:

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

on my above Coroutine call CustomApplication.database?.clearAllTables() to my Room database.

Here is my CustomApplication:

class CustomApplication : Application() {

    companion object {
        var database: AppDatabase? = null
    }

    override fun onCreate() {
        super.onCreate()
        CustomApplication.database = Room.databaseBuilder(this, AppDatabase::class.java, "AppDatabase").build()
    }

Why am I still getting the error if my coroutine context runs on the main thread?

Zorgan
  • 8,227
  • 23
  • 106
  • 207
  • see this https://stackoverflow.com/questions/44167111/android-room-simple-select-query-cannot-access-database-on-the-main-thread – Bunny Aug 26 '19 at 12:18

3 Answers3

10

The error says that it should NOT run on the main thread. Database operations (and every other form of IO) can take a long time and should be run in the background.

You should use Dispatchers.IO which is designed for running IO operations.

pshegger
  • 2,526
  • 24
  • 29
  • Thanks. What about query operations such as `@Query("SELECT COUNT(*) FROM matched_users WHERE :matchId = match_id LIMIT 1")` - would that use `Dispatchers.IO`? It's not input/output as it's only querying the DB. – Zorgan Aug 26 '19 at 22:08
  • 1
    Any DB call you make should be called from IO thread instead of Main thread which is very precious thing for Android from user perspectives. – Ashok Kumar Aug 27 '19 at 03:32
  • 2
    It is now possible to mark DAO functions as suspend functions. Then you don’t need to use Dispatchers.IO to call it. This is a cleaner solution than having to wrap blocking calls in the top level of your coroutine. – Tenfour04 Feb 10 '22 at 05:48
  • 1
    It's not just possible, it recommended. – yshahak Mar 24 '22 at 14:38
3

You can't use Dispatchers.Main for the long-running task. You have to use Dispatchers.IO for database operations, look likes:

class ClickPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs), CoroutineScope, View.OnClickListener {



override val coroutineContext: CoroutineContext
        get() = Dispatchers.IO

override fun onClick(v: View?) {
    when (key){
        "logout" -> {
            CoroutineScope(coroutineContext).launch {
                CustomApplication.database?.clearAllTables()
                Log.d("MapFragment", "Cleared Tables")
                if (Profile.getCurrentProfile() != null) LoginManager.getInstance().logOut()
                FirebaseAuth.getInstance().signOut()
            }

            val intent = Intent(context, MainActivity::class.java)
            context.startActivity(intent)
        }
    }
}
Abu Noman
  • 435
  • 3
  • 13
  • Thanks. What about query operations such as @Query("SELECT COUNT(*) FROM matched_users WHERE :matchId = match_id LIMIT 1") - would that use Dispatchers.IO? It's not input/output as it's only querying the DB. – Zorgan Aug 26 '19 at 22:09
  • Database query basically reads from DB file like file reading, so obviously it should be IO operation. – Abu Noman Aug 27 '19 at 06:17
-6

Hi, this problem is because the database must run on mainthread. Because of this you have to add this line of code to the database section

Room.databaseBuilder(context.getApplicationContext(),
            DataBaseTextChat.class, Constants.DB_TEXT_CHAT)
            .allowMainThreadQueries()
            .addCallback(roomCallBack).build();