gpt4 book ai didi

Android:Realm.getInstance(context) 返回一个已经关闭的 Realm 实例

转载 作者:塔克拉玛干 更新时间:2023-11-02 21:37:49 24 4
gpt4 key购买 nike

Realm.getInstance(context) 很少会返回一个已经关闭的 Realm 实例。这怎么可能?

根据 https://realm.io/news/using-realm-with-rxjava/,我将 Realm 与 RxJava 结合使用

特别是,此方法抛出一个 IllegalStateException: This Realm instance has already been closed, making it unusable.

@Override
public void call(final Subscriber<? super RealmList<T>> subscriber) {
final Realm realm = Realm.getInstance(context);
subscriber.add(Subscriptions.create(new Action0() {
@Override
public void call() {
try {
realm.close();
} catch (RealmException ex) {
subscriber.onError(ex);
}
}
}));

RealmList<T> object;
realm.beginTransaction(); //THROWS EXCEPTION

//...
}

如果我注释掉 realm.close(); 问题,没问题。不过我认为这会导致 native 内存泄漏。

关于为什么会发生这种情况,我最好的猜测是对该方法进行了多次调用,如果这些方法调用完全一致,那么可以检索已经关闭的 Realm 实例?

编辑:通过使用 Schedulers.io(),我收到了很多 Calling close() on a Realm that already closed 警告。我的猜测是,在我使用完 .io() 线程后,不知何故, Realm 实例会自动关闭。虽然不确定为什么会发生这种情况。

EDIT2:通过切换到对我的可观察对象使用 Schedulers.newThread() 而不是 Schedulers.io(),问题不再出现。但是我看到很多 Remember to call close() on all Realm instances 警告。我很确定我要关闭它们,所以我对此很困惑。

EDIT3:通过切换到使用 AndroidSchedulers.mainThread(),没有错误。除了我的 Realm 调用在主线程上运行,这很糟糕。我猜为什么这不会导致警告是因为 realm 现在位于主线程上,这也是调用 realm.close() 的地方(通过 rx .subscriber).

EDIT4:这是我的 Realm 可观察调用的逻辑。

@Override
public Observable<List<ImageArticleCategoryEntity>> getArticleBuckets() {

return RealmObservable.list(context, GET_ARTICLE_BUCKETS)
.filter(FILTER_OUT_NULL_OR_EMPTY_LIST)
.switchIfEmpty(refreshAndSaveAndLoadNewDataFromDb)
.map(CONVERT_FROM_REALMLIST_TO_IMAGE_ARTICLE_ENTITYLIST);

}

public void loadArticleImages() {
articleRepo.getArticleBuckets()
.subscribeOn(RealmThread.get())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<ImageArticleCategoryEntity>>() {
@Override
public void onCompleted() {
Timber.v("Loading article images complete!");
if (view != null)
view.hideLoadingAnimation();
}

@Override
public void onError(Throwable e) {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
if (view != null) {
view.hideLoadingAnimation();
view.showLoadingErrorToast();
}
}

@Override
public void onNext(List<ImageArticleCategoryEntity> integerImageArticleCategoryEntityHashMap) {
if (view != null)
view.loadArticleImages(integerImageArticleCategoryEntityHashMap);
}
});

最佳答案

让我们简化 Realm 的生命周期实例被管理。如果我们在 Activity 的恢复/暂停周期的上下文中管理它或 Fragment ,我们可以更轻松地控制和停止可能消耗 Realm 的工作实例。 RxAndroid 中的工具对此有很大帮助。

所以,对于这个例子,我假设这是从 Activity 发生的, 但可以从 Fragment 使用非常相似的方法,或者提取到辅助类中,只需一点点管道。

如果您绑定(bind)您的 ObservableActivity 的生命周期或 Fragment使用 RxAndroid,您似乎已经在使用它来处理 mainThread() ,您应该能够管理您的 Realm在生命周期内轻松实例化。我也在使用 RxAsyncUtil对于 Async.toAsync , 这使得 Realm 的原创实例更简单。

我使用 Rx 的次数比使用 Realm 的次数多得多(尽管我对它玩得很开心),所以如果我的某些 API 使用不完美,请原谅我。为了便于编写和阅读,我还将一些东西缩写为 java8 风格的 lambda。如果您不使用像 retrolambda 这样的东西,那么将它转换回来应该仍然很容易。

class MyActivity extends Activity {
private final CompositeSubscriptions realmSubscriptions = new CompositeSubscription();

private AsyncSubject<Realm> realm;

@Override
protected void onResume() {
super.onResume();
realm = AsyncSubject.create();
realmSubscriptions.add(getRealmInstance());

// This could actually happen anytime between onResume and onPause.
realmSubscriptions.add(loadArticlesImages());
}

private <T> Observable<T> bindToRealm(final Observable<T> observable) {
// Utility to bind to the activity lifecycle, observe on the main thread
// (implicit in the bindActivity call), and do work on the Realm thread.
return AppObservable.bindActivity(this, observable.subscribeOn(RealmThread.get()));
}

private Observable<List<ImageArticleCategoryEntity>> getArticleBuckets(
final Realm realm) {
/* As is, except the realm instance should be passed to RealmObservable.list instead
of the context. */
}

private Subscription getRealmInstance() {
// Grab the realm instance on the realm thread, while caching it in our AsyncSubject.
return bindtoRealm(Async.toAsync(() -> Realm.getInstance(MyActivity.this)))
.subscribe(realm);
}

private Subscription loadArticleImages() {
// Using the flatMap lets us defer this execution until the
// Realm instance comes back from being created on the Realm thread.
// Since it is in an AsyncSubject, it will be available nearly
// immediately once it has been created, and is cached for any future
// subscribers.
return bindToRealm(realm.flatMap((realm) ->
articleRepo.getArticleBuckets(realm).subscribeOn(RealmThread.get())))
.subscribe(
(next) -> {
if (view != null) {
view.loadArticleImages(next);
}
},
(error) -> {
Timber.e("Error loading article images", e);
Log.e("tag", "Error loading article images", e);
if (view != null) {
view.hideLoadingAnimation();
view.showLoadingErrorToast();
}
},
// onCompleted
() -> {
Timber.v("Loading article images complete!");
if (view != null) view.hideLoadingAnimation();
});
}

@Override
protected void onPause() {
// Stop any work which we added that involves the Realm instance.
realmSubscriptions.clear();

// Clean up the AsyncObservable. If it has a Realm instance, close it.
if (realm.getValue() != null) {
realm.getValue().close();
}
realm.dispose();
realm = null;

super.onPause();
}
}

您应该能够根据需要轻松地将其提取到 Activity 之外,并且只需为生命周期绑定(bind)传递一个 Activity/Fragment 实例,以及 Observable<Realm>在这种情况下将是 AsyncSubject .如果由于订阅工作仍然存在竞争条件,您可能需要尝试添加 .unsubscribeOn(AndroidSchedulers.mainThread())甚至 .unsubscribeOn(Schedulers.immediate()) (我实际上不确定在这种情况下哪个最好)到 bindToRealm以确保取消订阅在您想要的时候发生在 onPause 中, 在 Realm 之前实例已关闭。

关于Android:Realm.getInstance(context) 返回一个已经关闭的 Realm 实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30629355/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com