Room 数据库没有 运行 我在后台线程上实现的事务
Room Database doesn't run my implemented transaction on background thread
这个问题是我为我的另一个 尝试过的解决方案,所以如果你有第一个问题的解决方案可以避免这个问题,我会洗耳恭听。
所以我在 Dao (getPageCombinedData
) 中有这个方法,用 @transaction
注释并且应该 return 一个 MediatorLiveData,我想既然 MediatorLiveData 是 LiveData 的一个子类,那么 Room将使用其文档中引用的后台线程向 return 提供必要的代码。
When Room queries return LiveData, the queries are automatically run asynchronously on a background thread.
但是我得到了异常,告诉我我 运行 我在主线程上查询
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
我首先尝试在另一个线程上使用 Executer 进行此操作,但我发现每当数据库更新时,它永远不会将新数据发送给我的观察者,因为我的代码如下所示
我的 ViewModel 代码
public LiveData<PagesCombinedData> getPageCombinedData(){
MediatorLiveData<PagesCombinedData>dataLiveObj = new MediatorLiveData<>();
executor.execute(()->{
UserPageRelation userRelation = scheduleDB.userDao().getAllUserPages().getValue();
dataLiveObj .postValue(scheduleDB.userDao().getPagesCombinedData().getValue());
});
return dataLiveObj;
}
这种方式只会在线程完成执行时以及数据库更新时更新 returned LiveData 一次,我在 UI 片段中的观察者永远不会知道它。
所以现在我不知道我应该如何 return 这个 Mediator 以便我的 UI 观察它并在数据库中更新数据时更新 UI。
我的 Fragment 的代码应该观察 Mediator
pagesViewModel.getPageCombinedData().observe(getViewLifecycleOwner(), pagesCombinedData -> {
pagesAdapter.setUserPages(pagesCombinedData.getUserEntities());
pagesAdapter.setPageEntities(pagesCombinedData.getPageEntities());
pagesAdapter.notifyDataSetChanged();
});
我的 PagesViewModel 代码:
public LiveData<PagesCombinedData> getPageCombinedData(){
return scheduleDB.userDao().getPagesCombinedData();
}
我应该构造中介对象的 UserDao 代码:
@Dao
public abstract class UserDao {
@Transaction
public MediatorLiveData<PagesCombinedData> getPagesCombinedData (){
MediatorLiveData <PagesCombinedData> dataMediator = new MediatorLiveData<>();
LiveData<List<PageEntity>> value1 = getAllPages();
LiveData<UserPageRelation> value2 = getAllUserPages();
dataMediator.addSource(value1, value -> dataMediator.setValue(combineData(value1,value2)));
dataMediator.addSource(value2, value -> dataMediator.setValue(combineData(value1,value2)));
return dataMediator;
}
private PagesCombinedData combineData(LiveData<List<PageEntity>> pages,LiveData<UserPageRelation> userPages ){
return new PagesCombinedData(pages.getValue()==null?new ArrayList<>():pages.getValue()
,userPages.getValue()==null ?new ArrayList<>():userPages.getValue().getUserPages());
}
@Query("Select * from page")
public abstract LiveData<List<PageEntity>> getAllPages ();
@Transaction
@Query("Select * from user limit 1")
public abstract LiveData<UserPageRelation> getAllUserPages ();
}
更新
试图将我的代码移动到 ViewModel 中,但它告诉我无法在后台线程上添加源代码,因为它使用 observeForever -_-*
我在 ViewModel 中尝试的代码
private MediatorLiveData<PagesCombinedData> pagesCombinedData;
private ExecutorService executor ;
public LiveData<PagesCombinedData> getPageCombinedData(){
if (pagesCombinedData==null){
pagesCombinedData = new MediatorLiveData<>();
pagesCombinedData.setValue(new PagesCombinedData());
}
executor.execute(()->{
LiveData<List<PageEntity>> value1 = scheduleDB.userDao().getAllPages();
LiveData<UserPageRelation> value2 = scheduleDB.userDao().getAllUserPages();
pagesCombinedData.addSource(value1, value -> pagesCombinedData.setValue(combineData(value1,value2)));
pagesCombinedData.addSource(value2, value -> pagesCombinedData.setValue(combineData(value1,value2)));
});
return pagesCombinedData;
}
我得到的异常(第59行是addSource
行)
java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:224)
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.addSource(MediatorLiveData.java:96)
at com.example.pretest.schedule.pages.PagesViewModel.lambda$getPageCombinedData$PagesViewModel(PagesViewModel.java:59)
at com.example.pretest.schedule.pages.-$$Lambda$PagesViewModel$Z3rbrO9SPtD8Pwpbh3mfk47DjVE.run(lambda)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
简单明了地告诉我,我想要实现的目标是不可能的吗?
当使用 LiveData 作为来自 Dao 查询的 return 值时,我似乎没有将我的代码包含在任何执行器中。
只有当 Room 的抽象查询不是我写的查询的结果时,它才会真正在后台执行查询。
所以我的代码最后看起来像这样:
我的道
@Dao
public abstract class UserDao {
@Query("Select * from page")
public abstract LiveData<List<PageEntity>> getAllPages ();
@Transaction
@Query("Select * from user limit 1")
public abstract LiveData<UserPageRelation> getAllUserPages ();
}
我的视图模型
public class MyViewModel extends ViewModel {
private MediatorLiveData<PagesCombinedData> pagesCombinedData;
public LiveData<PagesCombinedData> getPageCombinedData(){
if (pagesCombinedData==null){
pagesCombinedData = new MediatorLiveData<>();
}
LiveData<List<PageEntity>> value1 = scheduleDB.userDao().getAllPages();
LiveData<UserPageRelation> value2 = scheduleDB.userDao().getAllUserPages();
pagesCombinedData.addSource(value1, value -> pagesCombinedData.setValue(combineData(value1,value2)));
pagesCombinedData.addSource(value2, value -> pagesCombinedData.setValue(combineData(value1,value2)));
return pagesCombinedData;
}
private PagesCombinedData combineData(LiveData<List<PageEntity>> pages,LiveData<UserPageRelation> userPages ){
return new PagesCombinedData(pages.getValue()==null?new ArrayList<>():pages.getValue()
,userPages.getValue()==null ?new ArrayList<>():userPages.getValue().getUserPages());
}
}
而且我可以从我想要的两个数据列表中观察到 MediatorLiveData 的魅力 return。
这个问题是我为我的另一个
所以我在 Dao (getPageCombinedData
) 中有这个方法,用 @transaction
注释并且应该 return 一个 MediatorLiveData,我想既然 MediatorLiveData 是 LiveData 的一个子类,那么 Room将使用其文档中引用的后台线程向 return 提供必要的代码。
When Room queries return LiveData, the queries are automatically run asynchronously on a background thread.
但是我得到了异常,告诉我我 运行 我在主线程上查询
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
我首先尝试在另一个线程上使用 Executer 进行此操作,但我发现每当数据库更新时,它永远不会将新数据发送给我的观察者,因为我的代码如下所示
我的 ViewModel 代码
public LiveData<PagesCombinedData> getPageCombinedData(){
MediatorLiveData<PagesCombinedData>dataLiveObj = new MediatorLiveData<>();
executor.execute(()->{
UserPageRelation userRelation = scheduleDB.userDao().getAllUserPages().getValue();
dataLiveObj .postValue(scheduleDB.userDao().getPagesCombinedData().getValue());
});
return dataLiveObj;
}
这种方式只会在线程完成执行时以及数据库更新时更新 returned LiveData 一次,我在 UI 片段中的观察者永远不会知道它。
所以现在我不知道我应该如何 return 这个 Mediator 以便我的 UI 观察它并在数据库中更新数据时更新 UI。
我的 Fragment 的代码应该观察 Mediator
pagesViewModel.getPageCombinedData().observe(getViewLifecycleOwner(), pagesCombinedData -> {
pagesAdapter.setUserPages(pagesCombinedData.getUserEntities());
pagesAdapter.setPageEntities(pagesCombinedData.getPageEntities());
pagesAdapter.notifyDataSetChanged();
});
我的 PagesViewModel 代码:
public LiveData<PagesCombinedData> getPageCombinedData(){
return scheduleDB.userDao().getPagesCombinedData();
}
我应该构造中介对象的 UserDao 代码:
@Dao
public abstract class UserDao {
@Transaction
public MediatorLiveData<PagesCombinedData> getPagesCombinedData (){
MediatorLiveData <PagesCombinedData> dataMediator = new MediatorLiveData<>();
LiveData<List<PageEntity>> value1 = getAllPages();
LiveData<UserPageRelation> value2 = getAllUserPages();
dataMediator.addSource(value1, value -> dataMediator.setValue(combineData(value1,value2)));
dataMediator.addSource(value2, value -> dataMediator.setValue(combineData(value1,value2)));
return dataMediator;
}
private PagesCombinedData combineData(LiveData<List<PageEntity>> pages,LiveData<UserPageRelation> userPages ){
return new PagesCombinedData(pages.getValue()==null?new ArrayList<>():pages.getValue()
,userPages.getValue()==null ?new ArrayList<>():userPages.getValue().getUserPages());
}
@Query("Select * from page")
public abstract LiveData<List<PageEntity>> getAllPages ();
@Transaction
@Query("Select * from user limit 1")
public abstract LiveData<UserPageRelation> getAllUserPages ();
}
更新
试图将我的代码移动到 ViewModel 中,但它告诉我无法在后台线程上添加源代码,因为它使用 observeForever -_-*
我在 ViewModel 中尝试的代码
private MediatorLiveData<PagesCombinedData> pagesCombinedData;
private ExecutorService executor ;
public LiveData<PagesCombinedData> getPageCombinedData(){
if (pagesCombinedData==null){
pagesCombinedData = new MediatorLiveData<>();
pagesCombinedData.setValue(new PagesCombinedData());
}
executor.execute(()->{
LiveData<List<PageEntity>> value1 = scheduleDB.userDao().getAllPages();
LiveData<UserPageRelation> value2 = scheduleDB.userDao().getAllUserPages();
pagesCombinedData.addSource(value1, value -> pagesCombinedData.setValue(combineData(value1,value2)));
pagesCombinedData.addSource(value2, value -> pagesCombinedData.setValue(combineData(value1,value2)));
});
return pagesCombinedData;
}
我得到的异常(第59行是addSource
行)
java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:487)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:224)
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.addSource(MediatorLiveData.java:96)
at com.example.pretest.schedule.pages.PagesViewModel.lambda$getPageCombinedData$PagesViewModel(PagesViewModel.java:59)
at com.example.pretest.schedule.pages.-$$Lambda$PagesViewModel$Z3rbrO9SPtD8Pwpbh3mfk47DjVE.run(lambda)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
简单明了地告诉我,我想要实现的目标是不可能的吗?
当使用 LiveData 作为来自 Dao 查询的 return 值时,我似乎没有将我的代码包含在任何执行器中。
只有当 Room 的抽象查询不是我写的查询的结果时,它才会真正在后台执行查询。
所以我的代码最后看起来像这样:
我的道
@Dao
public abstract class UserDao {
@Query("Select * from page")
public abstract LiveData<List<PageEntity>> getAllPages ();
@Transaction
@Query("Select * from user limit 1")
public abstract LiveData<UserPageRelation> getAllUserPages ();
}
我的视图模型
public class MyViewModel extends ViewModel {
private MediatorLiveData<PagesCombinedData> pagesCombinedData;
public LiveData<PagesCombinedData> getPageCombinedData(){
if (pagesCombinedData==null){
pagesCombinedData = new MediatorLiveData<>();
}
LiveData<List<PageEntity>> value1 = scheduleDB.userDao().getAllPages();
LiveData<UserPageRelation> value2 = scheduleDB.userDao().getAllUserPages();
pagesCombinedData.addSource(value1, value -> pagesCombinedData.setValue(combineData(value1,value2)));
pagesCombinedData.addSource(value2, value -> pagesCombinedData.setValue(combineData(value1,value2)));
return pagesCombinedData;
}
private PagesCombinedData combineData(LiveData<List<PageEntity>> pages,LiveData<UserPageRelation> userPages ){
return new PagesCombinedData(pages.getValue()==null?new ArrayList<>():pages.getValue()
,userPages.getValue()==null ?new ArrayList<>():userPages.getValue().getUserPages());
}
}
而且我可以从我想要的两个数据列表中观察到 MediatorLiveData 的魅力 return。