Struggling converting java RxBle Code to Kotlin - android

Im currently developing an android app with Kotlin. Its my first experience with this programming language and im currently struggeling to translate an example of Java code to Kotlin.
I want to implement the answer to this question in Kotlin.
my current implementation fails to compile because the Ovservable::from method seems to be removed.
This is my current approach:
connectionObservable!!.flatMap { connection ->
connection.discoverServices()
.flatMap { services ->
services.getService(UUID.fromString("My UUID")).map(BluetoothGattService::getCharacteristics)
//here occurs the error, he wants a Single Source but got a observable with the ble characteristic
.flatMap { characteristics: MutableList<BluetoothGattCharacteristic> -> Observable.fromIterable(characteristics) }
.flatMap { characteristic: BluetoothGattCharacteristic ->
connection.setupNotification(characteristic)
.flatMap { observable: Observable<ByteArray> -> observable ,Pair<BluetoothGattCharacteristic, ByteArray>(characteristic, observable)}
}
}
}.subscribe { pair: Pair<BluetoothGattCharacteristic, ByteArray> ->
genericModel[pair.first.uuid] = pair.second
throwable -> { /* handle errors */ }
}
Can you point out my errors so i can understand what im doing wrong?
Thanks in advance!
Jonas

There are several potential issues with your code.
First of all the code is not syntactically correct — see this line:
.flatMap { observable: Observable<ByteArray> -> observable ,Pair<BluetoothGattCharacteristic, ByteArray>(characteristic, observable)}
I assume you probably wanted to flatMap the observable: Observable<ByteArray> (type added by me for clarity) to get from it ByteArray objects. This would look like this:
.flatMap { observable: Observable<ByteArray> -> observable.map { bytes -> Pair(characteristic, bytes) }}
Additionally the code will not compile as you try to return an Observable from a lambda which expects a SingleSource — exactly as your compiler says. If you have a Single and you will .flatMap it — it's lambda is supposed to return another SingleSource. There is a .flatMapObservable function that expects ObservableSource which is what you should use. The end result would look like:
connectionObservable!!.flatMap { connection ->
connection.discoverServices()
.flatMapObservable { services ->
services.getService(UUID.fromString("My UUID")).map(BluetoothGattService::getCharacteristics)
//here occurs the error, he wants a Single Source but got a observable with the ble characteristic
.flatMapObservable { characteristics: MutableList<BluetoothGattCharacteristic> -> Observable.fromIterable(characteristics) }
.flatMap { characteristic: BluetoothGattCharacteristic ->
connection.setupNotification(characteristic)
.flatMap { observable: Observable<ByteArray> -> observable.map { bytes -> Pair(characteristic, bytes) }}
}
}
}.subscribe { pair: Pair<BluetoothGattCharacteristic, ByteArray> ->
genericModel[pair.first.uuid] = pair.second
throwable -> { /* handle errors */ }
}
Observable.fromIterable() is still the API of Observable. You probably do not use the Observable class from proper package. There is an Observable class in package java.util but we are using here one from RxJava 2 which has package io.reactivex

Related

Android. RxJava 2: Parallel multiple network calls

I need make two parallel requests with RxJava. For this I use zip operator. Here is my code:
public Disposable getBooksAndAuthors(String id, ReuqestCallback requestCallback) {
return singleRequest(Single.zip(
getBooks(id).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()),
getAuthors(id).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()),
(book, author) -> new ZipResponseWrapper(book, author).getResponse()), requestCallback);
}
private <T extends NetworkResponse> Disposable singleRequest(Single<T> single, RequestCallback requestCallback) {
return single.doOnSubscribe(d -> requestCallback.onStartRequest())
.doOnSuccess(s -> requestCallback.onSuccess(s))
.doOnError(ErrorConsumer.consume((t) -> requestCallback.onError(t)))
.doFinally(() -> requestCallback.onFinish())
.subscribe();
}
But I don’t understand how to receive response separately for each request. That is, I need to, if the answer came to the first request, immediately display the data received from this request and not wait for a response to the second request. And after the answer to the second request arrives, display the data received on the second request.This is necessary due to the fact that the second request fulfills a long time. Please help me.
Here is an example of how you can handle it with the responses for each function:
val disposable = Observable.zip(
firstNetworkCall().subscribeOn(Schedulers.io()),
secondNetworkCall().subscribeOn(Schedulers.io()),
BiFunction{
firstResonse: ResponseOneType,
secondResponse: ResponseTwoType ->
combineResult(firstResponse, secondResponse) }))
.observeOn(AndroidSchedulers.mainThread())
.subscribe { it -> doSomethingWithIndividualResponse(it) }
My suggestion (in Kotlin though):
val id = 0L
Observables.combineLatest(
getBooks(id).startWith(emptyList<Book>()).subscribeOn(Schedulers.io()).observeOn(Schedulers.computation()),
getAuthor(id).startWith(emptyList<Author>()).subscribeOn(Schedulers.io()).observeOn(Schedulers.computation())
) { book: List<Book>, author: List<Author> ->
Pair(book, author)
}.skip(1)
.observeOn(AndroidSchedulers.mainThread())
.subscribe { (books: List<Book>, authors: List<Author>) ->
view.show(books)
view.show(authors)
}

How to add the body of the subscribe method

In the below code, I am trying to add the body for the .subscribe(). I tried to add the lambda notation but it never worked. Would you please tell me how to implement the .subscribe() method?
Given that, the setupCommRequestService() returns Single<..>
code:
setupCommRequestService()?.
flatMap {
it.getAllPhotos()
.map {
Observable.fromIterable(it)
.map {
it
}
}
.toSortedList()
}
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(
)
There are 4 implementations for subscribe method according Single documentation. In a simple approach, you should implement a strategy for both onSucess and onError. therefor you should use the subscribe method either by passing a BiConsumer or 2 Consumer one for onSucess case and one for onError.
using BiConsumer in lambda:
val disposable = Single.just(1)
.subscribe { success, failure ->
/* whichever is not null */
}
or using 2 Consumer in lambda:
val disposable = Single.just(1)
.subscribe({ success ->
/* success */
}, { failure ->
/* failure */
})

Handling Error RXJava Android with Kotlin

Hi I'm new with RxJava and Kotlin and I loose some concepts about it.
I have "api" like this:
interface VehiclesService {
#GET("/vehicles/")
fun getVehicles(): Single<List<Vehicle>>
}
Then I create the retrofit client, etc.. like this:
var retrofit = RetrofitClient().getInstance()
vehiclesAPI = retrofit!!.create(VehiclesService ::class.java)
finally I do the call:
private fun fetchData() {
compositeDisposable.add(vehiclesAPI .getVehicles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { vehicles -> displayData(vehicles) }
)
}
And here is where I have the error when I try to launch:
The exception was not handled due to missing onError handler in the subscribe() method call
I know that the error is quite explicit. So I know what is missing, but what I don't know is HOW to handle this error.
I tried adding : .doOnError { error -> Log.d("MainClass",error.message) } but still telling same error message.
You can pass another lambda to subscribe to handle the errors for a specific stream like this:
private fun fetchData() {
compositeDisposable.add(vehiclesAPI .getVehicles()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( { vehicles -> displayData(vehicles) }, { throwable -> //handle error } )
)
}
P.S: doOnError and other Side Effect operators, will not affect the stream in anyway, they just anticipate the values emitted for side-effect operations like logging for example.

Rx Java Android Needs to call multiple api's in a sequence and save each results

I want to call multiple Rest Api's in a Sequence and having each Response Dto is different from each other.
Please help me to get rid from this situation that, How can i call these Api's using Rx Java Observables in Android.
no, you should use map() or doOnNext(), it will look like this
Observable.just(1)
.doOnNext(value -> {
someRequestX().execute();
})
.map(value -> {
return nextRequestY().execute();
})
.doOnNext(requestYResponse-> {
someRequesZ(requestYResponse.someValue).execute();
})
.map(requestYResponse-> {
return someRequesK(requestYResponse.someValue).execute();
})
.map(requestKResponse -> {
return someRequesJ(requestKResponse.someValue).execute();
})
.subscribe(requestJResponse -> {
doSOmethingWithFinalResponse(requestJResponse );
})
First of all, for network requests is better to use Single then Observable, because there always will be only one item. To switch from one requests to another, you can use flatMap.
Assuming your code is similar, you can try this:
class Dto1 {}
class Dto2 {}
class Dto3 {}
public interface Api {
Single<Dto1> getDto1();
Single<Dto2> getDto2();
Single<Dto3> getDto3();
}
private Api api;
public void callApi() {
api.getDto1()
.doOnSuccess(dto1 -> {/*do something with dto1*/})
.flatMap(dto1 -> api.getDto2())
.doOnSuccess(dto2 -> {/*do something with dto2*/})
.flatMap(dto2 -> api.getDto3())
.doOnSuccess(dto3 -> {/*do something with dto3*/})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe()
}
For the same scenario, i use concat operator which takes multiple Observables and concatenates their sequence
If response sequence doesn't require then you can use merge operator also.
Concat VS Merge operaror
try doOnNext() or map() method of Observable and use sync execute() of each response and pass them further

RxJava 1.x .zip() not working on RxJava 2.0

i'm trying to learn chain requests with RxJava + Retrofit + Kotlin.
The tutorial i was follwing was using RxJava 1.x, so when i try to re-implement with RxJava 2.x the i cannot get .zip() to work.
It's a simple app using Star Wars API, returning a list of movies, ando for each movie, get the characters from them.
fun loadMoviesFull(): Observable<Movie> {
return service.listMovies()
.flatMap { filmResults -> Observable.from(filmResults.results) }
.flatMap { film ->
Observable.zip(
Observable.just(Movie(film.title, film.episodeId, ArrayList<Character>())),
Observable.from(film.personUrls)
.flatMap { personUrl ->
service.loadPerson(Uri.parse(personUrl).lastPathSegment)
}
.map { person ->
Character(person!!.name, person.gender)
}
.toList(),
{ movie, characters ->
movie.characters.addAll(characters)
movie
})
}
}
If you want to see the whole implementation of the tutorial, this is the link(The comments are in portuguese):
http://www.nglauber.com.br/2017/03/rxjava-kotlin-retrofit-star-wars-api.html
I just want to know the syntax for ir, because i cannot implement on 2.x.
Thank you so much and i'm sorry for my bad english.
I don't know what error compiler produces but very probably your function should return Observable<List<Movie>>, zip's BiFunction requires passing types explicitly and should not have single movie at the end. And of course .toList() at the end.
Full code:
fun loadMoviesFull(): Observable<List<Movie>> {
return service.listMovies()
.flatMap { filmResults -> Observable.from(filmResults.results) }
.flatMap { film ->
Observable.zip(
Observable.just(Movie(film.title, film.episodeId, ArrayList<Character>())),
Observable.from(film.personUrls)
.flatMap { personUrl ->
service.loadPerson(Uri.parse(personUrl).lastPathSegment)
}
.map { person ->
Character(person!!.name, person.gender)
}
.toList(),
BiFunction<Movie, Character, Movie>{ movie, characters ->
movie.characters.addAll(characters)
})
}.toList()
}
It won't let me add a comment due to insufficient reputation, hence posting as an answer. Are you using rxkotlin? There are a number of helper functions, including Observable.zip() that help with the SAM ambiguity issues when using Rx2. Without knowing what your error is, that's the best advice I can give.

Categories

Resources