【问题标题】:Room Database doesn't run my implemented transaction on background thread房间数据库不在后台线程上运行我实现的事务
【发布时间】:2021-11-16 09:38:00
【问题描述】:

这个问题是我为我的另一个 question 尝试过的一个解决方案,所以如果你对第一个问题有一个避免这个问题的解决方案,我会全力以赴。 p>

所以我在 Dao (getPageCombinedData) 中有这个方法,它用 @transaction 注释并且应该返回一个 MediatorLiveData,我想既然 MediatorLiveData 是 LiveData 的子类,那么 Room 将提供必要的代码来使用背景返回它从其文档中引用的线程。

当 Room 查询返回 LiveData 时,查询会自动在后台线程上异步运行。

但是我得到了一个异常,告诉我我正在主线程上运行我的查询

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;
}

这种方式只会在线程完成执行时更新返回的 LiveData 一次,并且每当数据库更新时,我在 UI Fragment 中的观察者永远不会知道它。

所以现在我不知道应该如何返回这个 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 代码应该构造 Mediator 对象:

@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$2$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)

简单明了地告诉我,我想要实现的目标是不可能的吗?

【问题讨论】:

    标签: java android android-room android-livedata mediatorlivedata


    【解决方案1】:

    当使用 LiveData 作为 Dao 查询的返回值时,我似乎没有将我的代码包含在任何执行器中。
    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 的魅力一样,我可以从我想要的两个数据列表中观察到。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多