Kotlin coroutines provide structured concurrency, allowing developers to manage background tasks efficiently. However, when a coroutine is canceled, all its child coroutines and their associated operations are also canceled. This can lead to issues in scenarios where certain operations, such as logging, resource cleanup, or committing a database transaction, must be completed regardless of cancellation.To address this, Kotlin provides NonCancellable, a special coroutine context that allows critical operations to execute even if the coroutine is canceled. This article explores NonCancellable, its use cases, and how to apply it effectively in mobile app development.What is NonCancellable in Kotlin Coroutines?NonCancellable is a predefined object in Kotlin coroutines that prevents cancellation from affecting certain operations. It is typically used with withContext() to override the default cancellation behavior.
Syntax of NonCancellable
import kotlinx.coroutines.*
suspend fun performCriticalTask() {
withContext(NonCancellable) {
// Code that must execute even if the coroutine is cancelled
}
}Use Cases for NonCancellable
- Ensuring Cleanup in a try-finally Block
When a coroutine is canceled, the finally block still executes. However, if it contains a suspending function, that function might get canceled before completing. Using NonCancellable ensures that cleanup operations run completely.ProblemConsider a scenario where a coroutine performs a database operation and needs to release resources upon cancellation.
suspend fun performDatabaseOperation() {
try {
// Simulating a database operation
delay(5000)
println("Database operation completed")
} finally {
// Cleanup resources
println("Releasing resources...") // This might get canceled
}
}If the coroutine running performDatabaseOperation() gets canceled before completion, the cleanup operation might be skipped.
Solution: Use NonCancellable
suspend fun performDatabaseOperation() {
try {
delay(5000)
println("Database operation completed")
} finally {
withContext(NonCancellable) {
println("Releasing resources safely")
}
}
}Here, withContext(NonCancellable) ensures that resource cleanup executes even if the coroutine is canceled.
2. Committing a Database Transaction
In real-world mobile applications, database transactions must be committed or rolled back properly, even if the coroutine handling them is canceled.ProblemIf a transaction commit is a suspending function and the coroutine is canceled, the transaction might be left in an inconsistent state.
suspend fun saveData() {
try {
// Start transaction
delay(2000)
println("Data processing started")
delay(3000) // Simulating transaction execution
} finally {
println("Committing transaction") // Might be skipped
}
}Solution: Use NonCancellable to Ensure Transaction Commit
suspend fun saveData() {
try {
delay(2000)
println("Data processing started")
delay(3000)
} finally {
withContext(NonCancellable) {
println("Committing transaction safely")
}
}
}Using NonCancellable ensures that the transaction is committed, preventing potential data inconsistencies.
3. Logging Critical Events
Logging is essential in mobile applications for debugging and tracking errors. If a coroutine is canceled before logging completes, important log entries might be lost.
suspend fun processRequest() {
try {
println("Processing request...")
delay(3000)
} finally {
println("Logging cancellation event") // Might get canceled
}
}Solution: Use NonCancellable for Logging
suspend fun processRequest() {
try {
println("Processing request...")
delay(3000)
} finally {
withContext(NonCancellable) {
println("Logging cancellation event safely")
}
}
}Ensuring logs are written even after cancellation helps in debugging and auditing.
**Best Practices for Using NonCancellable
**• Use NonCancellable only for critical operations like resource cleanup, logging, or transaction commits.
• Avoid long-running tasks inside NonCancellable, as they will continue running even after cancellation.
• Handle exceptions properly within NonCancellable blocks to prevent crashes.
suspend fun safeCleanup() {
try {
withContext(NonCancellable) {
println("Performing critical cleanup")
}
} catch (e: Exception) {
println("Error during cleanup: ${e.message}")
}
}Key Takeaways
- NonCancellable ensures that critical operations (like cleanup, logging, or committing a transaction) execute even if the coroutine is canceled.
- It should be used only for essential tasks that must not be interrupted.
- Avoid running long or blocking operations inside NonCancellable to prevent resource leaks.
- Always handle exceptions properly within NonCancellable to ensure stability.
By applying these best practices, developers can ensure that essential operations are executed safely without disrupting app performance or user experience.


