首页 > 解决方案 > Room:无法访问主线程上的数据库 *但是*

问题描述

我有这段代码试图在我的 Android 应用程序中运行:

            ServerDatabase.getInstance(ListServersActivity.this).serverDao().getAll().observe(ListServersActivity.this, new Observer<List<Server>>() {
                @Override
                public void onChanged(List<Server> servers) {
                    serverListAdapter.setServers(servers);
                }
            });

我正在尝试在 UI 线程上运行它,所以我收到了这个看起来很简单的错误,并且在 StackOverflow 中有很多关于它的条目:

无法访问主线程上的数据库,因为它可能会长时间锁定 UI

现在,这个问题以及它与我认为以前的问题的不同之处在于,我在主线程中运行它是故意调用 runOnUiThread 的,因为当我在后台线程中执行它时,我遇到了另一个错误:

无法在后台线程上调用观察

如果我在主线程上运行它就搞砸了,如果我在后台线程上运行它就搞砸了。我该如何解决这个难题?

添加更多信息,ServerDao 是 Room Dao 接口,因此实际实现由 Room 生成。这是所涉及方法的声明:

public interface ServerDao {
    @Query("SELECT * FROM server ORDER BY id ASC")
    LiveData<List<Server>> getAll();
// ...
}

这是在后台线程中运行时的堆栈:

java.lang.Long); 返回 null 2020-03-13 13:36:54.223 24960-25084/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:没有崩溃值

这是在 UI 线程中运行时的堆栈:

2020-03-13 13:45:17.552 26410-26410/com.daon.identityx.docscan E/AndroidRuntime:致命异常:主进程:com.daon.identityx.docscan,PID:26410 java.lang.RuntimeException:无法启动活动 ComponentInfo{com.daon.identityx.doccan/com.daon.identityx.doccan.ui.activity.ListServersActivity}:java.lang.IllegalStateException:无法访问主线程上的数据库,因为它可能会长时间锁定 UI一段的时间。在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 在 android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78) 在 android。 app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108) 在 android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68) 在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1948) 在 android.os.Handler.dispatchMessage(Handler.java:106 ) 在 android.os.Looper.loop(Looper.java:214) 在 android.app.ActivityThread.main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android。 internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 原因:java.lang.IllegalStateException:无法访问主数据库线程,因为它可能会长时间锁定 UI。在 androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java: Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03 -13 13:45:17.694 26410-26410/com.daon.identityx.docscan E/UncaughtException: java.lang.RuntimeException: 无法启动活动 ComponentInfo{com.daon.identityx.doccan/com.daon.identityx.doccan。 ui.activity.ListServersActivity}:java.lang.IllegalStateException:无法访问主线程上的数据库,因为它可能会长时间锁定 UI。在 android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3114) 在 android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3257) 在 android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java: 无法访问主线程上的数据库,因为它可能会长时间锁定 UI。在 androidx.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:267) 在 androidx.room.RoomDatabase.query(RoomDatabase.java:323) 在 androidx.room.util.DBUtil.query(DBUtil.java:83) 在 com。 daon.identityx.docscan.database.models.ServerDao_Impl.getCount(ServerDao_Impl.java:300) 在 com.daon.identityx.docscan.repository.ServerDatabase.addDefaultDataIfEmpty(ServerDatabase.java:30) 在 com.daon.identityx.doccan。 repository.ServerDatabase.getInstance(ServerDatabase.java:23) 在 com.daon.identityx.docscan.ui.activity.ListServersActivity.initRecyclerViewAdapter(ListServersActivity.java:88) 在 com.daon.identityx.docscan.ui.activity.ListServersActivity。Handler.dispatchMessage(Handler.java:106) 在 android.os.Looper.loop(Looper.java:214) 在 android.app.ActivityThread.main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke (Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13 :45:17.732 26410-26729/com.daon.identityx.docscan E/CrashlyticsCore: 在 AppMeasurement.EventListener 上调用了意外的方法: onEvent(java.lang.String, java.lang.String, android.os.Bundle, java.lang 。长); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 106) 在 android.os.Looper.loop(Looper.java:214) 在 android.app.ActivityThread.main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android .internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/ com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent(java.lang.String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 106) 在 android.os.Looper.loop(Looper.java:214) 在 android.app.ActivityThread.main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android .internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/ com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent(java.lang.String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 在 com.android.internal。 os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent(java.lang .String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 main(ActivityThread.java:7050) 在 java.lang.reflect.Method.invoke(Native Method) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494) 在 com.android.internal。 os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent(java.lang .String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent (java.lang.String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值 android.internal.os.ZygoteInit.main(ZygoteInit.java:965) 2020-03-13 13:45:17.732 26410-26729/com.daon.identityx.docscan E/CrashlyticsCore:在 AppMeasurement.EventListener 上调用了意外的方法:onEvent (java.lang.String, java.lang.String, android.os.Bundle, java.lang.Long); 返回 null 2020-03-13 13:45:26.315 27259-27416/com.daon.identityx.docscan E/FirebaseCrash:无法解析 Json 响应字符串以获取消息:崩溃没有值

标签: androidandroid-roomandroid-livedata

解决方案


你的 DAO 上有一些方法。可以在主应用程序线程上调用那些返回反应类型LiveData(如 )的对象。getAll()Room 会为您安排后台线程的工作。

但是,您似乎也有一个getCount()可能看起来像这样的方法:

@Query("SELECT COUNT(*) FROM something")
public long getCount();

那不是返回反应类型,因此它将是同步调用。如果不遇到“无法在主应用程序线程上执行此操作”异常,您就无法在主应用程序线程上进行这种调用。

任何一个:

  • 让这个方法也返回一个反应类型

  • 仅从后台线程调用此方法

  • 切换到 Kotlin 和协程,您可以在其中两全其美(尽管实际上 I/O 是异步的,但语法看起来是同步的)

  • allowMainThreadQueries()通过您的 禁用后台线程检查RoomDatabase.Builder,并准备好被同事和/或用户在主应用程序线程上执行磁盘 I/O 大喊大叫


推荐阅读