EventBus Android کے لیے ایک لائبریری ہے جو ایونٹ بس کے ذریعے Publisher-Subscriber پیٹرن کو نافذ کرتی ہے، جس سے اجزاء کے درمیان براہ راست انحصار کے بغیر ڈیٹا کا تبادلہ ممکن ہوتا ہے۔ GreenRobot کے ذریعے تیار کردہ، یہ لائبریری Activity، Fragment، Service اور Background Thread کے درمیان مواصلات کو آسان بناتی ہے۔ GitHub ڈیٹا (2025) کے مطابق، EventBus کے 25 ہزار سے زیادہ ستارے ہیں اور یہ ہزاروں Android ایپلیکیشنز میں استعمال ہوتا ہے۔ اہم کارروائیاں subscribe (کسی ایونٹ کی سبسکرائب)، post (ایونٹ بھیجنا) اور sticky event (نئے سبسکرائبرز کے لیے موخر ایونٹ) ہیں۔
اہم نکات
EventBus Android کے لیے ایک ایونٹ بس لائبریری ہے جو Publisher-Subscriber پیٹرن کو نافذ کرتی ہے۔ یہ ایپلیکیشن کے اجزاء (Activity، Fragment، Service، ViewModel) کے درمیان واضح انحصار پیدا کیے بغیر ایونٹ منتقل کرنے کی اجازت دیتی ہے۔ معیاری Android میکانزم (Intent، BroadcastReceiver) کے برعکس، EventBus عمل کے اندر کام کرتا ہے اور IPC استعمال نہیں کرتا۔ لائبریری کارکردگی کے لیے بہتر بنائی گئی ہے اور Subscriber Index کے صحیح طریقے سے ترتیب دینے پر عکاسی استعمال نہیں کرتی۔
EventBus کا فن تعمیر تین اہم عناصر پر مشتمل ہے: Event (ڈیٹا کے ساتھ POJO کلاس)، Subscriber (@Subscribe سے نشان زد طریقوں والا آبجیکٹ) اور EventBus (مرکزی تقسیم کار)。 سبسکرائبر EventBus.getDefault().register(this) کے ذریعے رجسٹر ہوتا ہے اور unregister(this) کے ذریعے رجسٹریشن منسوخ کرتا ہے۔ ایونٹس ٹائپ کیے جاتے ہیں: ہینڈلرز کسی مخصوص ایونٹ کلاس کو سبسکرائب کرتے ہیں اور صرف اس وقت بلائے جاتے ہیں جب اس کلاس یا اس کے ذیلی کلاسز کا کوئی ایونٹ پوسٹ کیا جاتا ہے۔
// POJO ایونٹ
data class MessageEvent(
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
// Activity میں سبسکرائبر
class MainActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: MessageEvent) {
textView.text = event.message
}
}
// دوسرے جزو سے ایونٹ بھیجنا
EventBus.getDefault().post(MessageEvent("Hello from Service"))
پہلے سے طے شدہ طور پر، EventBus register() کے دوران @Subscribe طریقوں کو تلاش کرنے کے لیے عکاسی کا استعمال کرتا ہے۔ Subscriber Index ایک تشریحی پروسیسر کے ذریعے مرتب وقت پر ہینڈلر انڈیکس تیار کرتا ہے۔ یہ عکاسی کے اوور ہیڈ کو ختم کرتا ہے اور رجسٹریشن کو تیز کرتا ہے۔ اسے فعال کرنے کے لیے، build.gradle میں eventbus-annotation-processor شامل کریں۔ EventBus خود بخود انڈیکس استعمال کرتا ہے اگر یہ classpath میں دستیاب ہو۔ انڈیکس کے بغیر، لائبریری پھر بھی کام کرتی ہے لیکن کارکردگی میں معمولی کمی کے ساتھ۔
جب EventBus.getDefault().post(event) کو کال کیا جاتا ہے، لائبریری ایونٹ کی قسم کا تعین کرتی ہے، اس قسم کو قبول کرنے والے @Subscribe طریقوں والے تمام رجسٹرڈ سبسکرائبرز کو تلاش کرتی ہے، اور انہیں مخصوص ThreadMode کے مطابق بلاتی ہے۔ سبسکرائبر کی تلاش رجسٹریشن کے دوران بنائے گئے Class → CopyOnWriteArrayList
ایک سبسکرائبر کو onStart() میں رجسٹر ہونا چاہیے اور onStop() میں رجسٹریشن منسوخ کرنی چاہیے۔ اگر آپ onCreate() میں رجسٹر ہوتے ہیں اور onDestroy() میں منسوخ کرتے ہیں، تو finish() کی وجہ سے onDestroy کو کال کیے بغیر تباہ شدہ Activity سبسکرائبر کی فہرست میں رہ سکتی ہے۔ سبسکرائبر لیک EventBus کے اہم مسائل میں سے ایک ہے: سبسکرائبر کی فہرست میں رہنے والی Activity کو GC اس وقت تک آزاد نہیں کر سکتا جب تک وہ رجسٹریشن منسوخ نہ کرے۔ ہمیشہ register/unregister کو درست لائف سائیکل طریقوں میں جوڑیں۔
@Subscribe تشریح ایک priority پیرامیٹر (عدد صحیح، پہلے سے طے شدہ 0) کو سپورٹ کرتی ہے۔ زیادہ ترجیح والے ہینڈلرز پہلے بلائے جاتے ہیں۔ cancelEventDelivery() باقی سبسکرائبرز کو ایونٹ کی ترسیل میں خلل ڈالنے کی اجازت دیتا ہے۔ یہ ترجیحی ہینڈلرز (لاگنگ، تصدیق) کے لیے مفید ہے جو نیچے دھارے والے سبسکرائبرز کے ذریعے ایونٹ پروسیسنگ کو منسوخ کر سکتے ہیں۔ یہ فنکشن صرف ایونٹ پوسٹنگ تھریڈ میں دستیاب ہے۔
// ترجیح کے ساتھ پیچیدہ مثال
data class NavigationEvent(val screen: String, val data: Bundle)
class NavigationInterceptor {
@Subscribe(priority = 10, threadMode = ThreadMode.POSTING)
fun onNavigationEvent(event: NavigationEvent) {
if (event.screen == "restricted" && !isAuthorized) {
EventBus.getDefault().cancelEventDelivery(event)
}
}
}
class AnalyticsLogger {
@Subscribe(priority = 5)
fun logNavigation(event: NavigationEvent) {
analytics.logScreen(event.screen)
}
}
// ایونٹ بھیجنا
EventBus.getDefault().post(NavigationEvent("profile", bundle))
Android اندرونی عمل مواصلات کے لیے کئی میکانزم پیش کرتا ہے: EventBus، LocalBroadcastManager (متروک) اور LiveData/Flow۔ ہر ایک کے اپنے فوائد اور نقصانات ہیں۔ انتخاب کا انحصار تعمیری نقطہ نظر اور کارکردگی کی ضروریات پر ہے۔ Google کی جدید سفارشات Lifecycle انضمام اور لیک کی عدم موجودگی کی وجہ سے LiveData اور Flow کی طرف جھکتی ہیں۔
| خصوصیت | EventBus | LocalBroadcastManager | LiveData / Flow |
|---|---|---|---|
| ٹائپنگ | ایونٹ کلاس کے ذریعے | Intent فلٹر (String) کے ذریعے | جنیرک قسم کے ذریعے |
| Lifecycle-aware | نہیں (دستی منسوخی) | نہیں (دستی منسوخی) | ہاں (خودکار) |
| Sticky | ہاں (postSticky) | نہیں | ہاں (LiveData ہمیشہ sticky) |
| ThreadMode | MAIN, POSTING, BACKGROUND, ASYNC | صرف main | observe/observeOn کے ذریعے |
| کارکردگی | اعلی (Subscriber Index) | درمیانی (IPC ریپر) | اعلی (مشاہدہ) |
EventBus اہم لیگیسی کوڈ والے منصوبوں میں اور جہاں LiveData/Flow دستیاب نہیں ہیں (صرف Java منصوبے) مفید ہے۔ EventBus کے sticky events LocalBroadcastManager میں غائب لچک فراہم کرتے ہیں۔ EventBus بغیر ViewModel کے Service سے Activity میں ایونٹ بھیجنے کے لیے بھی آسان ہے — خاص طور پر جب آپ کو پس منظر کے کام کی پیشرفت کے بارے میں مطلع کرنے کی ضرورت ہو۔ لائبریری کا حجم کم سے کم (تقریباً 50 KB) ہے اور کوئی انحصار شامل نہیں کرتی۔
LiveData اور Flow Android Jetpack کا حصہ ہیں اور Lifecycle کے ساتھ مربوط ہیں۔ جب کوئی جزو تباہ ہوتا ہے تو وہ خود بخود سبسکرپشن منسوخ کر دیتے ہیں، میموری لیک کو ختم کرتے ہوئے۔ Flow coroutines اور پیچیدہ تبدیلی کے آپریٹرز کو سپورٹ کرتا ہے۔ Google UI پرت کے لیے LiveData اور ذخیروں کے لیے Flow کی سفارش کرتا ہے۔ EventBus کراس ماڈیول ایونٹس کے لیے مفید رہتا ہے جہاں نیویگیشن اور کاروباری منطق MVVM میں فٹ نہیں ہوتی۔
Subscribe @Subscribe تشریح کے ذریعے ایونٹ ہینڈلر کا رجسٹریشن ہے۔ طریقہ public، void ہونا چاہیے اور بالکل ایک پیرامیٹر — ایونٹ کی قسم قبول کرے۔ Post EventBus.getDefault().post(event) کے ذریعے تمام سبسکرائب کردہ ہینڈلرز کو ایونٹ بھیجنا ہے۔ post طریقہ کوئی نتیجہ واپس نہیں کرتا اور یہ نہیں بتاتا کہ کتنے ہینڈلرز کو بلایا گیا۔ جواب والے ایونٹ کے لیے، نتیجہ کے فیلڈ کے ساتھ ایک علیحدہ Event کلاس استعمال کریں۔
ایک ایونٹ کوئی بھی Java/Kotlin کلاس ہے۔ ناقابل تبدیل ایونٹس کے لیے data class اور قابل تبدیل فیلڈز والے ایونٹس کے لیے عام کلاس استعمال کرنے کی سفارش کی جاتی ہے۔ ایونٹ کا نام عمل کی عکاسی کرنا چاہیے: UserLoggedInEvent، DataLoadedEvent، NetworkErrorEvent۔ String قسم کے فیلڈ والی ایک عام Event کلاس سے پرہیز کریں — یہ ٹائپنگ کے فوائد کو ختم کرتا ہے۔ ایونٹ کا درجہ بندی (والدین Event) متعلقہ ایونٹس کے گروپ کو سبسکرائب کرنے کی اجازت دیتا ہے۔
// ایونٹ کا درجہ بندی
open class UserEvent
data class UserLoggedIn(val userId: String) : UserEvent()
data class UserLoggedOut(val reason: String) : UserEvent()
// بنیادی کلاس کو سبسکرائب کرنا
class SessionManager {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onUserEvent(event: UserEvent) {
when (event) {
is UserLoggedIn -> startSession(event.userId)
is UserLoggedOut -> endSession(event.reason)
}
}
}
// بھیجنا
EventBus.getDefault().post(UserLoggedIn("user_123"))
EventBus.getDefault().register(this) کو کال کرنا عکاسی یا Subscriber Index کے ذریعے سبسکرائبر کلاس کو اسکین کرتا ہے اور ملنے والے @Subscribe طریقوں کو ایونٹ کے نقشے میں محفوظ کرتا ہے۔ Unregister سبسکرائبر کو نقشے سے ہٹاتا ہے۔ رجسٹریشن منسوخ کیے بغیر دوبارہ رجسٹر کرنا ایک غلطی ہے (MultipleSubscriberException پھینکے گا)۔ Fragment کے لیے، onStart() میں رجسٹر ہوں اور onStop() میں منسوخ کریں۔ Service کے لیے، onCreate() اور onDestroy() میں۔ ViewModel کے لیے، سفارش نہیں کی جاتی — LiveData استعمال کریں۔
Sticky event ایک ایونٹ ہے جو بھیجنے کے بعد EventBus میں باقی رہتا ہے۔ postSticky() کے بعد رجسٹر ہونے والے نئے سبسکرائبرز فوری طور پر متعلقہ قسم کا آخری sticky event وصول کرتے ہیں۔ یہ ابتدائی حالت منتقل کرنے کے لیے آسان ہے: اسکرین کھولنے پر، یہ اپنی رجسٹریشن سے پہلے بھیجا گیا تازہ ترین ڈیٹا وصول کرتا ہے۔ آپ EventBus.getDefault().removeStickyEvent(Class) کے ذریعے sticky event ہٹا سکتے ہیں۔
ThreadMode اس بات کا تعین کرتا ہے کہ ہینڈلر کس تھریڈ میں عملدرآمد کرتا ہے۔ POSTING (پہلے سے طے شدہ) — ہینڈلر اسی تھریڈ میں چلتا ہے جہاں post کو کال کیا گیا تھا۔ MAIN — ہینڈلر Handler کے ذریعے مرکزی تھریڈ پر چلتا ہے۔ BACKGROUND — ہینڈلر پس منظر کے تھریڈ پر چلتا ہے؛ اگر post کو مرکزی تھریڈ پر کال کیا گیا تھا، EventBus ہینڈلر کو پس منظر کے تھریڈ کی قطار میں رکھتا ہے۔ ASYNC — ہر ہینڈلر تھریڈ پول سے علیحدہ پس منظر کے تھریڈ پر چلتا ہے۔ UI اپ ڈیٹس کے لیے، MAIN استعمال کریں۔
// Sticky Event
data class LocationEvent(val lat: Double, val lng: Double)
// LocationService سے sticky ایونٹ بھیجنا
EventBus.getDefault().postSticky(LocationEvent(55.7558, 37.6173))
// سبسکرائبر رجسٹریشن کے فوراً بعد آخری مقام وصول کرتا ہے
class MapFragment : Fragment() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
// اگر postSticky کال کیا گیا تھا تو فوری طور پر LocationEvent وصول کرے گا
}
override fun onStop() {
EventBus.getDefault().unregister(this)
super.onStop()
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
fun onLocationEvent(event: LocationEvent) {
moveMapTo(event.lat, event.lng)
}
}
// sticky ایونٹ ہٹانا
EventBus.getDefault().removeStickyEvent(LocationEvent::class.java)
BACKGROUND تمام ہینڈلرز کے لیے ایک پس منظر کا تھریڈ استعمال کرتا ہے — وہ ترتیب وار عملدرآمد کرتے ہیں۔ ASYNC ہر ہینڈلر کے لیے پول سے ایک نیا تھریڈ بناتا ہے — وہ متوازی طور پر عملدرآمد کرتے ہیں۔ BACKGROUND مشترکہ ڈیٹا بیس کے ساتھ I/O کارروائیوں کے لیے موزوں ہے۔ ASYNC آزاد طویل کارروائیوں (نیٹ ورک کی درخواستیں) کے لیے ہے۔ دونوں طریقوں کو مشترکہ وسائل تک تھریڈ محفوظ رسائی کی ضرورت ہوتی ہے۔ تھریڈ کی تعداد کو ذہن میں رکھیں: ASYNC پول لامحدود ہے۔
EventBus استعمال کرتے وقت، ڈویلپرز اکثر ایسی غلطیاں کرتے ہیں جو میموری لیک، غیر متوقع کالز اور کارکردگی میں کمی کا باعث بنتی ہیں۔ سب سے اہم: Activity میں رجسٹریشن منسوخ کرنا بھول جانا، onCreate میں رجسٹر ہونا (onStart/onStop کے بجائے)، Object (تمام ایونٹس) کو سبسکرائب کرنا، لامحدود لوپ میں ایونٹ بھیجنا۔ Android Profiler کے ساتھ پروفائلنگ مسائل کی شناخت میں مدد کرتی ہے۔
سب سے عام غلطی onDestroy() میں رجسٹریشن منسوخ کیے بغیر onCreate() میں Activity کو رجسٹر کرنا ہے۔ نتیجہ: EventBus Activity کا حوالہ رکھتا ہے، GC اسے آزاد نہیں کر سکتا۔ اسکرین گھمانے پر، ایک نئی Activity بنتی ہے جبکہ پچھلی میموری میں رہتی ہے۔ حل: ہمیشہ onStart/onStop میں register/unregister کو جوڑیں۔ Fragment کے لیے، اسی پیٹرن کا استعمال کریں۔ اگر finish کے بعد Activity EventBus کے ذریعے رکھی جاتی ہے، Memory Profiler سے چیک کریں۔
Subscriber Index کے بغیر، EventBus ہر register() پر @Subscribe طریقوں کو تلاش کرنے کے لیے عکاسی کا استعمال کرتا ہے۔ Android 6-7 آلات پر، عکاسی سست ہے، جس کی وجہ سے 50 ms تک تاخیر ہوتی ہے۔ Subscriber Index عکاسی کو مکمل طور پر ختم کر دیتا ہے: طریقوں کو تشریحی پروسیسر کے ذریعے مرتب وقت پر ترتیب دیا جاتا ہے۔ 20+ سبسکرائبرز والے منصوبوں کے لیے، انڈیکس لازمی ہے۔ یقینی بنائیں کہ build.gradle میں kapt یا annotationProcessor ترتیب دیا گیا ہے۔
// build.gradle (app) — Subscriber Index شامل کرنا
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// Kotlin کے لیے kapt استعمال کریں
plugins {
id 'kotlin-kapt'
}
dependencies {
implementation 'org.greenrobot:eventbus:3.3.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
// انڈیکس ترتیب (defaultConfig میں)
kapt {
arguments {
arg('eventBusIndex', 'com.app.EventBusIndex')
}
}
Kotlin اور Jetpack Compose استعمال کرنے والے جدید منصوبے kotlinx.coroutines لائبریری سے SharedFlow اور Channel کو ترجیح دیتے ہیں۔ SharedFlow ری پلے (sticky)، بفرنگ اور بیک پریشر کو سپورٹ کرتا ہے۔ Channel ایک بار استعمال ہونے والے ایونٹس (toast، نیویگیشن) کو ہینڈل کرتا ہے۔ دونوں حل repeatOnLifecycle کے ذریعے Lifecycle کے ساتھ مربوط ہیں اور دستی سبسکرپشن منسوخی کی ضرورت نہیں ہے۔ نئے منصوبوں کے لیے، EventBus کے بجائے SharedFlow کی سفارش کی جاتی ہے۔ موجودہ منصوبوں کے لیے، ری فیکٹرنگ کے دوران منتقلی جائز ہے۔
اکثر پوچھے گئے سوالات
EventBus کسی بھی اجزاء (Activity، Fragment، Service) کے درمیان ڈیٹا کے تبادلے کے لیے ایک ایونٹ بس ہے۔ LiveData UI جزو کے ذریعے مشاہدہ کردہ ڈیٹا کے لیے lifecycle-aware ریپر ہے۔ LiveData Lifecycle کے ذریعے خود بخود سبسکرپشن کا انتظام کرتا ہے۔ EventBus کو دستی register/unregister کی ضرورت ہے۔ LiveData UI پرت کے لیے تجویز کیا جاتا ہے، EventBus کراس ماڈیول مواصلات کے لیے جہاں LiveData غیر آسان ہے۔
Sticky event ایک ایونٹ ہے جو بھیجنے کے بعد EventBus میں باقی رہتا ہے۔ postSticky() کے بعد رجسٹر ہونے والے نئے سبسکرائبرز فوری طور پر آخری sticky event وصول کرتے ہیں۔ یہ ابتدائی حالت کے لیے استعمال ہوتا ہے: اسکرین کھولنے پر، یہ نئی درخواست کے بغیر تازہ ترین ڈیٹا وصول کرتا ہے۔ اسے removeStickyEvent() کے ذریعے یا اسی قسم کا نیا sticky event بھیجنے پر ہٹایا جاتا ہے۔
ہاں، EventBus تھریڈ محفوظ ہے۔ post() کو کسی بھی تھریڈ سے کال کیا جا سکتا ہے۔ سبسکرائبرز کو ایونٹ کی ترسیل اندرونی طور پر ہم آہنگ کی جاتی ہے۔ ThreadMode ہینڈلر عملدرآمد کے تھریڈ کا تعین کرتا ہے: MAIN (Handler کے ذریعے مرکزی تھریڈ)، POSTING (کالر تھریڈ)، BACKGROUND (پس منظر کے کام کی قطار)، ASYNC (علیحدہ تھریڈ)۔ UI اپ ڈیٹس کے لیے MAIN، بھاری کارروائیوں کے لیے ASYNC استعمال کریں۔
EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install() کے ذریعے لاگنگ فعال کریں۔ بغیر ہینڈلر کے ایونٹس کو ٹریک کرنے کے لیے NoSubscriberEvent کو سبسکرائب کریں۔ عالمی استثنیٰ ہینڈلنگ کے لیے SubscriberExceptionEvent استعمال کریں۔ Android Profiler لیک تلاش کرنے میں مدد کرتا ہے۔ پیچیدہ منظرناموں کے لیے، ایک ٹیسٹ لکھیں: EventBus.getDefault().register(mock) + post(event) + verify(mock).
نہیں، EventBus (GreenRobot) Android SDK اور JVM سے منسلک ہے۔ Kotlin Multiplatform کے لیے، Kotlin Multiplatform SharedFlow یا KMMBus — مشترکہ کوڈ کو سپورٹ کرنے والی لائبریریاں استعمال کریں۔ EventBus KMM پروجیکٹ کے Android سائیڈ پر کام کرتا ہے لیکن commonMain میں دستیاب نہیں ہے۔ کراس پلیٹ فارم ایونٹس کے لیے، پلیٹ فارم کے مقامی میکانزم کو ترجیح دیں یا expect/actual کے ذریعے تجرید کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں