Android Kotlin Fundamentals 06.2: Coroutines and Room

यह कोडलैब, Android Kotlin Fundamentals कोर्स का हिस्सा है. अगर कोडलैब को क्रम से पूरा किया जाता है, तो आपको इस कोर्स से सबसे ज़्यादा फ़ायदा मिलेगा. कोर्स के सभी कोडलैब, Android Kotlin Fundamentals कोडलैब के लैंडिंग पेज पर दिए गए हैं.

परिचय

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

इस कोडलैब में, TrackMySleepQuality ऐप्लिकेशन का वह हिस्सा लागू किया गया है जो उपयोगकर्ता को दिखता है. इसमें मुख्य थ्रेड से अलग डेटाबेस कार्रवाइयां करने के लिए, Kotlin को-रूटीन का इस्तेमाल किया गया है.

आपको पहले से क्या पता होना चाहिए

आपको इनके बारे में जानकारी होनी चाहिए:

  • ऐक्टिविटी, फ़्रैगमेंट, व्यू, और क्लिक हैंडलर का इस्तेमाल करके, बुनियादी यूज़र इंटरफ़ेस (यूआई) बनाना.
  • फ़्रैगमेंट के बीच नेविगेट करना और safeArgs का इस्तेमाल करके, फ़्रैगमेंट के बीच सामान्य डेटा पास करना.
  • मॉडल, मॉडल फ़ैक्ट्रियां, बदलाव, और LiveData देखें.
  • Room डेटाबेस बनाने, DAO बनाने, और इकाइयों को तय करने का तरीका.
  • अगर आपको थ्रेडिंग और मल्टीप्रोसेसिंग के कॉन्सेप्ट के बारे में जानकारी है, तो यह आपके लिए मददगार होगा.

आपको क्या सीखने को मिलेगा

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

आपको क्या करना होगा

  • TrackMySleepQuality ऐप्लिकेशन को इस तरह से बढ़ाओ कि वह डेटाबेस में डेटा इकट्ठा कर सके, उसे सेव कर सके, और उसे दिखा सके.
  • बैकग्राउंड में डेटाबेस से जुड़ी लंबी प्रोसेस चलाने के लिए, कोरूटीन का इस्तेमाल करें.
  • नेविगेशन और स्नैकबार को दिखाने के लिए, LiveData का इस्तेमाल करें.
  • बटन चालू और बंद करने के लिए, LiveData का इस्तेमाल करें.

इस कोडलैब में, TrackMySleepQuality ऐप्लिकेशन के व्यू मॉडल, कोरूटीन, और डेटा-डिस्प्ले वाले हिस्से बनाए जाते हैं.

ऐप्लिकेशन में दो स्क्रीन हैं, जिन्हें फ़्रैगमेंट के तौर पर दिखाया गया है. इनके बारे में यहां दी गई इमेज में बताया गया है.

बाईं ओर दिखाई गई पहली स्क्रीन में, ट्रैकिंग शुरू और बंद करने के बटन हैं. स्क्रीन पर, उपयोगकर्ता का नींद से जुड़ा सारा डेटा दिखता है. मिटाएं बटन दबाने पर, ऐप्लिकेशन ने उपयोगकर्ता के लिए जो भी डेटा इकट्ठा किया है वह हमेशा के लिए मिट जाता है.

दाईं ओर दिखाई गई दूसरी स्क्रीन, नींद की क्वालिटी की रेटिंग चुनने के लिए है. ऐप्लिकेशन में, रेटिंग को संख्या के तौर पर दिखाया जाता है. डेवलपमेंट के लिए, ऐप्लिकेशन में चेहरे के आइकॉन और उनके संख्यात्मक मान, दोनों दिखाए जाते हैं.

उपयोगकर्ता का फ़्लो इस तरह होता है:

  • उपयोगकर्ता ऐप्लिकेशन खोलता है और उसे नींद को ट्रैक करने वाली स्क्रीन दिखती है.
  • उपयोगकर्ता, शुरू करें बटन पर टैप करता है. यह कुकी, वीडियो शुरू होने का समय रिकॉर्ड करती है और उसे दिखाती है. शुरू करें बटन बंद हो जाता है और बंद करें बटन चालू हो जाता है.
  • उपयोगकर्ता बंद करें बटन पर टैप करता है. यह नींद के खत्म होने का समय रिकॉर्ड करता है और नींद की क्वालिटी वाली स्क्रीन खोलता है.
  • उपयोगकर्ता, नींद की क्वालिटी का आइकॉन चुनता है. स्क्रीन बंद हो जाती है. इसके बाद, ट्रैकिंग स्क्रीन पर नींद खत्म होने का समय और नींद की क्वालिटी दिखती है. बंद करें बटन बंद है और शुरू करें बटन चालू है. यह ऐप्लिकेशन, अगली रात के लिए तैयार है.
  • डेटाबेस में डेटा मौजूद होने पर, हटाएं बटन चालू हो जाता है. जब उपयोगकर्ता मिटाएं बटन पर टैप करता है, तो उसका पूरा डेटा मिट जाता है. इसके लिए, उससे कोई पुष्टि नहीं की जाती. जैसे, "क्या आपको पक्का मिटाना है?" मैसेज नहीं दिखता.

यह ऐप्लिकेशन, आसान आर्किटेक्चर का इस्तेमाल करता है. यहां पूरे आर्किटेक्चर के कॉन्टेक्स्ट में इसे दिखाया गया है. ऐप्लिकेशन सिर्फ़ इन कॉम्पोनेंट का इस्तेमाल करता है:

  • यूज़र इंटरफ़ेस (यूआई) कंट्रोलर
  • मॉडल और LiveData देखें
  • रूम डेटाबेस

इस टास्क में, फ़ॉर्मैट किए गए नींद के डेटा को दिखाने के लिए, TextView का इस्तेमाल किया जाता है. (यह फ़ाइनल इंटरफ़ेस नहीं है. आपको दूसरे कोडलैब में बेहतर तरीका बताया जाएगा.)

पिछले कोडलैब में बनाए गए TrackMySleepQuality ऐप्लिकेशन का इस्तेमाल जारी रखा जा सकता है. इसके अलावा, इस कोडलैब के लिए स्टार्टर ऐप्लिकेशन डाउनलोड किया जा सकता है.

पहला चरण: स्टार्टर ऐप्लिकेशन डाउनलोड और रन करना

  1. GitHub से TrackMySleepQuality-Coroutines-Starter ऐप्लिकेशन डाउनलोड करें.
  2. ऐप्लिकेशन बनाएं और उसे चलाएं. ऐप्लिकेशन में SleepTrackerFragment फ़्रैगमेंट के लिए यूज़र इंटरफ़ेस (यूआई) दिखता है, लेकिन कोई डेटा नहीं दिखता. बटन टैप करने पर काम नहीं करते.

दूसरा चरण: कोड की जांच करना

इस कोडलैब के लिए स्टार्टर कोड, 6.1 Create a Room database कोडलैब के सलूशन कोड के जैसा ही है.

  1. res/layout/activity_main.xml खोलें. इस लेआउट में nav_host_fragment फ़्रैगमेंट शामिल है. साथ ही, <merge> टैग पर भी ध्यान दें.

    लेआउट शामिल करते समय, merge टैग का इस्तेमाल करके एक जैसे लेआउट को हटाया जा सकता है. इसलिए, इसका इस्तेमाल करना बेहतर होता है. ज़्यादा लेआउट का एक उदाहरण ConstraintLayout > LinearLayout > TextView होगा. इसमें सिस्टम, LinearLayout को हटा सकता है. इस तरह के ऑप्टिमाइज़ेशन से, व्यू हैरारकी को आसान बनाया जा सकता है और ऐप्लिकेशन की परफ़ॉर्मेंस को बेहतर किया जा सकता है.
  2. navigation फ़ोल्डर में, navigation.xml खोलें. आपको दो फ़्रैगमेंट और उन्हें जोड़ने वाली नेविगेशन कार्रवाइयां दिख सकती हैं.
  3. इसके एक्सएमएल लेआउट को देखने के लिए, layout फ़ोल्डर में जाकर, स्लीप ट्रैकर फ़्रैगमेंट पर दो बार क्लिक करें. इन बातों पर ध्यान दें:
  • लेआउट डेटा को <layout> एलिमेंट में रैप किया जाता है, ताकि डेटा बाइंडिंग की जा सके.
  • ConstraintLayout और अन्य व्यू को <layout> एलिमेंट के अंदर व्यवस्थित किया जाता है.
  • फ़ाइल में <data> टैग का प्लेसहोल्डर मौजूद है.

स्टार्टर ऐप्लिकेशन, यूज़र इंटरफ़ेस (यूआई) के लिए डाइमेंशन, रंग, और स्टाइल भी उपलब्ध कराता है. ऐप्लिकेशन में Room डेटाबेस, DAO, और SleepNight इकाई शामिल है. अगर आपने पिछला कोडलैब पूरा नहीं किया है, तो पक्का करें कि आपने कोड के इन पहलुओं को खुद एक्सप्लोर किया हो.

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

पहला चरण: SleepTrackerViewModel जोड़ना

  1. sleeptracker पैकेज में, SleepTrackerViewModel.kt खोलें.
  2. SleepTrackerViewModel क्लास की जांच करें. यह क्लास, आपको स्टार्टर ऐप्लिकेशन में दी गई है. इसे यहां भी दिखाया गया है. ध्यान दें कि क्लास AndroidViewModel() तक फैली हुई है. यह क्लास, ViewModel जैसी ही होती है. हालांकि, यह ऐप्लिकेशन के कॉन्टेक्स्ट को पैरामीटर के तौर पर लेती है और इसे प्रॉपर्टी के तौर पर उपलब्ध कराती है. आपको इसकी बाद में ज़रूरत होगी.
class SleepTrackerViewModel(
       val database: SleepDatabaseDao,
       application: Application) : AndroidViewModel(application) {
}

दूसरा चरण: SleepTrackerViewModelFactory जोड़ना

  1. sleeptracker पैकेज में, SleepTrackerViewModelFactory.kt खोलें.
  2. फ़ैक्ट्री के लिए दिए गए कोड की जांच करें. यह कोड यहां दिखाया गया है:
class SleepTrackerViewModelFactory(
       private val dataSource: SleepDatabaseDao,
       private val application: Application) : ViewModelProvider.Factory {
   @Suppress("unchecked_cast")
   override fun <T : ViewModel?> create(modelClass: Class<T>): T {
       if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
           return SleepTrackerViewModel(dataSource, application) as T
       }
       throw IllegalArgumentException("Unknown ViewModel class")
   }
}

इन बातों का ध्यान रखें:

  • दिए गए SleepTrackerViewModelFactory में वही तर्क इस्तेमाल किया जाता है जो ViewModel में इस्तेमाल किया जाता है. साथ ही, यह ViewModelProvider.Factory को बढ़ाता है.
  • फ़ैक्ट्री के अंदर, कोड create() को बदल देता है. यह किसी भी क्लास टाइप को आर्ग्युमेंट के तौर पर लेता है और ViewModel दिखाता है.
  • create() के मुख्य हिस्से में, कोड यह जांच करता है कि SleepTrackerViewModel क्लास उपलब्ध है या नहीं. अगर उपलब्ध है, तो उसका एक इंस्टेंस दिखाता है. ऐसा न होने पर, कोड एक अपवाद दिखाता है.

तीसरा चरण: SleepTrackerFragment
को अपडेट करना

  1. SleepTrackerFragment में, ऐप्लिकेशन के कॉन्टेक्स्ट का रेफ़रंस पाएं. binding के नीचे onCreateView() में रेफ़रंस डालें. आपको उस ऐप्लिकेशन का रेफ़रंस चाहिए जिससे यह फ़्रैगमेंट अटैच है, ताकि इसे व्यू-मॉडल फ़ैक्ट्री प्रोवाइडर में पास किया जा सके.

    अगर value null है, तो requireNotNull Kotlin फ़ंक्शन एक IllegalArgumentException थ्रो करता है.
val application = requireNotNull(this.activity).application
  1. आपको डीएओ के रेफ़रंस के ज़रिए, अपने डेटा सोर्स का रेफ़रंस चाहिए. onCreateView() में, return से पहले dataSource तय करें. डेटाबेस के डीएओ का रेफ़रंस पाने के लिए, SleepDatabase.getInstance(application).sleepDatabaseDao का इस्तेमाल करें.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
  1. onCreateView() में, return से पहले, viewModelFactory का एक इंस्टेंस बनाएं. आपको dataSource और application को पास करना होगा.
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
  1. अब आपके पास फ़ैक्ट्री है. इसलिए, SleepTrackerViewModel का रेफ़रंस पाएं. SleepTrackerViewModel::class.java पैरामीटर, इस ऑब्जेक्ट के रनटाइम Java क्लास को दिखाता है.
val sleepTrackerViewModel =
       ViewModelProviders.of(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)
  1. आपका पूरा कोड ऐसा दिखना चाहिए:
// Create an instance of the ViewModel Factory.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)

// Get a reference to the ViewModel associated with this fragment.
val sleepTrackerViewModel =
       ViewModelProviders.of(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)

अब तक onCreateView() का इस्तेमाल इस तरह किया गया है:

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        // Get a reference to the binding object and inflate the fragment views.
        val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_sleep_tracker, container, false)

        val application = requireNotNull(this.activity).application

        val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao

        val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)

        val sleepTrackerViewModel =
                ViewModelProviders.of(
                        this, viewModelFactory).get(SleepTrackerViewModel::class.java)

        return binding.root
    }

चौथा चरण: व्यू मॉडल के लिए डेटा बाइंडिंग जोड़ना

बुनियादी ViewModel को लागू करने के बाद, आपको SleepTrackerFragment में डेटा बाइंडिंग सेट अप करनी होगी. इससे ViewModel को यूज़र इंटरफ़ेस (यूआई) से कनेक्ट किया जा सकेगा.


fragment_sleep_tracker.xml लेआउट फ़ाइल में:

  1. <data> ब्लॉक में, एक <variable> बनाएं जो SleepTrackerViewModel क्लास को रेफ़रंस करता हो.
<data>
   <variable
       name="sleepTrackerViewModel"
       type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>

SleepTrackerFragment में:

  1. मौजूदा ऐक्टिविटी को बाइंडिंग के लाइफ़साइकल के मालिक के तौर पर सेट करें. इस कोड को onCreateView() तरीके के अंदर, return स्टेटमेंट से पहले जोड़ें:
binding.setLifecycleOwner(this)
  1. sleepTrackerViewModel बाइंडिंग वैरिएबल को sleepTrackerViewModel पर असाइन करें. इस कोड को onCreateView() में, SleepTrackerViewModel बनाने वाले कोड के नीचे डालें:
binding.sleepTrackerViewModel = sleepTrackerViewModel
  1. आपको शायद कोई गड़बड़ी दिखेगी, क्योंकि आपको बाइंडिंग ऑब्जेक्ट को फिर से बनाना होगा. गड़बड़ी को ठीक करने के लिए, प्रोजेक्ट को क्लीन करें और फिर से बनाएं.
  2. आखिर में, हमेशा की तरह यह पक्का करें कि आपका कोड बिना किसी गड़बड़ी के काम कर रहा हो.

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

कोरूटीन में ये प्रॉपर्टी होती हैं:

  • कोरूटीन एसिंक्रोनस और नॉन-ब्लॉकिंग होते हैं.
  • कोरूटीन, एसिंक्रोनस कोड को सीक्वेंशियल बनाने के लिए suspend फ़ंक्शन का इस्तेमाल करते हैं.

कोरूटीन एसिंक्रोनस होते हैं.

कोरूटीन, आपके प्रोग्राम के मुख्य एक्ज़ीक्यूशन चरणों से अलग तौर पर काम करता है. यह काम, किसी अलग प्रोसेसर पर या साथ-साथ किया जा सकता है. ऐसा भी हो सकता है कि ऐप्लिकेशन के बाकी हिस्से के इनपुट का इंतज़ार करने के दौरान, आपने कुछ प्रोसेसिंग कर ली हो. एसिंक का एक अहम पहलू यह है कि जब तक आप नतीजे का इंतज़ार नहीं करते, तब तक आपको यह नहीं पता चलेगा कि नतीजा उपलब्ध है या नहीं.

उदाहरण के लिए, मान लें कि आपको किसी सवाल का जवाब पाने के लिए रिसर्च करनी है और आपने किसी सहकर्मी से जवाब ढूंढने के लिए कहा है. इसके बाद, वे इस पर काम करते हैं. इसका मतलब है कि वे "एसिंक्रोनस तरीके से" और "अलग थ्रेड पर" काम कर रहे हैं. जब तक आपका सहयोगी वापस नहीं आता और आपको जवाब नहीं बताता, तब तक ऐसे काम जारी रखें जिनके लिए जवाब की ज़रूरत नहीं है.

कोरूटीन, नॉन-ब्लॉकिंग होते हैं.

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

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

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

जब कोरूटीन सस्पेंड रहता है और नतीजे का इंतज़ार करता है, तो यह उस थ्रेड को अनब्लॉक करता है जिस पर चलता है. इस तरह, अन्य फ़ंक्शन या कोरूटीन चल सकते हैं.

suspend कीवर्ड, उस थ्रेड के बारे में नहीं बताता जिस पर कोड चलता है. सस्पेंड फ़ंक्शन, बैकग्राउंड थ्रेड या मुख्य थ्रेड पर चल सकता है.

Kotlin में कोरूटीन का इस्तेमाल करने के लिए, आपको इन तीन चीज़ों की ज़रूरत होगी:

  • कोई नौकरी
  • डिस्पैचर
  • स्कोप

जॉब: जॉब का मतलब है कि कोई भी ऐसी प्रोसेस जिसे रद्द किया जा सकता है. हर कोरूटीन का एक जॉब होता है. इस जॉब का इस्तेमाल करके, कोरूटीन को रद्द किया जा सकता है. जॉब को पैरंट-चाइल्ड के क्रम में व्यवस्थित किया जा सकता है. किसी पैरंट जॉब को रद्द करने पर, उससे जुड़ी सभी चाइल्ड जॉब तुरंत रद्द हो जाती हैं. यह हर को-रूटीन को मैन्युअल तरीके से रद्द करने की तुलना में ज़्यादा आसान है.

डिस्पैचर: डिस्पैचर, अलग-अलग थ्रेड पर चलने के लिए को-रूटीन भेजता है. उदाहरण के लिए, Dispatcher.Main मुख्य थ्रेड पर टास्क चलाता है और Dispatcher.IO, I/O टास्क को थ्रेड के शेयर किए गए पूल में ऑफ़लोड करता है.

स्कोप: किसी कोरूटीन का स्कोप, उस कॉन्टेक्स्ट को तय करता है जिसमें कोरूटीन चलता है. स्कोप में, किसी को-रूटीन के जॉब और डिस्पैचर के बारे में जानकारी शामिल होती है. स्कोप, कोरूटीन को ट्रैक करते हैं. जब कोई को-रूटीन लॉन्च किया जाता है, तो वह "स्कोप में होता है". इसका मतलब है कि आपने यह बताया है कि कौनसे स्कोप में को-रूटीन को ट्रैक किया जाएगा.

आपको उपयोगकर्ता को नींद के डेटा के साथ इन तरीकों से इंटरैक्ट करने की सुविधा देनी है:

  • जब उपयोगकर्ता शुरू करें बटन पर टैप करता है, तो ऐप्लिकेशन एक नई स्लीप नाइट बनाता है और उसे डेटाबेस में सेव करता है.
  • जब उपयोगकर्ता बंद करें बटन पर टैप करता है, तो ऐप्लिकेशन रात की अवधि को अपडेट करता है. इसमें रात के खत्म होने का समय शामिल होता है.
  • जब उपयोगकर्ता मिटाएं बटन पर टैप करता है, तो ऐप्लिकेशन डेटाबेस में मौजूद डेटा को मिटा देता है.

डेटाबेस से जुड़ी इन कार्रवाइयों में ज़्यादा समय लग सकता है. इसलिए, इन्हें अलग थ्रेड पर चलाना चाहिए.

पहला चरण: डेटाबेस के ऑपरेशन के लिए को-रूटीन सेट अप करना

जब Sleep Tracker ऐप्लिकेशन में शुरू करें बटन पर टैप किया जाता है, तब आपको SleepTrackerViewModel में किसी फ़ंक्शन को कॉल करना होता है, ताकि SleepNight का नया इंस्टेंस बनाया जा सके और इंस्टेंस को डेटाबेस में सेव किया जा सके.

किसी भी बटन पर टैप करने से, डेटाबेस से जुड़ा कोई ऑपरेशन शुरू हो जाता है. जैसे, SleepNight बनाना या उसे अपडेट करना. इस वजह और अन्य वजहों से, ऐप्लिकेशन के बटन के लिए क्लिक हैंडलर लागू करने के लिए, को-रूटीन का इस्तेमाल किया जाता है.

  1. ऐप्लिकेशन-लेवल की build.gradle फ़ाइल खोलें और कोरूटीन के लिए डिपेंडेंसी ढूंढें. कोरूटीन का इस्तेमाल करने के लिए, आपको इन डिपेंडेंसी की ज़रूरत होगी. इन्हें आपके लिए जोड़ा गया है.

    $coroutine_version को प्रोजेक्ट की build.gradle फ़ाइल में coroutine_version = '1.0.0' के तौर पर तय किया गया है.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
  1. SleepTrackerViewModel फ़ाइल खोलें.
  2. क्लास के मुख्य हिस्से में, viewModelJob को तय करें और उसे Job का एक इंस्टेंस असाइन करें. इस viewModelJob की मदद से, व्यू मॉडल का इस्तेमाल बंद होने और उसे बंद किए जाने पर, इस व्यू मॉडल से शुरू किए गए सभी कोरूटीन रद्द किए जा सकते हैं. इस तरह, आपको ऐसी को-रूटीन नहीं मिलती हैं जिन्हें कहीं वापस नहीं जाना होता.
private var viewModelJob = Job()
  1. क्लास के आखिर में, onCleared() को बदलें और सभी कोरूटीन रद्द करें. ViewModel के डिस्ट्रॉय होने पर, onCleared() को कॉल किया जाता है.
override fun onCleared() {
   super.onCleared()
   viewModelJob.cancel()
}
  1. viewModelJob की परिभाषा के ठीक नीचे, कोरूटीन के लिए uiScope तय करें. स्कोप से यह तय होता है कि कोरूटीन किस थ्रेड पर चलेगा. साथ ही, स्कोप को जॉब के बारे में भी पता होना चाहिए. स्कोप पाने के लिए, CoroutineScope का इंस्टेंस मांगें. साथ ही, डिस्पैचर और जॉब पास करें.

Dispatchers.Main का इस्तेमाल करने का मतलब है कि uiScope में लॉन्च किए गए कोरूटीन, मुख्य थ्रेड पर चलेंगे. ViewModel से शुरू की गई कई कोरूटीन के लिए यह सही है, क्योंकि ये कोरूटीन कुछ प्रोसेसिंग करने के बाद, यूआई को अपडेट करती हैं.

private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
  1. uiScope की परिभाषा के नीचे, tonight नाम का एक वैरिएबल तय करें. इससे मौजूदा रात की जानकारी सेव की जा सकेगी. वैरिएबल को MutableLiveData बनाएं, क्योंकि आपको डेटा देखने और उसमें बदलाव करने की ज़रूरत है.
private var tonight = MutableLiveData<SleepNight?>()
  1. tonight वैरिएबल को जल्द से जल्द शुरू करने के लिए, tonight की डेफ़िनिशन के नीचे एक init ब्लॉक बनाएं और initializeTonight() को कॉल करें. अगले चरण में, initializeTonight() तय किया जाता है.
init {
   initializeTonight()
}
  1. init ब्लॉक के नीचे, initializeTonight() लागू करें. uiScope में, कोरूटीन लॉन्च करें. इसके अंदर, getTonightFromDatabase() को कॉल करके डेटाबेस से tonight की वैल्यू पाएं और उस वैल्यू को tonight.value को असाइन करें. अगले चरण में, getTonightFromDatabase() तय किया जाता है.
private fun initializeTonight() {
   uiScope.launch {
       tonight.value = getTonightFromDatabase()
   }
}
  1. getTonightFromDatabase() लागू करें. इसे ऐसे private suspend फ़ंक्शन के तौर पर तय करें जो SleepNight शुरू न होने पर, शून्य हो सकने वाला SleepNight दिखाता है. इस वजह से, आपको एक गड़बड़ी दिखेगी, क्योंकि फ़ंक्शन को कुछ न कुछ रिटर्न करना होता है.
private suspend fun getTonightFromDatabase(): SleepNight? { }
  1. getTonightFromDatabase() के फ़ंक्शन बॉडी में, Dispatchers.IO कॉन्टेक्स्ट में चलने वाली कोरूटीन से नतीजा दिखाएं. I/O डिस्पैचर का इस्तेमाल करें, क्योंकि डेटाबेस से डेटा पाना एक I/O ऑपरेशन है और इसका यूज़र इंटरफ़ेस (यूआई) से कोई लेना-देना नहीं है.
  return withContext(Dispatchers.IO) {}
  1. return ब्लॉक में, कोरूटीन को डेटाबेस से आज रात (सबसे नई रात) का डेटा पाने दें. अगर शुरू और खत्म होने का समय एक जैसा नहीं है, तो इसका मतलब है कि रात खत्म हो चुकी है. ऐसे में, null वैल्यू दिखाएं. नहीं तो, नाइट मोड को चालू करो.
       var night = database.getTonight()
       if (night?.endTimeMilli != night?.startTimeMilli) {
           night = null
       }
       night

आपका पूरा किया गया getTonightFromDatabase() निलंबित करने का फ़ंक्शन ऐसा दिखना चाहिए. अब कोई गड़बड़ी नहीं होनी चाहिए.

private suspend fun getTonightFromDatabase(): SleepNight? {
   return withContext(Dispatchers.IO) {
       var night = database.getTonight()
       if (night?.endTimeMilli != night?.startTimeMilli) {
           night = null
       }
       night
   }
}

दूसरा चरण: 'शुरू करें' बटन के लिए क्लिक हैंडलर जोड़ना

अब Start बटन के लिए, क्लिक हैंडलर onStartTracking() लागू किया जा सकता है. आपको एक नया SleepNight बनाना होगा. इसके बाद, इसे डेटाबेस में इंसर्ट करना होगा और tonight को असाइन करना होगा. onStartTracking() का स्ट्रक्चर, initializeTonight() के स्ट्रक्चर जैसा ही होगा.

  1. onStartTracking() के लिए फ़ंक्शन की परिभाषा से शुरू करें. SleepTrackerViewModel फ़ाइल में, क्लिक हैंडलर को onCleared() से ऊपर रखा जा सकता है.
fun onStartTracking() {}
  1. onStartTracking() के अंदर, uiScope में एक कोरूटीन लॉन्च करें. ऐसा इसलिए, क्योंकि आपको इस नतीजे की ज़रूरत है, ताकि यूआई को अपडेट किया जा सके और काम जारी रखा जा सके.
uiScope.launch {}
  1. कोरूटीन लॉन्च के अंदर, एक नया SleepNight बनाएं. यह मौजूदा समय को शुरू होने के समय के तौर पर कैप्चर करता है.
        val newNight = SleepNight()
  1. अब भी को-रूटीन लॉन्च के अंदर, insert() को कॉल करके डेटाबेस में newNight डालें. आपको एक गड़बड़ी दिखेगी, क्योंकि आपने अब तक इस insert() निलंबित करने के फ़ंक्शन को तय नहीं किया है. (यह इसी नाम का DAO फ़ंक्शन नहीं है.)
       insert(newNight)
  1. कोरूटीन लॉन्च के अंदर, tonight को भी अपडेट करें.
       tonight.value = getTonightFromDatabase()
  1. नीचे दिए गए onStartTracking() में, insert() को private suspend फ़ंक्शन के तौर पर तय करें. यह फ़ंक्शन, SleepNight को आर्ग्युमेंट के तौर पर लेता है.
private suspend fun insert(night: SleepNight) {}
  1. insert() के मुख्य हिस्से के लिए, I/O कॉन्टेक्स्ट में एक कोरूटीन लॉन्च करें. साथ ही, डीएओ से insert() को कॉल करके, रात की जानकारी को डेटाबेस में इंसर्ट करें.
   withContext(Dispatchers.IO) {
       database.insert(night)
   }
  1. fragment_sleep_tracker.xml लेआउट फ़ाइल में, onStartTracking() के लिए क्लिक हैंडलर को start_button में जोड़ें. इसके लिए, डेटा बाइंडिंग की सुविधा का इस्तेमाल करें. @{() -> फ़ंक्शन नोटेशन, एक ऐसा लैम्डा फ़ंक्शन बनाता है जो कोई आर्ग्युमेंट नहीं लेता है. साथ ही, sleepTrackerViewModel में क्लिक हैंडलर को कॉल करता है.
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"
  1. अपना ऐप्लिकेशन बनाएं और उसे चलाएं. शुरू करें बटन पर टैप करें. इस कार्रवाई से डेटा बनता है, लेकिन फ़िलहाल आपको कुछ भी नहीं दिखेगा. इसके बाद, आपको इस समस्या को ठीक करना होगा.
fun someWorkNeedsToBeDone {
   uiScope.launch {

        suspendFunction()

   }
}

suspend fun suspendFunction() {
   withContext(Dispatchers.IO) {
       longrunningWork()
   }
}

तीसरा चरण: डेटा दिखाना

SleepTrackerViewModel में, nights वैरिएबल LiveData को रेफ़र करता है, क्योंकि DAO में getAllNights(), LiveData दिखाता है.

यह Room की एक सुविधा है. इसकी मदद से, डेटाबेस में डेटा बदलने पर, LiveData nights को अपडेट किया जाता है, ताकि नया डेटा दिखाया जा सके. आपको LiveData को सेट करने या अपडेट करने की ज़रूरत नहीं होती. Room डेटाबेस से मेल खाने के लिए डेटा को अपडेट करता है.

हालांकि, अगर टेक्स्ट व्यू में nights दिखाया जाता है, तो यह ऑब्जेक्ट रेफ़रंस दिखाएगा. ऑब्जेक्ट का कॉन्टेंट देखने के लिए, डेटा को फ़ॉर्मैट की गई स्ट्रिंग में बदलें. ऐसे Transformation मैप का इस्तेमाल करें जो डेटाबेस से नया डेटा मिलने पर, हर बार अपने-आप अपडेट हो जाता है.nights

  1. Util.kt फ़ाइल खोलें और formatNights() की परिभाषा और उससे जुड़े import स्टेटमेंट के लिए कोड से टिप्पणी हटाएं. Android Studio में कोड से टिप्पणी हटाने के लिए, // के निशान वाले सभी कोड को चुनें. इसके बाद, Cmd+/ या Control+/ दबाएं.
  2. ध्यान दें कि formatNights(), Spanned टाइप दिखाता है. यह एचटीएमएल फ़ॉर्मैट वाली स्ट्रिंग होती है.
  3. strings.xml खोलें. नींद से जुड़ा डेटा दिखाने के लिए, स्ट्रिंग रिसॉर्स को फ़ॉर्मैट करने के लिए CDATA का इस्तेमाल किया गया है.
  4. SleepTrackerViewModel खोलें. SleepTrackerViewModel क्लास में, uiScope की परिभाषा के नीचे, nights नाम का वैरिएबल तय करें. डेटाबेस से सभी रातों की जानकारी पाएं और उन्हें nights वैरिएबल को असाइन करें.
private val nights = database.getAllNights()
  1. nights की परिभाषा के ठीक नीचे, nights को nightsString में बदलने के लिए कोड जोड़ें. Util.kt से formatNights() फ़ंक्शन का इस्तेमाल करें.

    Transformations क्लास से map() फ़ंक्शन में nights पास करें. अपने स्ट्रिंग संसाधनों को ऐक्सेस करने के लिए, मैपिंग फ़ंक्शन को formatNights() को कॉल करने के तौर पर तय करें. nights और Resources ऑब्जेक्ट उपलब्ध कराएं.
val nightsString = Transformations.map(nights) { nights ->
   formatNights(nights, application.resources)
}
  1. fragment_sleep_tracker.xml लेआउट फ़ाइल खोलें. TextView में, android:text प्रॉपर्टी में अब संसाधन स्ट्रिंग को nightsString के रेफ़रंस से बदला जा सकता है.
"@{sleepTrackerViewModel.nightsString}"
  1. अपने कोड को फिर से बनाएं और ऐप्लिकेशन चलाएं. अब आपको नींद का पूरा डेटा, शुरू होने के समय के साथ दिखेगा.
  2. शुरू करें बटन पर कुछ और बार टैप करें. इसके बाद, आपको ज़्यादा डेटा दिखेगा.

अगले चरण में, बंद करें बटन के लिए फ़ंक्शन चालू करें.

चौथा चरण: 'रोकें' बटन के लिए क्लिक हैंडलर जोड़ना

पिछले चरण में इस्तेमाल किए गए पैटर्न का इस्तेमाल करके, SleepTrackerViewModel. में Stop बटन के लिए क्लिक हैंडलर लागू करें

  1. ViewModel में onStopTracking() जोड़ें. uiScope में कोरूटीन लॉन्च करें. अगर खत्म होने का समय अभी तक सेट नहीं किया गया है, तो endTimeMilli को सिस्टम के मौजूदा समय पर सेट करें और रात के डेटा के साथ update() को कॉल करें.

    Kotlin में, return@label सिंटैक्स, कई नेस्ट किए गए फ़ंक्शन में से उस फ़ंक्शन के बारे में बताता है जिससे यह स्टेटमेंट वापस आता है.
fun onStopTracking() {
   uiScope.launch {
       val oldNight = tonight.value ?: return@launch
       oldNight.endTimeMilli = System.currentTimeMillis()
       update(oldNight)
   }
}
  1. update() को उसी पैटर्न में लागू करें जिस पैटर्न में आपने insert() को लागू किया था.
private suspend fun update(night: SleepNight) {
   withContext(Dispatchers.IO) {
       database.update(night)
   }
}
  1. क्लिक हैंडलर को यूज़र इंटरफ़ेस (यूआई) से कनेक्ट करने के लिए, fragment_sleep_tracker.xml लेआउट फ़ाइल खोलें और stop_button में क्लिक हैंडलर जोड़ें.
android:onClick="@{() -> sleepTrackerViewModel.onStopTracking()}"
  1. अपना ऐप्लिकेशन बनाएं और उसे चलाएं.
  2. शुरू करें पर टैप करें. इसके बाद, बंद करें पर टैप करें. आपको सोने का समय, उठने का समय, नींद की क्वालिटी (कोई वैल्यू नहीं) और सोने की अवधि दिखती है.

पांचवां चरण: 'मिटाएं' बटन के लिए क्लिक हैंडलर जोड़ना

  1. इसी तरह, onClear() और clear() को लागू करें.
fun onClear() {
   uiScope.launch {
       clear()
       tonight.value = null
   }
}

suspend fun clear() {
   withContext(Dispatchers.IO) {
       database.clear()
   }
}
  1. क्लिक हैंडलर को यूज़र इंटरफ़ेस (यूआई) से कनेक्ट करने के लिए, fragment_sleep_tracker.xml खोलें और क्लिक हैंडलर को clear_button में जोड़ें.
android:onClick="@{() -> sleepTrackerViewModel.onClear()}"
  1. अपना ऐप्लिकेशन बनाएं और उसे चलाएं.
  2. पूरा डेटा मिटाने के लिए, मिटाएं पर टैप करें. इसके बाद, नया डेटा बनाने के लिए शुरू करें और बंद करें पर टैप करें.

Android Studio प्रोजेक्ट: TrackMySleepQualityCoroutines

  • ऐप्लिकेशन के यूज़र इंटरफ़ेस आर्किटेक्चर को सेट अप करने के लिए, ViewModel, ViewModelFactory, और डेटा बाइंडिंग का इस्तेमाल करें.
  • यूज़र इंटरफ़ेस (यूआई) को आसानी से चलाने के लिए, लंबे समय तक चलने वाले टास्क के लिए कोरूटीन का इस्तेमाल करें. जैसे, सभी डेटाबेस ऑपरेशन.
  • कोरूटीन एसिंक्रोनस और नॉन-ब्लॉकिंग होते हैं. ये एसिंक्रोनस कोड को क्रम से लगाने के लिए, suspend फ़ंक्शन का इस्तेमाल करते हैं.
  • जब कोई कोरूटीन, suspend के निशान वाले फ़ंक्शन को कॉल करता है, तो उस फ़ंक्शन को सामान्य फ़ंक्शन कॉल रिटर्न करने तक ब्लॉक करने के बजाय, यह नतीजा मिलने तक एक्ज़ीक्यूशन को सस्पेंड करता है. इसके बाद, यह नतीजे के साथ वहीं से शुरू होता है जहां इसे छोड़ा गया था.
  • ब्लॉक करने और निलंबित करने के बीच का अंतर यह है कि किसी थ्रेड को ब्लॉक करने पर, कोई अन्य काम नहीं होता. अगर थ्रेड को निलंबित कर दिया जाता है, तो नतीजा मिलने तक अन्य काम होता रहता है.

कोरूटीन लॉन्च करने के लिए, आपको जॉब, डिस्पैचर, और स्कोप की ज़रूरत होती है:

  • असल में, जॉब ऐसा कोई भी काम होता है जिसे रद्द किया जा सकता है. हर कोरूटीन का एक जॉब होता है. कोरूटीन को रद्द करने के लिए, जॉब का इस्तेमाल किया जा सकता है.
  • डिस्पैचर, अलग-अलग थ्रेड पर चलाने के लिए को-रूटीन भेजता है. Dispatcher.Main, मुख्य थ्रेड पर टास्क चलाता है. वहीं, Dispartcher.IO का इस्तेमाल, I/O टास्क को थ्रेड के शेयर किए गए पूल में ऑफ़लोड करने के लिए किया जाता है.
  • स्कोप में नौकरी और डिस्पैचर जैसी जानकारी शामिल होती है. इससे यह तय होता है कि कोरूटीन किस कॉन्टेक्स्ट में चलेगा. स्कोप, कोरूटीन को ट्रैक करते हैं.

डेटाबेस के ऑपरेशन ट्रिगर करने वाले क्लिक हैंडलर लागू करने के लिए, इस पैटर्न का पालन करें:

  1. ऐसी कोरूटीन लॉन्च करें जो मुख्य या यूआई थ्रेड पर चलती हो, क्योंकि नतीजे का असर यूआई पर पड़ता है.
  2. लंबे समय तक चलने वाले काम को पूरा करने के लिए, सस्पेंड फ़ंक्शन को कॉल करें. इससे, नतीजे का इंतज़ार करते समय यूज़र इंटरफ़ेस (यूआई) थ्रेड ब्लॉक नहीं होगी.
  3. लंबे समय तक चलने वाले काम का यूज़र इंटरफ़ेस (यूआई) से कोई लेना-देना नहीं है. इसलिए, I/O कॉन्टेक्स्ट पर स्विच करें. इस तरह, काम को थ्रेड पूल में चलाया जा सकता है. यह थ्रेड पूल, इस तरह की कार्रवाइयों के लिए ऑप्टिमाइज़ किया गया होता है और इसे अलग से सेट किया जाता है.
  4. इसके बाद, काम करने के लिए डेटाबेस फ़ंक्शन को कॉल करें.

LiveData ऑब्जेक्ट में बदलाव होने पर, हर बार उससे स्ट्रिंग बनाने के लिए Transformations मैप का इस्तेमाल करें.

Udacity का कोर्स:

Android डेवलपर का दस्तावेज़:

अन्य दस्तावेज़ और लेख:

इस सेक्शन में, उन छात्र-छात्राओं के लिए होमवर्क असाइनमेंट की सूची दी गई है जो किसी शिक्षक के कोर्स के हिस्से के तौर पर इस कोडलैब पर काम कर रहे हैं. शिक्षक के पास ये विकल्प होते हैं:

  • अगर ज़रूरी हो, तो होमवर्क असाइन करें.
  • छात्र-छात्राओं को बताएं कि होमवर्क असाइनमेंट कैसे सबमिट किए जाते हैं.
  • होमवर्क असाइनमेंट को ग्रेड दें.

शिक्षक इन सुझावों का इस्तेमाल अपनी ज़रूरत के हिसाब से कर सकते हैं. साथ ही, वे चाहें, तो कोई दूसरा होमवर्क भी दे सकते हैं.

अगर आपको यह कोडलैब खुद से पूरा करना है, तो अपनी जानकारी की जांच करने के लिए, इन होमवर्क असाइनमेंट का इस्तेमाल करें.

इन सवालों के जवाब दें

पहला सवाल

कोरूटीन के इनमें से कौनसे फ़ायदे हैं:

  • ये नॉन-ब्लॉकिंग होते हैं
  • ये एसिंक्रोनस तरीके से काम करते हैं.
  • इन्हें मुख्य थ्रेड के अलावा किसी अन्य थ्रेड पर चलाया जा सकता है.
  • इनसे ऐप्लिकेशन हमेशा तेज़ी से काम करता है.
  • वे अपवादों का इस्तेमाल कर सकते हैं.
  • इन्हें लीनियर कोड के तौर पर लिखा और पढ़ा जा सकता है.

दूसरा सवाल

सस्पेंड फ़ंक्शन क्या होता है?

  • suspend कीवर्ड के साथ एनोटेट किया गया सामान्य फ़ंक्शन.
  • ऐसा फ़ंक्शन जिसे कोरूटीन के अंदर कॉल किया जा सकता है.
  • जब कोई सस्पेंड फ़ंक्शन चल रहा होता है, तब कॉल करने वाले थ्रेड को सस्पेंड कर दिया जाता है.
  • सस्पेंड फ़ंक्शन हमेशा बैकग्राउंड में चलने चाहिए.

तीसरा सवाल

किसी थ्रेड को ब्लॉक करने और निलंबित करने में क्या अंतर है? लागू होने वाले सभी विकल्प चुनें.

  • जब किसी थ्रेड को ब्लॉक किया जाता है, तो उस पर कोई दूसरा काम नहीं किया जा सकता.
  • जब एक्ज़ीक्यूशन सस्पेंड होता है, तो थ्रेड, ऑफ़लोड किए गए काम के पूरा होने का इंतज़ार करते समय अन्य काम कर सकती है.
  • निलंबित करने की सुविधा ज़्यादा असरदार होती है, क्योंकि थ्रेड इंतज़ार नहीं कर सकती हैं और कुछ नहीं कर सकती हैं.
  • ब्लॉक या निलंबित किए जाने के बावजूद, एक्ज़ीक्यूशन तब तक जारी नहीं रहेगा, जब तक कोरूटीन का नतीजा नहीं मिल जाता.

अगला लेसन शुरू करें: 6.3 बटन की स्थितियों को कंट्रोल करने के लिए LiveData का इस्तेमाल करना

इस कोर्स में मौजूद अन्य कोडलैब के लिंक के लिए, Android Kotlin Fundamentals कोडलैब का लैंडिंग पेज देखें.