Room Database 不会在后台线程上运行我实现的事务 [英] Room Database doesn't run my implemented transaction on background thread

查看:31
本文介绍了Room Database 不会在后台线程上运行我实现的事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题是我为我的其他问题,因此,如果您对第一个问题有解决方案可以避免此问题,那么我会全力以赴.

This question is a solution I've tried for my other question, so if you have a solution to the first one that avoids this question problems, I'm all ears.

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

So I have this method in Dao (getPageCombinedData) that is annotated with @transaction and should return a MediatorLiveData, I thought since MediatorLiveData is a subclass of LiveData, then Room will provide the necessary code to return it using background thread as quoted from its documentation.

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

When Room queries return LiveData, the queries are automatically run asynchronously on a background thread.

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

but instead I got the exception telling me that I'm running my query on Main 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 执行此操作,但我发现每当数据库更新时,它永远不会将新数据发送到我的观察者,因为我的代码如下所示

I firstly tried making this with an Executer on another thread but I found out that whenever the database gets updated, it will never reach my observer with the new data as my code looked like this

我的 ViewModel 代码

my ViewModel code

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 中的观察者永远不会知道它.

where that way will only update the returned LiveData only once when the thread finish executing and whenever the database gets updated, my observer in UI Fragment will never know about it.

所以现在我不知道我应该如何返回这个 Mediator 以便我的 UI 观察它并在数据库中更新数据时更新 UI.

so now I don't know how should I return this Mediator in order for my UI to observe it and update the UI whenever data is updated in the database.

我的 Fragment 代码应该观察 Mediator

My Fragment's code that should observe the Mediator

pagesViewModel.getPageCombinedData().observe(getViewLifecycleOwner(), pagesCombinedData -> {
            pagesAdapter.setUserPages(pagesCombinedData.getUserEntities());
            pagesAdapter.setPageEntities(pagesCombinedData.getPageEntities());
            pagesAdapter.notifyDataSetChanged();
});

我的页面视图模型代码:

My PagesViewModel Code :

public LiveData<PagesCombinedData> getPageCombinedData(){
        return scheduleDB.userDao().getPagesCombinedData();
}

我应该构造中介对象的 UserDao 代码:

my UserDao code that should construct the Mediator Object :

@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 -_-*


Update

tried to move my code into the ViewModel but it tells me that I can't addsource on a background thread because it uses observeForever -_-*

我在 ViewModel 中尝试的代码

the code I tried in my 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 行)

the exception I got (line 59 is addSource line)

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)

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

just tell me plain and simple, is what I'm trying to achieve impossible ?

推荐答案

当使用 LiveData 作为 Dao 查询的返回值时,我似乎没有将我的代码包含在任何执行程序中.
Room 真正在后台执行查询,只有当它产生于抽象查询而不是我写的查询时.

it appears that I don't to include my code inside any executer when using LiveData as return value from Dao queries.
Room really execute queries on background only if it results from its abstract queries not the one I wrote.

所以我的代码最后看起来像这样:

so my code looks like this at the end :

我的道

@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,我可以从我想要的两个数据列表中观察到.

and that work like charm returning the MediatorLiveData that I could observe from both of the data lists I wanted.

这篇关于Room Database 不会在后台线程上运行我实现的事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆