0

这个问题是我为我的另一个问题尝试过的解决方案,所以如果您对第一个问题有解决方案,可以避免这个问题,我会全神贯注。

所以我在 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 进行此操作,但我发现每当数据库更新时,它永远不会用新数据到达我的观察者,因为我的代码看起来像这样

我的视图模型代码

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)

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

4

1 回答 1

0

当使用 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 的魅力一样,我可以从我想要的两个数据列表中观察到。

于 2021-09-24T22:46:59.303 回答