Emit Observable only after second Observable is received - android

I'm using Rx for calling our API with Retrofit. At some point I need to call our API, wait for response 1, extract some metadata from it and then call API again waiting for response 2. After I have response 2 I can emit my Observable. My problem is, I don't know how to:
Make a call 2 and emit only after I have response 2
Here are my functions from the class that should emit Model Observable. Method get2 doesn't have to be visible for outside world.
public Observable<Model> get1(String slug) {
return api1
.getInfo(slug)
.subscribeOn(Schedulers.io())
.map(resonse1 -> {
String metadata = response1.getMetadata();
//Make call2 with metadata
//call(2)
Model model = response1.getModel();
model.setInfo(/*Info from call2*/)
return model;
})
.observeOn(AndroidSchedulers.mainThread());
}
private Observable<Info> get2(String metadata) {
return api2.getInfo(new InfoAsset(metadata))
.subscribeOn(Schedulers.io())
.map(response2 -> {
return response2.getInfo;
})
.observeOn(AndroidSchedulers.mainThread());
}

Instead of map use flatMap:
.flatMap(response1 -> {
String metadata = response1.getMetadata();
return get2(metadata)
.map(info -> {
Model model = response1.getModel();
model.setInfo(info);
return model;
});
})
...
Be careful though because you are using mutable objects across threads so you may have visibility problems. Consider using immutable objects or ensure changes are synchronized.

Use nested flatMaps, and don't use observeOn unless you want to do thread hopping:
private Observable<Info> get2(String metadata) {
return api2.getInfo(new InfoAsset(metadata))
.subscribeOn(Schedulers.io())
.map(response2 -> {
return response2.getInfo;
});
// no ObserveOn here.
}
public Observable<Model> get1(String slug) {
return api1
.getInfo(slug)
.subscribeOn(Schedulers.io())
.flatMap (response1 -> {
Model model = response1.getModel();
return get2(response1.getMetadata())
.map(response2 -> {
model.setInfo(response2);
return model;
});
);
});
}

Related

Chaining Calls on RxJava

There are cases when I need to chain RxJava calls.
The simplest one:
ViewModel:
fun onResetPassword(email: String) {
...
val subscription = mTokenRepository.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(
//UI update calls
)
...
}
My Repository:
fun resetPassword(email: String): Single<ResetPassword> {
return Single.create { emitter ->
val subscription = mSomeApiInterface.resetPassword(email)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe({
emitter.onSuccess(...)
}, { throwable ->
emitter.onError(throwable)
})
...
}
}
My Question
Do I need to Add:
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for both calls to avoid any app freeze? or the second one for API call is enough?
No, you don't need to add
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
for the repo and the viewmodel.
.observeOn usually should be called right before handling the ui rendering. So usually, you'll need it in the ViewModel right before updating the ui or emitting the LiveData values.
Also, you properly don't need to subscribe to mSomeApiInterface in your repo, I think it would be better off to just return in as it's from your method up the chain, somthing like this:
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email);
}
and if you have any mapping needed you can chain it normally
fun resetPassword(email: String): Single<ResetPassword> {
return mSomeApiInterface.resetPassword(email)
.map{it -> }
}
This way you can write your ViewModel code as follow
fun onResetPassword(email: String) {
...
// note the switcing between subscribeOn and observeOn
// the switching is in short: subscribeOn affects the upstream,
// while observeOn affects the downstream.
// So we want to do the work on IO thread, then deliver results
// back to the mainThread.
val subscription = mTokenRepository.resetPassword(email)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
//UI update calls
)
...
}
This will run the API request on the io thread, will returning the result on the mainThread, which is probably what you want. :)
This artical has some good examples and explanations for subscribeOn and observeOn, I strongly recommend checking it.
Observable<RequestFriendModel> folderAllCall = service.getUserRequestslist(urls.toString());
folderAllCall.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(result -> result.getRequested())
.subscribe(this::handleResults, this::handleError);
private void handleResults(List<Requested> folderList) {
if (folderList != null && folderList.size() != 0) {
usersList.addAll(folderList);
}
adapter.notifyDataSetChanged();
}
}
private void handleError(Throwable t) {
Toast.makeText(getContext(),t.getMessage(),Toast.LENGTH_LONG).show();
}
in interface:
#Headers({ "Content-Type: application/json;charset=UTF-8"})
#GET
Observable<RequestFriendModel> getUserRequestslist(#Url String url);
POJO model :
public class RequestFriendModel {
#SerializedName("requested")
#Expose
private List<Requested> requested = null;
public List<Requested> getRequested() {
return requested;
}
public void setRequested(List<Requested> requested) {
this.requested = requested;
}
}

Best way to get List from Observable in Rxjava

I'm just exploring Rxjava in one of my android application, and got stuck at one place, honestly speaking I'm very new to this library so don't mind if my question frustrate someone;-)
So I'm trying to access the Room Database using RxJava where I'm returning the Observable List, once I get this Observable I'm trying to use map operator to get a list of ids & query again the database, which again returns me the Observable List but the map operator expects List as a return type. How can I tackle this please suggest?
Below is the code snippet:
private void getAllPcbs() {
isLoading.setValue(true);
getCompositeDisposable().add(
getRepositoryManager().loadAllPcbDetails()
.flatMap((Function<List<PcbDetails>, ObservableSource<?>>) pcbDetails -> {
List<Long> pcbList = new ArrayList<>();
for (PcbDetails details : pcbDetails)
pcbList.add(details.getPcbId());
return getRepositoryManager().loadAllPcbs(pcbList);
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onSuccess, this::onError)
);
}
private void onError(Throwable throwable) {
isLoading.setValue(false);
}
private void onSuccess(Object o) {
isLoading.setValue(false);
pcbList.setValue((List<Pcb>) o);
}
public interface DbHelper {
Observable<List<PcbDetails>> loadAllPcbDetails();
Observable<List<Pcb>> loadAllPcbs(List<Long> pcbIdList);
}
Go like
getRepositoryManager().loadAllPcbDetails()
.flatMapIterable {
listPcbDetail-> listPcbDetail
// listPcbDetail is ArrayList<PcbDetails>
// Converts your list of ids into an Observable
// which emits every item in the list
}
.flatMap { pcbDetail ->
// pcbDetail is PcbDetails
getRepositoryManager().loadAllPcbs(pcbDetail.pcbIdList)
}.subscribe { listPcb ->
// listPcb is ArrayList<Pcb>
}

RxJava Fetch from Api if cache expired

I need to query data from Api then save it to Realm object. I need to get the data(observable) to the Presenter from the realm object unless 5 minutes from the last Api query elapsed, in other case I need to fetch from Api again. I`m new to RxJava. Any suggestions?
You could create a class hosting a ReplaySubject and some update logic:
class TimedCache<T> {
final Subject<T> cache =
ReplaySubject.createWithTime(5, TimeUnit.MINUTES).toSerialized();
final Single<T> valueProvider;
TimedCache(Single<T> valueProvider) {
this.valueProvider = valueProvider;
}
public Observable<T> valueObservable() {
return cache.take(1)
.switchIfEmpty(
valueProvider
.doOnSuccess(v -> {
cache.onNext(v);
// update realm here
})
.toObservable()
);
}
}
How about setting up two different observables:
1.) for observing the Realm data:
realm.where(MyData.class)
.findAllAsync()
.asFlowable()
.filter(RealmResults::isLoaded)
.subscribe(...);
2.) for fetching the data every 5 minutes
Observable.interval(5, TimeUnit.MINUTES)
.subscribeOn(Schedulers.io())
.switchMap((ignored) -> {
return apiService.getData();
})
.subscribe((data) -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction((r) -> {
r.insertOrUpdate(data);
});
}
});
EDIT: then just call a method like
flowable = realm.where(MyData.class)
.findAllAsync()
.asFlowable()
.filter(RealmResults::isLoaded)
.subscribe(...);
if(cache.isOlderThanFiveMinutes()) {
startRefreshTaskOnBackgroundThread();
}

Api call and show data with RxJava and Retrofit

I'm using Retrofit to get list of pallet types from Api.
This is how my call looks like.
#GET("pallettypes")
Observable<List<PalletType>> getPalletTypes();
Then I have some function that gets the response from the api and map it (I don't know if I'm using the map function as it should - new to RxJava)
private static Observable<List<PalletType>> getTypes() {
return getApiService().getPalletTypes()
.map(response -> {
//Here i need some code to get the response from the api and put it in Store.palletTypes()
}
return response;
And then to call the function in the onViewCreated part.
getTypes().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(() ->{
})
.doOnTerminate(() -> {
})
.subscribe(response -> {
PalletsManager.getInstance().setTypes(response);
populateTypes(response);
}, throwable -> {
});
I need populateTypes function to show the type using a custom view
public void populateTypes (List<PalletType> palletTypes) {
for(PalletType type : palletTypes) {
palletView = new PalletView(getContext());
palletView.setLabel(type.getType());
delivered_pallets.addView(palletView);
}
}
This is my idea but it doesn't work because I never get in the .subscribe block and noting is shown.

Realm Proper way of Copy Object In Transaction with RxJava

Currently, when copying a value to realm, i do the following:
public void addToRealm(Home item, RealmChangeListener<E> listener) {
realm.executeTransaction((Realm realm1) ->
realm1.copyToRealm(item).addChangeListener<Home>(listener));
}
And then i can access the newly added object inside the listener. What is the proper RxJava way of accomplishing the same? The observable must return
Observable<Home>, which is the realmCopy not the original object. Can any1 please provide a sample?
Managed to get it working by doing this, altought im not sure it is the best approach... What is the recommended approach?
return Observable.just(homeItem)
.map { (HomeItem homeItem) ->
return AnotherHomeItem(homeItem.xxx, homeItem.yyy)
}
.flatMap { (AnotherHomeItem anotherItem) ->
realm.beginTransaction()
val newItem = realm.copyToRealm(anotherItem).asObservable< AnotherHomeItem >()
realm.commitTransaction()
return newItem
}
.filter {
return it.isLoaded
}
You should write to the Realm on a background thread, and observe with a different subscription on the UI thread.
You persist with one subscription on the background thread:
public Subscription downloadObjectsFromNetwork() {
return objectApi.getObjects()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
}
});
}
And you read with asObservable() on the UI thread:
public Subscription readFromRealm() {
return realm.where(SomeObject.class)
.findAllAsync()
.asObservable()
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.filter(RealmResults::isLoaded)
.subscribe(objects -> adapter.updateData(objects));
}
Using Realm with RxJava
For queries, Realm provides the realmResults.asObservable() method. Observing results is only possible on looper threads (typically the UI thread).
For this to work, your configuration must contain the following
realmConfiguration = new RealmConfiguration.Builder(context) //
.rxFactory(new RealmObservableFactory()) //
//...
.build();
Afterwards, you can use your results as an observable.
Observable<RealmResults<SomeObject>> observable = results.asObservable();
For asynchronous queries, you should filter the results by isLoaded(), so that you receive an event only when the query has been executed. This filter() is not needed for synchronous queries (isLoaded() always returns true on sync queries).
Subscription subscription = RxTextView.textChanges(editText).switchMap(charSequence ->
realm.where(SomeObject.class)
.contains("searchField", charSequence.toString(), Case.INSENSITIVE)
.findAllAsync()
.asObservable())
.filter(RealmResults::isLoaded) //
.subscribe(objects -> adapter.updateData(objects));
For writes, you should either use the executeTransactionAsync() method, or open a Realm instance on the background thread, execute the transaction synchronously, then close the Realm instance.
public Subscription loadObjectsFromNetwork() {
return objectApi.getObjects()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realmInstance = Realm.getDefaultInstance()) {
realmInstance.executeTransaction(realm -> realm.insertOrUpdate(response.objects));
}
});
}

Categories

Resources