RxJava run on subscription method every time an event occurs - android

I have an observable that on subscribe does a long operation but when a user click on a button I want to notify my observable to do again the long operation because something change.
I don't want to make a new subscription every time the user clicks on the button. Which is the best solution to achieve this?
I would like to know if is possible to use a solution, which use the rxjava simplified way to run code on different threads.
Should I use something like this?
BehaviourProcessor<boolean> processor = BehaviourProcessor.createDefault(true);
public Flowable<List<Item>> getItems(){
return Flowable.create(e -> e.onNext(longOp()))
.subscribeOn(Schedulers.io())
.switchMap(items -> processor.map(notify -> returnItems(notify)));
}
public void notifyChange(){
processor.onNext(true);
}
Android Room library achieve this result, in fact when you subscribe this:
#Query("SELECT * FROM user")
Flowable<List<Item>> getUsers();
Every time you delete an item from database you immediately get the new list from the database in the subscription on next method.

Rather than have getItems() return the observable chain that you have shown, return a shared observable.
Flowable<List<Item>> itemGetter =
Flowable.create(e -> e.onNext( longOp() ) )
.subscribeOn( Schedulers.io() )
.switchMap(items -> processor.map(notify -> returnItems(notify)))
.replay( 1 )
.publish();
Flowable<List<Item>> getItem() {
return itemGetter;
}
This creates only on observer chain that you can subscribe to as many times as you want. However, if there are no subscribers and another subscriber comes along, longOp() will be called again.
If you don't want that to happen, then you should consider using a BehaviorSubject<List<Item>> to cache the value.

Related

How to obtain onComplete with Room Flowable<List<Item>>?

I have an Android app using Room to save my favorites.
Here is my DAO :
#Query("SELECT * FROM favorites ORDER BY lastConsultation DESC")
fun getAll() : Flowable<List<Favorite>>
I want to use Flowable to enable my MainActivity to be notified every time a favorite is added or removed.
On my MainActivity, I want to retrieve all my favorites and make a network request to check some information about my favorite.
In my UseCase, I have the following piece of code to make my call
favoritesRepository.getAll()
.flatMap { Flowable.just(it) }
.concatMapEager { Flowable.fromIterable(it) }
.concatMapEager {
itemRepository.getItem(it.id)
.toFlowable()
}
.toList()
.toFlowable()
The itemRepository returns a Single when getItem is called. I retrieve a Flowable<List<Favorite>> from my favoritesRepository and want to turn each Favorite in an Item after making a network request, my method returning a Flowable<List<Item>>
I thought that adding .flatMap { Flowable.just(it) } would create a new Flowable that would emit onComplete once the item has been emitted (since Room will not emit onComplete).
That piece of code is not working, the onComplete is never called so the .toList() is not called either.
Is there a way I could achieve those calls (with concurrency hence the concatMapEager) while keeping my Flowable implementation (I could use a Single to be rid of the problem but I would lose the "auto notification" on the MainActivity) ?
I'll make an assumption that realtyRepository returns a Single. Try this.
favoritesRepository
.getAll()
.switchMapSingle { Flowable
.fromIterable(it)
.concatMapEager { realtyRepository.getRealty(it.id).toFlowable() }
.toList()
}
This will get what I understand you want, yet you'll still get notified when DB changes. Also, switchMapSingle will make sure that whatever work you have ongoing, will get cancelled if any DB updates occurs in the meantime.

why method with Flowable<List> of Room DAO never completes?

Fetching data from DB , Room DAO has a method that returns a Flowable userDao.getInfo(), this Flowable will never completes, I tested adding doOnNext() it emits 5 times (DB contains 5 items) but complete is never called, but I need as I have toList(),what could be the alternative for this
return userDatas()
.flatMapIterable(items -> items)
.flatMap(userData -> userDao.getInfo(userData.getId())
.map(user -> user.toStoreModel(...)//added doOnNext()-works 5 times and doOnComplete()doesn't work
.doOnNext(userData -> Log.i("test",""+userData))
.doOnComplete(() -> Log.i("test","complete"))
.toList()
.map(UserModel::fromUserModels)
.toFlowable();
#Query("SELECT * FROM user WHERE id = :id")
Flowable<...> getInfo(Long Id);
public Flowable<List<UserStore>> userDatas() {
return userDao.allUserDatas()
.take(1)//added complete and next works
.filter(userDatas -> !userDatas.isEmpty())
.switchIfEmpty(userIds()
.doOnNext(userDatas -> userDao.insert(userDatas)));
}
I have tested and even when I'm replacing userDatas() only with userDao.allUserDatas() (I'm sure it exists in DB) it gives the same results
If you need to have called complete method you can use take(1).but in that case you could not listener further DB changes
Everything is ok with your code ,it would never complete
Db Flowables are observable ,so they keep listening if database changes, so it never completes.
Ideally you should fix userDao so that it completes normally. If that is not possible for some reason, you can time it out and map error to empty, forcing completion like so:
userDao.getInfo(userData.getId())
.timeout(1, TimeUnit.SECOND)
.onErrorResumeNext(Observable.empty())

RxJava data flow with SqlBrite and Retrofit

In my Android app I am using domain level Repository interface, which is backed with local DB implemented using SqlBrite and network api with Retrofit observables. So I have method getDomains(): Observable<List<Domain>> in Repository and two corresponding methods in my Retrofit and SqlBrite.
I don't want to concatenate or merge, or amb these two observables. I want my Repository to take data only from SqlBrite and since SqlBrite returns QueryObservable, which triggers onNext() every time underlying data changed, I can run my network request independently and store results to SqlBrite and have my Observable updated with fetched from network and stored to DB data.
So I tried to implement my Repository's getDomains() method as follow:
fun getDomains(): Observable<List<Domain>> {
return db.getDomains()
.doOnSubscribe {
networkClient.getDomains()
.doOnNext { db.putDomains(it) }
.onErrorReturn{ emptyList() }
.subscribe()
}
}
But in this case every time the client should subscribe, every time it would make network requests, that is not so good. I thought about other do... operators to move requests there, but doOnCompleted() in case of QueryObservable would never be called, until I call toBlocking() somewhere, which I won't, doOnEach() also not good as it makes requests every time item from db extracted.
I also tried to use replay() operator, but though the Observable cached in this case, the subscription happens and results in network requests.
So, how can combine these two Observables in the desired way?
Ok, it depends on the concrete use case you have: i.e. assuming you want to display the latest data from your local database and from time to time update the database by doing a network request in the background.
Maybe there is a better way, but maybe you could do something like this
fun <T> createDataAwareObservable(databaseQuery: Observable<T>): Observable<T> =
stateDeterminer.getState().flatMap {
when (it) {
State.UP_TO_DATE -> databaseQuery // Nothing to do, data is up to date so observable can be returned directly
State.NO_DATA ->
networkClient.getDomains() // no data so first do the network call
.flatMap { db.save(it) } // save network call result in database
.flatMap { databaseQuery } // continue with original observable
State.SYNC_IN_BACKGROUND -> {
// Execute sync in background
networkClient.getDomains()
.flatMap { db.save(it) }
.observeOn(backgroundSyncScheduler)
.subscribeOn(backgroundSyncScheduler)
.subscribe({}, { Timber.e(it, "Error when starting background sync") }, {})
// Continue with original observable in parallel, network call will then update database and thanks to sqlbrite databaseQuery will be update automatically
databaseQuery
}
}
}
So at the end you create your SQLBrite Observable (QueryObservable) and pass it into the createDataAwareObservable() function. Than it will ensure that it loads the data from network if no data is here, otherwise it will check if the data should be updated in background (will save it into database, which then will update the SQLBrite QueryObservable automatically) or if the data is up to date.
Basically you can use it like this:
createDataAwareObservable( db.getAllDomains() ).subscribe(...)
So for you as user of this createDataAwareObservable() you always get the same type Observable<T> back as you pass in as parameter. So essentially it seems that you were always subscribing to db.getAllDomains() ...
if your problem is that you have to subscribe your observer every time that you want to get data you can use relay, which never unsubscribe the observers because does not implement onComplete
/**
* Relay is just an observable which subscribe an observer, but it wont unsubscribe once emit the items. So the pipeline keep open
* It should return 1,2,3,4,5 for first observer and just 3, 4, 5 fot the second observer since default relay emit last emitted item,
* and all the next items passed to the pipeline.
*/
#Test
public void testRelay() throws InterruptedException {
BehaviorRelay<String> relay = BehaviorRelay.create("default");
relay.subscribe(result -> System.out.println("Observer1:" + result));
relay.call("1");
relay.call("2");
relay.call("3");
relay.subscribe(result -> System.out.println("Observer2:" + result));
relay.call("4");
relay.call("5");
}
Another examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/relay/Relay.java

Perform doOn<...> even if unsubscribed

I have an Observable that can emit events in some time after subscribing (e.g. an Observable from the Retrofit). The subscription to this Observable is strictly View-related, so when the View is destroyed I'm unsubscribing from the Observable. I want to perform some actions in doOnNext even if I unsubscribe the Subscription.
Example code:
final Observable<String> observable = ...; // will emit an event in some time in future
final Subscription subscription =
observable.doOnNext(new Action1<String>() {
#Override
public void call(String s) {
//this should be called even if the subscription is unsubscribed
}
}).subscribe();
subscription.unsubscribe();
Is there a way to make sure doOn<something> will be called even if the Subscription is unsubscribed?
EDIT:
Let me give you a bit clearer example:
final Observable<List<GithubRepo>> observable = getGithubReposFromApi();
subscription = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.subscribe(githubRepos -> displayGithubReposInCurrentActivity(githubRepos));
And in Activity's onDestroy:
subscription.unsubscribe();
Now... If githubRepos were received after the Activity has been destroyed, the result wouldn't be cached in the database. And I would like it to be.
Okay, so you want the computation to run but want to cut off the Activity-dependent subscriber. You can publish() the sequence and subscribe to it. When unsubscribed, the original sequence will still go on:
ConnectableObservable<T> co = observable
.doOnNext(githubRepos -> cacheGithubReposInDb(githubRepos))
.publish();
Subscription s = co.subscribe(githubRepos ->
displayGithubReposInCurrentActivity(githubRepos));
co.connect();
// ...
s.unsubscribe();
I think you could do one of two things:
1.) Just have two Subscribers: one for View related stuff and one for the other side-effects that you are currently handling in doOnNext. To still have only one upstream subscription you could use:
Observable sharedObservable = observable.replay().refCount();
or maybe just
Observable sharedObservable = observable.share();
// which is the same as observable.publish().refCount();
2.) Use
Observable observableThatWillNeverTrulyUnsubscribe = observable.doOnNext(/* your doOnNext */).publish().autoConnect();
Now, when your Subscriber subscribes to observableThatWillNeverTrulyUnsubscribe it will subscribe to observable and will start emitting items. However, when the Subscriber unsubscribes, it will not unsubscribe upwards and thus doOnNext will continue to receive items.
I think I would prefer to first option as with the second you are giving up any possibility of ever stopping the work of observable.

RxJava Observable based on items on a list

I need an Observable that never ends, and just process some data and chain another observable when there are items on a list. Is there any way of accomplish that, and what would be the best approach=?
My closest idea was to create a timer observable and check every x seconds if there are items on the list. This idea is not ideal, because i need to process the data as soon as there are values on that list, which i modify outside the observable chain.
return Observable.timer(2, TimeUnit.SECONDS)
.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.repeat()
I think you can use Subject, and push your next items there.
PublishSubject<Integer> subject = PublishSubject.<Integer>create();
subject.flatMap(integer -> captureList.getLatestCaptureCut())
.flatMap(vp::processVideo)
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
//push new items
subject.onNext(0);
subject.onNext(1);
I would suggest a PublishSubject in your CaptureList class. Instead of providing a pull method getLatestCaptureCut(), you could provide a push method, with a Subject:
PublishSubject<VP> captured = PublishSubject.create();
You could then .subscribe() to the PublishSubject and process the data when they come in.
In your CaptureList you would call
captured.onNext(vp);
every time new data is available. For instance, in your setLatestCaptureCut(). I'm assuming you already have some kind of routine that generates the CaptureCut and store it, to make it available in getLatestCaptureCut().

Categories

Resources