9 votes

Le royaume se ferme

J'utilise une instance statique globale de Realm (jamais fermée) sur l'objet Application uniquement pour l'utilisation du Thread UI,

@UiThread
public static Realm getRealm() {
    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        return realmInstance;
    } else {
        Timber.e("Illegal access to getRealmObservable");
        throw new IllegalStateException("Only UI Thread can access this realm");
    }
}

et un autre domaine à usage unique pour WorkerThread comme suit :

@WorkerThread
public static void executeOnSingleUseRealm(final Realm.Transaction transaction) {
    if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
        Timber.e("Wrong thread for Realm");
        throw new IllegalStateException("Wrong thread for Single use Realm");
    }

    Realm realm = null;
    try {
        realm = Realm.getDefaultInstance();
        realm.executeTransaction(transaction);
    } catch (Exception e) {
        Timber.e(e, "Exception in Single Use Realm transaction");
        throw e;
    } finally {
        if (realm != null) {
            realm.close();
        }
    }
}

Cependant, je constate toujours des plantages lors de l'utilisation d'une seule instance globale de Realm : This Realm instance has already been closed, making it unusable.

Je ne sais pas comment c'est possible.

Voici comment j'initialise l'instance Realm :

Application onCreate

void onCreate(){
    ....
        Observable
            .fromCallable(() -> {
                Realm.init(SVApplication.this);
                RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
                        .deleteRealmIfMigrationNeeded()
                        .build();
                Realm.compactRealm(realmConfiguration);
                Realm.setDefaultConfiguration(realmConfiguration);
                return true;
            })
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe((v) -> onRealmLoaded());
    }
}

void onRealmLoaded(){
    realmInstance = Realm.getDefaultInstance();
    ....
}

Voici l'un des accidents de l'une des activités :

void onStart(){
    ....    subscribeUntilDetach(realmInstance.where(Notification.class).findAllAsync().asObservable()
                        .onBackpressureLatest()
                        .switchIfEmpty(emptyNotification())
                        .map(notifications -> notifications.where().isNull("readTime").or().isEmpty("readTime").count())
                        .onBackpressureLatest()
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(count -> {
                            if (count == 0L) {
                                mNotificationBadge.setVisibility(View.GONE);
                            } else {
                                mNotificationBadge.setText(String.format(Locale.getDefault(), "%d", count));
                                mNotificationBadge.setVisibility(View.VISIBLE);
                            }
                        }, throwable -> Timber.e(throwable, "Error setting notification count")));
}

@Override
protected void onPause() {
    super.onPause();
    if (isFinishing()) {
        mCompositeSubscription.clear();
    }
}

protected void subscribeUntilDetach(@NonNull Subscription subscription) {
    mCompositeSubscription.add(subscription);
}

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mCompositeSubscription.hasSubscriptions()) {
        mCompositeSubscription.unsubscribe();
    }
}

sur certaines activités, cette ligne se plante également avec la même erreur :

mCompositeSubscription.unsubscribe();

Voici une trace de pile de Crashlytics, qui peut ne pas être tout à fait exacte. 

Fatal Exception: java.lang.RuntimeException: Unable to resume activity {com.myapp.mobile/com.teknoloji.myapp.ui.pages.HomeActivity}: rx.b.f: This Realm instance has already been closed, making it unusable.
   at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3353)
   at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1443)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:168)
   at android.app.ActivityThread.main(ActivityThread.java:5885)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
Caused by rx.b.f: This Realm instance has already been closed, making it unusable.
   at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(Unknown Source)
   at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(Unknown Source)
   at rx.internal.util.ActionSubscriber.onError(Unknown Source)
   at rx.observers.SafeSubscriber._onError(Unknown Source)
   at rx.observers.SafeSubscriber.onError(Unknown Source)
   at rx.exceptions.Exceptions.propagate(Unknown Source)
   at rx.observers.SafeSubscriber.onNext(Unknown Source)
   at rx.internal.producers.SingleProducer.request(Unknown Source)
   at rx.Subscriber.setProducer(Unknown Source)
   at rx.Subscriber.setProducer(Unknown Source)
   at rx.internal.operators.OperatorSingle$ParentSubscriber.onCompleted(Unknown Source)
   at rx.internal.operators.OperatorTake$1.onNext(Unknown Source)
   at rx.internal.operators.NotificationLite.next(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.accept(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitNext(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitFirst(Unknown Source)
   at rx.subjects.BehaviorSubject$1.call(Unknown Source)
   at rx.subjects.BehaviorSubject$1.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.Observable.create(Unknown Source)
   at rx.Observable.unsafeCreate(Unknown Source)
   at rx.Observable.create(Unknown Source)
   at com.teknoloji.myapp.ui.pages.HomeActivity.onStart(Unknown Source)
   at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1288)
   at android.app.Activity.performStart(Activity.java:6279)
   at android.app.Activity.performRestart(Activity.java:6325)
   at android.app.Activity.performResume(Activity.java:6330)
   at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)
   at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1443)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:168)
   at android.app.ActivityThread.main(ActivityThread.java:5885)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)
Caused by java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable.
   at io.realm.BaseRealm.checkIfValid(Unknown Source)
   at io.realm.Realm.init(Unknown Source)
   at com.teknoloji.myapp.ui.pages.HomeActivity.lambda$onStart$3(Unknown Source)
   at com.teknoloji.myapp.ui.pages.HomeActivity$$Lambda$1.call(Unknown Source)
   at rx.internal.util.ActionSubscriber.onNext(Unknown Source)
   at rx.observers.SafeSubscriber.onNext(Unknown Source)
   at rx.internal.producers.SingleProducer.request(Unknown Source)
   at rx.Subscriber.setProducer(Unknown Source)
   at rx.Subscriber.setProducer(Unknown Source)
   at rx.internal.operators.OperatorSingle$ParentSubscriber.onCompleted(Unknown Source)
   at rx.internal.operators.OperatorTake$1.onNext(Unknown Source)
   at rx.internal.operators.NotificationLite.next(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.accept(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitNext(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager$SubjectObserver.emitFirst(Unknown Source)
   at rx.subjects.BehaviorSubject$1.call(Unknown Source)
   at rx.subjects.BehaviorSubject$1.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.subjects.SubjectSubscriptionManager.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.internal.operators.OnSubscribeLift.call(Unknown Source)
   at rx.Observable.create(Unknown Source)
   at rx.Observable.unsafeCreate(Unknown Source)
   at rx.Observable.create(Unknown Source)
   at com.teknoloji.myapp.ui.pages.HomeActivity.onStart(Unknown Source)
   at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1288)
   at android.app.Activity.performStart(Activity.java:6279)
   at android.app.Activity.performRestart(Activity.java:6325)
   at android.app.Activity.performResume(Activity.java:6330)
   at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)
   at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1443)
   at android.os.Handler.dispatchMessage(Handler.java:102)
   at android.os.Looper.loop(Looper.java:168)
   at android.app.ActivityThread.main(ActivityThread.java:5885)
   at java.lang.reflect.Method.invoke(Method.java)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:687)

2voto

muffls Points 794

Le problème est que :

Une exception fermée indique que vous tentez d'utiliser un fichier fermé. fermée, en utilisant Realm d'une manière qui ne serait pas considérée comme la meilleure pratique. ... Chaque activité est censée avoir sa propre instance de Realm pour de meilleures performances. ... créer une instance de royaume lors de onCreate() et détruisez l'instance à la onDestory(). https://github.com/realm/realm-java/issues/2594#issuecomment-211793848

En outre,

...ce n'est pas une bonne idée de garder un champ statique de Realm dans la classe. Il est recommandé de contrôler le cycle de vie du Realm dans l'Activity/Fragment/etc. https://realm.io/docs/java/latest/#controlling-the-lifecycle-of-realm-instances

Comme la création de nouvelles instances dans Realm, lorsqu'il y en a au moins une d'ouverte, est super rapide, n'ayez pas peur de créer plusieurs instances de Realm.

Un ingénieur logiciel senior de Realm le fait remarquer ici :

Tant que vous avez au moins une instance ouverte sur un thread appelant Realm.getInstance(), il s'agit simplement d'une consultation de HashMap... La meilleure pratique est de garder l'instance de Realm ouverte aussi longtemps que votre thread vit.

La meilleure pratique est décrite dans le Documentation sur les royaumes :

// Setup Realm in your Application
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Realm.init(this);
        RealmConfiguration realmConfiguration = new RealmConfiguration.Builder().build();
        Realm.setDefaultConfiguration(realmConfiguration);
    }
}

// onCreate()/onDestroy() overlap when switching between activities.
// Activity2.onCreate() will be called before Activity1.onDestroy()
// so the call to getDefaultInstance in Activity2 will be fast.
public class MyActivity extends Activity {
    private Realm realm;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        realm = Realm.getDefaultInstance();
        // ...
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        realm.close();
    }
}

Pour les threads de travail, le La documentation recommande pour obtenir une nouvelle instance de Realm au début et la fermer à la fin.

protected Void doInBackground(Void... params) {
    Realm realm = Realm.getDefaultInstance();
    try {
        // ... Use the Realm instance ...
    } finally {
        realm.close();
    }

    return null;
}

0voto

bluebrain Points 862

Je pense que le problème est autour de RealmObservableFactory . J'ai supprimé tous les Rx + Realm les relations ( .asObservable() etc.), j'ai utilisé la mise en œuvre de la vieille école et tout s'est bien passé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X