Customize ObservableTransformer with RxJav/RxKotlin2 when using with compose() - java

I try to write a transformation function which is used with compose() in order to reduce boilerplate code. It's pretty simple like this:
fun <R> withSchedulers(): ObservableTransformer<R, R> {
return ObservableTransformer {
it.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}
}
So everytime I want to subscribe to anything on ioThread and listen the result on mainThread, it's just few lines of code:
Observable.just(1)
.compose(MyUtilClass.withSchedulers())
.subscribe()
But there isn't Observable only, but we also have Single, Completable, Maybe and Flowable. So every time I want to combine them with my withSchedulers() function, I have to transform it into the new type (which I don't expect).
For example,
Completable.fromAction {
Log.d("nhp", "hello world")
}//.compose(MyUtilClass.withSchedulers()) <-- This is not compiled
.toObservable() <--- I have to transform it into Observable
.compose(MyUtilClass.withSchedulers())
.subscribe()
So my question is, is there any way to write the above function to use with compose() for any kind of Observable (Single, Completable,...) ? Or we have to write different functions which use ObservableTransformer, SingleTransformer, ....?

I created a helper method using reified type :
inline fun <reified T> withSchedulers(): T {
when (T::class) {
ObservableTransformer::class -> return ObservableTransformer<Unit, Unit> {
it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
} as T
SingleTransformer::class -> return SingleTransformer<Unit, Unit> {
it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
} as T
CompletableTransformer::class -> return CompletableTransformer {
it.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
} as T
}
throw IllegalArgumentException("not a valid Transformer type")
}
Examples :
Observable.just("1", "2")
.compose(withSchedulers<ObservableTransformer<String, String>>())
.subscribe(System.out::println)
Single.just(3)
.compose(withSchedulers<SingleTransformer<Int, Int>>())
.subscribe(Consumer { println(it) })
Completable.defer { Completable.complete() }
.compose(withSchedulers<CompletableTransformer>())
.subscribe { println("completed") }
Output :
1
2
3
completed
Probably other ways of doing this, but this came to mind.

Related

Webflux combine results of two downstream calls (and ignore failures)

I know that my question has been asked already multiple times, but I feel like there is still no satisfying answer to it.
Basically I have two downstream services which I want to call (in parallel) and then I want to combine the results and return it (as Json). Both calls can fail but both results are not mandatory, so also an empty combined response is possible:
class FirstResponse {...}
class SecondResponse {...}
class CombinedResponse {
private FirstResponse first;
private SecondResponse second;
}
class FirstService {
Mono<FirstResponse> get(){
return webclient.get(...)
.bodyToMono(FirstResponse.class)
.onErrorResume(throwable -> Mono.empty);
}
}
class SecondService {
Mono<SecondResponse> get(){
return webclient.get(...)
.bodyToMono(SecondResponse.class)
.onErrorResume(throwable -> Mono.empty);
}
}
#RestController(...)
class CombinationController {
#GetMapping(...)
Mono<CombinedResponse> getCombined() {
Mono.zip(firstService.get(), secondService.get(), (first, second) -> {
return new CombinedResponse(first, second);
})
}
}
Now in case the calls to firstService fails, also the response from secondService gets ignored. But what I actually would like to have, is that CombinedResponse still gets (partially populated).
As a disclaimer I have to say, that I am currently migrating my code from rxjava1 and there in case of downstream errors I just return Single.just(null). This allows me to zip both results and just sets the values to null.
About Mono.zip() :
An error or empty completion of any source will cause other sources to
be cancelled and the resulting Mono to immediately error or complete,
respectively.
Also, reactor does not allow null values, so you should do some workaround in your case. In some simple cases it is easy to define some default value in case of error (for example, empty String), but for custom types it would be weird to create an empty object.
As an alternative for such cases I would suggest to use Optional.
This solution adds some boilerplate code, though.
First service:
class FirstService {
Mono<Optional<FirstResponse>> get(){
return webclient.get(...)
.bodyToMono(FirstResponse.class)
.map(Optional::of)
.onErrorReturn(Optional.empty());
}
}
Second service:
class SecondService {
Mono<Optional<SecondResponse>> get(){
return webclient.get(...)
.bodyToMono(SecondResponse.class)
.map(Optional::of)
.onErrorReturn(Optional.empty());
}
}
And "combiner" :
#GetMapping(...)
Mono<CombinedResponse> getCombined() {
Mono.zip(firstService.get(), secondService.get())
.map(tuple -> {
// check optionals here from tuple.getT1() and tuple.getT2()
// and do whatever you want
})
...
}

Kotlin: Type mismatch: inferred type is Unit but Void was expected

How can I rewrite in Kotlin a (Throwable) -> Unit into a (Throwable) -> Void?
The thing I need to achieve is make the following code compile:
fun myKotlinMethod(onError: (Throwable) -> Unit) { // Can not change here
val onErrorJavaCompatible: (Throwable) -> Void = { t -> onError.invoke(t) } // Compilation error
MyJavaLibrary.aMethod(onErrorJavaCompatible) // Can not change here
}
If you want to use a Kotlin function as a Java functional interface parameter, you need to convert it to that interface using SAM conversion, or you need to define an anonymous implementation of the interface (but that is verbose). Either way, Kotlin-Java interop will automatically switch between Unit/Void.
Assuming the interface in Java is named MyJavaCallback, here are two ways to do SAM conversion:
fun myKotlinMethod(onError: (Throwable) -> Unit) { // Can not change here
val onErrorJavaCompatible = MyJavaCallback { t -> onError(t) }
MyJavaLibrary.aMethod(onErrorJavaCompatible)
}
fun myKotlinMethod(onError: (Throwable) -> Unit) {
MyJavaLibrary.aMethod { t -> onError(t) }
}
Since you are directly passing through the code, you can prevent an unnecessary wrapper object around the Kotlin function by using inline:
inline fun myKotlinMethod(crossinline onError: (Throwable) -> Unit) {
MyJavaLibrary.aMethod { t -> onError(t) }
}
One more step you can take for simpler code. When passing a function as a function, you don't need to put it in a lambda and invoke it:
inline fun myKotlinMethod(crossinline onError: (Throwable) -> Unit) {
MyJavaLibrary.aMethod(onError)
}
Just to be complete, here's how you would do it the verbose way, assuming the function in the Java interface is named onSomeEvent:
inline fun myKotlinMethod(crossinline onError: (Throwable) -> Unit) {
val onErrorJavaCompatible = object: MyJavaCallback {
override fun onSomeEvent(t: Throwable) {
onError(t)
}
}
MyJavaLibrary.aMethod(onErrorJavaCompatible)
}

Continuation<? super String> cannot be applied to () [duplicate]

Assume we have the following suspend function:
suspend fun doSomething(): List<MyClass> { ... }
If I want to call this function in one of my existing Java classes (which I'm not able to convert to Kotlin for now) and get its return value I have to provide a Continuation<? super List<MyClass>> as its parameter (Obviously).
My question is, How can I implement one. Specially its getContext getter.
First, add org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 module to your dependencies. In your Kotlin file define the following async function that corresponds to Java style of writing async APIs:
fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
GlobalScope.future { doSomething() }
Now use doSomethingAsync from Java in the same way as you are using other asynchronous APIs in the Java world.
If you dont want to use org.jetbrains.kotlinx:kotlinx-coroutines-jdk8, I have a new idea.
Write below code in your kotlin project.
#JvmOverloads
fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
}
I write it in my Coroutines class
Then you can call your suspend function like:
Coroutines coroutines = new Coroutines();
UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
(tokenResult, throwable) -> {
System.out.println("Coroutines finished");
System.out.println("Result: " + tokenResult);
System.out.println("Exception: " + throwable);
}
));
login() function is a suspend function.
suspend fun login(username: String, password: String): TokenResult
For your code, you can:
doSomething(getContinuation((result, throwable) -> {
//TODO
}));
Besides, you may want to run your callback code in different thread (e.g. Main thread), just use launch(Dispathers.Main) to wrap resumeWith()
Update: My friend has developed a plugin kotlin-jvm-blocking-bridge that can automatically generate blocking bridges for calling suspend functions from Java with minimal effort, also give it a try.
For coroutines 1.3.0 use this:
BuildersKt.launch(GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
(coroutineScope, continuation) -> suspendFunction(arguments)
);
For java < 8:
BuildersKt.launch(
GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
new Function2<CoroutineScope, Continuation<? super Unit>, Unit/*or your return type here*/>() {
#Override
public Unit/*or your return type here*/ invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
//do what you want
return Unit.INSTANCE; //or something with the defined type
}
}
);
My gradle file:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
Kotlin uses static classes for extension functions, launch is an extension function, so it is defined in BuildersKt. The first parameter is the target of the extension function, the rest are the parameters from the extension functions.
I created interface class based on #Kenvix answer to make it compatible with old Android SDK (lower than API 24)
interface CoroutineCallback<RESULT> {
companion object {
#JvmOverloads
fun <R> call(
callback: CoroutineCallback<R>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
callback.onComplete(result.getOrNull(), result.exceptionOrNull())
}
}
}
}
fun onComplete(result: RESULT?, error: Throwable?)
}
usage
class kotlinClass {
suspend doSomething(foo, bar) : FooBar {}
}
class javaClass {
void doSomething(){
kotlinClassObject.doSomething("foo", "bar", CoroutineCallback.Companion.call((fooBar, error) -> {
//do something with result or error
}));
}
}
now call suspend function from any java class by passing CoroutineCallback

Call Kotlin suspend function in Java class

Assume we have the following suspend function:
suspend fun doSomething(): List<MyClass> { ... }
If I want to call this function in one of my existing Java classes (which I'm not able to convert to Kotlin for now) and get its return value I have to provide a Continuation<? super List<MyClass>> as its parameter (Obviously).
My question is, How can I implement one. Specially its getContext getter.
First, add org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 module to your dependencies. In your Kotlin file define the following async function that corresponds to Java style of writing async APIs:
fun doSomethingAsync(): CompletableFuture<List<MyClass>> =
GlobalScope.future { doSomething() }
Now use doSomethingAsync from Java in the same way as you are using other asynchronous APIs in the Java world.
If you dont want to use org.jetbrains.kotlinx:kotlinx-coroutines-jdk8, I have a new idea.
Write below code in your kotlin project.
#JvmOverloads
fun <R> getContinuation(onFinished: BiConsumer<R?, Throwable?>, dispatcher: CoroutineDispatcher = Dispatchers.Default): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
onFinished.accept(result.getOrNull(), result.exceptionOrNull())
}
}
}
I write it in my Coroutines class
Then you can call your suspend function like:
Coroutines coroutines = new Coroutines();
UserUtils.INSTANCE.login("user", "pass", coroutines.getContinuation(
(tokenResult, throwable) -> {
System.out.println("Coroutines finished");
System.out.println("Result: " + tokenResult);
System.out.println("Exception: " + throwable);
}
));
login() function is a suspend function.
suspend fun login(username: String, password: String): TokenResult
For your code, you can:
doSomething(getContinuation((result, throwable) -> {
//TODO
}));
Besides, you may want to run your callback code in different thread (e.g. Main thread), just use launch(Dispathers.Main) to wrap resumeWith()
Update: My friend has developed a plugin kotlin-jvm-blocking-bridge that can automatically generate blocking bridges for calling suspend functions from Java with minimal effort, also give it a try.
For coroutines 1.3.0 use this:
BuildersKt.launch(GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
(coroutineScope, continuation) -> suspendFunction(arguments)
);
For java < 8:
BuildersKt.launch(
GlobalScope.INSTANCE,
Dispatchers.getMain(),//context to be ran on
CoroutineStart.DEFAULT,
new Function2<CoroutineScope, Continuation<? super Unit>, Unit/*or your return type here*/>() {
#Override
public Unit/*or your return type here*/ invoke(CoroutineScope coroutineScope, Continuation<? super Unit> continuation) {
//do what you want
return Unit.INSTANCE; //or something with the defined type
}
}
);
My gradle file:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0"
Kotlin uses static classes for extension functions, launch is an extension function, so it is defined in BuildersKt. The first parameter is the target of the extension function, the rest are the parameters from the extension functions.
I created interface class based on #Kenvix answer to make it compatible with old Android SDK (lower than API 24)
interface CoroutineCallback<RESULT> {
companion object {
#JvmOverloads
fun <R> call(
callback: CoroutineCallback<R>,
dispatcher: CoroutineDispatcher = Dispatchers.Default
): Continuation<R> {
return object : Continuation<R> {
override val context: CoroutineContext
get() = dispatcher
override fun resumeWith(result: Result<R>) {
callback.onComplete(result.getOrNull(), result.exceptionOrNull())
}
}
}
}
fun onComplete(result: RESULT?, error: Throwable?)
}
usage
class kotlinClass {
suspend doSomething(foo, bar) : FooBar {}
}
class javaClass {
void doSomething(){
kotlinClassObject.doSomething("foo", "bar", CoroutineCallback.Companion.call((fooBar, error) -> {
//do something with result or error
}));
}
}
now call suspend function from any java class by passing CoroutineCallback

I have a problem after Java to Kotlin conversion with the error in Android Studio, Cannot infer a type for this parameter

As I converted this method in Java:
private void enqueueDownloads() {
final List<Request> requests = Data.getFetchRequestWithGroupId(GROUP_ID);
fetch.enqueue(requests, updatedRequests -> {
}, error -> Timber.d("DownloadListActivity Error: %1$s", error.toString()));
}
It led to this method which has a lot of errors:
private fun enqueueDownloads() {
val requests = Data.getFetchRequestWithGroupId(GROUP_ID)
fetch.enqueue(requests, { updatedRequests ->
}, { error -> Timber.d("DownloadListActivity Error: %1\$s", error.toString()) })
}
This method in Kotlin has a lot of errors at the method fetch.enqueue where the value updatedRequestsand error it says Cannot infer a type for this parameter.
So I hit hover on the method and hit Ctrl+B and the method declaration in the library is:
fun enqueue(requests: List<Request>, func: Func<List<Request>>? = null, func2: Func<Error>? = null): Fetch
/** Pause a queued or downloading download.
* #param ids ids of downloads to be paused.
* #param func Callback the paused downloads will be returned on. Note. Only downloads that
* were paused will be returned in the result list.
* #param func2 Callback that is called when attempting to pause downloads fail. An error is returned.
* #throws FetchException if this instance of Fetch has been closed.
* #return Instance
* */
The problem has something to do with CallBack based on the method documentation but I can not get it to work! How can I make it fully Kotlin and call it in Kotlin?.
The library is Fetch2 and written in Kotlin. Also I can not see whole code for the methods in the library.
TLDR: the shortes syntax in your specific case is:
fetch.enqueue(requests, Func { updatedRequests ->
}, Func { error -> Timber.d("DownloadListActivity Error: %1\$s", error) })
The issue here is that you are calling function written in Kotlin. You can't use the short lambda syntax here, since Kotlin will not turn lambdas automatically into the right interface in that case.
"Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported." (source)
Usually to implement (Kotlin) interfaces anonymously in Kotlin you'll have to use the full-blown object Syntax:
interface KFunc<T> { fun call(result: T) }
val func = object : KFunc<String> {
override fun call(result: String) {
println(result)
}
}
However Func is an interface defined in Java so Kotlin offers an automatic conversion utility and you can write
val func: Func<String> = Func {
result -> println(result)
}
This is because there is an automatically generated method for every Java interface. In this case below code is generated
fun <T> Func(function: (result: T) -> Unit): Func<T> {
return object : Func<T> {
override fun call(result: T) {
function(result) // call the function
}
}
}
If the method that takes the Func was also written in Java that's where you can even leave out the Func {} part. E.g. given
public class JavaClass {
public static void doWithFunc(Func<String> func) {
func.call("Hello");
}
}
you could write
JavaClass.doWithFunc { result -> println(result) }
However given
object KotlinClass {
#JvmStatic
fun doWithFunc(func: Func<String>) {
func.call("Hello")
}
}
you must write at least
KotlinClass.doWithFunc(Func { result -> println(result) })
On the other hand from Java(8+) you can use lambdas in both cases
JavaClass.doWithFunc(string -> System.out.println(string));
KotlinClass.doWithFunc(string -> System.out.println(string));
It's a little confusing. At the moment, APIs written in Kotlin that are meant for Kotlin consumption shouldn't use functional interfaces but actual function parameters, i.e. for the enqueue function which is
fun enqueue(requests: List<Request>, func: Func<List<Request>>? = null, func2: Func<Error>? = null): Fetch
they would ideally also offer
fun enqueue(requests: List<Request>, func: ((List<Request>) -> Unit)? = null, func2: ((Error) -> Unit)? = null): Fetch
which would allow you to call it like expected in Kotlin
fixedFetch.enqueue(requests, { println(it) }, { Timber.w(it) })
The downside is that this gives a fairly odd looking method for Java users of the library since Kotlin uses it's Function1 interface to represent function parameters. You'll also have to return Unit.INSTANCE since that's actually a type in Kotlin.
Fetch enqueue(List<? extends Request>, Function1<? super List<? extends Request>, Unit>, Function1<? super Error, Unit>)

Categories