web-dev-qa-db-fra.com

Méthode Rxjava2 Just - Comment exécuter l'insertion de pièce sur un autre thread?

J'ai une méthode d'insertion de base de données persistante qui ressemble à ceci:

@Dao
public interface CountriesDao{

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

Je me rends compte que cela ne peut pas être exécuté sur le thread principal. Voici comment je définis ma base de données:

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

J'essaie d'utiliser rxjava2 pour ne pas exécuter sur le thread principal. J'ai créé la méthode suivante:

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");
            }
        });
    }

Pour moi, cela fonctionne clairement maintenant sur un autre thread. PAS le thread principal, mais lorsque j'exécute ce code, j'obtiens l'erreur suivante:

La trace complète de la pile est ci-dessous. Pourquoi cela arrive-t-il ?

Processus: com.mobile.myapp.staging, PID: 12990
Java.lang.IllegalStateException: exception fatale levée sur le planificateur. Causé par: Java.lang.IllegalStateException: impossible d'accéder à la base de données sur le thread principal car il peut potentiellement verrouiller l'interface utilisateur pendant une longue période. à io.reactivex.Android.schedulers.HandlerScheduler $ ScheduledRunnable.run (HandlerScheduler.Java:111) à Android.os.Handler.handleCallback (Handler.Java:751) à Android.os.Handler.dispatchMessage (Handler.Java:95 ) sur Android.os.Looper.loop (Looper.Java:154) sur Android.app.ActivityThread.main (ActivityThread.Java:6077) sur Java.lang.reflect.Method.invoke (Native Method) sur com.Android. internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.Java:866) sur com.Android.internal.os.ZygoteInit.main (ZygoteInit.Java:756) Provoqué par: Java.lang.IllegalStateException: impossible d'accéder à la base de données sur le principal thread car il peut potentiellement verrouiller l'interface utilisateur pendant une longue période de temps. sur Android.Arch.persistence.room.RoomDatabase.assertNotMainThread (RoomDatabase.Java:138) sur Android.Arch.persistence.room.RoomDatabase.beginTransaction (RoomDatabase.Java:185) sur com.mobile.myapp.data.room.dao .CountriesDao_Impl.addCountries (CountriesDao_Impl.Java:165) sur com.mobile.myapp.data.repositories.CountryRepository.storeCountries (CountryRepository.Java:42) sur com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter.cacheCountries ( .Java: 40) sur com.mobile.myapp.UI.mvp.Presenters.SignUpPresenter $ CountriesSubscriber.onNext (SignUpPresenter.Java:60) sur 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.ObservableObserveOnObserverObserverObserverObserverObserverObserverObserverObserverObserver.ObservableObserverObserverObserverObservableObservableObservableObservableObservableOn ) sur io.reactivex.Android.schedulers.HandlerScheduler $ ScheduledRunnable.run (HandlerSch eduler.Java:109) sur Android.os.Handler.handleCallback (Handler.Java:751) sur Android.os.Handler.dispatchMessage (Handler.Java:95) sur Android.os.Looper.loop (Looper.Java:154 ) Sur Android.app.ActivityThread.main (ActivityThread.Java:6077) sur Java.lang.reflect.Method.invoke (Native Method) sur com.Android.internal.os.ZygoteInit $ MethodAndArgsCaller.run (ZygoteInit.Java:866) ) Sur com.Android.internal.os.ZygoteInit.main (ZygoteInit.Java:756)

Pas important mais si vous avez besoin de savoir à quoi ressemble la classe defaultSubscriber ici:

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();
    }
  }
}
17
j2emanue

C'est une erreur courante: just() n'exécutera pas le "code" entre parenthèses car just prend une valeur, pas un calcul. Vous avez besoin de fromCallable:

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

Encore mieux, vous pouvez utiliser un Completable. Sa description: Représente un calcul sans aucune valeur mais seulement indication pour l'achèvement ou exception.

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

Remarque: Room ne prend pas en charge l'accès à la base de données sur le thread principal, sauf si vous avez appelé allowMainThreadQueries () sur le générateur, car cela pourrait verrouiller l'interface utilisateur pendant une longue période. Requêtes asynchrones - les requêtes qui renvoient des instances de LiveData ou Flowable sont exemptées de cette règle car elles exécutent la requête de manière asynchrone sur un thread d'arrière-plan lorsque cela est nécessaire.

Donc votre code peut être comme ça

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

De la chambre chambre 2.1.0-alpha02, vous pouvez utiliser (Completeable, Single, Maybe) lors de l'insertion ( https://medium.com/androiddevelopers/room-rxjava-acb0cd4f3757 )

Exemple

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

En utilisant

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
    }
});
3
Phan Van Linh

Vous pouvez également utiliser le seul observable

        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) {

        }
});
1
Farid Z