Last active
April 6, 2024 18:13
-
-
Save zarini/c70401f48a953b09e64912e0d6b3dfcb to your computer and use it in GitHub Desktop.
Now you can call every kotlin suspend function in java code if you want to block until function call finished
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@file:kotlin.jvm.JvmName("SuspendRunner") | |
import kotlinx.coroutines.* | |
import java.util.concurrent.FutureTask | |
import java.util.concurrent.TimeUnit | |
import java.util.concurrent.TimeoutException | |
import kotlin.coroutines.CoroutineContext | |
/** | |
* This class design for call kotlin suspend function in Java code. | |
* | |
* This code run on IO Dispatcher, So do not update UI in suspend function body. | |
* | |
* | |
* @author Amir Khodadadzarini | |
*/ | |
private class SuspendFuture : CoroutineScope { | |
override val coroutineContext: CoroutineContext = Dispatchers.IO + Job() + CoroutineExceptionHandler { _, throwable -> | |
result.cancel(throwable) | |
} | |
private val result = SuspendFutureResult() | |
fun run( | |
timeoutInMillis: Long? = null, | |
block: suspend CoroutineScope.() -> Any | |
): Any { | |
launch { result.emit(block()) } | |
return result.future(timeoutInMillis) | |
} | |
} | |
private class SuspendFutureResult { | |
private lateinit var data: Any | |
private var cancelCause: Throwable? = null | |
private val future = FutureTask { data } | |
fun emit(data: Any) { | |
this.data = data | |
future.run() | |
} | |
fun future(timeoutInMillis: Long?): Any { | |
return try { | |
if (timeoutInMillis != null) { | |
future.get(timeoutInMillis, TimeUnit.MILLISECONDS) | |
} else { | |
future.get() | |
} | |
} catch (ex: CancellationException) { | |
throw RuntimeException(cancelCause) | |
} | |
} | |
fun cancel(cause: Throwable) { | |
this.cancelCause = cause | |
future.cancel(true) | |
} | |
} | |
/** | |
* This function run suspend function in Java code, | |
* Suspend function run on IO Dispatcher so do not update UI in suspend function. | |
* Suspend functions that call in [runSuspend] function can return unit, so you can ignore return value | |
* of the function call. | |
* | |
* Warning: This function blocks until suspend function result received. | |
* | |
* @sample Object result = SuspendRunner.runSuspend( | |
* (coroutineScope, continuation) -> (Object) RunnerFunctionTestKt.getResult(continuation) // suspend function | |
* ); | |
* | |
* @author Amir Khodadadzarini | |
*/ | |
fun runSuspend( | |
block: suspend CoroutineScope.() -> Any | |
): Any { | |
return SuspendFuture().run(null, block) | |
} | |
/** | |
* This function run suspend function in Java code, | |
* Suspend function run on IO Dispatcher so do not update UI in suspend function. | |
* Suspend functions that call in [runSuspend] function can return unit, so you can ignore return value | |
* of the function call. | |
* | |
* Warning: This function blocks until suspend function result received Or reach timeout. | |
* | |
* @sample Object result = SuspendRunner.runSuspend( | |
* 1000, // delay | |
* (coroutineScope, continuation) -> (Object) RunnerFunctionTestKt.getResult(continuation) // suspend function | |
* ); | |
* | |
* @return null if reach timeout before suspend function result received. | |
* @throws TimeoutException Exception thrown when a blocking operation times out. | |
* @author Amir Khodadadzarini | |
*/ | |
@Throws(TimeoutException::class) | |
fun runSuspend( | |
timeoutInMillis: Long, | |
block: suspend CoroutineScope.() -> Any | |
): Any { | |
val suspendFuture = SuspendFuture() | |
return try { | |
suspendFuture.run(timeoutInMillis, block) | |
} catch (ex: Exception) { | |
try { | |
suspendFuture.coroutineContext.cancelChildren() | |
} catch (cancelException: Exception) { | |
cancelException.printStackTrace() | |
} | |
throw ex | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment