RxJava2 Convert Flowable to Single - android

How do I convert a Flowable to a Single? Or if there's another way to make it stop emitting after the first response that is also of interest.
I've tried this but it doesn't seem to work:
disposables.add(
repository.getAllSomethings()
.subscribeOn(SchedulerProvider.getInstance().computation())
.observeOn(SchedulerProvider.getInstance().ui())
.toSingle()
.subscribeWith(object : DisposableSingleObserver<List<Something>>() {
override fun onSuccess(t: List<Something>) {
}
override fun onError(e: Throwable) {
}
})
getAllSomethings() returns a Flowable<List<Something>>
In the above code .subscribeWith() is underlined in red complaining that:
Type parameter bound for E in
fun <E : SingleObserver<in Flowable<List<Something>!>!>!> subscribeWith(observer: E!): E!
is not satisfied: inferred type ! is not a subtype of SingleObserver<in Flowable<List<Something>!>!>!

Ok so thanks to #akarnokd I found the answer as you can see in the comments.
Teaching me how to fish rather than giving me the straight answer he suggested looking here: http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Flowable.html (which really I should have done in the first place!).
Looking there I found firstOrError() which solves my problem.
Even though the object I was calling toSingle() from was a Flowable, the IDE didn't complain. Yet looking at the link above, toSingle() isn't even a valid option!

In my case, I actually had a Flowable and wanted the benefits of that (i.e. backpressure), but I still wanted to return a Single, e.g. Single<List<String>>. In my case, I was using Android WorkManager's RxWorker which expects my function to return Single<Result>, not a Flowable.
In this case,
val flowable = Flowable.just("my flowable")
return flowable.toList() // type: Single<List<String>>
or if you wanted to return a specific value because you don't care about the flowable output:
return flowable.toList().map {} // returns unit
return flowable.toList().map { "Hello" } // returns String
return flowable.toList().map { Result.success() } // returns Result

Related

CompletableTransformer is not applying subscribeOn and observeOn to the Upstream

I'm creating offline first app as my side project using rxKotlin, MVVM + Clean Architecture and yesterday I decided to get ride off boilerplate subscribeOn and observeOn by using transformers. I quickly realized that apply function of transformers are ignored.
Here is code of my base completable use case (interactor):
abstract class CompletableUseCase(private val transformer: CompletableTransformer) {
abstract fun createCompletable(data: Map<String, Any>? = null) : Completable
fun completable(data: Map<String, Any>? = null) : Completable {
return createCompletable(data).compose(transformer)
}
}
And here is implementation of specific interactor:
class SaveRouteInteractor(
transformer: CompletableTransformer,
private val routeRepository: RouteRepository
) : CompletableUseCase(transformer) {
companion object {
private const val PARAM_ROUTE = "param_route"
}
fun saveRoute(route: Route) : Completable {
val data = HashMap<String, Route>()
data[PARAM_ROUTE] = route
return completable(data)
}
override fun createCompletable(data: Map<String, Any>?): Completable {
val routeEntity = data?.get(PARAM_ROUTE)
routeEntity?.let {
return routeRepository.saveRoute(routeEntity as Route)
} ?: return Completable.error(IllegalArgumentException("Argument #route must be provided."))
}
}
My custom transformer that is passed to the constructor of SaveRouteInteractor:
class IOCompletableTransformer(private val mainThreadScheduler: Scheduler) : CompletableTransformer {
override fun apply(upstream: Completable): CompletableSource {
return upstream.subscribeOn(Schedulers.io()).observeOn(mainThreadScheduler)
}
}
And implementation of RouteRepository method:
override fun saveRoute(route: Route): Completable {
return localRouteSource.saveRoute(route)
.flatMap { localID ->
route.routeId = localID
remoteRouteSource.saveRoute(route)
}
.flatMapCompletable { localRouteSource.updateRouteID(route.routeId, it) }
}
I'm using Room as my local source so after calling save interactor in my ViewModel I'm getting IlligalStateException telling me that I'm not allowed to access database on the main thread.
Maybe I'm missing something but it seems that transform function is ignored. I debugged this method and it is applying subscribeOn and observeOn to the upstream.
Thanks for help in advance,
Pace!
It's hard to tell you where the issue is because the code is partial.
For example here:
return localRouteSource.saveRoute(route)
.flatMap { localID ->
route.routeId = localID
remoteRouteSource.saveRoute(route)
}
.flatMapCompletable { localRouteSource.updateRouteID(route.routeId, it) }
I suppose the localRouteSource.saveRoute() is using the interactor you show us but it is not clear how remoteRouteSource.saveRoute() or localRouteSource.updateRouteID() are implemented.
they also need to be subscribed on the IO thread.
As a rule of thumb you should switch thread when you KNOW that you need it.
In other words, you should use subscribeOn() in places where you know you are doing IO as close as possible to the actual job. ObserveOn instead is to be used when you know you need to obtain those results in the UI thread and that you might get in some other thread.
in your example there's absolutely no need to keep using observeOn(MAIN_THREAD), the only time you do need it (I suppose) is when you want to show the result.
A couple of other things:
This code
override fun createCompletable(data: Map<String, Any>?): Completable {
val routeEntity = data?.get(PARAM_ROUTE)
routeEntity?.let {
return routeRepository.saveRoute(routeEntity as Route)
} ?: return Completable.error(IllegalArgumentException("Argument #route must be provided."))
}
it is evaluated at the time when the method is called rather then when the completable is subscribed.
In other words it break the Rx contract and compute data?.get(PARAM_ROUTE) when you call the method. If it is immutable there's no much difference, but if it can change value during execution it should be wrapped in a Completable.defer { }
Finally, here
.flatMap { localID ->
route.routeId = localID
remoteRouteSource.saveRoute(route)
}
you are modyfing something outside the chain (route.routeId = localID), this is called a side effect.
be careful with those kind of stuff, Rx is build in a way that is safer to be used with immutable objects.
I personally wouldn't mind too much as long as you understand what's going on and when it could create issues.

Kotlin about passing lambda as property

I am confusing about merit of writing code as following 2 case:
class TestA {
private val foo: Boolean by lazy {
// Here is logic that return true or false
}
Case 1:
fun main() {
TestB({foo})
}
Case 2:
fun main() {
TestB(foo)
}
}
Case 1:
class TestB(private val isFoo: () -> Boolean ) {
fun checkFoo(): Boolean {
return isFoo.invoke()
}
}
Case 2:
class TestB(private val isFoo: Boolean ) {
fun checkFoo(): Boolean {
return isFoo
}
}
When should I use case 1 or case 2 ?
By the way, please let me know how does invoke() method work?
You only pass lambdas into other class constructors if you want something to be invoked on the other end, that might make sense if used as a callback, or if you need to have a function that creates objects again and again rather than being static. In this case, you'd store the lambda for later referral and invoke it whenever needed. If you just pass a static instance around, that is for example foo in your code, there's no reason for a lambda. You should always prefer not to use lambdas for constructors; scenarios in which they are useful or necessary are rather rare IMO.
As to your question regarding invoke: Kotlin has a number of functions that work "by convention", e.g. rangeTo, equals, contains, compareTo, the index operators and also invoke. Learn about conventions here.
Now, whenever a class provides the invoke operator, you can call instances of that class as if they were functions:
class InvokeMe(){
operator fun invoke(value: Int) = println("invoked with $value")
}
val obj = InvokeMe()
//both are compiled to the same code
obj(10)
obj.invoke(5)
Since every lambda is being compiled into a Function instance (see kotlin.jvm.functions) which comes with an implementation of invoke, you can call lambdas as shown above, i.e., using lambda(args) or lambda.invoke(args)
.invoke() will simply call your lambda and give your result, same as calling a function.
As for when you should pass a lambda or an actual value, it very depends.
Personally, I would only suggest using lambdas in very specific situations, overusing them can make your code very confusing and hard to refactor. If you just want a result to be passed into the function, just pass the actual value. Don't make someone else call .invoke().
But a few good example for a lambda are callsbacks, or onClickListeners.
// A login network request with a lambda handling the result.
fun login( username: String, password: String, onResult: (Result) -> Unit ) {
// do some network call, and return a Result.
}
// note: if the last param is a lambda, you can simply move it outside the function call like this.
login( username, password ) { result ->
// use the result of the network request.
}
Hopefully that helps.

Listening to Room database insert with RXJava

I have a simple Android application with Room database and I am trying to react to an #Insert query with RxJava but I am unable to chain the calls correctly.
This is my view model method calling the insert:
fun insertTopic(): Single<Long> {
val topic = Topic(null, topicText.value!!, difficulty.value!!, false)
return Single.create<Long> { Observable.just(topicDao.insert(topic)) }
}
And this is the code in my activity triggering the save action:
disposable.add(RxView.clicks(button_save)
.flatMapSingle {
viewModel.insertTopic()
.subscribeOn(Schedulers.io())
}.observeOn(AndroidSchedulers.mainThread())
.doOnError { Toast.makeText(this, "Error inserting topic", Toast.LENGTH_SHORT).show() }
.subscribe { id ->
// NOT INVOKED
hideKeyboard()
Toast.makeText(this, "Topic inserted. ID: $id", Toast.LENGTH_SHORT).show()
this.finish
})
When I click the button, the entity is saved but none of the subscribe code is invoked (no toast is shown). Could someone point out to me what am I doing wrong? I am fairly new to RX java.
The problem is in incorrect usage of Single.create. There is no need in wrapping topicDao.insert(topic) into Observable. Moreover, you are implementing new Single manually, which means you must pass the result id to the #NonNull SingleEmitter<T> emitter argument. But there is no need in using Single.create here.
Single.fromCallable is exactly what you need:
fun insertTopic(): Single<Long> = Single.fromCallable {
val topic = Topic(null, topicText.value!!, difficulty.value!!, false)
return#fromCallable topicDao.insert(topic)
}
Please note, that I create topic object inside lambda so that it isn't captured in a closure. Also keep in mind that fromCallable may throw UndeliverableException if you unsubscribe from Single during the lambda code execution. It will probably never happen in your specific case, but you should understand RxJava2 error handling design.

Return value of if/else condition, but also run code

I'm a newbie to Kotlin, but I freaking love it. There's a wonderful feeling when you can turn three lines of code into one. Now I often find myself looking at my code and thinking "there's some redundancy here. There should be a more concise way to do it." And when I do some searching, often Kotlin does provide a more succinct way.
Here's a problem where I FEEL there should be a simple, concise Kotlin solution, but am not aware of it. Hopefully you can enlighten me!
Take code like this contrived example:
fun doSomething(): Boolean {
if (randomInt % 2 == 0) {
foo = Foo()
true
} else {
bar = null
false
}
}
Based on an if condition, I want to run some code and then return the value of the condition. It just bothers me that I have to explicitly say "if the condition is true, return true. If it is false, return false." It seems redundant. Of course, I could say return randomInt % 2, but I want to run code based on if it is true.
Is there a magic operator Kotlin has that I don't know about? Should I make a function to handle this situation for me, although the syntax of calling that would be different than an if statement? Is there some kind of mapping function, or some way to overload if? It seems like there should be a concise, clever answer, but it's not coming to me.
You can refactor your code a bit so that the return and code happen in different places:
fun doSomething(): Boolean {
val isEven = randomInt % 2 == 0
if (isEven) foo = Foo() else bar = null
return isEven
}
I upvoted some of the answers. I should have considered splitting out the condition to a variable or having the true/false test after the condition.
But after sleeping on it, I have another solution.
New utility functions:
fun Boolean.onTrue(block: () -> Unit): Boolean {
if (this) block()
return this
}
fun Boolean.onFalse(block: () -> Unit): Boolean {
if (!this) block()
return this
}
And then my original code sample can be condensed to:
fun doSomething() = (randomInt % 2).onTrue { foo() }.onFalse { bar = null }
This is the most concise option, although it has its own disadvantages. Nothing prevents the user from, say, calling onTrue() twice or calling onFalse() before onTrue(). It looks quite different from a standard if/else, and if you use both paths, both onTrue() and onFalse() have to check the condition. And then, of course, there's remembering to use the new utility function instead of standard Kotlin operators. Still, it has an appealing brevity. I'll be interested to see what other people think.
You may explore some useful extension functions from Kotlin's standard library. For example, you may use apply:
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Here, randomInt % 2 == 0 will be the return value of apply.
fun doSomething(): Boolean = (randomInt % 2 == 0).apply { if (this) foo = Foo() else bar = null }
Updates: If you prefer a more readable code, it is not a good solution. Have fun :)
It's actually quite simple, you just have to get used to Kotlin's fantastic stdlib functions like apply, with and let. In this case, apply is what you need:
fun idiomatic(myInt: Int) = (myInt % 2 == 0).apply {
if (this) println("is equal") else println("in not equal")
}
What happens: apply is invoked on Any object, a Boolean (the condition) in this case, which directly becomes the functions receiver, referred to as this. The important thing is, that apply will, after the supplied code block has been executed, return its receiver, which is your Boolean here.
I hope this is what you need!
#pixel-elephant's solution looks concise and good.
However, from the clean code perspective, your doSomething() function is doing two things.
If possible you can separate it out to two functions and move the if check to the topmost function:
if ( randomInt.isEven() ) {
doSomethingEven()
// ...
} else {
doSomethingOdd()
// ...
}

How to pass null to an Observable with nullable type in RxJava 2 and Kotlin

I initialize my variable like this:-
val user: BehaviorSubject<User?> user = BehaviorSubject.create()
But I can't do this. IDE throws an error:-
user.onNext(null)
And doing this, IDE says u will never be null:-
user.filter( u -> u!=null)
As Guenhter explained, this is not possible. However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional type:
data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)
This makes your intent much clearer, and you can use a destructuring declaration in your functions:
Observable.just(Optional("Test"))
.map { (text: String?) -> text?.substring(1)?.asOptional() }
.subscribe()
Using the null-object pattern here can cause more bugs than it solves.
If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't. The reason is explained here.
This is a break of the interface. Have a look at the Observable Interface
public interface Observer<T> {
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
...
The #NonNull will be considered by the Kotlin compiler and therefore you CAN'T pass null.
Even if you could, the onNext would immediately throw an error:
#Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
...
}
If you really need such a thing as null you have to fake it. e.g. by creating a static object of User which represents your null-element.
e.g.
data class User(val username, val password) {
companion object {
val NULL_USER = User("", "")
}
}
...
val user = BehaviorSubject.create<User>()
...
user.onNext(User.NULL_USER)
...
user.filter { it !== User.NULL_USER }
But if is somehow possible, try to avoid the null concept and maybe think of another solution where this isn't needed.
Thank you very much for all your answers but I ultimately went with this solution:-
class UserEnvelope(val user:User?) {}
And using this in the observables.
This best suited my requirements.
I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?
To implement the solution mentioned in the nhaarman's answer, you can use the util class Optional (doc) from the Android SDK which was added in API level 24.
If your app's minSdkVersion less than 24 then you still need to implement it by yourself.
Since RxJava 2 does not support null values, there are some other acceptable solutions you can use:
Work with a custom or third party wrapper library of Optionals like some of the posted answers suggest. When I got rid of Java in favour of Kotlin, Optionals went away in the same package since Kotlin per se supports nullability as part of its type System. Just by this change the code was much more clearer, and I personally don't want to get Optionals back in my code as long as I can avoid them.
Emit Any class instances with your subject type. For example you could create an Empty.INSTANCE enum class which would emulate the null value and then filter by the enum class.
The last one is the one I use and prefer being a variant of the previous solution and is based on specialisations. Our friends of JetBrains always emphasise that classes are very cheap in Kotlin, so this would be a quick example to distinguish logged users and not logged ones:
abstract class SessionUser
sealed class LoggedUser(val username: String, val password: String) : SessionUser()
sealed class LogoutUser : SessionUser()
private val user = BehaviorSubject.create<SessionUser>()
private val loggedUser =
user.filter { it is LoggedUser }.cast(LoggedUser::class.java)
fun login(username: String, password: String) {
user.onNext(LoggedUser(username, password))
}
fun logout() {
user.onNext(LogoutUser())
}
I've taken an approach similar to Optional<User> and UserEnvelope. I make a simple User class and a ReifiedUser class that inherits from it. The User class has a companion object that has a NONE instance. The BehaviorSubject is instantiated with the User.NONE instance. It looks something like this:
open class User {
companion object {
val NONE = User()
}
}
class ReifiedUser(
#field:JsonProperty(J.FirstName) val firstName: String,
#field:JsonProperty(J.LastName) val lastName: String
) : User()
My BehaviorSubject is instantiated like this:
val activeUser: BehaviorSubject<User> = BehaviorSubject.createDefault(User.NONE)
And wherever I need to use activeUser I either flatMap it to Observable.empty() if it's NONE or just figure out what it is and what to do in the subscriber.
I don't like mixing java Optional with kotlin nullable because mixing map and let gets really confusing and ugly. This way it's very obvious what's going on.
I think it makes more sense to write a container class such as Result. An example of that would be
data class Result<T>(value: T?, error: Throwable?)
Usage
Observable.create { observer ->
upstreamService.listen(object: UpstreamListener {
onSuccess(data: User) {
observer.onSuccess(Result(data))
}
onError(exception: Throwable) {
observer.onSuccess(Result(null, exception))
}
}
}

Categories

Resources