26

从我读过的内容来看,Room 不允许您在主线程上发出数据库查询(因为可能会导致主线程延迟)。 所以想象一下我正在尝试更新 UI 主线程上的文本视图,其中一些数据我将如何获得回调。让我给你看一个例子。想象一下,我想将我的业务模型数据存储到一个名为 Events 的对象中。因此我们会有一个 EventDao 对象:

假设我们在下面有这个 DAO 对象:

@Dao
public interface EventDao {

   @Query("SELECT * FROM " + Event.TABLE_NAME + " WHERE " + Event.DATE_FIELD + " > :minDate" limit 1)
   LiveData<List<Event>> getEvent(LocalDateTime minDate);

   @Insert(onConflict = REPLACE)
   void addEvent(Event event);

   @Delete
   void deleteEvent(Event event);

   @Update(onConflict = REPLACE)
   void updateEvent(Event event);

}

现在在某些活动中,我有一个 textview,我想更新它的值,所以我这样做:

 myTextView.setText(EventDao.getEvent(someDate));/*i think this is illegal as im trying to call room dao on mainthread, therefore how is this done correctly ? would i need to show a spinner while it updates ?*/

由于获取是在主线程之外发生的,我认为我不能这样称呼它并期待顺利更新。这里最好的方法是什么?

更多信息:我想使用房间数据库作为检索模型信息的机制,而不是将其静态保存在内存中。因此,在我通过休息服务下载模型后,我可以通过数据库在本地使用该模型。

更新:所以因为我要返回一个实时数据,所以我可以这样做:

eventDao = eventDatabase.eventDao();
eventDao.getEvent().observe(this, event -> {
     myTextView.setText(event.get(0));
});

这适用于非常小的东西。但想象一下我的数据库有一百万个项目。然后当我打这个电话时,检索数据会有延迟。第一次调用它时,用户会看到有延迟。如何避免这种情况?所以要明确一点,有时我不想要实时数据,我只需要在查看后更新。我需要知道该怎么做?即使它没有使用 liveData。

4

4 回答 4

12

如果您想同步进行查询并且不接收数据集更新的通知,请不要将返回值包装在 LiveData 对象中。查看来自 Google 的示例代码。

看看loadProductSync() 这里

于 2017-06-13T07:11:47.730 回答
7

有一种方法可以关闭异步并允许同步访问。

构建数据库时,您可以使用:allowMainThreadQueries()

并用于内存:Room.inMemoryDatabaseBuilder()

虽然不推荐。所以最后,如果我想要超快速访问,我可以使用内存数据库和主线程访问。我想这取决于我的数据有多大,在这种情况下非常小。

但如果你确实想使用回调.... 使用 rxJava 这是我为我想存储在数据库中的国家/地区列表制作的一个:

public Observable<CountryModel> queryCountryInfoFor(final String isoCode) {
    return Observable.fromCallable(new Callable<CountryModel>() {
        @Override
        public CountryModel call() throws Exception {
            return db.countriesDao().getCountry(isoCode);
        }
    }).subscribeOn(Schedulers.io())
       
 .observeOn(AndroidSchedulers.mainThread());

}

然后,您可以轻松地向此函数添加订阅者,以使用 Rxjava 获取回调。

于 2017-05-31T20:36:45.897 回答
3

正如Bohsen建议的那样,使用 livedata 进行同步查询。但是在某些特殊情况下,我们想做一些基于逻辑的异步操作。在下面的示例中,我需要为父评论获取一些子评论。它已经在 DB 中可用,但需要根据它在 recyclerview 适配器中的 parent_id 获取。为此,我使用了 AsyncTask 的返回概念来获取结果。(在 Kotlin 中返回)

存储库类

fun getChildDiscussions(parentId: Int): List<DiscussionEntity>? {
        return GetChildDiscussionAsyncTask(discussionDao).execute(parentId).get()
    }

private class GetChildDiscussionAsyncTask constructor(private val discussionDao: DiscussionDao?): AsyncTask<Int, Void, List<DiscussionEntity>?>() {
        override fun doInBackground(vararg params: Int?): List<DiscussionEntity>? {
            return discussionDao?.getChildDiscussionList(params[0]!!)
        }
    }

道类

@Query("SELECT * FROM discussion_table WHERE parent_id = :parentId")
fun getChildDiscussionList(parentId: Int): List<DiscussionEntity>?
于 2019-02-13T11:56:43.373 回答
1

好吧,正确的答案是使用ListenableFutureObservable,具体取决于您是否需要一次性查询或数据库更改后发出的新值以及您要使用的框架。

来自文档“为了防止查询阻塞 UI,Room 不允许在主线程上访问数据库。这个限制意味着您必须使您的 DAO 查询异步。Room 库包括与几个不同框架的集成,以提供异步查询执行。 "

以一次性查询为例。您只需将其添加到您的 gradle 文件中。

// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"

然后你的 DAO 中的 SQL 查询就变成了。

@Query("SELECT * FROM " + Event.TABLE_NAME)
ListenableFuture<List<Event>> getEventList();

最后一步是未来调用本身。

 ListenableFuture<List<Event>> future = dao.getEventList();
 future.addListener(new Runnable() {
    @Override
    public void run() {
        try {
            List<Event>> result = future.get();
        } catch (ExecutionException | InterruptedException e) {
        }
    }
}, Executors.newSingleThreadExecutor());

来源:https ://developer.android.com/training/data-storage/room/async-queries#guava-livedata

于 2021-11-02T22:19:46.630 回答