Kotlin कोरूटीन की मदद से ऐप्लिकेशन की परफ़ॉर्मेंस को बेहतर बनाएं

Kotlin कोरूटीन आपको साफ़ और आसान एसिंक्रोनस कोड लिखने में मदद करता है, जो लंबे समय तक चलने वाले टास्क मैनेज करते समय, जैसे कि नेटवर्क कॉल की प्रक्रिया भी हो सकती है.

यह विषय, Android पर कोरूटीन के बारे में ज़्यादा जानकारी देता है. अगर आप: कोरूटीन के बारे में कोई जानकारी नहीं है, तो इस विषय को पढ़ने से पहले, Android पर Kotlin कोरूटीन.

लंबे समय तक चलने वाले टास्क मैनेज करें

दो ऑपरेशन जोड़ने से, नियमित फ़ंक्शन को हैंडल करने के लिए कोरूटीन तैयार होते हैं लंबे समय तक चलने वाले टास्क. invoke (या call) और return के अलावा, कोरूटीन, suspend और resume को जोड़ते हैं:

  • suspend, मौजूदा कोरूटीन के एक्ज़ीक्यूशन को रोक देता है. साथ ही, सभी लोकल कैश मेमोरी में सेव किया जाता है वैरिएबल.
  • resume, यहां से निलंबित कोरूटीन को चलाना जारी रखता है जहां इसे निलंबित कर दिया गया था.

आपके पास सिर्फ़ अन्य suspend फ़ंक्शन से या suspend फ़ंक्शन को कॉल करने का विकल्प है नया कोरूटीन शुरू करने के लिए, launch जैसे कोरूटीन बिल्डर का इस्तेमाल किया जा सकता है.

नीचे दिए गए उदाहरण में, एक सामान्य कोरूटीन को लागू करने के बारे में बताया गया है. लंबे समय तक चलने वाला काल्पनिक टास्क:

suspend fun fetchDocs() {                             // Dispatchers.Main
    val result = get("https://developer.android.com") // Dispatchers.IO for `get`
    show(result)                                      // Dispatchers.Main
}

suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }

इस उदाहरण में, get() अब भी मुख्य थ्रेड पर चलता है, लेकिन यह कोरूटीन शुरू करें. नेटवर्क का अनुरोध करने पर पूरा हो जाता है, तो get कॉलबैक का इस्तेमाल करने के बजाय, निलंबित कोरूटीन को फिर से चालू करता है मुख्य थ्रेड को सूचना दें.

Kotlin में स्टैक फ़्रेम का इस्तेमाल करके यह मैनेज किया जाता है कि कौनसा फ़ंक्शन साथ में चल रहा है का इस्तेमाल करें. कोरूटीन को निलंबित करते समय, मौजूदा स्टैक को कॉपी करके बाद के लिए सेव किया जाता है. फिर से शुरू करते समय, स्टैक फ़्रेम जहां से इसे सेव किया गया था वहीं से कॉपी करने पर, फ़ंक्शन फिर से काम करना शुरू कर देता है. भले ही, कोड किसी सामान्य क्रम में चलने वाले ब्लॉकिंग कोड जैसा दिख सकता है अनुरोध है, तो कोरूटीन यह पक्का करता है कि नेटवर्क अनुरोध, मुख्य थ्रेड.

मुख्य तौर पर सुरक्षा के लिए कोरूटीन इस्तेमाल करना

Kotlin कोरूटीन, डिस्पैचर का इस्तेमाल करके यह तय करते हैं कि कौनसे थ्रेड का इस्तेमाल किया जाए कोरूटीन एक्ज़ीक्यूट होने की सुविधा मौजूद है. कोड को मुख्य थ्रेड के बाहर चलाने के लिए, Kotlin को निर्देश दें कोरूटीन, डिफ़ॉल्ट या IO डिसपैचर पर काम करते हैं. तय सीमा में Kotlin, सभी कोरूटीन, डिस्पैचर में चलने चाहिए. भले ही, वे इस पर चल रहे हों मुख्य थ्रेड. कोरूटीन खुद को निलंबित कर सकते हैं और डिस्पैचर, उन्हें फिर से चलाने की ज़िम्मेदारी भी

यह तय करने के लिए कि कोरूटीन कहां चलने चाहिए, Kotlin तीन डिस्पैचर की सुविधा देती है इन तरीकों का इस्तेमाल किया जा सकता है:

  • Dispatchers.Main - इस डिसपैचर का इस्तेमाल मुख्य Android थ्रेड. इसका इस्तेमाल सिर्फ़ यूज़र इंटरफ़ेस (यूआई) के साथ इंटरैक्ट करने के लिए किया जाना चाहिए और तेज़ी से काम करने में मदद मिलती है. उदाहरण के लिए, suspend फ़ंक्शन को कॉल करना और रन करना Android के यूज़र इंटरफ़ेस (यूआई) फ़्रेमवर्क से जुड़ी कार्रवाइयां और अपडेट करना LiveData ऑब्जेक्ट.
  • Dispatchers.IO - इस डिस्पैचर को डिस्क या नेटवर्क चलाने के लिए ऑप्टिमाइज़ किया गया मुख्य थ्रेड के बाहर I/O. उदाहरण के लिए, रूम कॉम्पोनेंट, फ़ाइलों को पढ़ना या उनमें लिखना, और नेटवर्क से जुड़ी कार्रवाइयां करना.
  • Dispatchers.Default - इस डिस्पैचर को कार्रवाई करने के लिए ऑप्टिमाइज़ किया गया है मुख्य थ्रेड के बाहर काम करने के लिए, सीपीयू की ज़रूरत नहीं होती. इस्तेमाल के उदाहरणों में, JSON को पार्स करने और सूची में शामिल करने के बारे में जानकारी.

पिछले उदाहरण में, डिस्पैचर का इस्तेमाल करके get फ़ंक्शन का इस्तेमाल करना होगा. get के मुख्य भाग के अंदर, withContext(Dispatchers.IO) को कॉल करें IO थ्रेड पूल पर चलने वाला ब्लॉक बनाना. कोई भी कोड, जिसे आपने ब्लॉक हमेशा IO डिस्पैचर के ज़रिए काम करता है. withContext खुद एक सस्पेंड फ़ंक्शन, फ़ंक्शन get एक सस्पेंड फ़ंक्शन भी है.

suspend fun fetchDocs() {                      // Dispatchers.Main
    val result = get("developer.android.com")  // Dispatchers.Main
    show(result)                               // Dispatchers.Main
}

suspend fun get(url: String) =                 // Dispatchers.Main
    withContext(Dispatchers.IO) {              // Dispatchers.IO (main-safety block)
        /* perform network IO here */          // Dispatchers.IO (main-safety block)
    }                                          // Dispatchers.Main
}

कोरूटीन की मदद से, ज़्यादा बारीकी से कंट्रोल करके थ्रेड भेजे जा सकते हैं. क्योंकि withContext() की मदद से, कोड की किसी भी लाइन के थ्रेड पूल को कंट्रोल किया जा सकता है कॉलबैक की सुविधा का इस्तेमाल करके, इसे पढ़ने जैसे छोटे फ़ंक्शन पर लागू किया जा सकता है किसी डेटाबेस से या नेटवर्क के लिए अनुरोध करके. इस्तेमाल करने का बेहतर तरीका withContext() ताकि यह पक्का किया जा सके कि हर फ़ंक्शन मुख्य-सुरक्षित है, जिसका मतलब है कि आप मुख्य थ्रेड से फ़ंक्शन को कॉल कर सकते हैं. इस तरह से, कॉलर को सोचें कि फ़ंक्शन को चलाने के लिए किस थ्रेड का इस्तेमाल करना चाहिए.

पिछले उदाहरण में, fetchDocs() मुख्य थ्रेड पर लागू होता है; हालांकि, यह get को सुरक्षित रूप से कॉल कर सकता है, जो बैकग्राउंड में नेटवर्क अनुरोध करता है. ऐसा इसलिए है, क्योंकि कोरूटीन suspend और resume के साथ काम करते हैं. इसलिए, कोरूटीन मुख्य पेज पर withContext ब्लॉक के होते ही, थ्रेड को get नतीजे के साथ फिर से शुरू किया जाता है हो गया.

withContext() की परफ़ॉर्मेंस

withContext() कॉलबैक-आधारित मिलती-जुलती वैल्यू की तुलना में, ज़्यादा ओवरहेड नहीं जोड़ता है लागू करना. इसके अलावा, withContext() कॉल को ऑप्टिमाइज़ किया जा सकता है लागू करने के बजाय कॉलबैक पर आधारित होता है. इसके लिए उदाहरण के लिए, अगर कोई फ़ंक्शन किसी नेटवर्क को दस कॉल करता है, तो Kotlin को आउटर withContext() का इस्तेमाल करके, सिर्फ़ एक बार थ्रेड स्विच करें. इसके बाद, भले ही नेटवर्क लाइब्रेरी, withContext() का इस्तेमाल कई बार करती है, लेकिन यह उसी पर रहती है और थ्रेड के बीच स्विच करने से बचा जाता है. इसके अलावा, Kotlin में स्विच करने की सुविधा को ऑप्टिमाइज़ किया जाता है थ्रेड स्विच होने से रोकने के लिए, Dispatchers.Default से Dispatchers.IO के बीच मदद मिलती है.

कोरूटीन शुरू करना

कोरूटीन शुरू करने के लिए, इन दो में से किसी एक तरीके का इस्तेमाल किया जा सकता है:

  • launch एक नया कोरूटीन शुरू करता है और कॉलर को नतीजा नहीं दिखाता है. कोई भी ऐसा काम जिसे "फ़ायर ऐंड फ़ाॅरगेट" माना जाता है launch का इस्तेमाल करके शुरू किया जा सकता है.
  • async एक नया कोरूटीन शुरू करता है और आपको निलंबन के साथ नतीजा वापस करने की अनुमति देता है await नाम का फ़ंक्शन.

आम तौर पर, आपको किसी सामान्य फ़ंक्शन से, एक नए कोरूटीन को launch करना चाहिए. क्योंकि एक सामान्य फ़ंक्शन await को कॉल नहीं कर सकता. घर के अंदर होने पर ही async का इस्तेमाल करें किसी अन्य कोरूटीन या सस्पेंड फ़ंक्शन में होने पर, समानांतर अपघटन.

पैरलल डिकम्पोज़िशन

suspend फ़ंक्शन में शुरू होने वाले सभी कोरूटीन तब बंद होने चाहिए, जब उस फ़ंक्शन का इस्तेमाल करके वैल्यू कैलकुलेट की जाती है. इसलिए, आपको यह गारंटी देनी होगी कि ये कोरूटीन वापस आने से पहले पूरा कर लें. Kotlin में स्ट्रक्चर्ड कॉनएंसी की मदद से, एक coroutineScope जो एक या एक से ज़्यादा कोरूटीन शुरू करता है. इसके बाद, await() का इस्तेमाल करके (एक कोरूटीन के लिए) या awaitAll() (एक से ज़्यादा कोरूटीन के लिए), आपके पास ये काम करने का विकल्प होता है गारंटी दें कि ये कोरूटीन, फ़ंक्शन से वापस आने से पहले खत्म हो जाते हैं.

उदाहरण के लिए, ऐसे coroutineScope का इस्तेमाल करें जो दो दस्तावेज़ फ़ेच करता है एसिंक्रोनस रूप से. हर स्थगित रेफ़रंस पर await() को कॉल करने पर, हम गारंटी देते हैं कि दोनों async कार्रवाइयां, कोई वैल्यू देने से पहले पूरी हो जाती हैं:

suspend fun fetchTwoDocs() =
    coroutineScope {
        val deferredOne = async { fetchDoc(1) }
        val deferredTwo = async { fetchDoc(2) }
        deferredOne.await()
        deferredTwo.await()
    }

आपके पास कलेक्शन के लिए भी awaitAll() का इस्तेमाल करने का विकल्प है, जैसा कि इस उदाहरण में दिखाया गया है:

suspend fun fetchTwoDocs() =        // called on any Dispatcher (any thread, possibly Main)
    coroutineScope {
        val deferreds = listOf(     // fetch two docs at the same time
            async { fetchDoc(1) },  // async returns a result for the first doc
            async { fetchDoc(2) }   // async returns a result for the second doc
        )
        deferreds.awaitAll()        // use awaitAll to wait for both network requests
    }

भले ही fetchTwoDocs(), async के साथ नए कोरूटीन लॉन्च करता हो, लेकिन फ़ंक्शन लॉन्च किए गए कोरूटीन के खत्म होने से पहले इंतज़ार करने के लिए, awaitAll() का इस्तेमाल करता है वापस आ रहा है. हालांकि, ध्यान दें कि भले ही हमने awaitAll() को कॉल न किया हो, लेकिन coroutineScope बिल्डर, कॉल करने वाले कोरूटीन को फिर से शुरू नहीं करता है सभी नए कोरूटीन पूरे होने तक fetchTwoDocs.

इसके अलावा, coroutineScope उन अपवादों को कैच कर लेता है जिन्हें कोरूटीन फेंकते हैं और उन्हें वापस कॉलर के पास भेज देता है.

समानांतर अपघटन के बारे में ज़्यादा जानकारी के लिए देखें सस्पेंड करने के फ़ंक्शन को लिखना.

कोरूटीन कॉन्सेप्ट

CoroutineScope

CoroutineScope launch या async का इस्तेमाल करके बनाए गए किसी भी कोरूटीन को ट्रैक करता है. कॉन्टेंट बनाने चल रहे काम (जैसे, चल रहे कोरूटीन) को कॉल करके रद्द किया जा सकता है किसी भी समय scope.cancel(). Android में, कुछ KTX लाइब्रेरी कुछ लाइफ़साइकल क्लास के लिए, अपना CoroutineScope. उदाहरण के लिए, ViewModel ने viewModelScope, और Lifecycle में lifecycleScope है. हालांकि, डिसपैचर के उलट, CoroutineScope कोरूटीन नहीं चलता.

viewModelScope का इस्तेमाल, यहां दिए गए उदाहरणों में भी किया गया है Android पर कोरूटीन की सुविधा के साथ बैकग्राउंड थ्रेडिंग. हालांकि, अगर आपको मैन्युअल तरीके से कस्टमाइज़ करने के लिए अपना CoroutineScope बनाना हो आपके ऐप्लिकेशन की किसी खास लेयर में कोरूटीन का लाइफ़साइकल, आपके पास एक बनाने का विकल्प है इस तरह से:

class ExampleClass {

    // Job and Dispatcher are combined into a CoroutineContext which
    // will be discussed shortly
    val scope = CoroutineScope(Job() + Dispatchers.Main)

    fun exampleMethod() {
        // Starts a new coroutine within the scope
        scope.launch {
            // New coroutine that can call suspend functions
            fetchDocs()
        }
    }

    fun cleanUp() {
        // Cancel the scope to cancel ongoing coroutines work
        scope.cancel()
    }
}

रद्द किए गए स्कोप से ज़्यादा कोरूटीन नहीं बन सकते. इसलिए, आपको scope.cancel() को सिर्फ़ तब कॉल करें, जब कोई ऐसी क्लास हो जो इसकी लाइफ़साइकल को कंट्रोल करती है को खत्म किया जा रहा है. viewModelScope का इस्तेमाल करते समय, ViewModel क्लास रद्द कर देती है आपके लिए ViewModel के onCleared() तरीके में अपने-आप स्कोप दिखता है.

काम

Job कोरूटीन के लिए हैंडल है. launch की मदद से बनाया गया हर कोरूटीन या async, Job इंस्टेंस की वैल्यू दिखाता है, जो कोरूटीन है और वह अपनी लाइफ़साइकल को मैनेज करता है. आप Job को CoroutineScope, इसकी लाइफ़साइकल को और मैनेज करता है, जैसा कि यहां दिखाया गया है उदाहरण:

class ExampleClass {
    ...
    fun exampleMethod() {
        // Handle to the coroutine, you can control its lifecycle
        val job = scope.launch {
            // New coroutine
        }

        if (...) {
            // Cancel the coroutine started above, this doesn't affect the scope
            // this coroutine was launched in
            job.cancel()
        }
    }
}

कोरूटीन कॉन्टेक्स्ट

CoroutineContext एलिमेंट के इन सेट का इस्तेमाल करके, कोरूटीन के व्यवहार को तय करता है:

  • Job: कोरूटीन की लाइफ़साइकल को कंट्रोल करता है.
  • CoroutineDispatcher: डिस्पैच सही थ्रेड पर काम करते हैं.
  • CoroutineName: कोरूटीन का नाम. इससे डीबग करने में मदद मिलती है.
  • CoroutineExceptionHandler: ऐसे अपवादों को हैंडल करता है जिनकी पहचान नहीं हो पाई है.

किसी स्कोप में बनाए गए नए कोरूटीन के लिए, नया Job इंस्टेंस यह है नए कोरूटीन और दूसरे CoroutineContext एलिमेंट को असाइन किया जाता है इन्हें मौजूदा स्कोप से इनहेरिट किया जाता है. इनहेरिट की गई सेटिंग को बदला जा सकता है launch या async में नया CoroutineContext पास करके एलिमेंट फ़ंक्शन का इस्तेमाल करना होगा. ध्यान दें कि Job को launch या async पर पास करने से कोई असर नहीं पड़ता, क्योंकि Job का नया इंस्टेंस, हमेशा एक नए कोरूटीन को असाइन किया जाता है.

class ExampleClass {
    val scope = CoroutineScope(Job() + Dispatchers.Main)

    fun exampleMethod() {
        // Starts a new coroutine on Dispatchers.Main as it's the scope's default
        val job1 = scope.launch {
            // New coroutine with CoroutineName = "coroutine" (default)
        }

        // Starts a new coroutine on Dispatchers.Default
        val job2 = scope.launch(Dispatchers.Default + CoroutineName("BackgroundCoroutine")) {
            // New coroutine with CoroutineName = "BackgroundCoroutine" (overridden)
        }
    }
}

कोरूटीन के अतिरिक्त संसाधन

कोरूटीन से जुड़े और संसाधनों के लिए, ये लिंक देखें: