Android Room单查询同步实现及数据赋值异常求助
问题描述
我在Android中使用Room框架,多数查询采用异步方式,但有一个查询需要设为同步执行。我不使用Kotlin的suspend语法,也不想直接操作SQLite。目前DAO中已定义同步查询方法,在Firestore查询的回调中调用Room的findByTitle方法获取Favourite对象,将其属性设置到Calculators实例后,该属性未被正确添加到calculators集合中,日志显示fave.getFav()取值正常,需解决该问题及同步查询相关疑问。
相关代码
Activity中的同步查询代码
Favourite fave = appDb.favouriteDao().findByTitle(formTitleText);
DAO代码
@Dao public interface FavouriteDao { @Query("SELECT * FROM favourite") List<Favourite> getAll(); @Insert void insertFavourite(Favourite favourite); @Update void updateFavourite(Favourite favourite); @Delete void delete(Favourite favourite); @Query("SELECT * FROM favourite WHERE calc_title LIKE :title LIMIT 1") Favourite findByTitle(String title); @Query("SELECT EXISTS(SELECT * FROM favourite WHERE calc_title LIKE :title LIMIT 1)") boolean isRowIsExist(String title); @Query("UPDATE favourite SET fav_status = :value WHERE calc_title LIKE :title") void updateFavourite(String value, String title); }
核心业务代码
private void getCalculators(Source path) { db.collection("Calculators") .get(path) .addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { public void onComplete(@NonNull Task<QuerySnapshot> task) { if (task.isSuccessful()) { for (QueryDocumentSnapshot document : task.getResult()) { String formTitleText = (document.getString("title")); String formTagLine = (document.getString("tagLine")); String numberOfFields = (document.getString("NumberFields")); String formInfo = (document.getString("formInfo")); String formRef = (document.getString("formRef")); String formula = (document.getString("formula")); String resultUnits = (document.getString("resultUnits")); String formSections = (document.getString("sections")); Calculators dataSection = new Calculators(); dataSection.setFormTitleText(formTitleText); dataSection.setFormTagLine(formTagLine); dataSection.setNumberOfFields(numberOfFields); dataSection.setFormInfo(formInfo); dataSection.setFormRef(formRef); dataSection.setFormula(formula); dataSection.setResultUnits(resultUnits); dataSection.setFormSections(formSections); // add favourite status to room db Favourite fave = appDb.favouriteDao().findByTitle(formTitleText); dataSection.setFormFavourite(fave.getFav()); calculators.add(dataSection);
解决方法
1. 修复Room同步查询的线程违规问题
Room默认禁止在主线程执行同步查询,而Firestore的onComplete回调运行在主线程,直接调用同步查询会触发线程违规,即使暂时未崩溃,也会导致数据不一致或后续集合操作异常。
合规实现方式(推荐)
将Room同步查询逻辑放到子线程执行,完成后回到主线程更新集合:
// 在Firestore回调的循环内修改 String finalTitle = formTitleText; Executors.newSingleThreadExecutor().execute(() -> { // 子线程执行Room同步查询 Favourite fave = appDb.favouriteDao().findByTitle(finalTitle); // 回到主线程更新数据与集合 runOnUiThread(() -> { if (fave != null) { // 避免空指针异常 dataSection.setFormFavourite(fave.getFav()); } calculators.add(dataSection); // 若为RecyclerView数据源,需通知适配器更新 // adapter.notifyItemInserted(calculators.size() - 1); }); });
临时兼容方式(不推荐)
若必须在主线程执行同步查询,需在创建RoomDatabase时开启主线程查询许可,但会增加主线程阻塞导致ANR的风险:
AppDatabase appDb = Room.databaseBuilder(context, AppDatabase.class, "db-name") .allowMainThreadQueries() .build();
2. 排查calculators集合数据未正确更新的问题
- 线程安全校验:若calculators被多线程访问,替换为线程安全集合(如
CopyOnWriteArrayList),避免并发修改导致的数据丢失。 - 实体类setter校验:检查
Calculators的setFormFavourite方法是否正确赋值,例如:public class Calculators { private String formFavourite; // 匹配fav_status的数据类型 public void setFormFavourite(String fav) { this.formFavourite = fav; // 确保赋值逻辑无错误 } } - 集合更新通知:若calculators是UI组件的数据源,添加元素后必须调用适配器的更新方法(如
notifyDataSetChanged()),否则UI无法感知数据变化。
内容的提问来源于stack exchange,提问作者Nicholas Farmer
相关产品推荐
相关产品推荐

