“无法访问主线程上的数据库,因为它可能会长时间锁定 UI。”我的协程错误

Posted

技术标签:

【中文标题】“无法访问主线程上的数据库,因为它可能会长时间锁定 UI。”我的协程错误【英文标题】:"Cannot access database on the main thread since it may potentially lock the UI for a long period of time." error on my Coroutine 【发布时间】:2019-12-30 15:14:47 【问题描述】:

我的协程在我的协程上下文中指定的主线程上运行:

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)
        
    

但我仍然收到此错误:

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

在我上面的协程调用CustomApplication.database?.clearAllTables() 到我的Room 数据库。

这是我的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()
    

如果我的协程上下文在主线程上运行,为什么我仍然会收到错误消息?

【问题讨论】:

看到这个***.com/questions/44167111/… 【参考方案1】:

错误表明它不应该在主线程上运行。数据库操作(以及所有其他形式的 IO)可能需要很长时间,并且应该在后台运行。

您应该使用为运行 IO 操作而设计的Dispatchers.IO

【讨论】:

谢谢。诸如@Query("SELECT COUNT(*) FROM matched_users WHERE :matchId = match_id LIMIT 1") 之类的查询操作会如何使用Dispatchers.IO?它不是输入/输出,因为它只是查询数据库。 您进行的任何数据库调用都应该从 IO 线程而不是主线程调用,从用户的角度来看,这对于 android 来说是非常宝贵的。 现在可以将 DAO 函数标记为挂起函数。那么你就不需要使用 Dispatchers.IO 来调用它了。与必须在协程的顶层包装阻塞调用相比,这是一种更简洁的解决方案。【参考方案2】:

您不能将Dispatchers.Main 用于长时间运行的任务。您必须使用Dispatchers.IO 进行数据库操作,如下所示:

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)
        
    

【讨论】:

谢谢。 @Query("SELECT COUNT(*) FROM matching_users WHERE :matchId = match_id LIMIT 1") 之类的查询操作会使用 Dispatchers.IO 吗?它不是输入/输出,因为它只是查询数据库。 数据库查询基本上是像文件读取一样从DB文件中读取,所以显然应该是IO操作。【参考方案3】:

您好,这个问题是因为数据库必须在主线程上运行。 因此,您必须将这行代码添加到数据库中 部分

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

【讨论】:

这不是一个正确的解决方案!做负责任有思想的程序员,不要用!

以上是关于“无法访问主线程上的数据库,因为它可能会长时间锁定 UI。”我的协程错误的主要内容,如果未能解决你的问题,请参考以下文章