Channel، Kotlin Coroutines لائبریری سے ایک سنکرونائزیشن پریمیٹو ہے جو کوروٹینز کے درمیان ڈیٹا منتقل کرنے کے لیے استعمال ہوتا ہے۔ Kotlin Documentation, 2025 کے مطابق، Channel suspend-فنکشنز کے ذریعے بلاک کرنے والے بھیجنے کے ساتھ producer-consumer پیٹرن کو نافذ کرتا ہے۔ Channel Rendezvous، Buffered اور Conflated موڈز کو سپورٹ کرتا ہے، جن میں سے ہر ایک اوور فلو ہونے پر رویے کا تعین کرتا ہے۔
اہم نکات
Channel تصوراتی طور پر Java کے BlockingQueue سے ملتا جلتا ہے، لیکن بلاک کرنے والے put() اور take() کے بجائے suspend فنکشنز send() اور receive() کے ساتھ۔ ایک Kotlin ڈویلپر مشترکہ میموری کے ذریعے سنکرونائزیشن کے بغیر کوروٹینز کے درمیان ڈیٹا کے تبادلے کو منظم کرنے کے لیے Channel استعمال کرتا ہے۔ چینل ترتیب وار ترسیل کی ضمانت دیتا ہے — بھیجنے کا ترتیب وصول کرنے کے ترتیب سے ملتا ہے۔
Channel بنانے کے لیے فیکٹری فنکشن Channel<T>(capacity) کو کال کیا جاتا ہے۔ پیرامیٹر capacity چینل کی قسم کا تعین کرتا ہے: RENDEZVOUS (0)، UNLIMITED (Int.MAX_VALUE)، CONFLATED (-1) یا کوئی مخصوص نمبر۔ عنصر کی قسم T جینرکس کے ذریعے مخصوص کی جاتی ہے۔ close() کے ذریعے چینل بند کرنا اشارہ کرتا ہے کہ کوئی نیا عنصر نہیں آئے گا۔
send(value) ایک suspend فنکشن ہے جو چینل بھرنے پر بھیجنے والے کوروٹین کو معطل کر دیتا ہے۔ receive() ایک suspend فنکشن ہے جو چینل خالی ہونے پر وصول کنندہ کو معطل کر دیتا ہے۔ متبادل trySend() اور tryReceive() غیر بلاک کرنے والے ورژن ہیں جو آپریشن ممکن نہ ہونے پر Boolean یا null لوٹاتے ہیں۔ یہ غیر suspend سیاق و سباق میں مفید ہیں۔
Kotlin بفر گنجائش کے ذریعے Channel کی چار اقسام فراہم کرتا ہے: Rendezvous (گنجائش 0)، Buffered (گنجائش N)، Conflated (گنجائش 1، اوور رائٹ) اور Unlimited (گنجائش Int.MAX_VALUE)۔ ہر قسم اپنا کام حل کرتی ہے، سخت سنکرونائزیشن سے لے کر بڑے پیمانے پر ڈیٹا بفرنگ تک۔
Rendezvous Channel سب سے سخت ہے: send() اس وقت تک بلاک رہتا ہے جب تک کسی دوسرے کوروٹین میں receive() نہ بلایا جائے۔ بنیادی طور پر، یہ دو کوروٹینز کا سنگم نقطہ ہے۔ سخت مصافحہ کے لیے مثالی جب بھیجنے والے کو وصول کنندہ کے عنصر پر کارروائی کرنے کا انتظار کرنا ہو۔ ڈیٹا کا نقصان خارج کر دیا گیا ہے — receive کے عمل میں آنے تک send مکمل نہیں ہوتا۔
Conflated Channel صرف آخری بھیجی گئی قدر ذخیرہ کرتا ہے۔ اگر بھیجنے والا وصول کنندہ کے پرانی قدر لینے سے پہلے نیا عنصر ڈالے تو پرانی خارج کر دی جاتی ہے۔ Conflated Channel UI حالت کے لیے مفید ہے: اگر صارف تیزی سے سلائیڈر تبدیل کرے تو درمیانی قدریں خارج کی جا سکتی ہیں اور صرف آخری پر کارروائی کی جا سکتی ہے۔
Channel پر کلاسک پروڈیوسر-کنزیومر پیٹرن متوازی کوروٹینز کے ذریعے نافذ کیا جاتا ہے۔ پروڈیوسر لوپ میں send(value) کال کرتا ہے، کنزیومر receive(value) کال کرتا ہے۔ پروڈیوسر اور کنزیومر مختلف Dispatchers پر کام کر سکتے ہیں: پروڈیوسر Dispatchers.IO پر، کنزیومر Dispatchers.Main پر۔ Channel Lock یا synchronized کے بغیر خود بخود رسائی کو سنکرونائز کرتا ہے۔
Fan-out — ایک چینل پر متعدد کنزیومر۔ ہر عنصر بالکل ایک کنزیومر کو جاتا ہے (round-robin تقسیم)۔ Fan-in — متعدد پروڈیوسر ایک چینل میں لکھتے ہیں۔ بھیجنے والے کوروٹینز بھیجنے کے لیے مقابلہ کرتے ہیں، لیکن عناصر کی ترتیب محفوظ رہتی ہے۔ دونوں منظرناموں میں اضافی سنکرونائزیشن کی ضرورت نہیں ہے۔
Produce ایک کوروٹین بلڈر ہے جو خودکار بندش کے ساتھ چینل بناتا ہے۔ فنکشن produce { } ReceiveChannel لوٹاتا ہے — کنزیومر کے لیے صرف پڑھنے والا چینل۔ بلڈر کے اندر send() ڈیٹا بھیجتا ہے، اور بلاک مکمل ہونے یا مستثنیٰ ہونے پر چینل خود بخود بند ہو جاتا ہے، رساو کو روکتا ہے۔
kotlinx.coroutines لائبریری select فراہم کرتی ہے — ایک اظہار جو کئی متبادلات میں سے پہلے مکمل ہونے والے چینل کا انتظار کرتا ہے۔ Select کئی چینلز کو ملٹی پلیکس کرنے کی اجازت دیتا ہے: مثال کے طور پر، دو ذرائع سے ڈیٹا کا انتظار کرنا اور پہلے جواب دینے والے پر کارروائی کرنا۔ نحو — select<T> { channel1.onReceive { } channel2.onReceive { } }۔ یہ Rx میں amb آپریٹر کا متبادل ہے۔
پہلی مثال ایک سادہ Rendezvous Channel ہے جہاں بھیجنے والا وصولی کا انتظار کرتا ہے:
val channel = Channel<String>()
scope.launch {
channel.send("Hello")
println("بھیج دیا گیا")
}
scope.launch {
val msg = channel.receive()
println("وصول ہوا: $msg")
}
دوسری مثال — ایک چینل پر متعدد کنزیومر (fan-out):
val channel = Channel<Int>(Channel.UNLIMITED)
scope.launch {
for (x in 1..10) channel.send(x)
channel.close()
}
repeat(2) { id ->
scope.launch {
for (msg in channel) {
println("Consumer #$id: $msg")
}
}
}
تیسری مثال — غلطی سے نمٹنے کے ساتھ produce بلڈر کا استعمال:
val source = produce {
for (i in 1..5) {
delay(200)
send(i)
}
}
scope.launch {
source
.consumeAsFlow()
.catch { println("خرابی: $it") }
.collect { println("عنصر: $it") }
}
Channel ایک ہاٹ پریمیٹو ہے: ڈیٹا سبسکرائبرز سے آزادانہ طور پر خارج ہوتا ہے۔ Flow سرد ہے: ڈیٹا سبسکرپشن پر پیدا ہوتا ہے۔ Channel ہر عنصر کی ایک کنزیومر کو ضمانتی ترسیل (fan-out) کے ساتھ متعدد پروڈیوسر اور کنزیومر کو سپورٹ کرتا ہے۔ Flow متعدد آزاد پروڈیوسر کے لیے ڈیزائن نہیں کیا گیا۔
Channel بیک پریشر مینجمنٹ کے لیے قابل ترتیب گنجائش کا بفر اور suspend فنکشنز send/receive استعمال کرتا ہے۔ Flow کوروٹینز کے ذریعے خودکار بیک پریشر کے ساتھ suspend میکانزم collect استعمال کرتا ہے۔ Channel مخصوص منظرناموں کے لیے ایک نچلی سطح کا آلہ ہے: کال بیک کنورژن، ایکٹر ماڈل، متعدد بھیجنے والوں کے ساتھ کام کی قطار۔
Android میں روزمرہ کے منظرناموں (UI حالت، DB سے رد عمل والے سٹریمز) کے لیے Google Channel کے بجائے Flow تجویز کرتا ہے۔ Channel اس وقت استعمال کرنا چاہیے جب صحیح بفر کنٹرول کے ساتھ کوروٹینز کے درمیان ہاٹ ڈیٹا کے تبادلے کی ضرورت ہو، یا callbackFlow کے ذریعے کال بیک انٹرفیس تبدیل کرتے وقت، جس کا اندرونی نفاذ Channel استعمال کرتا ہے۔
ایک اہم عملی مثال: WebSocket کلائنٹ کو لاگو کرتے وقت، Channel ایک کوروٹین سے پیغامات لکھنے اور دوسرے سے پڑھنے کی اجازت دیتا ہے اس ضمانت کے ساتھ کہ ہر پیغام بالکل ایک بار پروسیس ہوگا۔ Flow اس کام کے لیے موزوں نہیں ہے کیونکہ یہ سرد ہے اور متعدد پروڈیوسر کو سپورٹ نہیں کرتا۔ UNLIMITED گنجائش والا Channel یقینی بناتا ہے کہ عارضی کنزیومر تاخیر کے دوران آنے والے پیغامات گم نہ ہوں۔
چینل لائف سائیکل مینجمنٹ Channel کے ساتھ کام کا ایک اہم حصہ ہے۔ جب تمام ڈیٹا بھیج دیا جائے تو چینل بند کر دینا چاہیے تاکہ کنزیومر تکرار مکمل کر سکے۔ channel.close() کال کرنا اشارہ کرتا ہے کہ کوئی نیا عنصر نہیں آئے گا۔ کنزیومر for (item in channel) کے ذریعے تکرار کر سکتا ہے — close() اور بفر خالی ہونے کے بعد لوپ خود بخود ختم ہو جائے گا۔ متبادل طور پر کنزیومر ClosedReceiveChannelException سے نمٹنے کے ساتھ لوپ میں receive() کال کر سکتا ہے۔
Channel Android میں بغیر انحصار کے EventBus لاگو کرنے کے لیے فعال طور پر استعمال ہوتا ہے: براڈکاسٹ حکمت عملی کے ساتھ ایک عالمی Channel<Event> ایپلیکیشن کے کسی بھی نقطہ سے واقعات بھیجنے کی اجازت دیتا ہے۔ LiveData پر مبنی بسوں کے برعکس، Channel لائف سائیکل سے منسلک نہیں ہے اور اسکرینوں کے درمیان منتقل ہوتے وقت صفائی کی ضرورت نہیں ہے۔ ViewModel سے send() اور Activity/Fragment میں lifecycleScope کے ذریعے receive() Event کلاسز کے بغیر ٹائپ محفوظ مواصلت فراہم کرتے ہیں۔ Channel پر متعدد کنزیومر بوجھ تقسیم کرتے ہیں — ہر عنصر ایک بار پروسیس ہوتا ہے، جو مختلف سبسکرائبرز میں ایک ہی واقعہ کی ڈپلیکیٹ پروسیسنگ کو روکتا ہے۔
ایکٹر سسٹمز میں Channel میل باکس — ایکٹر کے لیے پیغام قطار — لاگو کرنے کی بنیاد کے طور پر کام کرتا ہے۔ ایک ایکٹر ایک کوروٹین ہے جو لوپ میں Channel سے پیغامات پڑھتا ہے اور انہیں ترتیب وار پروسیس کرتا ہے۔ یہ نقطہ نظر ضمانت دیتا ہے کہ ہر پیغام ڈیٹا ریس کے بغیر بھیجنے کی ترتیب میں پروسیس ہوتا ہے۔ Kotlin میں (Akka کے برعکس) بلٹ ان ایکٹر بطور قسم نہیں ہے، لیکن Channel + launch ایک ہلکا متبادل ہے۔
دو طرفہ تبادلے کے لیے چینل جوڑے استعمال کیے جاتے ہیں: ایک چینل کلائنٹ سے سرور تک درخواستوں کے لیے، دوسرا سرور سے کلائنٹ تک جوابات کے لیے۔ مثال کے طور پر، ملٹی تھریڈ ایپلیکیشن میں Pipe لاگو کرتے وقت: پروڈیوسر OutputChannel میں لکھتا ہے، کنزیومر InputChannel سے پڑھتا ہے۔ suspend فنکشنز send اور receive ضمانت دیتے ہیں کہ پروڈیوسر-کنزیومر کال سٹیک کو اوور فلو نہیں کرے گا، کیونکہ کوروٹینز بلاک ہونے کے بجائے معطل ہوتی ہیں۔ BUFFERED گنجائش والا Channel زیادہ تر منظرناموں کے لیے موزوں ہے جہاں پروڈیوسر اور کنزیومر کی رفتار تقریباً برابر ہو۔ غیر متناسب منظرناموں کے لیے UNLIMITED استعمال کریں تاکہ کنزیومر مصروف ہونے پر پروڈیوسر معطل نہ ہو — اس سے ڈیڈ لاک کا خطرہ کم ہوتا ہے لیکن میموری کی کھپت بڑھ جاتی ہے۔
چینلز کے ساتھ آرکیٹیکچر ڈیزائن کرتے وقت capacity کو یاد رکھنا ضروری ہے: گنجائش کا انتخاب چوٹی لوڈ کے تحت رویے کو براہ راست متاثر کرتا ہے۔ BUFFERED(N) گنجائش والے چینلز ایک ہموار بفر کے طور پر کام کرتے ہیں: اگر کنزیومر عارضی طور پر پروڈیوسر سے سست ہو تو عناصر جمع ہوتے ہیں۔ اگر کنزیومر کی اوسط رفتار مسلسل پروڈیوسر سے کم ہو تو بفر بھر جائے گا اور بھیجنے والا کوروٹین معطل ہو جائے گا — یہ خودکار بیک پریشر ہے جو میموری اوور لوڈ سے بچاتا ہے۔
Channel کی نگرانی اور ڈیبگنگ کے لیے kotlinx-coroutines-debug استعمال کریں: یوٹیلیٹی فعال کوروٹینز کی تعداد، ان کے چینل کی حالت (کھلا/بند، بفر میں عناصر کی تعداد)، اور معطل send/receive آپریشنز کا کال سٹیک دکھاتی ہے۔ Channel کو لاگنگ پراکسی میں بھی لپیٹا جا سکتا ہے: LoggingChannel<T> کلاس اصلی Channel کو کالز ڈیلیگیٹ کرتی ہے، send، receive اور close آپریشنز لاگ کرتی ہے۔ اس سے چینل لیک کی نشاندہی کرنے میں مدد ملتی ہے جب close() کال نہیں کیا گیا اور کنزیومر کوروٹین ہمیشہ کے لیے نئے عناصر کا انتظار کر رہا ہے۔
اکثر پوچھے گئے سوالات
Channel بلاک کرنے والے put() اور take() کے بجائے suspend فنکشنز send() اور receive() استعمال کرتا ہے۔ BlockingQueue کے برعکس، Channel اوور فلو ہونے پر تھریڈ کو بلاک نہیں کرتا — کوروٹین معطل ہو جاتا ہے، دوسرے کوروٹینز کے لیے تھریڈ خالی کرتا ہے۔ Kotlin میں موثر تھریڈ استعمال کے لیے یہ اہم ہے۔
بند چینل پر send() کال کرنے پر ClosedSendChannelException پھینکا جاتا ہے۔ بھیجنے سے پہلے isClosedForSend چیک کریں یا trySend() استعمال کریں جو بند ہونے پر false لوٹاتا ہے۔ close() ضمانت دیتا ہے کہ پہلے سے بھیجے گئے عناصر مستثنیٰ پھینکے جانے سے پہلے وصول کر لیے جائیں گے۔
Conflated Channel ان واقعات کے لیے مفید ہے جہاں صرف آخری حالت اہم ہو — پیش رفت بار، سلائیڈر پوزیشن، ٹچ کوآرڈینیٹ۔ اگر کنزیومر تمام واقعات پر کارروائی نہ کر سکے تو درمیانی واقعات خارج کر دیے جاتے ہیں اور آخری کی ضمانتی طور پر کارروائی کی جاتی ہے۔ Conflated Channel کی capacity=-1 ہے۔
channel.close() کال کریں — چینل بھیجنے کے لیے بند کے طور پر نشان زد ہو جاتا ہے، لیکن پہلے سے بھیجے گئے عناصر receive() کے ذریعے پڑھے جاتے رہتے ہیں۔ for (item in channel) کے ساتھ تکرار بفر خالی ہونے کے بعد خود بخود ختم ہو جاتی ہے۔ isClosedForSend فوری طور پر true لوٹاتا ہے، isClosedForReceive خالی ہونے کے بعد true لوٹاتا ہے۔
ہمیشہ نہیں۔ Flow سرد ہے — ایک اخراج فی collect۔ اگر ایک سٹریم میں لکھنے والے متعدد آزاد پروڈیوسر کی ضرورت ہو تو Channel لازمی ہے۔ دو کوروٹینز کے درمیان سادہ ڈیٹا منتقلی کے لیے Channel استعمال کریں۔ ڈیٹا کے ساتھ رد عمل والے سٹریمز کے لیے Flow استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں