कंपोज़ में स्थिरता

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

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

  • स्टेबल पैरामीटर: Compose, स्ट्रक्चरल समानता (Object.equals()) का इस्तेमाल करके, स्टेबल पैरामीटर की तुलना करता है. अगर किसी कंपोज़ेबल के स्टेबल पैरामीटर की वैल्यू, उनकी पिछली वैल्यू के बराबर होती है, तो Compose उसे स्किप कर देता है.
  • अस्थिर पैरामीटर: स्ट्रॉन्ग स्किपिंग मोड चालू होने पर (Kotlin 2.0.20 और इसके बाद के वर्शन में डिफ़ॉल्ट रूप से चालू होता है), Compose इंस्टेंस की समानता (===) का इस्तेमाल करके, अस्थिर पैरामीटर की तुलना करता है. साथ ही, अगर एक ही ऑब्जेक्ट इंस्टेंस पास किए जाते हैं, तो कंपोज़ेबल को स्किप कर देता है. Kotlin 2.0.20 से पहले के वर्शन में या स्ट्रॉन्ग स्किपिंग मोड बंद होने पर, Compose हमेशा अनस्टेबल पैरामीटर वाले कंपोज़ेबल को फिर से कंपोज़ करता है. ऐसा तब होता है, जब उसका पैरंट कंपोज़ेबल फिर से कंपोज़ होता है.

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

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

इम्यूटेबल ऑब्जेक्ट

यहां दिए गए स्निपेट, स्थिरता और फिर से कॉम्पोज़ करने के सामान्य सिद्धांतों के बारे में बताते हैं.

Contact क्लास, एक ऐसी डेटा क्लास है जिसमें बदलाव नहीं किया जा सकता. ऐसा इसलिए है, क्योंकि इसके सभी पैरामीटर प्रिमिटिव हैं और इन्हें val कीवर्ड के साथ तय किया गया है. Contact का इंस्टेंस बनाने के बाद, ऑब्जेक्ट की प्रॉपर्टी की वैल्यू नहीं बदली जा सकती. अगर ऐसा किया जाता है, तो एक नया ऑब्जेक्ट बन जाएगा.

data class Contact(val name: String, val number: String)

ContactRow कंपोज़ेबल में Contact टाइप का पैरामीटर होता है.

@Composable
fun ContactRow(contact: Contact, modifier: Modifier = Modifier) {
   var selected by remember { mutableStateOf(false) }

   Row(modifier) {
      ContactDetails(contact)
      ToggleButton(selected, onToggled = { selected = !selected })
   }
}

देखें कि जब उपयोगकर्ता टॉगल बटन पर क्लिक करता है और selected की स्थिति बदलती है, तो क्या होता है:

  1. Compose यह आकलन करता है कि उसे ContactRow के अंदर मौजूद कोड को फिर से कंपोज़ करना चाहिए या नहीं.
  2. यह देखता है कि ContactDetails के लिए दिया गया एकमात्र आर्ग्युमेंट, Contact टाइप का है.
  3. Contact एक ऐसी डेटा क्लास है जिसमें बदलाव नहीं किया जा सकता. इसलिए, Compose को पक्का पता होता है कि ContactDetails के किसी भी आर्ग्युमेंट में बदलाव नहीं हुआ है.
  4. इसलिए, Compose ContactDetails को स्किप कर देता है और उसे फिर से कंपोज़ नहीं करता.
  5. दूसरी ओर, ToggleButton के आर्ग्युमेंट बदल गए हैं और Compose उस कॉम्पोनेंट को फिर से कंपोज़ करता है.

म्यूटेबल ऑब्जेक्ट

पिछले उदाहरण में, इम्यूटेबल ऑब्जेक्ट का इस्तेमाल किया गया है. हालांकि, म्यूटेबल ऑब्जेक्ट भी बनाया जा सकता है. यहां दिया गया स्निपेट देखें:

data class Contact(var name: String, var number: String)

Contact का हर पैरामीटर अब var है. इसलिए, क्लास को अब बदला जा सकता है. अगर इसकी प्रॉपर्टी में बदलाव होता है, तो Compose को इसकी जानकारी नहीं मिलेगी. ऐसा इसलिए होता है, क्योंकि Compose सिर्फ़ Compose स्टेट ऑब्जेक्ट में होने वाले बदलावों को ट्रैक करता है.

Compose, ऐसी क्लास को अनस्टेबल मानता है. स्ट्रॉन्ग स्किपिंग मोड (Kotlin 2.0.20+) की मदद से, Compose इंस्टेंस की समानता (===) का इस्तेमाल करके Contact की तुलना करता है: contact.name में बदलाव करने से, फिर से कॉम्पोज़ करने की प्रोसेस ट्रिगर नहीं होगी. हालांकि, एक जैसी वैल्यू वाले नए Contact इंस्टेंस को पास करने पर, ContactDetails को फिर से कॉम्पोज़ करने के लिए मजबूर किया जाएगा. स्ट्रॉन्ग स्किपिंग मोड के बिना, selected में बदलाव होने पर ContactDetails हर बार फिर से कॉम्पोज़ होता है.

Compose में लागू करना

यह जानना मददगार हो सकता है कि Compose, रीकंपोज़िशन के दौरान किन फ़ंक्शन को स्किप करना है. हालांकि, यह ज़रूरी नहीं है.

जब Compose कंपाइलर आपके कोड पर चलता है, तो वह हर फ़ंक्शन और टाइप को कई टैग में से किसी एक के साथ मार्क करता है. इन टैग से पता चलता है कि Compose, फिर से कंपोज़ करने के दौरान फ़ंक्शन या टाइप को कैसे मैनेज करता है.

फ़ंक्शन

Compose, फ़ंक्शन को skippable या restartable के तौर पर मार्क कर सकता है. ध्यान दें कि यह किसी फ़ंक्शन को इनमें से एक, दोनों या किसी के तौर पर भी मार्क नहीं कर सकता:

  • स्किप किया जा सकने वाला: अगर कंपाइलर किसी कंपोज़ेबल को स्किप किया जा सकने वाला के तौर पर मार्क करता है, तो Compose उसे फिर से कंपोज़ करने के दौरान स्किप कर सकता है. ऐसा तब होता है, जब उसके सभी आर्ग्युमेंट की वैल्यू, पिछली वैल्यू के बराबर हों. (Kotlin 2.0.20+ में स्ट्रॉन्ग स्किपिंग मोड की मदद से, फिर से शुरू किए जा सकने वाले सभी कंपोज़ेबल अपने-आप स्किप हो जाते हैं.)
  • रीस्टार्ट किया जा सकने वाला: रीस्टार्ट किया जा सकने वाला कंपोज़ेबल, एक "स्कोप" के तौर पर काम करता है. इसमें फिर से कंपोज़ करने की प्रोसेस शुरू की जा सकती है. दूसरे शब्दों में, फ़ंक्शन एक ऐसा पॉइंट हो सकता है जहां से Compose, स्थिति में बदलाव होने के बाद, फिर से कंपोज़ करने के लिए कोड को फिर से चलाना शुरू कर सकता है.

टाइप

Compose, टाइप को इम्यूटेबल या स्टेबल के तौर पर मार्क करता है. हर टाइप, इनमें से कोई एक होता है:

  • इम्यूटेबल: Compose किसी टाइप को इम्यूटेबल के तौर पर मार्क करता है, अगर उसकी प्रॉपर्टी की वैल्यू कभी नहीं बदल सकती और सभी तरीके रेफ़रंस के तौर पर पारदर्शी होते हैं.
    • ध्यान दें कि सभी प्रिमिटिव टाइप को इम्यूटेबल के तौर पर मार्क किया गया है. इनमें String, Int, और Float शामिल हैं.
  • स्टेबल: इससे ऐसे टाइप का पता चलता है जिसकी प्रॉपर्टी, कंस्ट्रक्शन के बाद बदल सकती हैं. अगर रनटाइम के दौरान उन प्रॉपर्टी में कोई बदलाव होता है, तो Compose को उन बदलावों के बारे में पता चल जाता है.

स्टेबिलिटी से जुड़ी समस्याओं को डीबग करना

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

Compose में स्टेबिलिटी से जुड़ी जटिल समस्याओं का पता लगाने के तरीके के बारे में ज़्यादा जानने के लिए, डीबग स्टेबिलिटी गाइड देखें.

ऐप्लिकेशन के क्रैश या हैंग होने से जुड़ी समस्याएं ठीक करना

Compose को लागू करने के दौरान स्टेबिलिटी से जुड़ी समस्याओं को ठीक करने के बारे में जानने के लिए, स्टेबिलिटी से जुड़ी समस्याएं ठीक करना गाइड देखें.

खास जानकारी

कुल मिलाकर, आपको इन बातों का ध्यान रखना चाहिए:

  • पैरामीटर: Compose, आपके कंपोज़ेबल के हर पैरामीटर की स्टेबिलिटी तय करता है. इससे यह तय किया जाता है कि रीकंपोज़िशन के दौरान, उनकी तुलना स्ट्रक्चरल इक्वालिटी (.equals()) या इंस्टेंस इक्वालिटी (===) का इस्तेमाल करके की जाए या नहीं.
  • तुरंत ठीक करने के तरीके: अगर आपको लगता है कि आपका कंपोज़ेबल स्किप नहीं किया जा रहा है और इससे परफ़ॉर्मेंस की समस्या हो रही है, तो देखें कि हर पास पर अनस्टेबल पैरामीटर के नए इंस्टेंस फिर से बनाए जा रहे हैं या नहीं. इसके अलावा, यह भी देखें कि State के बजाय var प्रॉपर्टी का इस्तेमाल किया जा रहा है या नहीं.
  • कंपाइलर रिपोर्ट: कंपाइलर रिपोर्ट का इस्तेमाल करके, यह पता लगाया जा सकता है कि आपकी क्लास के बारे में क्या अनुमान लगाया जा रहा है.
  • कलेक्शन: Compose, स्टैंडर्ड कलेक्शन इंटरफ़ेस (List, Set, और Map) को अनस्टेबल मानता है. इसकी वजह यह है कि उनके मौजूदा इंप्लिमेंटेशन में बदलाव किया जा सकता है. स्ट्रॉन्ग स्किपिंग मोड (Kotlin 2.0.20 और इसके बाद के वर्शन में डिफ़ॉल्ट रूप से चालू होता है) की मदद से, अनस्टेबल कलेक्शन अब भी इंस्टेंस इक्वालिटी (===) का इस्तेमाल करके, फिर से कॉम्पोज़ करने की प्रोसेस को स्किप कर देते हैं. ऐसा तब होता है, जब कलेक्शन का रेफ़रंस नहीं बदलता. स्टैंडर्ड कलेक्शन को Kotlinx के ऐसे कलेक्शन से बदलने में सावधानी बरतें जिनमें बदलाव नहीं किया जा सकता. इसके अलावा, कलेक्शन वाली क्लास को @Immutable या @Stable से एनोटेट करने में भी सावधानी बरतें. ऐसा इसलिए, क्योंकि हर रीकंपोज़िशन पास पर हर एलिमेंट की तुलना .equals() से करना, रीकंपोज़िशन से ज़्यादा महंगा हो सकता है.
  • अन्य मॉड्यूल: Compose, उन मॉड्यूल की क्लास को अनस्टेबल मानता है जिनमें Compose कंपाइलर नहीं चलता. इनकी तुलना, स्ट्रॉन्ग स्किपिंग मोड में === का इस्तेमाल करके की जाती है. अगर कोई डेटा सोर्स, एक जैसी वैल्यू वाले नॉन-Compose मॉड्यूल से छोटे और फ़्लैट मॉडल को बार-बार फिर से इंस्टैंशिएट करता है, तो स्टेबिलिटी कॉन्फ़िगरेशन फ़ाइल को कॉन्फ़िगर किया जा सकता है. इसके अलावा, @Stable या @Immutable का इस्तेमाल किया जा सकता है, जहां .equals() सस्ता है.

इस बारे में और पढ़ें