I'm trying to figure out a way to save my Parsed objects from retro into realm through rxjava observable.
Right now I have an API call:
fetchCatalogData().subscribeOn(Schedulers.io())
.subscribe(data ->
saveToRealm();
)
This doesn't work because either I'm using realm on different treads or that scheduler.io doesn't have a looper.
I'm not sure what's the best way to work with retrofit, realm and rxjava. I want to be able to save all my data when it arrives from retrofit and then access it afterwards.
You may leverage a very good library that combines all.
https://github.com/FabianTerhorst/ApiClient
"An easy to use api client that combines the power of Retrofit, Realm, Gson, Rxjava and Retrolambda in a easy to use library for Java and Android"
It's actually rather simple, all you need to do is have two different streams - one for the download, and one for displaying the data.
I'm assuming fetchCatalogData() returns the data via Retrofit, therefore unmanaged objects.
fetchCatalogData()
.subscribeOn(Schedulers.io())
.subscribe(data ->
saveToRealm(data);
)
In this case this works just fine, as long as saveToRealm() opens a thread-specific Realm-instance:
private void saveToRealm(CatalogData data) {
try(Realm r = Realm.getDefaultInstance()) {
r.executeTransaction((realm) -> {
realm.insertOrUpdate(data);
});
}
}
This would allow you to create a Realm (which gets auto-closed) on the background thread, and persist to the Realm.
For the UI thread, you would create a query for CatalogData, and via RxJava-support of Realm you get a RealmChangeListener appended to the result set, and receive a "hot Observable" which gives you the results whenever the underlying table changes.
Subscription showCatalog = realm.where(CatalogData.class)
.findAllAsync()
.filter(RealmResults::isLoaded)
.filter(RealmResults::isValid)
.subscribe((results) -> {
adapter.updateData(results);
});
Realm works with both Retrofit 1.* and 2.* out of the box, but note that Retrofit does not automatically add objects to Realm, instead you must manually add them using the realm.copyToRealm() or realm.copyToRealmOrUpdate() methods.
GitHubService service = restAdapter.create(GitHubService.class);
List<Repo> repos = service.listRepos("octocat");
// Copy elements from Retrofit to Realm to persist them.
realm.beginTransaction();
List<Repo> realmRepos = realm.copyToRealmOrUpdate(repos);
realm.commitTransaction();
Also Realm has first-class support for RxJava, which means that the following Realm classes can be exposed as an Observable: Realm, RealmResults, RealmObject, DynamicRealm and DynamicRealmObject.
// Combining Realm, Retrofit and RxJava (Using Retrolambda syntax for brevity)
// Load all persons and merge them with their latest stats from GitHub (if they have any)
Realm realm = Realm.getDefaultInstance();
GitHubService api = retrofit.create(GitHubService.class);
realm.where(Person.class).isNotNull("username").findAllAsync().asObservable()
.filter(persons.isLoaded)
.flatMap(persons -> Observable.from(persons))
.flatMap(person -> api.user(person.getGithubUserName())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user -> showUser(user));
This this official Realm docs.
Related
I am building an android application. In which in need to handle multiple api calls in queue to avoid collision using retrofit and also i need to manage this in common Applicationclass. It is possible?
You can achieve this using RxJava and Retrofit. RxJava provides us zip operator.
Sample code for this would be in your repository class
Observable.zip(
getCricketFansObservable(),
getFootballFansObservable(),
BiFunction<List<User>, List<User>, List<User>> { cricketFans, footballFans ->
// here we get both the results at a time.
return#BiFunction filterUserWhoLovesBoth(cricketFans, footballFans)
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver())
I am trying to use Realm in a Kotlin based application.
I set up a basic project, which tested adding elements into realm, and observing them with live data and a view model.
Then I wanted to add in some other observables, and use combineLatest of RxJava to combine them.
I created Rx Observables for all observables. I observe the live data from Realm, and then call RxJava BehaviourSubject's onNext() method to set one of the Rx Observables to be the same value as the observed realm data. This works fine, but as soon as I added in a Throttle to the Rx Operators, I got the following error message:
Realm access from incorrect thread.
Realm objects can only be accessed on the thread they were created.
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:442)
at io.realm.com_example_myapplication_ItemRealmProxy.realmGet$id(com_example_myapplication_ItemRealmProxy.java:105)
at io.realm.com_example_myapplication_ItemRealmProxy.toString(com_example_myapplication_ItemRealmProxy.java:661)
This is parts of my code:
private val itemViewModel: ItemViewModel by lazy {
ViewModelProviders.of(this).get(ItemViewModel::class.java)
}
val itemsSubject = BehaviorSubject.createDefault(listOf<Item>())
val intsSubject = BehaviorSubject.createDefault(4)
itemViewModel.getItemData().observe(this, Observer<RealmResults<Item>> { t ->
val items = t.map {
it
}
itemsSubject.onNext(items)
})
Observables.combineLatest(itemsSubject, intsSubject) { a, b ->
a
}.throttleLast(500, TimeUnit.MILLISECONDS).subscribe {
Log.i("Items", "Items combined read: ${it}")
}
I can't see how realm is coupled to the onNext call, nor the throttle. Seems very weird.
Ideally I would like to stick to just Live data, but I need to do operations with other observables, and RxJava is good for this.
I needed to observe the RxJava on the main thread, as follows:
Observables.combineLatest(itemsSubject, intsSubject) { a, b ->
a
}.throttleLast(500, TimeUnit.MILLISECONDS).observeOn(AndroidSchedulers.mainThread()).subscribe {
Log.i("Items", "Items combined read: ${it}")
}
}
In a sense, the Realm error was misleading, because it was ultimately an incorrect use of RxJava.
In my application I'm using RxJava2 and new class from Architecture Components ViewModel. In my case, I need to push SQL clause to ViewModel, which will do some magic and return Observable that will give me the data I need. Everything works fine, but I am not sure if I am using RX in the best way.
My data flow:
ViewModel has PublishSubject on which I am pushing SQL's. ViewModel has also Observable which is created by mapping subject. Also, I used distinctUntilChanged on Subject, to prevent from executing the same query again.
To cache data I used replay(1).autoconnect(1) on Observable, but that approach had a flaw. Sometimes my Subject pushed Sql when Observable wasn't yet connect, and my data never arrived to me. Should I use BehaviourSubject? Or maybe I shouldn't use replay(1).autoconnect(1) in the first place? Or maybe my whole flow is wrong? Example:
val listSubject: Subject<RawSql> = PublishSubject.create()
val sqlListEmitter: Observable<List<T>> =
listSubject
.subscribeOn(Schedulers.computation())
.map { // SOME MAGIC HERE }
.replay(1).autoConnect(1, { compositeDisposable.add(it) })
In your case autoConnect() just waits for the first subscription to connect() to your stream. Since your subject and your stream build an inherent entity, you might not want to wait for it at all and instead connect it directly.
val listSubject: Subject<RawSql> = PublishSubject.create()
val sqlListEmitter: Observable<List<T>> =
listSubject
.observeOn(Schedulers.computation())
.map { // SOME MAGIC HERE }
.replay(1)
.let {
it.connect(compositeDisposable::add)
it.publish()
}
Also you might need to change subscribeOn() to observeOn(). The subject emits on the same thread as the data is pushed to it and does not consider the thread it's subscribed on.
A bit of context, I’ve tried to apply some clean-architecture to one of my projects and I’m having trouble with the (Realm) disk implementation of my repository. I have a Repository which pulls some data from different DataStores depending on some conditions (cache). This is the theory, the problem comes when mixing all of this with UseCases and RxJava2.
First I get the list of objects from Realm and then I manually create an Observable of it. But the subscribe (as expected) is executed on a different thread so realm ends up crashing… (second block of code)
This is the code I use to create the Observables (from an abstract class DiskStoreBase):
Observable<List<T>> createListFrom(final List<T> list) {
return Observable.create(new ObservableOnSubscribe<List<T>>() {
#Override
public void subscribe(ObservableEmitter<List<T>> emitter) throws Exception {
if (list != null) {
emitter.onNext(list);
emitter.onComplete();
} else {
emitter.onError(new ExceptionCacheNotFound());
}
}
});
}
How can I deal with this scenario?
More code of DiskStoreForZone:
#Override
public Observable<List<ResponseZone>> entityList() {
Realm realm = Realm.getDefaultInstance();
List<ResponseZone> result = realm.where(ResponseZone.class).findAll();
return createListFrom(result);
}
The exact crash:
E/REALM_JNI: jni: ThrowingException 8, Realm accessed from incorrect thread.
E/REALM_JNI: Exception has been thrown: Realm accessed from incorrect thread.
It doesn't work because despite using Rx, your data layer is not reactive.
Realm by its nature is a reactive datasource, and its managed objects by nature are also mutable (updated in place by Realm), and thread-confined (can only be accessed on the same thread where the Realm was opened).
For your code to work, you'd need to copy out the data from the Realm.
#Override
public Single<List<ResponseZone>> entityList() {
return Single.fromCallable(() -> {
try(Realm realm = Realm.getDefaultInstance()) {
return realm.copyFromRealm(realm.where(ResponseZone.class).findAll());
}
});
}
I took the liberty and represented your Single as a Single, considering it's not an Observable, it does not listen for changes, there is only 1 event and that is the list itself. So sending it through an ObservableEmitter doesn't really make sense as it does not emit events.
Therefore, this is why I said: your data layer is not reactive. You are not listening for changes. You are just obtaining data directly, and you are never notified of any change; despite using Rx.
I drew some pictures in paint to illustrate my point. (blue means side-effects)
in your case, you call a one-off operation to retrieve the data from multiple data-sources (cache, local, remote). Once you obtain it, you don't listen for changes; technically if you edit the data in one place and another place, the only way to update is by "forcing the cache to retrieve the new data manually"; for which you must know that you modified the data somewhere else. For which you need a way to either directly call a callback, or send a message/event - a notification for change.
So in a way, you must create a cache invalidation notification event. And if you listen to that, the solution could be reactive again. Except you're doing this manually.
----------------------------------------------------------------------
Considering Realm is already a reactive data source (similarly to SQLBrite for SQLite), it is able to provide change notifications by which you can "invalidate your cache".
In fact, if your local data source is the only source of data, and any write from network is a change that you listen to, then your "cache" can be written down as replay(1).publish().refCount() (replay latest data for new subscribers, replace data with new if new data is evaluated) which is RxReplayingShare.
Using a Scheduler created from the looper of a handler thread, you can listen to changes in the Realm on a background thread, creating a reactive data source that returns up-to-date unmanaged copies that you can pass between threads (although mapping directly to immutable domain models is preferred to copyFromRealm() if you choose this route - the route being clean architecture).
return io.reactivex.Observable.create(new ObservableOnSubscribe<List<ResponseZone>>() {
#Override
public void subscribe(ObservableEmitter<List<ResponseZone>> emitter)
throws Exception {
final Realm observableRealm = Realm.getDefaultInstance();
final RealmResults<ResponseZone> results = observableRealm.where(ResponseZone.class).findAllAsync();
final RealmChangeListener<RealmResults<ResponseZone>> listener = results -> {
if(!emitter.isDisposed()) {
if(results.isValid() && results.isLoaded()) {
emitter.onNext(observableRealm.copyFromRealm(results));
}
}
};
emitter.setDisposable(Disposables.fromRunnable(() -> {
if(results.isValid()) {
results.removeChangeListener(listener);
}
observableRealm.close();
}));
results.addChangeListener(listener);
// initial value will be handled by async query
}
}).subscribeOn(looperScheduler).unsubscribeOn(looperScheduler);
Where looper scheduler is obtained as
handlerThread = new HandlerThread("LOOPER_SCHEDULER");
handlerThread.start();
synchronized(handlerThread) {
looperScheduler = AndroidSchedulers.from(handlerThread.getLooper());
}
And that is how you create reactive clean architecture using Realm.
ADDED:
The LooperScheduler is only needed if you intend to actually enforce Clean Architecture on Realm. This is because Realm by default encourages you to use your data objects as domain models and as a benefit provides lazy-loaded thread-local views that mutate in place when updated; but Clean Architecture says you should use immutable domain models instead (independent from your data layer). So if you want to create reactive clean architecture where you copy from Realm on a background thread any time when Realm changes, then you'll need a looper scheduler (or observe on a background thread, but do the copying from a refreshed Realm on Schedulers.io()).
With Realm, generally you'd want to use RealmObjects as your domain models, and rely on lazy-evaluation. In that case, you do not use copyFromRealm() and you don't map the RealmResults to something else; but you can expose it as a Flowable or a LiveData.
You can read related stuff about this here.
In my Android projects, I use realm as my data storage engine. I love it!
I also use RxJava because it makes "threading" so much easier, and I really like the whole "reactive mindset". I love it!
I use an MVP pattern + some "Clean architecture" ideas to build my apps.
My Interactors are the only ones who know about Realm. I expose data with the help of Observable, like this:
#Override
public Observable<City> getHomeTown() {
final Realm realm = Realm.getDefaultInstance();
return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable()
.doOnUnsubscribe(new Action0() {
#Override
public void call() {
realm.close();
}
})
.compose(new NullIfNoRealmObject<City>());
}
The problem is my doOnUnsubscribe side-effect gets called before Realm can do its thing, handling the exposed observable:
Caused by: java.lang.IllegalStateException: This Realm instance has already been closed, making it unusable.
at io.realm.BaseRealm.checkIfValid(BaseRealm.java:344)
at io.realm.RealmResults.removeChangeListener(RealmResults.java:818)
at io.realm.rx.RealmObservableFactory$3$2.call(RealmObservableFactory.java:137)
at rx.subscriptions.BooleanSubscription.unsubscribe(BooleanSubscription.java:71)
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124)
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113)
at rx.Subscriber.unsubscribe(Subscriber.java:98)
at rx.internal.util.SubscriptionList.unsubscribeFromAll(SubscriptionList.java:124)
at rx.internal.util.SubscriptionList.unsubscribe(SubscriptionList.java:113)
at rx.Subscriber.unsubscribe(Subscriber.java:98)
at rx.subscriptions.CompositeSubscription.unsubscribeFromAll(CompositeSubscription.java:150)
at rx.subscriptions.CompositeSubscription.unsubscribe(CompositeSubscription.java:139)
at ro.tudorluca.realm.sandbox.city.CityPresenter.onDestroy(CityPresenter.java:62)
at ro.tudorluca.realm.sandbox.city.CityActivity.onDestroy(CityActivity.java:35)
I created a sandbox project for this use case.
I really like using Realm+RxJava, but I can't seem to find a clean solution to close the Realm instance when I unsubscribe (I usually unsubscribe when the activity gets destroyed). Any ideas?
Edit 1: https://github.com/realm/realm-java/issues/2357
Edit 2: thanks to the very active realm team, there is already a pull request to fix this issue.
21 hours later and this is what I came up with:
#Override
public Observable<City> getHomeTown() {
return getManagedRealm()
.concatMap(new Func1<Realm, Observable<City>>() {
#Override
public Observable<City> call(Realm realm) {
return realm.where(City.class).equalTo("name", "Cluj-Napoca").findAllAsync().asObservable()
.compose(new NullIfNoRealmObject<City>());
}
});
}
private static Observable<Realm> getManagedRealm() {
return Observable.create(new Observable.OnSubscribe<Realm>() {
#Override
public void call(final Subscriber<? super Realm> subscriber) {
final Realm realm = Realm.getDefaultInstance();
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
realm.close();
}
}));
subscriber.onNext(realm);
}
});
}
I tried something like this before posting the question on stackoverflow, but my mistake was using flatMap(), instead of concatMap().
Unlike flatMap(),concatMap() will keep the order of the emissions which, in my case, means that my Action0 -> realm.close() will be the last action being called after unsubscribing from the stream, after Realm's Action0 -> results.removeChangeListener(listener) which was causing the problem.
A full example can be found on github.
Edit: thanks to the very active realm team, there is already a pull request to fix this issue.
Since you said that only the Interactor "knows" about the Realm framework I would say not to even return a managed Realm object, instead return an unmanaged copy of the results using copyFromRealm. That way you don't have to care about the Realm instance being open or closed in the Presenter.
At the same time I would let the Presenter chose wether the call should be done asynchronous or not since RxJava does that pretty cool and easy and you won't have issues calling the Interactor load method within another thread(which can be avoided using Loopers but why overcomplicate the situation if you can make it simpler :P ).
So I would go for:
Override
public Observable<City> getHomeTown() {
final Realm realm = Realm.getDefaultInstance();
City city = realm.where(City.class).equalTo("name", "Cluj-Napoca").findFirst();
// make sure we don't send back Realm stuff, this is a deep copy that will copy all referenced objects (as the method doc says)
City cityUnmanaged = realm.copyFromRealm(city);
// safe to close the realm instance now
realm.close();
return Observable.just(cityUnmanaged);
}
I am curious to see more options :).
As per me, one of the major thing to look after in a good architecture is modularity. All the major modules(or libraries) should be isolated from rest of the code. Since Realm, RealmObject or RealmResult can not be passed across threads, it is even more important to make Realm & Realm related operations isolated from rest of the code.
Keeping this philosophy in mind, I came up with the following approach.
For every jsonModel class, we create a realmModel class and a DAO (Data Access Object) class. Idea here is that other than DAO class none of the class must know or access realmModel or Realm entities. DAO class takes jsonModel, converts it to realmModel, performs read/write/edit/remove operations & for read operations DAO converts resulted realmModel to jsonModel and returns them.
This way it is easy to maintain Realm, avoid all thread related issues, easy to test and debug.
Here is an article about Realm best practices with a good architechture https://medium.com/#Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f
Also a sample project demonstrating Integration of Realm on Android with MVP(Model View Presenter), RxJava, Retrofit, Dagger, Annotations & Testing. https://github.com/viraj49/Realm_android-injection-rx-test