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

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

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

  • स्टेबल पैरामीटर: कंपोज़, स्ट्रक्चरल समानता (Object.equals()) का इस्तेमाल करके स्टेबल पैरामीटर की तुलना करता है. अगर कंपोज़ेबल के स्टेबल पैरामीटर, उनकी पिछली वैल्यू के बराबर हैं, तो कंपोज़ उन्हें स्किप कर देता है.
  • बदलाव होने वाले पैरामीटर: ज़्यादा स्किप करने वाला मोड चालू होने पर (Kotlin 2.0.20 और इसके बाद के वर्शन में डिफ़ॉल्ट रूप से चालू होता है), Compose, इंस्टेंस की समानता (===) का इस्तेमाल करके बदलाव होने वाले पैरामीटर की तुलना करता है. अगर एक ही ऑब्जेक्ट इंस्टेंस पास किए जाते हैं, तो Compose, कंपोज़ेबल को स्किप कर देता है. Kotlin 2.0.20 से पहले के वर्शन में (या जब Strong skipping mode बंद होता है), 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. इसलिए, कंपोज़ ContactDetails को स्किप कर देता है और उसे फिर से कंपोज़ नहीं करता.
  5. दूसरी ओर, ToggleButton के लिए आर्ग्युमेंट बदल गए हैं. इसलिए, Compose उस कॉम्पोनेंट को फिर से कंपोज़ करता है.

बदले जा सकने वाले ऑब्जेक्ट

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

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

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

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

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

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

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

फ़ंक्शन

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

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

टाइप

Compose में मार्क टाइप को बदला नहीं जा सकता या वे स्थिर होते हैं. हर टाइप इनमें से कोई एक होता है:

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

डीबग स्टेबिलिटी

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

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

स्थिरता से जुड़ी समस्याओं को ठीक करना

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

खास जानकारी

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

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

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