withContext — ایک کوروٹین کے اندر سیاق و سباق تبدیل کرنے والا فنکشن ہے جو کوڈ کے دیے گئے بلاک کے لیے عارضی طور پر تھریڈ یا ڈسپیچر تبدیل کرتا ہے اور نتیجہ اصل سیاق و سباق میں واپس لاتا ہے۔ JetBrains, 2025 کے مطابق، withContext نیٹ ورک کی درخواستوں اور ڈسک آپریشنز کے لیے سب سے زیادہ استعمال ہونے والے کوروٹین ٹولز میں سے ایک ہے۔ فنکشن اس بات کی ضمانت دیتا ہے کہ بلاک مکمل ہونے کے بعد کوروٹین اصل ڈسپیچر پر عمل جاری رکھے، جس سے اتفاقی تھریڈ سیفٹی کی غلطیاں روکی جاتی ہیں۔
اہم نکات
withContext kotlinx.coroutines پیکیج سے ایک معطل کرنے والا فنکشن ہے جو کوڈ کے دیے گئے بلاک کو ایک مخصوص CoroutineContext میں انجام دیتا ہے اور نتیجہ اصل سیاق و سباق میں واپس لاتا ہے۔ فنکشن کا دستخط اس طرح ہے:
suspend fun withContext (
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T
context پیرامیٹر کسی بھی CoroutineContext کو قبول کرتا ہے — عام طور پر معیاری Dispatchers.IO، Dispatchers.Default یا Dispatchers.Main میں سے ایک۔ بلاک اس سیاق و سباق میں عملدرآمد ہوتا ہے، اور نتیجہ وہاں واپس آتا ہے جہاں سے withContext کو کال کیا گیا تھا۔
لیمبڈا مکمل ہونے کے بعد، withContext یقینی طور پر عملدرآمد کو اصل ڈسپیچر پر واپس لاتا ہے۔ اس کا مطلب ہے کہ ڈویلپر کو بیک گراؤنڈ آپریشن کے بعد دستی طور پر withContext(Dispatchers.Main) کال کرنے کی ضرورت نہیں ہے — واپسی خود بخود ہوتی ہے۔ یہ رویہ Kotlin Coroutines تصریح میں ورژن 1.3 سے دستاویزی ہے۔
Android ڈویلپمنٹ withContext کے استعمال کا بنیادی شعبہ ہے۔ ایک عام منظرنامہ: ViewModel مرکزی تھریڈ پر ایک کوروٹین شروع کرتا ہے، اس کے اندر نیٹ ورک کی درخواست کے لیے withContext(Dispatchers.IO) کال کرتا ہے، اور Main پر خودکار واپسی کے بعد نتیجہ UI کو اپ ڈیٹ کرنے کے لیے استعمال ہوتا ہے۔ یہ طریقہ MVVM آرکیٹیکچر کی بنیاد ہے اور Google کے سرکاری کوروٹین گائیڈ میں تجویز کیا گیا ہے۔
withContext کو سمجھنے کے لیے، آپ کو CoroutineContext اور اس کے اہم جز — ڈسپیچر کو سمجھنا ہوگا۔ ہر کوروٹین میں سیاق و سباق کے عناصر کا ایک سیٹ ہوتا ہے، جن میں سے ڈسپیچر یہ طے کرتا ہے کہ کوڈ کس تھریڈ یا تھریڈ پول پر چلے گا۔
| ڈسپیچر | مقصد | پول کا سائز |
|---|---|---|
| Dispatchers.Main | مرکزی UI تھریڈ (Android, JavaFX, Swing) | 1 (مرکزی تھریڈ) |
| Dispatchers.IO | ڈسک اور نیٹ ورک آپریشنز | 64 تھریڈ (حد بڑھتی ہے) |
| Dispatchers.Default | CPU گنتی کے حساب | max(2, کور کی تعداد) |
| Dispatchers.Unconfined | کوئی مقررہ تھریڈ نہیں | لامحدود |
یہ سمجھنا ضروری ہے کہ withContext نیا کوروٹین نہیں بناتا — یہ صرف موجودہ کوروٹین کا سیاق و سباق تبدیل کرتا ہے۔ یہ launch اور async سے بنیادی فرق ہے، جو نئے کوروٹین بناتے ہیں۔ withContext کا اندرونی نفاذ بہتر بنایا گیا ہے: اگر درخواست کردہ سیاق و سباق موجودہ سے مطابقت رکھتا ہے، تو کوئی تبدیلی نہیں ہوتی — فنکشن اسی ڈسپیچر پر عملدرآمد کرتا ہے۔
withContext(Dispatchers.Main) کے اندر Dispatchers.Main تبدیلی کا سبب نہیں بنتا — Kotlin Coroutines سیاق و سباق کی مماثلت کو پہچانتا ہے اور غیر ضروری آپریشن کو چھوڑ دیتا ہے۔ اسی طرح، پہلے سے Default پر چلنے والے کوروٹین کے اندر withContext(Dispatchers.Default) اضافی بوجھ نہیں بناتا۔ یہ اصلاح ContinuationInterceptor میں نافذ کی گئی ہے۔
ابتدائی افراد اکثر withContext کو launch اور async کے ساتھ الجھاتے ہیں، کیونکہ تینوں فنکشن کوروٹین اور سیاق و سباق کے ساتھ کام کرتے ہیں۔ تاہم، ان کا مقصد بنیادی طور پر مختلف ہے۔
| خصوصیت | withContext | launch | async |
|---|---|---|---|
| نیا کوروٹین بناتا ہے | نہیں | ہاں | ہاں |
| نتیجہ لوٹاتا ہے | ہاں (T براہ راست) | نہیں (Job) | ہاں (Deferred<T>) |
| عملدرآمد | ترتیب وار | متوازی | متوازی |
| نتیجے کا انتظار | خودکار | join() | await() |
| عام استعمال کا معاملہ | ڈسپیچر تبدیل کرنا | فائر-اینڈ-فورگیٹ | متوازی حسابات |
اگر آپ کو بیک گراؤنڈ تھریڈ پر ایک آپریشن انجام دینا ہے اور نتیجہ حاصل کرنا ہے — withContext استعمال کریں۔ اگر آپ کو کئی آزاد آپریشن متوازی طور پر چلانے ہیں — await کے ساتھ async استعمال کریں۔ اگر نتیجہ کی ضرورت نہیں ہے (لاگنگ، کیش لکھنا) — launch استعمال کریں۔ Google Android آرکیٹیکچر میں Repository پرت کے لیے withContext کو ترجیحی ٹول کے طور پر تجویز کرتا ہے۔
آئیے Kotlin Android ایپلیکیشنز میں withContext استعمال کرنے کے تین عملی منظرنامے دیکھتے ہیں۔ ہر مثال ایک مخصوص کام اور صحیح پیٹرن کو ظاہر کرتی ہے۔
ViewModel Main پر کوروٹین سے ریپوزٹری طریقہ کال کرتا ہے۔ اندر، withContext(Dispatchers.IO) HTTP درخواست انجام دیتا ہے، اور نتیجہ خود بخود واپس آتا ہے:
class UserRepository(
private val api: UserApi
) {
suspend fun getUser(id: String): User {
return withContext(Dispatchers.IO) {
api.fetchUser(id)
}
}
}
ViewModel میں کوروٹین getUser کو کسی بھی عام معطل کرنے والے فنکشن کی طرح کال کرتا ہے — بغیر ڈسپیچر کی واضح وضاحت کے۔ withContext تھریڈ تبدیل کرنے کی تفصیلات چھپاتا ہے۔
جب آپ کو ایک کے بعد ایک کئی IO آپریشن کرنے کی ضرورت ہوتی ہے، withContext انہیں ایک بلاک میں یکجا کرتا ہے۔ یہ ہر آپریشن کو علیحدہ withContext میں لپیٹنے سے زیادہ موثر ہے:
suspend fun loadUserProfile(id: String): Profile {
return withContext(Dispatchers.IO) {
val user = api.fetchUser(id)
val posts = api.fetchPosts(id)
Profile(user, posts)
}
}
دونوں آپریشن Dispatchers.IO پر چلتے ہیں، اور Profile نتیجہ غیر ضروری سیاق و سباق کی تبدیلیوں کے بغیر بنایا اور واپس کیا جاتا ہے۔ اگر آپریشن آزاد ہیں، تو متوازی عملدرآمد کے لیے async استعمال کرنا بہتر ہے۔
کچھ منظرناموں میں، آپ کو ایسا کوڈ انجام دینے کی ضرورت ہوتی ہے جسے منسوخ نہیں کیا جا سکتا — مثال کے طور پر، اسکرین بند کرتے وقت حالت محفوظ کرنا۔ withContext + NonCancellable کا امتزاج اس کام کو حل کرتا ہے:
withContext(Dispatchers.IO + NonCancellable) {
cache.saveState(state)
analytics.logEvent("state_saved")
}
آپریٹر + دو سیاق و سباق کے عناصر کو جوڑتا ہے: IO ڈسپیچر اور NonCancellable جھنڈی۔ بلاک اس وقت بھی عملدرآمد ہوتا ہے جب پیرنٹ کوروٹین منسوخ کر دیا گیا ہو — اختتامی کارروائیوں کے لیے مفید۔
withContext کا اندرونی نفاذ Continuation میکانزم پر مبنی ہے — Kotlin کوروٹین کا مرکزی تجرید۔ ہر معطلی پوائنٹ عملدرآمد کی حالت کو Continuation آبجیکٹ میں محفوظ کرتا ہے، اور withContext اس سے مستثنیٰ نہیں ہے۔
Kotlin کمپائلر withContext کو kotlinx.coroutines سے withContext طریقہ کال میں ترجمہ کرتا ہے، جو اندرونی طور پر DispatchedContinuation کی ایک نئی مثال بناتا ہے۔ یہ آبجیکٹ اصل Continuation کو لپیٹتا ہے اور اس کے ڈسپیچر کو تبدیل کرتا ہے۔ اگر نیا ڈسپیچر موجودہ سے مختلف ہے، تو عملدرآمد معطل ہو جاتا ہے، بلاک متعلقہ تھریڈ پول کو بھیجا جاتا ہے، اور مکمل ہونے کے بعد — اصل سیاق و سباق کے ساتھ دوبارہ شروع ہوتا ہے۔
جب withContext کو اسی ڈسپیچر کے ساتھ کال کیا جاتا ہے جس پر کوروٹین پہلے سے چل رہا ہے، Kotlin تیز راستہ (fast-path) فعال کرتا ہے: بلاک ہم آہنگی سے عملدرآمد کرتا ہے، بغیر DispatchedContinuation بنائے اور بغیر تھریڈ پول کو بھیجے۔ یہ withContext کو ایک ہی سیاق و سباق کے ساتھ بار بار کال کرنے پر عملی طور پر مفت بنا دیتا ہے۔ JetBrains بینچ مارکس (kotlinx.coroutines 1.8) کے مطابق، تیز راستہ 0.1 µs سے کم میں مکمل ہوتا ہے۔
withContext کی ہر کال مختلف ڈسپیچر کے ساتھ ایک نیا DispatchedContinuation بناتی ہے اور تھریڈ تبدیل کرنے کی ضرورت ہوتی ہے — اس میں بوجھ کے لحاظ سے 1 سے 5 µs لگتے ہیں۔ زیادہ تر ایپلیکیشنز کے لیے یہ تاخیر ناقابلِ دید ہے، لیکن ہزاروں تکرار والے لوپ کے اندر، آپریشنز کو ایک withContext بلاک میں جمع کرنا مناسب ہے۔
تجربہ کار ڈویلپر بھی withContext کے ساتھ کام کرتے وقت غلطیاں کرتے ہیں۔ آئیے چار سب سے عام مسائل اور انہیں روکنے کے طریقے دیکھتے ہیں۔
ڈویلپر اکثر آپریشنز کو ایک بلاک میں یکجا کرنے کے بجائے ہر لائن کو علیحدہ withContext میں لپیٹتے ہیں۔ مختلف ڈسپیچر کے ساتھ ہر اضافی کال اضافی بوجھ بناتی ہے۔
درست: ترتیب وار IO آپریشنز کو ایک withContext(Dispatchers.IO) { ... } میں یکجا کریں۔ اگر کچھ آپریشن CPU گنتی کے حامل ہیں — اسی بلاک کے اندر withContext(Dispatchers.Default) استعمال کریں۔
withContext کوڈ کو ترتیب وار انجام دیتا ہے۔ اگر دو آزاد نیٹ ورک درخواستیں ایک withContext میں لپیٹی گئی ہیں، تو وہ ایک کے بعد ایک چلیں گی۔ متوازی عملدرآمد کے لیے، async + await استعمال کریں۔
// ترتیب وار — سست
withContext(Dispatchers.IO) {
val a = api.fetchA()
val b = api.fetchB()
}
// متوازی — تیز
coroutineScope {
val a = async { api.fetchA() }
val b = async { api.fetchB() }
println("${a.await()} ${b.await()}")
}
اگر withContext کے دوران کوروٹین منسوخ کر دیا جاتا ہے، تو Dispatchers.IO پر بلاک بھی روک دیا جاتا ہے۔ ان آپریشنز کے لیے جو ہر قیمت پر مکمل ہونے چاہئیں (ڈیٹا بیس لکھنا، تجزیات بھیجنا)، withContext کو NonCancellable کے ساتھ جوڑیں۔
withContext(Dispatchers.IO) کے اندر کبھی بھی View اجزاء کو اپ ڈیٹ نہ کریں۔ withContext پورے بلاک کے مکمل ہونے تک Main پر واپس نہیں آتا۔ UI اپ ڈیٹس کو withContext کے بند ہونے والے بریکٹ کے بعد رکھیں — تب کوروٹین پہلے سے مرکزی تھریڈ پر ہوگا۔
اکثر پوچھے گئے سوالات
withContext ایک معطل کرنے والا فنکشن ہے جو تھریڈ کو بلاک نہیں کرتا، بلکہ موجودہ کوروٹین کے اندر سیاق و سباق تبدیل کرتا ہے۔ runBlocking کوروٹین اور عام کوڈ کے درمیان ایک پل ہے جو مکمل ہونے تک موجودہ تھریڈ کو بلاک کرتا ہے۔ withContext UI تھریڈ کے لیے محفوظ ہے، runBlocking نہیں ہے۔
نہیں، withContext ایک suspend فنکشن ہے، لہذا اسے صرف کسی دوسرے suspend فنکشن یا کوروٹین (launch/async) سے کال کیا جا سکتا ہے۔ عام فنکشن سے withContext کو کال نہیں کیا جا سکتا — اس کے لیے runBlocking یا CoroutineScope کی ضرورت ہے۔
Kotlin تیز راستہ (fast-path) فعال کرتا ہے — بلاک اسی تھریڈ پر تبدیلی کے بغیر ہم آہنگی سے عملدرآمد کرتا ہے۔ اضافی بوجھ 0.1 µs سے کم ہے۔ یہ کوئی غلطی نہیں ہے، لیکن ایسی کال بے کار ہے — بغیر withContext کے کوڈ کو انجام دینا بہتر ہے۔
withContext کے اندر استثنیات عام کوڈ کی طرح پھیلتی ہیں — try-catch کے ذریعے۔ اگر بلاک استثنیٰ پھینکتا ہے، تو یہ پیرنٹ کوروٹین میں پھیلتا ہے اور اگر ہینڈل نہ کیا گیا تو اسے منسوخ کر دیتا ہے۔ withContext کے اندر یا اس کے ارد گرد try-catch استعمال کریں۔
نہیں، withContext نیا کوروٹین نہیں بناتا۔ یہ موجودہ کوروٹین استعمال کرتا ہے لیکن عارضی طور پر اس کا سیاق و سباق تبدیل کرتا ہے۔ یہ اسے launch اور async سے ممتاز کرتا ہے، جو چائلڈ کوروٹین بناتے ہیں۔ اس رویے کی تصدیق kotlinx.coroutines سورس کوڈ سے ہوتی ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں