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