Observable to list in Kotlin - android

I have a service like this
#GET("/")
fun getInfo(#Query("pew") pew1: Double, #Query("pew2") pew2: Double): Observable<PewResponse>
So now, when I have this
I have a List to fill with server data
private var mPewList: MutableList<PewList> = arrayListOf()
And then I have this function
override fun getResultFromNetwork(): Observable<PewResponse> {
val pewObservable = pewService.getInfo("pew1","pew2")
return pewObservable.concatMap {
//How do I fill my list ??
}
}
I'm pretty new to this rxJava so I saw people using Single and other guys using Observable what's better?
EDIT
I'm following this example https://github.com/joanby/android-mvp/blob/master/MoviesFeed/app/src/main/java/com/juangabriel/moviesfeed/movies/MoviesRepository.java
And now I want to do something like this :
#Override
public Observable<Result> getResultFromNetwork() {
Observable<TopMoviesRated> topMoviesRatedObservable = moviesApiService.getTopMoviesRated(1)
/*.concatWith(moviesApiService.getTopMoviesRated(2))
.concatWith(moviesApiService.getTopMoviesRated(3))*/;
return topMoviesRatedObservable
.concatMap(new Function<TopMoviesRated, Observable<Result>>() {
#Override
public Observable<Result> apply(TopMoviesRated topMoviesRated) {
return Observable.fromIterable(topMoviesRated.getResults());
}
}).doOnNext(new Consumer<Result>() {
#Override
public void accept(Result result) {
results.add(result);
}
});
}
So as I'm seeing he fills the result list, and then on presenter do this : https://github.com/joanby/android-mvp/blob/master/MoviesFeed/app/src/main/java/com/juangabriel/moviesfeed/movies/MoviesPresenter.java

If your data from server is monolithic you should to use Single because it return only once value. The Observable will do when your data arrives in chunks.
For more details you can see this answer: https://stackoverflow.com/a/42759432/9060113
EDIT:
#GET("/")
fun getInfo(#Query("pew") pew1: Double, #Query("pew2") pew2: Double): Single<PewResponse>
override fun getResultFromNetwork() {
pewService
.getInfo("pew1","pew2")
.subscribeOn(Schedulers.io()) // for background processing
.observeOn(AndroidSchedulers.mainThread()) // get result in Main Thread (it require rxAndroid lib)
.subscribe(
{ pewResponse -> // parse response and fill list },
{ // handle error }
)
}

Related

I want to call several api and combine the response values of different object types

I want to call several api and combine the response values of different object types.
val itemList : MutableList<BaseItem>
private fun fetchAllData() {
viewModelScope.launch{
val deferreds = listOf(
async { loadData1()},
async { loadData2()}
)
deferreds.awaitAll().forEach {
itemList.add(it)
}
}
}
I want to get a return by combining datatype1 and datatype2 into BaseItem.
Unable to return the callback data from the repository.
I think there's a way to do it using live data. What should I do?
fun loadData1(): ArrayList<DataType1> {
repository.getData1(param, callback) {
onSuccess(List<DataType1>) {
return
}
}
}
fun loadData2(): ArrayList<DataType2> {
repository.getData1(param, callback) {
onSuccess(List<DataType2>) {
return
}
}
}
I'll be waiting for your help.
Well, what I would do is I would switch repository functions to be suspend functions and write the code in synchronized way:
val itemsLiveData = MutableLiveData<BaseItem>()
private fun fetchAllData() = viewModelScope.launch{
try {
val itemList : MutableList<BaseItem>
itemsList.addAll(repository.loadData1(param))
itemsList.addAll(repository.loadData2(param))
itemsLiveData.postValue(itemsList)
} catch(e: Exception) {
// do something with exception
}
}
And if you want to call several Rest API for example, I would go with Retrofit which has built-in support for suspend functions since ver. 2.6.0
https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-260-2019-06-05

Use RxJava to emit a Single<List<Item>> from firestore given multiple ids

I'm building an app that uses GeoFirestore to make location based queries of items stored in a firestore database. I have my function that gets a list of all the ids that meet the location criteria:
private fun loadIds(location: Location, distance: Double): Single<List<String>> {
val query = geoFirestore.queryAtLocation(GeoPoint(location.latitude, location.longitude), distance)
val ids = ArrayList<String>()
return Single.create<List<String>> {
query.addGeoQueryEventListener(object : GeoQueryEventListener {
override fun onKeyEntered(uid: String, p1: GeoPoint?) {
ids.add(uid)
}
override fun onGeoQueryReady() {
it.onSuccess(ids)
}
override fun onKeyMoved(p0: String?, p1: GeoPoint?) {
}
override fun onKeyExited(p0: String?) {
}
override fun onGeoQueryError(p0: Exception?) {
}
})
}
}
but now I'm having problems trying to combine the results of items from each id into a Single<List<Item>> as a return value.
This is what I was doing before:
fun loadItems(ids: List<String>): Observable<Item> {
return Observable.create<Item> { emitter ->
for (id in ids) {
val reference = firestore.collection("items").document(id)
reference.get().addOnSuccessListener {
emitter.onNext(it.toObject(Item::class.java)!!)
if (ids.indexOf(id) == ids.size - 1) {
emitter.onComplete()
}
}
}
}
}
where ids was the result of loadIds(). This worked fine, but in my activity where I called this I had to append each item in the activity as it came through and then listen for onComplete, which would sometimes fire before all items were even loaded.
I'm trying to improve my code and further separate the database logic from my activity, so I want to be able to return a Single<List<Item>> so once I get that in my activity I can just take it and run. I've been trying to figure it out on my own, but I'm pretty new to RxJava and don't quite understand it too well. Here is my latest attempt:
fun loadItems(filter: Filter): Single<List<Item>> {
return Single.create<List<Item>> { emitter ->
val items= mutableListOf<Item>()
loadIds(filter.currentLocation, filter.distance).map {
for (uid in it) {
getItem(uid).map {item ->
items.add(item)
}.subscribe()
}
}.subscribe { _ ->
emitter.onSuccess(items)
}
}
}
private fun getItem(uid: String): Single<Item> {
return Single.create<Item> { emitter ->
firestore.collection("items").document(uid).get().addOnSuccessListener {
it.toObject(Item::class.java)?.let { item ->
emitter.onSuccess(item)
} ?: run {
emitter.onError(Throwable("Error finding item"))
}
}.addOnFailureListener {
emitter.onError(it)
}
}
}
but obviously onSuccess is called almost immediately so I'm not getting any results.
The getItem function looks fine, the problem lies in with the loadItems function.
You wrapped the Firebase callback mechanism nicely with Single in getItem function, but it is not necessary in the loadItems function. It's best to keep one chain in the function for readability reasons (IMO). That means, whenever you can, don't wrap the existing reactive object (Observable, Single, Flowable) into it's subscribe, but use flatMap (or any of it's versions).
fun loadItems(ids: List<String>): Single<List<Item>> {
return Observable.just(ids) // -> Observable<List<String>>
.flatMapIterable { it } // -> Observable<String>
.flatMapSingle { getItem(it) } // -> Observable<Item>
// Create a single from the observable
.collect<MutableList<Item>>(
Callable { ArrayList<Item>() },
BiConsumer { list, elem -> list.add(elem) }
) // -> Single<MutableList<Item>>
.map { it } // -> Single<List<Item>>
}
Hope it helps.

How to gey body() response using RxJava in Android

I am new to Kotlin and I am making a method that makes a call to an interface of Endpoints and uses one of the methods present there. I am using Observable<> instead of Call<> into the response. I wanted to know how to obtain the response body() in the "result" above. This is my method
private fun refreshUser(userLogin: String) {
executor.execute {
// Check if user was fetched recently
val userExists = userDao.hasUser(userLogin, getMaxRefreshTime(Date())) != null
// If user have to be updated
if (!userExists) {
disposable = endpoints.getUser(userLogin)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> /*Get the response body() HERE*/},
{ error -> Log.e("ERROR", error.message) }
)
}
}
}
It all depends on how you have defined the Retrofit interface. In order to get the Response you need to return something from the interface that looks like:
fun getUsers() : Observable<Response<User>>
Then inside { result -> /*Get the response body() HERE*/}, you will get something of the form Response<User>, which has the response's body.
Also to note, you do not need to enclosing executor if you leverage Room for the dao interactions; it has RxJava support. You can use RxJava operators to combine the dao lookup with the server call.
See this tutorial
https://medium.freecodecamp.org/rxandroid-and-kotlin-part-1-f0382dc26ed8
//Kotlin
Observable.just("Hello World")
.subscribeOn(Schedulers.newThread())
//each subscription is going to be on a new thread.
.observeOn(AndroidSchedulers.mainThread()))
//observation on the main thread
//Now our subscriber!
.subscribe(object:Subscriber<String>(){
override fun onCompleted() {
//Completed
}
override fun onError(e: Throwable?) {
//TODO : Handle error here
}
override fun onNext(t: String?) {
Log.e("Output",t);
}
})
if you wanna use retrofit 2 and rxjava 2
https://medium.com/#elye.project/kotlin-and-retrofit-2-tutorial-with-working-codes-333a4422a890
interface WikiApiService {
#GET("api.php")
fun hitCountCheck(#Query("action") action: String,
#Query("format") format: String,
#Query("list") list: String,
#Query("srsearch") srsearch: String):
Observable<Model.Result>
}
Observable is the class response.
private fun beginSearch(srsearch: String) {
disposable =
wikiApiServe.hitCountCheck("query", "json", "search", srsearch)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ result -> showResult(result.query.searchinfo.totalhits) },
{ error -> showError(error.message) }
)
}
If, as you mentioned to #Emmanuel, the return type of your getUser() method is Observable<Response<User>> then calling result.body() will yield the resulting User.
{ result ->
val user: User = result.body()
}
If however, you are looking for the the raw response, you can instead call result.raw().body(); which will return an okhttp3.ResponseBody type.
{ result ->
val body: ResponseBody = result.raw().body()
val text: String = body.string()
}

Conditional delay before emitting Single

A RESTful API End-point returns a JSON Response.
The JSON Response consists of 20 different JSON-Array elements. Each element has a Image URL.
After receiving the first response, I intend to loop through the JSON-Array and fetch the images as well, using Retrofit Dynamic URLs.
class Content { var items List<Item>? = null }
class Item { var image: String? = null
var imageBytes: ByteArray? = null
}
interface RestApi { #GET fun getContent(): Single<Content>
#GET fun getImageBytes(#Url url: String): Single<ResponseBody>
}
restApi.getContent()
.map { content: Content -> {
content.items?.forEach { item ->
restApi.getImageBytes(item.image)
.subscribe(object : DisposableSingleObserver<ResponseBody>() {
onSuccess(body: ResponseBody) {
item.imageBytes = body.getBytes()
}
onError(e: Throwable) { // TODO Implementation }
})
}
}
}
How do I make the getContent() method delay before emitting until all invocations of the getImageBytes() also complete with either success or error?
With Single you should use flatten observable it will give a list of observables which the flat map will take up
getListOfItems()
.flattenAsObservable(new Function<Object, Iterable<?>>() {
#Override
public Iterable<?> apply(#NonNull Object o) throws Exception {
return toItems(o);
}
})
.flatMap(item -> doSomethingWithItem())
.toList()

How map Observable object to Flowable<?> ?

private Observable< SimpleResource > resource;
return resource.map(new Function<SimpleResource, Flowable<Data>>() {
#Override
public Flowable< Data > apply(SimpleResource resource) throws Exception {
return resource.data().toFlowable();
}
});
Single<Data> data();
I need to have Flowable but my result is Observable>
Since you mentioned that data() returns a Single, you need to transform all of the single streams into one large stream. To transform streams into streams, we generally use the flatmap operator:
resource.flatMapSingle(
new Function<SimpleResource, Single<Data>>() {
#Override
public Single<Data> apply(SimpleResource resource) throws Exception {
return resource.data();
}
}
).toFlowable(BackpressureStrategy.BUFFER);
What you are doing wrong is applying .toFlowable at not the right spot.
Observable.fromCallable { 1 }.map {
it * 2
}.toFlowable(BackpressureStrategy.BUFFER)
If you have different data type returned by data (sorry for Kotlin, but the concept is the same)
data class A(
val data: Single<Int>
) {
constructor() : this(data = Single.fromCallable { 1 })
}
val result: Flowable<Int> = Flowable
.fromCallable {
A()
}
.flatMap {
it.data.toFlowable()
}

Categories

Resources