Room返回MediatorLiveData时出现主线程访问数据库报错如何解决?
问题根源
你踩了三个核心误区:
- Room返回
LiveData的查询方法,仅在LiveData被观察者绑定的时候才会自动异步执行查询,主动调用LiveData.getValue()会触发同步数据库操作,若在主线程执行就会抛出主线程访问数据库异常。 MediatorLiveData.addSource()内部依赖observeForever()方法,该方法必须在主线程调用,不能放到子线程执行。- Dao层只应该负责定义数据库查询逻辑,不应该承担LiveData合并的业务逻辑,Room的
@Transaction注解仅对数据库操作生效,对Dao层自定义方法里的LiveData操作没有额外的异步处理能力。
可行实现方案
第一步:清理Dao层逻辑
删除Dao中自定义的getPagesCombinedData方法,仅保留基础查询方法即可:
@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 (); }
第二步:ViewModel层实现合并逻辑
所有LiveData实例获取、addSource操作都放在主线程执行(这些操作仅返回对象不涉及IO,不会阻塞主线程),维护两个变量存储两个查询的最新结果,每次任意结果更新时触发合并:
public class PagesViewModel extends ViewModel { private MediatorLiveData<PagesCombinedData> pagesCombinedData; // 存储两个查询的最新结果 private List<PageEntity> latestPages = new ArrayList<>(); private UserPageRelation latestUserRelation = null; // 注入你的数据库实例 private ScheduleDB scheduleDB; public LiveData<PagesCombinedData> getPageCombinedData(){ if (pagesCombinedData == null) { pagesCombinedData = new MediatorLiveData<>(); // 获取Room返回的LiveData实例,该操作不会执行IO,主线程调用安全 LiveData<List<PageEntity>> pagesLiveData = scheduleDB.userDao().getAllPages(); LiveData<UserPageRelation> userRelationLiveData = scheduleDB.userDao().getAllUserPages(); // 监听页面数据更新 pagesCombinedData.addSource(pagesLiveData, pages -> { latestPages = pages != null ? pages : new ArrayList<>(); combineAndPostResult(); }); // 监听用户关联数据更新 pagesCombinedData.addSource(userRelationLiveData, userRelation -> { latestUserRelation = userRelation; combineAndPostResult(); }); } return pagesCombinedData; } // 合并数据并通知观察者 private void combineAndPostResult() { List<UserEntity> userPages = latestUserRelation != null ? latestUserRelation.getUserPages() : new ArrayList<>(); PagesCombinedData combinedData = new PagesCombinedData(latestPages, userPages); pagesCombinedData.setValue(combinedData); } }
实现说明
- Room返回的
LiveData会自动在后台线程执行查询,数据更新时自动将结果抛到主线程回调,不会触发主线程IO异常。 - 任意一个数据源更新时都会触发合并逻辑,UI层可以实时收到最新的合并后数据,自动更新界面。
- 你的Fragment层监听代码不需要做任何修改,可以直接沿用原有逻辑。
内容的提问来源于stack exchange,提问作者Omar Shawky
相关产品推荐
相关产品推荐

