EventBus: यह क्या है, कार्य सिद्धांत और Android इवेंट बस

लेखक: IT Sectr प्रकाशित: 2026-03-18 पढ़ने का समय: 10 मिनट

EventBus Android के लिए एक लाइब्रेरी है जो इवेंट बस के माध्यम से Publisher-Subscriber पैटर्न लागू करती है, जिससे बिना प्रत्यक्ष निर्भरता के कंपोनेंट्स के बीच डेटा आदान-प्रदान संभव होता है। GreenRobot द्वारा विकसित, यह लाइब्रेरी Activity, Fragment, Service और Background Thread के बीच संचार को सरल बनाती है। GitHub डेटा (2025) के अनुसार, EventBus के 25 हज़ार से अधिक स्टार हैं और इसका उपयोग हज़ारों Android ऐप्लिकेशन में होता है। मुख्य संचालन हैं subscribe (इवेंट की सदस्यता), post (इवेंट भेजना) और sticky event (नए सब्सक्राइबर्स के लिए विलंबित इवेंट)।

मुख्य बिंदु

  • EventBus Android में शिथिल युग्मित संचार के लिए इवेंट बस लाइब्रेरी है।
  • @Subscribe एक एनोटेशन है जो किसी विधि को किसी विशिष्ट इवेंट प्रकार के हैंडलर के रूप में चिह्नित करता है।
  • EventBus.getDefault().post() सभी सब्सक्राइब्ड हैंडलर्स को इवेंट भेजता है।
  • Sticky event नए सब्सक्राइबर्स को डिलीवरी के लिए अंतिम इवेंट को सुरक्षित रखता है।
  • ThreadMode हैंडलर निष्पादन थ्रेड निर्धारित करता है: MAIN, POSTING, BACKGROUND, ASYNC।

EventBus क्या है?

EventBus Android के लिए एक इवेंट बस लाइब्रेरी है जो Publisher-Subscriber पैटर्न लागू करती है। यह ऐप्लिकेशन कंपोनेंट्स (Activity, Fragment, Service, ViewModel) के बीच बिना स्पष्ट निर्भरता बनाए इवेंट पास करने की अनुमति देती है। मानक Android तंत्रों (Intent, BroadcastReceiver) के विपरीत, EventBus प्रक्रिया के भीतर काम करता है और IPC का उपयोग नहीं करता। लाइब्रेरी प्रदर्शन के लिए अनुकूलित है और Subscriber Index सही ढंग से कॉन्फ़िगर होने पर रिफ्लेक्शन का उपयोग नहीं करती।

GreenRobot EventBus: आर्किटेक्चर

EventBus आर्किटेक्चर तीन मुख्य तत्वों से बना है: Event (डेटा के साथ POJO क्लास), Subscriber (@Subscribe से चिह्नित विधियों वाली वस्तु) और EventBus (केंद्रीय डिस्पैचर)। सब्सक्राइबर EventBus.getDefault().register(this) के माध्यम से पंजीकृत होता है और unregister(this) के माध्यम से पंजीकरण रद्द करता है। इवेंट टाइप किए जाते हैं: हैंडलर किसी विशिष्ट इवेंट क्लास की सदस्यता लेते हैं और केवल तभी बुलाए जाते हैं जब उस क्लास या उसके उपवर्गों का इवेंट पोस्ट किया जाता है।

kotlin
// 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"))

प्रदर्शन के लिए Subscriber Index

डिफ़ॉल्ट रूप से, EventBus register() के दौरान @Subscribe विधियों को खोजने के लिए रिफ्लेक्शन का उपयोग करता है। Subscriber Index एनोटेशन प्रोसेसर के माध्यम से संकलन समय पर हैंडलर इंडेक्स उत्पन्न करता है। यह रिफ्लेक्शन ओवरहेड को समाप्त करता है और पंजीकरण को गति देता है। इसे सक्षम करने के लिए, build.gradle में eventbus-annotation-processor जोड़ें। EventBus स्वचालित रूप से इंडेक्स का उपयोग करता है यदि वह classpath में उपलब्ध हो। इंडेक्स के बिना, लाइब्रेरी अभी भी काम करती है लेकिन थोड़ी कमी के साथ।

Android में EventBus कैसे काम करता है?

जब EventBus.getDefault().post(event) कॉल किया जाता है, लाइब्रेरी इवेंट प्रकार निर्धारित करती है, उस प्रकार को स्वीकार करने वाली @Subscribe विधियों वाले सभी पंजीकृत सब्सक्राइबर्स को ढूंढती है, और निर्दिष्ट ThreadMode के अनुसार उन्हें बुलाती है। सब्सक्राइबर खोज पंजीकरण के दौरान निर्मित Class → CopyOnWriteArrayList मैप का उपयोग करके की जाती है। यदि किसी इवेंट का कोई सब्सक्राइबर नहीं है, तो post बिना त्रुटि के पूरा होता है — यह safe-fail व्यवहार है।

पंजीकरण जीवनचक्र

सब्सक्राइबर को onStart() में पंजीकृत होना चाहिए और onStop() में पंजीकरण रद्द करना चाहिए। यदि आप onCreate() में पंजीकृत होते हैं और onDestroy() में पंजीकरण रद्द करते हैं, तो finish() के कारण onDestroy को कॉल किए बिना नष्ट की गई Activity सब्सक्राइबर सूची में रह सकती है। सब्सक्राइबर लीक EventBus की मुख्य समस्याओं में से एक है: सब्सक्राइबर सूची में रहने वाली Activity को जब तक वह पंजीकरण रद्द नहीं करती, GC मुक्त नहीं कर सकता। हमेशा register/unregister को सही जीवनचक्र विधियों में जोड़ें।

हैंडलर प्राथमिकता

@Subscribe एनोटेशन priority पैरामीटर (पूर्णांक, डिफ़ॉल्ट 0) का समर्थन करता है। उच्च प्राथमिकता वाले हैंडलर पहले बुलाए जाते हैं। cancelEventDelivery() शेष सब्सक्राइबर्स को इवेंट डिलीवरी में बाधा डालने की अनुमति देता है। यह प्राथमिकता वाले हैंडलर्स (लॉगिंग, प्रमाणीकरण) के लिए उपयोगी है जो निचले सब्सक्राइबर्स द्वारा इवेंट प्रसंस्करण रद्द कर सकते हैं। यह फ़ंक्शन केवल इवेंट पोस्टिंग थ्रेड में उपलब्ध है।

kotlin
// प्राथमिकता के साथ जटिल उदाहरण
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))

EventBus बनाम LocalBroadcastManager बनाम LiveData

Android इंट्रा-प्रोसेस संचार के लिए कई तंत्र प्रदान करता है: EventBus, LocalBroadcastManager (अप्रचलित) और LiveData/Flow। प्रत्येक के अपने लाभ और हानियाँ हैं। चुनाव आर्किटेक्चरल दृष्टिकोण और प्रदर्शन आवश्यकताओं पर निर्भर करता है। Google की आधुनिक अनुशंसाएँ Lifecycle एकीकरण और लीक की अनुपस्थिति के कारण LiveData और Flow की ओर झुकती हैं।

विशेषताEventBusLocalBroadcastManagerLiveData / Flow
टाइपिंगइवेंट क्लास के माध्यम सेIntent फ़िल्टर (String) के माध्यम सेजेनेरिक प्रकार के माध्यम से
Lifecycle-awareनहीं (मैनुअल अनरजिस्टर)नहीं (मैनुअल अनरजिस्टर)हाँ (स्वचालित)
Stickyहाँ (postSticky)नहींहाँ (LiveData हमेशा sticky)
ThreadModeMAIN, POSTING, BACKGROUND, ASYNCकेवल mainobserve/observeOn के माध्यम से
प्रदर्शनउच्च (Subscriber Index)मध्यम (IPC रैपर)उच्च (अवलोकन)

EventBus कब बेहतर है

EventBus महत्वपूर्ण लीगेसी कोड वाली परियोजनाओं में उपयोगी है और जहाँ LiveData/Flow उपलब्ध नहीं हैं (केवल Java परियोजनाएँ)। EventBus के sticky events LocalBroadcastManager में अनुपलब्ध लचीलापन प्रदान करते हैं। EventBus बिना ViewModel के Service से Activity में इवेंट भेजने के लिए भी आसान है — खासकर जब आपको बैकग्राउंड कार्य प्रगति के बारे में सूचित करने की आवश्यकता हो। लाइब्रेरी का आकार न्यूनतम (लगभग 50 KB) है और यह कोई निर्भरता नहीं जोड़ती।

LiveData/Flow कब बेहतर है

LiveData और Flow Android Jetpack का हिस्सा हैं और Lifecycle के साथ एकीकृत हैं। वे कंपोनेंट नष्ट होने पर स्वचालित रूप से अनसब्सक्राइब हो जाते हैं, मेमोरी लीक को समाप्त करते हुए। Flow कोरूटीन और जटिल रूपांतरण ऑपरेटरों का समर्थन करता है। Google UI परत के लिए LiveData और रिपॉजिटरी के लिए Flow की अनुशंसा करता है। EventBus क्रॉस-मॉड्यूल इवेंट के लिए उपयोगी रहता है जहाँ नेविगेशन और व्यावसायिक तर्क MVVM में फ़िट नहीं होते।

Subscribe और Post: बुनियादी संचालन

Subscribe @Subscribe एनोटेशन के माध्यम से इवेंट हैंडलर का पंजीकरण है। विधि सार्वजनिक, void होनी चाहिए और ठीक एक पैरामीटर — इवेंट प्रकार स्वीकार करनी चाहिए। Post EventBus.getDefault().post(event) के माध्यम से सभी सब्सक्राइब्ड हैंडलर्स को इवेंट भेजना है। Post विधि कोई परिणाम नहीं लौटाती और यह नहीं बताती कि कितने हैंडलर बुलाए गए। प्रतिक्रिया वाले इवेंट के लिए, परिणाम फ़ील्ड के साथ एक अलग Event क्लास का उपयोग करें।

कस्टम इवेंट बनाना

इवेंट कोई भी Java/Kotlin क्लास है। अपरिवर्तनीय इवेंट के लिए data class और परिवर्तनीय फ़ील्ड वाले इवेंट के लिए सामान्य क्लास का उपयोग करने की अनुशंसा की जाती है। इवेंट नामकरण क्रिया को प्रतिबिंबित करना चाहिए: UserLoggedInEvent, DataLoadedEvent, NetworkErrorEvent। String type फ़ील्ड वाली एक सामान्य Event क्लास से बचें — यह टाइपिंग के लाभों को समाप्त करता है। इवेंट पदानुक्रम (पैरेंट Event) संबंधित इवेंट के समूह की सदस्यता लेने की अनुमति देता है।

kotlin
// इवेंट पदानुक्रम
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 Events और ThreadMode

Sticky event एक इवेंट है जो भेजे जाने के बाद EventBus में बना रहता है। postSticky() के बाद पंजीकृत नए सब्सक्राइबर्स तुरंत संबंधित प्रकार का अंतिम sticky event प्राप्त करते हैं। यह प्रारंभिक स्थिति पास करने के लिए सुविधाजनक है: स्क्रीन खोलने पर, यह अपने पंजीकरण से पहले भेजा गया नवीनतम डेटा प्राप्त करता है। आप EventBus.getDefault().removeStickyEvent(Class) के माध्यम से sticky event हटा सकते हैं।

ThreadMode: चार निष्पादन मोड

ThreadMode निर्धारित करता है कि हैंडलर किस थ्रेड में निष्पादित होता है। POSTING (डिफ़ॉल्ट) — हैंडलर उसी थ्रेड में चलता है जहाँ post कॉल किया गया था। MAIN — हैंडलर Handler के माध्यम से मुख्य थ्रेड पर चलता है। BACKGROUND — हैंडलर बैकग्राउंड थ्रेड पर चलता है; यदि post मुख्य थ्रेड पर कॉल किया गया था, तो EventBus हैंडलर को बैकग्राउंड थ्रेड कतार में रखता है। ASYNC — प्रत्येक हैंडलर थ्रेड पूल से एक अलग बैकग्राउंड थ्रेड पर चलता है। UI अपडेट के लिए, MAIN का उपयोग करें।

kotlin
// 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)

ThreadMode.BACKGROUND बनाम ASYNC

BACKGROUND सभी हैंडलर्स के लिए एक बैकग्राउंड थ्रेड का उपयोग करता है — वे क्रमिक रूप से निष्पादित होते हैं। ASYNC प्रत्येक हैंडलर के लिए पूल से एक नया थ्रेड बनाता है — वे समानांतर में निष्पादित होते हैं। BACKGROUND साझा डेटाबेस के साथ I/O संचालन के लिए उपयुक्त है। ASYNC स्वतंत्र लंबे संचालन (नेटवर्क अनुरोध) के लिए है। दोनों मोड में साझा संसाधनों तक थ्रेड-सुरक्षित पहुँच की आवश्यकता होती है। थ्रेड संख्या का ध्यान रखें: ASYNC पूल असीमित है।

सामान्य EventBus त्रुटियाँ और प्रदर्शन

EventBus का उपयोग करते समय, डेवलपर्स अक्सर ऐसी गलतियाँ करते हैं जो मेमोरी लीक, अप्रत्याशित कॉल और प्रदर्शन में गिरावट का कारण बनती हैं। सबसे महत्वपूर्ण: Activity में पंजीकरण रद्द करना भूलना, onCreate में पंजीकरण (onStart/onStop के बजाय), Object (सभी इवेंट) की सदस्यता लेना, अनंत लूप में इवेंट भेजना। Android Profiler के साथ प्रोफाइलिंग समस्याओं की पहचान करने में मदद करता है।

EventBus के माध्यम से मेमोरी लीक

सबसे आम गलती onDestroy() में पंजीकरण रद्द किए बिना onCreate() में Activity पंजीकृत करना है। परिणाम: EventBus Activity का संदर्भ रखता है, GC इसे मुक्त नहीं कर सकता। स्क्रीन घुमाने पर, एक नई Activity बनती है जबकि पिछली मेमोरी में रहती है। समाधान: हमेशा register/unregister को onStart/onStop में जोड़ें। Fragment के लिए, उसी पैटर्न का उपयोग करें। यदि finish के बाद Activity EventBus द्वारा बनाए रखी जाती है, तो Memory Profiler से जाँच करें।

प्रदर्शन: Subscriber Index

Subscriber Index के बिना, EventBus प्रत्येक register() पर @Subscribe विधियों को खोजने के लिए रिफ्लेक्शन का उपयोग करता है। Android 6-7 उपकरणों पर, रिफ्लेक्शन धीमा है, जिससे 50 ms तक की देरी होती है। Subscriber Index रिफ्लेक्शन को पूरी तरह से समाप्त कर देता है: विधियों को एनोटेशन प्रोसेसर के माध्यम से संकलन समय पर अनुक्रमित किया जाता है। 20+ सब्सक्राइबर्स वाली परियोजनाओं के लिए, इंडेक्स अनिवार्य है। सुनिश्चित करें कि build.gradle में kapt या annotationProcessor कॉन्फ़िगर किया गया है।

groovy
// 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')
    }
}

आधुनिक Android में EventBus के विकल्प

Kotlin और Jetpack Compose का उपयोग करने वाली आधुनिक परियोजनाएँ kotlinx.coroutines लाइब्रेरी से SharedFlow और Channel पसंद करती हैं। SharedFlow रिप्ले (sticky), बफरिंग और बैकप्रेशर का समर्थन करता है। Channel एकल-उपयोग इवेंट (toast, नेविगेशन) संभालता है। दोनों समाधान repeatOnLifecycle के माध्यम से Lifecycle के साथ एकीकृत हैं और मैन्युअल अनसब्सक्रिप्शन की आवश्यकता नहीं होती। नई परियोजनाओं के लिए, EventBus के बजाय SharedFlow की अनुशंसा की जाती है। मौजूदा परियोजनाओं के लिए, रिफैक्टरिंग के दौरान माइग्रेशन उचित है।

अक्सर पूछे जाने वाले प्रश्न

EventBus और LiveData में क्या अंतर है?

EventBus किसी भी कंपोनेंट (Activity, Fragment, Service) के बीच डेटा आदान-प्रदान के लिए एक इवेंट बस है। LiveData UI कंपोनेंट द्वारा देखे गए डेटा के लिए lifecycle-aware रैपर है। LiveData Lifecycle के माध्यम से स्वचालित रूप से सदस्यता प्रबंधित करता है। EventBus को मैन्युअल register/unregister की आवश्यकता होती है। LiveData UI परत के लिए अनुशंसित है, EventBus क्रॉस-मॉड्यूल संचार के लिए जहाँ LiveData असुविधाजनक है।

sticky event क्या है?

Sticky event एक इवेंट है जो भेजे जाने के बाद EventBus में बना रहता है। postSticky() के बाद पंजीकृत नए सब्सक्राइबर्स तुरंत अंतिम sticky event प्राप्त करते हैं। इसका उपयोग प्रारंभिक स्थिति के लिए किया जाता है: स्क्रीन खोलने पर, यह बिना नए अनुरोध के नवीनतम डेटा प्राप्त करता है। इसे removeStickyEvent() के माध्यम से या उसी प्रकार का नया sticky event भेजने पर हटाया जाता है।

क्या EventBus थ्रेड-सुरक्षित है?

हाँ, EventBus थ्रेड-सुरक्षित है। post() कॉल किसी भी थ्रेड से संभव है। सब्सक्राइबर्स को इवेंट डिलीवरी आंतरिक रूप से सिंक्रनाइज़ की जाती है। ThreadMode हैंडलर निष्पादन थ्रेड निर्धारित करता है: MAIN (Handler के माध्यम से मुख्य थ्रेड), POSTING (कॉलर थ्रेड), BACKGROUND (बैकग्राउंड कार्य कतार), ASYNC (अलग थ्रेड)। UI अपडेट के लिए MAIN का उपयोग करें। भारी संचालन के लिए ASYNC का उपयोग करें।

EventBus को डीबग कैसे करें?

EventBus.builder().logNoSubscriberMessages(true).sendNoSubscriberEvent(true).install() के माध्यम से लॉगिंग सक्षम करें। बिना हैंडलर वाले इवेंट को ट्रैक करने के लिए NoSubscriberEvent की सदस्यता लें। वैश्विक अपवाद प्रबंधन के लिए SubscriberExceptionEvent का उपयोग करें। Android Profiler लीक खोजने में मदद करता है। जटिल परिदृश्यों के लिए, एक परीक्षण लिखें: EventBus.getDefault().register(mock) + post(event) + verify(mock).

क्या Kotlin Multiplatform में EventBus का उपयोग किया जा सकता है?

नहीं, EventBus (GreenRobot) Android SDK और JVM से बंधा है। Kotlin Multiplatform के लिए, Kotlin Multiplatform SharedFlow या KMMBus का उपयोग करें — साझा कोड का समर्थन करने वाली लाइब्रेरी। EventBus KMM प्रोजेक्ट के Android पक्ष पर काम करता है लेकिन commonMain में उपलब्ध नहीं है। क्रॉस-प्लेटफ़ॉर्म इवेंट के लिए, प्लेटफ़ॉर्म के मूल तंत्र या expect/actual के माध्यम से एब्स्ट्रक्शन पसंद करें।

सारांश

  • EventBus Android के लिए Publisher-Subscriber लाइब्रेरी है जो POJO क्लास के माध्यम से टाइपिंग के साथ इवेंट बस लागू करती है।
  • पैरामीटर threadMode, sticky, priority के साथ @Subscribe एनोटेशन इवेंट हैंडलर व्यवहार को परिभाषित करता है।
  • post() सभी सब्सक्राइबर्स को समकालिक रूप से इवेंट भेजता है; postSticky() नए सब्सक्राइबर्स के लिए इवेंट सुरक्षित रखता है।
  • ThreadMode निष्पादन थ्रेड को नियंत्रित करता है: POSTING (कॉलर थ्रेड), MAIN (UI), BACKGROUND (कतार), ASYNC (पूल)।
  • एनोटेशन प्रोसेसर के माध्यम से Subscriber Index रिफ्लेक्शन को समाप्त करता है और पंजीकरण को गति देता है।
  • मेमोरी लीक Activity या Fragment के onStart/onStop में युग्मित register/unregister द्वारा रोके जाते हैं।
  • नई परियोजनाओं के लिए, kotlinx.coroutines से SharedFlow/Channel बेहतर हैं — वे lifecycle-aware और थ्रेड-सुरक्षित हैं।

हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे

IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।

परियोजना पर चर्चा करें

यह भी पढ़ें