withContext — fungsi pengalih konteks eksekusi di dalam korutin yang untuk sementara mengubah thread atau dispatcher untuk blok kode yang ditentukan dan mengembalikan hasilnya kembali ke konteks asli. Menurut data JetBrains, 2025, withContext adalah salah satu alat korutin yang paling sering digunakan untuk bekerja dengan permintaan jaringan dan operasi disk. Fungsi ini menjamin bahwa setelah blok selesai, korutin melanjutkan eksekusi pada dispatcher asli, yang mencegah kesalahan keamanan thread yang tidak disengaja.
Poin Utama
withContext — fungsi penangguhan dari paket kotlinx.coroutines yang mengeksekusi blok kode yang diberikan dalam CoroutineContext yang ditentukan dan mengembalikan hasilnya ke konteks asli. Signatur fungsi terlihat seperti ini:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
Parameter context menerima CoroutineContext apa pun — paling sering salah satu dari Dispatchers.IO, Dispatchers.Default atau Dispatchers.Main standar. Blok dieksekusi tepat dalam konteks ini, dan hasilnya dikembalikan ke tempat withContext dipanggil.
Setelah lambda selesai, withContext secara terjamin mengalihkan eksekusi kembali ke dispatcher asli. Ini berarti pengembang tidak perlu memanggil withContext(Dispatchers.Main) secara manual setelah operasi latar belakang — pengembalian terjadi secara otomatis. Perilaku ini ditetapkan dalam spesifikasi Kotlin Coroutines sejak versi 1.3.
Pengembangan Android — area utama penerapan withContext. Skenario umum: ViewModel meluncurkan korutin di thread utama, di dalamnya withContext(Dispatchers.IO) dipanggil untuk permintaan jaringan, dan hasilnya setelah pengembalian otomatis ke Main digunakan untuk memperbarui UI. Pendekatan ini mendasari arsitektur MVVM dan direkomendasikan oleh Google dalam panduan resmi tentang korutin.
Untuk memahami withContext, Anda perlu mengenal CoroutineContext dan komponen utamanya — dispatcher. Setiap korutin memiliki serangkaian elemen konteks, di antaranya dispatcher menentukan di thread atau kumpulan thread mana kode dieksekusi.
| Dispatcher | Tujuan | Ukuran kumpulan |
|---|---|---|
| Dispatchers.Main | Thread utama UI (Android, JavaFX, Swing) | 1 (thread utama) |
| Dispatchers.IO | Operasi disk dan jaringan | 64 thread (batas bertambah) |
| Dispatchers.Default | Perhitungan intensif CPU | max(2, jumlah inti) |
| Dispatchers.Unconfined | Tanpa thread tetap | tidak terbatas |
Penting untuk dipahami bahwa withContext tidak membuat korutin baru — ia hanya mengubah konteks untuk yang sudah ada. Ini adalah perbedaan utama dari launch dan async, yang membuat korutin baru. Implementasi internal withContext dioptimalkan: jika konteks yang diminta cocok dengan yang saat ini, pengalihan tidak terjadi — fungsi dieksekusi pada dispatcher yang sama.
Dispatchers.Main di dalam withContext(Dispatchers.Main) tidak menyebabkan pengalihan — Kotlin Coroutines mengenali identitas konteks dan melewatkan operasi yang tidak perlu. Demikian pula, withContext(Dispatchers.Default) di dalam korutin yang sudah berjalan di Default tidak membuat overhead. Optimasi ini diimplementasikan dalam ContinuationInterceptor.
Pemula sering bingung membedakan withContext dengan launch dan async, karena ketiga fungsi tersebut bekerja dengan korutin dan konteks. Namun, tujuannya pada dasarnya berbeda.
| Karakteristik | withContext | launch | async |
|---|---|---|---|
| Membuat korutin baru | Tidak | Ya | Ya |
| Mengembalikan hasil | Ya (T langsung) | Tidak (Job) | Ya (Deferred<T>) |
| Eksekusi | Sekuensial | Paralel | Paralel |
| Menunggu hasil | Otomatis | join() | await() |
| Use-case umum | Mengganti dispatcher | Fire-and-forget | Perhitungan paralel |
Jika perlu menjalankan satu operasi di thread latar belakang dan mendapatkan hasilnya — gunakan withContext. Jika perlu menjalankan beberapa operasi independen secara paralel — gunakan async dengan await. Jika hasil tidak diperlukan (logging, menulis cache) — launch. Google merekomendasikan withContext sebagai alat pilihan untuk lapisan Repository dalam arsitektur Android.
Mari kita lihat tiga skenario praktis penggunaan withContext di aplikasi Android dalam Kotlin. Setiap contoh menunjukkan tugas spesifik dan pola yang benar.
ViewModel memanggil metode repositori dari korutin di Main. Di dalamnya, withContext(Dispatchers.IO) menjalankan permintaan HTTP, dan hasilnya dikembalikan secara otomatis:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
Korutin di ViewModel memanggil getUser seperti fungsi penangguhan biasa — tanpa menentukan dispatcher secara eksplisit. withContext menyembunyikan detail pengalihan thread.
Ketika perlu menjalankan beberapa operasi IO satu demi satu, withContext menggabungkannya dalam satu blok. Ini lebih efisien daripada membungkus setiap operasi dalam withContext terpisah:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
Kedua operasi dijalankan pada Dispatchers.IO dan hasil Profile dibuat dan dikembalikan tanpa pengalihan konteks yang tidak perlu. Jika operasi independen, lebih baik menggunakan async untuk eksekusi paralel.
Dalam beberapa skenario, perlu menjalankan kode yang tidak dapat dibatalkan — misalnya, menyimpan status saat menutup layar. Kombinasi withContext + NonCancellable menyelesaikan tugas ini:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
Operator + menggabungkan dua elemen konteks: dispatcher IO dan bendera NonCancellable. Blok akan dieksekusi bahkan jika korutin induk dibatalkan — ini berguna untuk operasi finalisasi.
Implementasi internal withContext didasarkan pada mekanisme Continuation — abstraksi sentral korutin Kotlin. Setiap titik penangguhan (suspend point) menyimpan status eksekusi dalam objek Continuation, dan withContext tidak terkecuali.
Kompilator Kotlin menerjemahkan withContext menjadi panggilan ke metode withContext dari kotlinx.coroutines, yang secara internal membuat instance baru DispatchedContinuation. Objek ini membungkus Continuation asli dan mengganti dispatcher di dalamnya. Jika dispatcher baru berbeda dari yang saat ini, eksekusi ditangguhkan, blok dikirim ke kumpulan thread yang sesuai, dan setelah selesai — dilanjutkan dengan konteks asli.
Ketika withContext dipanggil dengan dispatcher yang sama tempat korutin sudah berjalan, Kotlin mengaktifkan fast-path: blok dieksekusi secara sinkron, tanpa membuat DispatchedContinuation dan tanpa mengirim ke kumpulan thread. Ini membuat withContext praktis gratis pada panggilan berulang dengan konteks yang sama. Menurut benchmark JetBrains (kotlinx.coroutines 1.8), fast-path dieksekusi dalam waktu kurang dari 0,1 μs.
Setiap panggilan withContext dengan dispatcher yang berbeda membuat DispatchedContinuation baru dan memerlukan pengalihan thread — ini memakan waktu 1 hingga 5 μs tergantung pada beban. Untuk sebagian besar aplikasi, keterlambatan ini tidak terlihat, tetapi dalam loop dengan ribuan iterasi, lebih baik menggabungkan operasi dalam satu blok withContext.
Bahkan pengembang berpengalaman pun membuat kesalahan saat bekerja dengan withContext. Mari kita lihat empat masalah paling umum dan cara mencegahnya.
Pengembang sering membungkus setiap baris dalam withContext terpisah, alih-alih menggabungkan operasi dalam satu blok. Setiap panggilan tambahan dengan dispatcher berbeda menciptakan overhead.
Benar: gabungkan operasi IO sekuensial dalam satu withContext(Dispatchers.IO) { ... }. Jika sebagian operasi intensif CPU — gunakan withContext(Dispatchers.Default) di dalam blok yang sama.
withContext mengeksekusi kode secara sekuensial. Jika dua permintaan jaringan independen dibungkus dalam satu withContext, keduanya akan dijalankan satu demi satu. Untuk paralelisme, gunakan async + await.
// Sekuensial — lambat
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// Paralel — cepat
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
Jika korutin dibatalkan selama withContext, blok pada Dispatchers.IO juga akan terputus. Untuk operasi yang harus diselesaikan bagaimanapun caranya (menulis ke basis data, mengirim analitik), gabungkan withContext dengan NonCancellable.
Jangan pernah memperbarui komponen View di dalam withContext(Dispatchers.IO). withContext tidak kembali ke Main hingga seluruh blok selesai. Lakukan pembaruan UI setelah kurung tutup withContext — pada saat itu korutin sudah berada di thread utama.
Pertanyaan yang Sering Diajukan
withContext — fungsi penangguhan yang tidak memblokir thread, melainkan mengalihkan konteks di dalam korutin yang ada. runBlocking — jembatan antara korutin dan kode biasa yang memblokir thread saat ini hingga selesai. withContext aman untuk thread UI, runBlocking — tidak.
Tidak, withContext adalah fungsi penangguhan, sehingga hanya dapat dipanggil dari fungsi penangguhan lain atau dari korutin (launch/async). Dari fungsi biasa, withContext tidak dapat dipanggil — untuk itu diperlukan runBlocking atau CoroutineScope.
Kotlin mengaktifkan fast-path — blok dieksekusi secara sinkron pada thread yang sama tanpa pengalihan. Overhead kurang dari 0,1 μs. Ini bukan kesalahan, tetapi panggilan semacam itu berlebihan — lebih baik jalankan kode tanpa withContext.
Pengecualian di dalam withContext disebarkan sama seperti dalam kode biasa — melalui try-catch. Jika blok melempar pengecualian, itu menyebar ke korutin induk dan membatalkannya jika tidak ditangani. Gunakan try-catch di dalam withContext atau di sekitarnya.
Tidak, withContext tidak membuat korutin baru. Ia menggunakan korutin yang sudah ada, tetapi untuk sementara mengubah konteksnya. Ini membedakannya dari launch dan async, yang membuat korutin anak. Perilaku ini dikonfirmasi oleh kode sumber kotlinx.coroutines.
Ringkasan
Kami akan mengembangkan aplikasi seluler turnkey
IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.
Baca juga