如何使用RxJava从Android Room数据库的查询操作中获取返回值

3

这是我的查询

@Query("SELECT EXISTS(SELECT 1 FROM media2 WHERE path = :path AND isFav = 1 LIMIT 1)")
    int checkIsFav(String path);

从代码库调用

public boolean checkIsFav(String path) {
    return mDatabase.favDAO().checkIsFav(path) == 1;
}

有时会出现以下错误:
 java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

我理解原因,但需要返回值才能确定某个项目是否为“Fav”。
我使用RXjava进行其他数据库操作,但它们不需要返回值。
Observable.fromCallable(() -> mDatabase.favDAO().checkIsFav(path) == 1);

或者
Observable.just(mDatabase)
    .subscribeOn(Schedulers.io())
    .subscribe(db -> db.favDAO().insertMedia2(media));

我在我的片段中使用了这种方法

if (mediaViewModel.checkIsFav(media.getPath())){
    mediaViewModel.removeFav(media.getPath());
} else {
    media.setFav(true);
    mediaViewModel.addFav(media);
}

和回收适配器相关。
 boolean isfav = ((App)context.getApplicationContext()).getRepository()
     .checkIsFav(currentItem.getPath());

当运行此查询时,最简单的方法是获得我的回调函数是什么?

使用Single更新问题:

这是我在Dao中的查询。

@Query("SELECT EXISTS(SELECT 1 FROM media2 WHERE path = :path AND isFav = 1 LIMIT 1)")
Single<Integer> checkIsFav(String path);

我在代码库中的方法

public Single<Integer> checkIsFav(String path){
    return mDatabase.favDAO().checkIsFav(path);
}

我需要从我的适配器和片段中进行此调用。当我这样调用时,我该如何做?
((App)context.getApplicationContext()).getRepository().checkIsFav(media.getPath())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread()).subscribe(
        new SingleObserver<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onSuccess(Integer integer) {

            }

            @Override
            public void onError(Throwable e) {

            }
        }
    );

这似乎不太对劲。在我转换到房间之前,这是我的呼叫。

if (App.favDB.existFav(currentItem.getPath()))

这在我的SQLiteOpenHelper中引发了这个问题

 public boolean existFav(String path) {
        SQLiteDatabase db = getReadableDatabase();
        Cursor cur = db.rawQuery(
            String.format("SELECT EXISTS(SELECT 1 FROM %s WHERE %s=? LIMIT 1)", TABLE_NAME, MEDIA_PATH),
            new String[]{ path });
        boolean tracked = cur.moveToFirst() &&  cur.getInt(0) == 1;
        cur.close();
        return tracked;
    }

也许我的思考不够全面。我所尝试的只是确定我的表中是否存在这种媒体。

如果您想在UI线程上读取,则可以将allowMainThreadQueries添加到Room构建器中。当然,出于某种原因,默认情况下会阻止此操作。 - EpicPandaForce
6个回答

2

我认为你应该在主线程中观察数据。因此,请使用

observeOn(AndroidSchedulers.mainThread()).

尝试将其更改为
Observable.just(mDatabase)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread()) 
        .subscribe(db -> db.favDAO().insertMedia2(media));

还可以查看关于observeOn()注释


0

尝试更改:

@Query("SELECT EXISTS(SELECT 1 FROM media2 WHERE path = :path AND isFav = 1 LIMIT 1)")
int checkIsFav(String path);

到:

@Query("SELECT EXISTS(SELECT 1 FROM media2 WHERE path = :path AND isFav = 1 LIMIT 1)")
Single<Integer> checkIsFav(String path);

使用RxJava with Room是可行的,尽管我还没有尝试过与SELECT EXISTS一起使用,所以可能会有不同的结果。


但是我该如何实际执行回调来获取单个呢?这是什么样子的? - BigDX
@Bignadad:嗯,那只是DAO上的一个方法。Single是RxJava Observable类型之一,因此您只需设置一个链来对该值进行操作(例如,subscribeOn()observeOn())。所以,dao.checkIsFav(path).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(...) - CommonsWare
我更新了我的问题。请告诉我是否我考虑错了。 - BigDX
@Bignadad:“在我转换为Room之前,这是我的调用方式” - 希望不是这种情况。那将意味着您正在主应用程序线程上执行数据库I/O操作。 - CommonsWare
是的,但它还没有发布。但我没有任何问题打电话。我现在正在阅读你的书(我有订阅),试图理解它。 - BigDX
@Bignadad:你的旧代码比新代码简单的原因是因为你在新代码中处理了线程,而在旧代码中没有。就像俗话说的,“你在比较苹果和橙子”。 - CommonsWare

0
我们可以像这样创建单例:
public Single<SomeObject> getSomeObjectFromDB(Session session) {
    return createAsyncSingle(() -> database.getSomeDao().getSomeObject())
        .subscribeOn(Schedulers.io()) // We can move it on upper level;
}

在哪里:

protected <T> Single<T> createAsyncSingle(final Callable<T> func) {
    return Single.create(emitter -> {
        try {
            T result = func.call();
            if(result != null) {
                emitter.onSuccess(result);
            } else {
                emitter.onError(new NullPointerException("Empty result from DB"));
            }
        } catch (Exception ex) {
            Logger.e("TAG", "Error operation with DB");
        }
    });
}

0

0

哎呀,以前你总是在 UI 线程上阅读这篇文章。现在 Room 默认强制执行不允许这样做,因为它可能会阻塞 UI 线程。

如果你不想关心这个限制,只想让旧类型的代码正常工作,那么你可以使用 RoomDatabase.Builder 的 allowMainThreadQueries()

否则,只需暴露 Flowable 并在 Io 上订阅,在 Ui 上观察,就像通常一样。


0
有点晚回答这个问题,但希望能帮助到任何在使用RXJava的Room中实现一次性读取查询的人。
  1. 在DAO中定义你的查询:
    @Query("SELECT id, name, lastName FROM Students WHERE age = :age")
     Single<List<Students_Schema>> selectStudents(int age);

使用blockingGet()方法来访问这些值。
    result = student_DB.student_dao().selectStudents(age);
result.blockingGet().get(0).name;

这是一份关于访问Single的很棒的指南。

https://medium.com/@katcri/ways-to-handle-what-your-rxjava-single-is-emitting-2f3e335b8f41


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接