android - 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 响应字符串以获取消息:崩溃没有值
解决方案
你的 DAO 上有一些方法。可以在主应用程序线程上调用那些返回反应类型LiveData
(如 )的对象。getAll()
Room 会为您安排后台线程的工作。
但是,您似乎也有一个getCount()
可能看起来像这样的方法:
@Query("SELECT COUNT(*) FROM something")
public long getCount();
那不是返回反应类型,因此它将是同步调用。如果不遇到“无法在主应用程序线程上执行此操作”异常,您就无法在主应用程序线程上进行这种调用。
任何一个:
让这个方法也返回一个反应类型
仅从后台线程调用此方法
切换到 Kotlin 和协程,您可以在其中两全其美(尽管实际上 I/O 是异步的,但语法看起来是同步的)
allowMainThreadQueries()
通过您的 禁用后台线程检查RoomDatabase.Builder
,并准备好被同事和/或用户在主应用程序线程上执行磁盘 I/O 大喊大叫
推荐阅读
- java - 对于以下代码,哪些测试用例可能会给出错误的结果?
- visual-foxpro - Visual FoxPro 函数调用中的双星号是什么意思?
- matlab - 在 Twitter Premium Search API 中使用附加请求参数
- javascript - 我无法将并非来自tripadvisor 的所有流量重定向到另一个网站(WordPress)
- powerbi - 将未来日期的行的值更改为 0
- android - 抽屉预览未在菜单文件中正确显示
- html - Bootstrap 列中的垂直对齐项目
- vba - 如果变量 AND 变量 Then
- javascript - 如何根据字段搜索嵌套对象和分组
- macos - 如果我在套接字过滤器中设置了一个 mbuf 标记,我以后可以在 IP 过滤器中找到这些标记的数据包吗?