Android: Realm.getInstance(context) returns an already closed realm instance -
realm.getinstance(context) return closed realm instance. how possible?
i using realm rxjava, per https://realm.io/news/using-realm-with-rxjava/
in particular, method throws illegalstateexception: realm instance has been closed, making 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 //... } if comment out realm.close(); issue, no problems. though think lead native memory leak, then.
my best guess why occurring multiple calls method being made, , if these method calls line perfectly, closed realm instance can retrieved?
edit: using schedulers.io(), lot of calling close() on realm closed warnings. guess here somehow after done using .io() thread, realm instance automatically closed. not sure why happen though.
edit2: switching using schedulers.newthread() instead of schedulers.io() observables, issue stopped appearing. seeing lot of remember call close() on realm instances warnings. pretty sure closing them, confused this.
edit3: switching using androidschedulers.mainthread(), no errors. except realm calls run on main thread, bad bad bad. guess why causes no warnings because realm lives on main thread, realm.close() called (via rx.subscriber).
edit4: here's logic realm observable call.
@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); } });
let's simplify how lifecycle of realm instance managed. if manage in context of resume/pause cycle of activity or fragment, can more control , stop work might consume realm instance. tools in rxandroid lot that.
so, i'm going assume example happening activity, similar approach used fragment, or extracted helper classes hint more plumbing.
if bind observable lifecycle of activity or fragment using rxandroid, appears using mainthread(), should able manage realm instance within lifecycle. i'm using rxasyncutil async.toasync, makes original creation of realm instance easier.
i've used rx lot more realm (though i've toyed bit), forgive me if of api usage isn't perfect. i've abbreviated things java8-style lambdas ease-of-writing , reading. if you're not using retrolambda should still pretty easy convert back.
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()); // happen anytime between onresume , onpause. realmsubscriptions.add(loadarticlesimages()); } private <t> observable<t> bindtorealm(final observable<t> observable) { // utility bind activity lifecycle, observe on main thread // (implicit in bindactivity call), , work on realm thread. return appobservable.bindactivity(this, observable.subscribeon(realmthread.get())); } private observable<list<imagearticlecategoryentity>> getarticlebuckets( final realm realm) { /* is, except realm instance should passed realmobservable.list instead of context. */ } private subscription getrealminstance() { // grab realm instance on realm thread, while caching in our asyncsubject. return bindtorealm(async.toasync(() -> realm.getinstance(myactivity.this))) .subscribe(realm); } private subscription loadarticleimages() { // using flatmap lets defer execution until // realm instance comes being created on realm thread. // since in asyncsubject, available // once has been created, , cached 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 work added involves realm instance. realmsubscriptions.clear(); // clean asyncobservable. if has realm instance, close it. if (realm.getvalue() != null) { realm.getvalue().close(); } realm.dispose(); realm = null; super.onpause(); } } you should able extract outside of activity needed, , pass activity/fragment instance lifecycle binding, observable<realm> in case asyncsubject. if there still race conditions due subscription work, may want experiment adding .unsubscribeon(androidschedulers.mainthread()) or .unsubscribeon(schedulers.immediate()) (i'm not sure best in scenario) bindtorealm ensure unsubscribing happens when want in onpause, before realm instance closed.
Comments
Post a Comment