Production-grade Kotlin coroutine utilities for Android & JVM. Write safer, cleaner, and more testable asynchronous code — without reinventing the wheel.
| Module | What it gives you |
|---|---|
FlowExt |
throttleFirst, retryWithDelay, onEachCatching and more Flow operators |
SuspendExt |
suspendRunCatching (CancellationException-safe), withTimeoutOrDefault, launchCatching |
ManagedScope |
Lifecycle-aware CoroutineScope with SupervisorJob and Closeable |
RetryPolicy |
Fixed, Exponential Backoff, and Immediate retry strategies |
JavaInterop |
CompletableFuture bridge + universal Callback → Flow adapter |
DispatcherUtils |
Centralized, test-mockable dispatcher provider |
Add the dependency to your module's build.gradle.kts:
dependencies {
implementation("com.yangcyzhang:coroutinekit:0.1.0")
}Make sure mavenCentral() is in your repository list.
// Never accidentally swallows CancellationException
val result: Result<User> = suspendRunCatching {
userRepository.fetchUser(id)
}
result.onSuccess { user -> render(user) }
.onFailure { e -> showError(e) }val data = withTimeoutOrDefault(timeMillis = 3_000, defaultValue = emptyList()) {
networkService.fetchFeed()
}buttonClickFlow
.throttleFirst(windowDuration = 500)
.onEach { handleClick() }
.launchIn(viewModelScope)apiFlow
.retryWithDelay(times = 3, initialDelay = 200, factor = 2.0)
.collect { result -> process(result) }class MyRepository : Closeable {
private val scope = ManagedScope(Dispatchers.IO)
fun startSync() = scope.launch {
while (isActive) {
sync()
delay(30_000)
}
}
override fun close() = scope.close() // cancels everything safely
}// Exponential backoff: 100ms → 200ms → 400ms → ...
val result = withRetry(
policy = RetryPolicy.ExponentialBackoff(times = 4, initialDelayMs = 100)
) { attempt ->
println("Attempt $attempt")
apiCall()
}// Expose a suspend function to Java callers
val future: CompletableFuture<User> = suspendToFuture {
userRepository.fetchUser(id)
}val sensorFlow: Flow<SensorData> = callbackFlow(
register = { onResult, onError ->
sensorManager.register(object : SensorListener {
override fun onData(d: SensorData) = onResult(d)
override fun onError(e: Throwable) = onError(e)
})
},
unregister = { sensorManager.unregister() }
)./gradlew :coroutinekit:testContributions are welcome! Please read CONTRIBUTING.md before submitting a pull request.
MIT License — Copyright (c) 2026 yangcyzhang
See LICENSE for full text.