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。