Rxjava2 просто метод - как запустить помещение в другую тему?

У меня есть метод вставки базы данных постоянный номер, который выглядит следующим образом:

@Dao
public interface CountriesDao{

    @Insert(onConflict = REPLACE)
    List<Long> addCountries(List<CountryModel> countryModel);
}

Я понимаю, что это не может быть запущено в главном потоке. Вот как я определяю свою базу данных:

Room.inMemoryDatabaseBuilder(context.getApplicationContext(), MyDatabase.class).build();

Я пытаюсь использовать rxjava2, чтобы не запускаться в главном потоке. Я создал следующий метод:

public void storeCountries(List<CountryModel> countriesList) {
        Observable.just(db.countriesDao().addCountries(countriesList))
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DefaultSubscriber<List<Long>>(){
            @Override
            public void onSubscribe(@NonNull Disposable d) {
                super.onSubscribe(d);
            }

            @Override
            public void onNext(@NonNull List<Long> longs) {
                super.onNext(longs);
                Timber.d("insert countries transaction complete");
            }

            @Override
            public void onError(@NonNull Throwable e) {
                super.onError(e);
                Timber.d("error storing countries in db"+e);
            }

            @Override
            public void onComplete() {
                Timber.d("insert countries transaction complete");
            }
        });
    }

Для меня это явно сейчас работает в другой теме. НЕ основной поток, но когда я запускаю этот код, я получаю следующую ошибку:

Полная трассировка стека приведена ниже. Почему это происходит?

Процесс: com.mobile.myapp.staging, PID: 12990
java.lang.IllegalStateException: фатальное исключение, генерируемое в планировщике. Вызвано: java.lang.IllegalStateException: Не удается получить доступ к базе данных на основной поток, так как он может потенциально заблокировать пользовательский интерфейс на длительный период времени.                                                                                    в io.reactivex.android.schedulers.HandlerScheduler $ ScheduledRunnable.run(HandlerScheduler.java:111)                                                                                    на android.os.Handler.handleCallback(Handler.java:751)                                                                                    на android.os.Handler.dispatchMessage(Handler.java:95)                                                                                    на android.os.Looper.loop(Looper.java:154)                                                                                    на android.app.ActivityThread.main(ActivityThread.java:6077)                                                                                    в java.lang.reflect.Method.invoke (родной метод)                                                                                    в com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:866)                                                                                    на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)                                                                                 Вызвано: java.lang.IllegalStateException: Не удается получить доступ к базе данных на основной поток, так как он может потенциально заблокировать пользовательский интерфейс на длительный период времени.                                                                                    в android.arch.persistence.room.RoomDatabase.assertNotMainThread(RoomDatabase.java:138)                                                                                    в android.arch.persistence.room.RoomDatabase.beginTransaction(RoomDatabase.java:185)                                                                                    в com.mobile.myapp.data.room.dao.CountriesDao_Impl.addCountries(CountriesDao_Impl.java:165)                                                                                    в com.mobile.myapp.data.repositories.CountryRepository.storeCountries(CountryRepository.java:42)                                                                                    в com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter.cacheCountries(SignUpPresenter.java:40)                                                                                    в com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter $ CountriesSubscriber.onNext(SignUpPresenter.java:60)                                                                                    в com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter $ CountriesSubscriber.onNext(SignUpPresenter.java:49)                                                                                    в io.reactivex.internal.operators.observable.ObservableObserveOn $ ObserveOnObserver.drainNormal(ObservableObserveOn.java:200)                                                                                    в io.reactivex.internal.operators.observable.ObservableObserveOn $ ObserveOnObserver.run(ObservableObserveOn.java:252)                                                                                    в io.reactivex.android.schedulers.HandlerScheduler $ ScheduledRunnable.run(HandlerScheduler.java:109)                                                                                    на android.os.Handler.handleCallback(Handler.java:751)                                                                                    на android.os.Handler.dispatchMessage(Handler.java:95)                                                                                    на android.os.Looper.loop(Looper.java:154)                                                                                    на android.app.ActivityThread.main(ActivityThread.java:6077)                                                                                    в java.lang.reflect.Method.invoke (родной метод)                                                                                    в com.android.internal.os.ZygoteInit $ MethodAndArgsCaller.run(ZygoteInit.java:866)                                                                                    на com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)

Не важно, но если вам нужно знать, как выглядит класс defaultSubscriber, то он здесь:

DefaultSubscriber.java

public class DefaultSubscriber<T> implements Observer<T> {

Disposable disposable;

@Override
public void onSubscribe(@NonNull Disposable d) {
    disposable = d;
}

@Override
public void onNext(@NonNull T t) {

}

@Override
public void onError(@NonNull Throwable e) {
    Timber.e(e);
}

@Override
public void onComplete() {

}

public void unsubscribe(){
    if(disposable!=null && !disposable.isDisposed()){
        disposable.dispose();
    }
  }
}

Ответ 1

Это распространенная ошибка: just() не будет выполнять "код" в своей скобке, так как just принимает значение, а не вычисляет. Вам нужно fromCallable:

Observable.fromCallable(() -> db.countriesDao().addCountries(countriesList))

Ответ 2

Еще лучше, вы можете использовать Completable. Его описание: представляет собой вычисление без какого-либо значения, но только указание для завершения или исключение.

Completable.fromAction(() -> db.countriesDao().addCountries(list));

Ответ 3

  Примечание: Room не поддерживает доступ к базе данных в главном потоке, если только вы вызвали allowMainThreadQueries() в компоновщике, потому что это может заблокировать интерфейс на длительный период времени. Асинхронные запросы - запросы что возвращаемые экземпляры LiveData или Flowable освобождаются от этого правило, потому что они асинхронно запускают запрос в фоновом потоке когда нужно.

Таким образом, ваш код может быть таким

Completable.fromAction(() -> db.countriesDao()
                .addCountries(list))
                .subscribeOn(Schedulers.io())
                .subscribe();

Ответ 4

Из комнаты 2.1.0-alpha02 вы можете использовать (Completeable, Single, Maybe) при вставке ( https://medium.com/androiddevelopers/room-rxjava-acb0cd4f3757)

Пример

@Dao
interface UserDao{
     @Insert
     Completable insert(final User user); // currently, we must put final before user variable or you will get error when compile
}

Использование

db.userDao().insert(user).subscribeOn(Schedulers.io()).subscribe(new Action() {
    @Override
    public void run() throws Exception {
        // success
    }
}, new Consumer < Throwable > () {
    @Override
    public void accept(Throwable throwable) throws Exception {
        // error
    }
});

Ответ 5

Вы также можете использовать Единый наблюдаемый

        Single.create(new SingleOnSubscribe<List<Long>>() {
        @Override
        public void subscribe(SingleEmitter<List<Long>> emitter) throws Exception {
            try {
                List<Long> ids = db.countriesDao().addCountries(countriesList);
                emitter.onSuccess(ids);
            } catch (Throwable t) {
                emitter.onError(t);
            }
        }})
    .observeOn(AndroidSchedulers.mainThread())
    .subscribeOn(Schedulers.io())
    .subscribeWith(new DisposableSingleObserver<Long>() {
        @Override
        public void onSuccess(List<Long> ids) {

        }

        @Override
        public void onError(Throwable e) {

        }
});