Api call and show data with RxJava and Retrofit - android

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.

Related

RxJava - how to wait to result of async tasks wihiting doOnNext

How can I achieve that doOnNext wait to the results of multiple asynchronous tasks?
For example -
public void getImages(User user) {
Flowable.create(new FlowableOnSubscribe<User>() {
#Override
public void subscribe(#io.reactivex.rxjava3.annotations.NonNull FlowableEmitter<User> emitter) throws Throwable {
emitter.onNext(user);
}
}, BackpressureStrategy.BUFFER)
.observeOn(Schedulers.io())
.doOnNext(user -> {
ArrayList<String> imagesUrls = user.getUrls();
for (String url : imagesUrls) {
storage.getReference().child("images").child(url).getBytes(ParametersConventions.FIREBASE_DOWNLOAD_IMAGE_MAX_SIZE).
addOnSuccessListener(bytes -> {
doSomething(bytes);
});
}
})
.doOnNext(user -> {
doSomething();
})
.doOnComplete(...);
}
and I want that the doOnNext which calls to doSomething will be called after all the asynchronous calls to download the images are finished.
Turn that API call into a reactive type and merge it into the main flow:
int max = ParametersConventions.FIREBASE_DOWNLOAD_IMAGE_MAX_SIZE;
public Completable downloadAsync(URL url) {
return Completable.create(inner -> {
storage.getReference()
.child("images")
.child(url)
.getBytes(max)
.addOnSuccessListener(bytes -> {
doSomething(bytes);
inner.onComplete();
});
});
}
Together:
Flowable.create(emitter-> {
emitter.onNext(user);
}, BackpressureStrategy.BUFFER)
.observeOn(Schedulers.io())
.concatMapSingle(user ->
Flowable.fromIterable(user.getUrls())
.concatMapCompletable(url -> downloadAsync(url))
.andThen(Single.just(user))
)
.doOnNext(user -> {
doSomething();
})
.doOnComplete(...);
doOnNext operator is fired every time there is a new item on a stream so it is not the best option for you. Try using map/flatMap/concatMap operator depending on your needs. If you need to make several calls and then do something with the data you can look at similar question I've already answered link: Chaining API Requests with Retrofit + Rx
in which you can find a way to make sequential network calls and then do whatever you want with a list of data :D

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>
}

RxJava2 - Error returned by child Single is propagated despite handling in parent Single

So, I have a Repository class, which has a search() method which creates a zipped Single and returns data to a listener.
Inside of this method, I need to call 4 services and zip their results. The services return different data types, from which I build the SearchResult object.
The method looks like this:
fun search() {
Single.just(SearchResult.Builder())
.zipWith(
service1.search()
.subscribeOn(Schedulers.io())
.onErrorReturnItem(emptyList()),
{ result, data -> result.apply { this.data1 = data } })
.zipWith(
service2.search()
.subscribeOn(Schedulers.io())
.onErrorReturnItem(emptyList()),
{ result, data -> result.apply { this.data2 = data } })
// Other two services done in the same way
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ listener.onSearchComplete(it.build()) },
{ listener.onSearchFailed() })
}
The implementation of search() in the services looks like this:
fun search(): Single<List<DataTypeX>> =
Single.create<List<DataTypeX>> { subscriber ->
makeNetworkRequest()
?.let{
//logic omitted for clarity
subscriber.onSuccess(it)
} ?: subscriber.onError(IllegalStateException())
The problem is the last line. When this network call fails and the response is null, the IllegalStateException will be propagated and crash the app, instead of being silently caught by onErrorReturnItem(emptyList()).
Is there any reason for this? Am I misunderstanding the idea behind onErrorReturnItem()?

How to Invoke a Method When All of the Parallel HTTP Requests Are Completed?

I need to get the categories, and then get the channels of that categories, and finally invoke a method when all categories and their channels are retrieved from the server. I guess that I need to use RxJava, but I could not find a similar implementation. (Preferably without using lambda/retrolambda expressions).
#GET("/api/{categoryId})
Call<Category> getCategory(#Path("categoryId") String categoryId)
private void getCategories() {
for (Tab t : tabs) {
Call<Category> getCategory = videoAPI.getCategory(t.getId());
getCategory.enqueue(new Callback<Category>() {
#Override
public void onResponse(Call<Category> call, Response<Category> response) {
Category cat = response.body();
categories.add(cat);
// I will call the getChannels(String categoryId) method here,
// however I think implementing RxJava would be much better.
}
#Override
public void onFailure(Call<Category> call, Throwable t) {
Log.i(TAG, "failure: " + t.getLocalizedMessage());
}
});
}
}
You can do that with
Observable
.fromArray(/*your list of observables go here, make sure that within flatMap you get as type Observable<T>, not Observable<List<T>>*/)
.flatMap(/*here you subscribe every item to a different thread, so they're parallel requests: subscribeOn(Schedulers.computation())*/)
.subscribe (/*each request*/,/*error*/,/*completed all requests*/)
Now your request needs to be of type Observable
#GET("/api/{categoryId})
Observable<Category> getCategory(#Path("categoryId") String categoryId)
Example code in Java:
// Setup a list of observables
List<Observable<Category>> parallelRequests = new ArrayList<>();
for (Tab t : tabs) {
parallelRequests.add(videoAPI.getCategory(t.getId()));
}
Observable[] array = new Observable[parallelRequests.size()];
// Convert the list to array
parallelRequests.toArray(array);
Observable
.fromArray(array)
.flatMap(observable -> observable.subscribeOn(Schedulers.computation()))
.subscribe(o -> {
// each request is fulfilled
}, Throwable::printStackTrace, () -> finishFunctionHere());
Or if you're using Kotlin
Observable
// The asterisk is called "spread operator": It converts an array to vararg
.fromArray(*tabs.map { api.getCategory(it.getId()) }.toTypedArray())
.flatMap { it.subscribeOn(Schedulers.computation()) }
.subscribe({ category ->
// onEach
}, Throwable::printStackTrace, {
// All requests were fulfilled
})

Emit Observable only after second Observable is received

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;
});
);
});
}

Categories

Resources