लाइब्रेरी के लेखकों के लिए ऑप्टिमाइज़ेशन

लाइब्रेरी के लेखक के तौर पर, आपको यह पक्का करना चाहिए कि ऐप्लिकेशन डेवलपर, आपकी लाइब्रेरी को अपने ऐप्लिकेशन में आसानी से शामिल कर सकें. साथ ही, ऐप्लिकेशन इस्तेमाल करने वाले लोगों को अच्छी क्वालिटी का अनुभव भी मिल सके. आपको यह पक्का करना चाहिए कि आपकी लाइब्रेरी, अतिरिक्त सेटअप के बिना Android के ऑप्टिमाइज़ेशन के साथ काम करती हो. इसके अलावा, यह भी हो सकता है कि लाइब्रेरी, Android पर इस्तेमाल करने के लिए सही न हो.

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

अगर आप ऐप्लिकेशन डेवलपर हैं और आपको अपने Android ऐप्लिकेशन को ऑप्टिमाइज़ करने के बारे में जानना है, तो ऐप्लिकेशन ऑप्टिमाइज़ेशन की सुविधा चालू करना लेख पढ़ें. यह जानने के लिए कि कौनसी लाइब्रेरी इस्तेमाल करना सही है, लाइब्रेरी को समझदारी से चुनना लेख पढ़ें.

रिफ़्लेक्शन के बजाय codegen का इस्तेमाल करना

जब भी हो सके, रिफ़्लेक्शन के बजाय कोड जनरेशन (codegen) का इस्तेमाल करें. प्रोग्रामिंग करते समय, बायलरप्लेट कोड से बचने के लिए, कोड जनरेशन और रिफ़्लेक्शन, दोनों सामान्य तरीके हैं. हालांकि, R8 जैसे ऐप्लिकेशन ऑप्टिमाइज़र के साथ कोड जनरेशन ज़्यादा काम का है:

  • codegen की मदद से, बिल्ड करने की प्रोसेस के दौरान कोड का विश्लेषण किया जाता है और उसमें बदलाव किया जाता है. कंपाइल होने के बाद, कोड में कोई बड़ा बदलाव नहीं किया जाता. इसलिए, ऑप्टिमाइज़र को पता होता है कि आखिर में किस कोड की ज़रूरत है और किस कोड को सुरक्षित तरीके से हटाया जा सकता है.
  • रिफ़्लेक्शन की मदद से, रनटाइम के दौरान कोड का विश्लेषण किया जाता है और उसमें बदलाव किया जाता है. कोड तब तक पूरी तरह से लागू नहीं होता, जब तक वह लागू नहीं हो जाता. इसलिए, ऑप्टिमाइज़र को यह नहीं पता कि कौनसा कोड सुरक्षित तरीके से हटाया जा सकता है. ऐसा करने से, रनटाइम के दौरान रिफ़्लेक्शन की मदद से डाइनैमिक तौर पर इस्तेमाल किया जाने वाला कोड हट जाएगा. इसकी वजह से, उपयोगकर्ताओं के लिए ऐप्लिकेशन क्रैश हो सकता है.

कई आधुनिक लाइब्रेरी, रिफ़्लेक्शन के बजाय codegen का इस्तेमाल करती हैं. सामान्य एंट्री पॉइंट के लिए, KSP देखें. इसका इस्तेमाल Room, Dagger2 वगैरह करते हैं.

प्रतिबिंब कब ठीक है

अगर आपको रिफ़्लेक्शन का इस्तेमाल करना है, तो सिर्फ़ इनमें से किसी एक का इस्तेमाल करें:

  • टारगेट किए गए खास टाइप (इंटरफ़ेस लागू करने वाले खास लोग या सबक्लास)
  • किसी खास रनटाइम एनोटेशन का इस्तेमाल करने वाला कोड

इस तरह से रिफ़्लेक्शन का इस्तेमाल करने से, रनटाइम की लागत कम होती है. साथ ही, टारगेट किए गए उपभोक्ता के लिए, डेटा को सेव रखने के नियम लिखे जा सकते हैं.

रिफ़्लेक्शन का यह खास और टारगेट किया गया फ़ॉर्म, एक पैटर्न है. इसे Android फ़्रेमवर्क (उदाहरण के लिए, गतिविधियों, व्यू, और ड्रॉबल को फ़्लोरेट करते समय) और AndroidX लाइब्रेरी, दोनों में देखा जा सकता है. उदाहरण के लिए, WorkManager ListenableWorkers या RoomDatabases बनाते समय. इसके उलट, Gson को Android ऐप्लिकेशन में इस्तेमाल करना सही नहीं है.

उपभोक्ता के डेटा को सेव रखने के नियम लिखना

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

एएआर लाइब्रेरी

किसी AAR लाइब्रेरी के लिए उपभोक्ता नियम जोड़ने के लिए, Android लाइब्रेरी मॉड्यूल की बिल्ड स्क्रिप्ट में consumerProguardFiles विकल्प का इस्तेमाल करें. ज़्यादा जानकारी के लिए, लाइब्रेरी मॉड्यूल बनाने के बारे में दिशा-निर्देश देखें.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

JAR लाइब्रेरी

JAR के तौर पर शिप की जाने वाली Kotlin/Java लाइब्रेरी के साथ नियमों को बंडल करने के लिए, अपनी नियम फ़ाइल को किसी भी फ़ाइल नाम के साथ, फ़ाइनल JAR की META-INF/proguard/ डायरेक्ट्री में डालें. उदाहरण के लिए, अगर आपका कोड <libraryroot>/src/main/kotlin में है, तो <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro पर उपभोक्ता के लिए बने नियमों की फ़ाइल डालें. इससे, नियम आपके आउटपुट JAR में सही जगह पर बंडल हो जाएंगे.

पुष्टि करें कि फ़ाइनल JAR, नियमों को सही तरीके से बंडल करता है. इसके लिए, देखें कि नियम META-INF/proguard डायरेक्ट्री में मौजूद हैं या नहीं.

AAR लाइब्रेरी का बिल्ड ऑप्टिमाइज़ करना (बेहतर)

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

अगर आपको अब भी बिल्ड के समय अपनी लाइब्रेरी को ऑप्टिमाइज़ करना है, तो Android Gradle Plugin की मदद से ऐसा किया जा सकता है.

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

ध्यान दें कि proguardFiles का व्यवहार, consumerProguardFiles से काफ़ी अलग होता है:

  • proguardFiles का इस्तेमाल, लाइब्रेरी बनाने के समय किया जाता है. आम तौर पर, इसे getDefaultProguardFile("proguard-android-optimize.txt") के साथ इस्तेमाल किया जाता है. इससे यह तय किया जाता है कि लाइब्रेरी बनाने के दौरान, आपकी लाइब्रेरी के किस हिस्से को शामिल रखना है. कम से कम, यह आपका सार्वजनिक एपीआई होना चाहिए.
  • इसके उलट, consumerProguardFiles को लाइब्रेरी में पैकेज किया जाता है, ताकि बाद में, आपकी लाइब्रेरी का इस्तेमाल करने वाले ऐप्लिकेशन के बिल्ड के दौरान, ऑप्टिमाइज़ेशन पर असर पड़े.

उदाहरण के लिए, अगर आपकी लाइब्रेरी इंटरनल क्लास बनाने के लिए रिफ़्लेक्शन का इस्तेमाल करती है, तो आपको proguardFiles और consumerProguardFiles, दोनों में सेव करने के नियम तय करने पड़ सकते हैं.

अगर लाइब्रेरी के बिल्ड में -repackageclasses का इस्तेमाल किया जाता है, तो अपनी लाइब्रेरी के पैकेज में अंदर मौजूद सब-पैकेज में क्लास को फिर से पैकेज करें. उदाहरण के लिए, -repackageclasses 'internal' के बजाय -repackageclasses 'com.example.mylibrary.internal' का इस्तेमाल करें.

R8 के अलग-अलग वर्शन के साथ काम करना (बेहतर)

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

टारगेट किए गए R8 नियमों की जानकारी देने के लिए, आपको उन्हें AAR के classes.jar में मौजूद META-INF/com.android.tools डायरेक्ट्री या JAR की META-INF/com.android.tools डायरेक्ट्री में शामिल करना होगा.

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

META-INF/com.android.tools डायरेक्ट्री में, r8-from-<X>-upto-<Y> के तौर पर नाम वाली कई सबडायरेक्ट्री हो सकती हैं. इससे यह पता चलता है कि नियम, R8 के किन वर्शन के लिए लिखे गए हैं. हर सबडायरेक्ट्री में, R8 नियमों वाली एक या उससे ज़्यादा फ़ाइलें हो सकती हैं. इन फ़ाइलों के नाम और एक्सटेंशन, किसी भी तरह के हो सकते हैं.

ध्यान दें कि -from-<X> और -upto-<Y> वाले हिस्से ज़रूरी नहीं हैं. <Y> वर्शन खास होता है. आम तौर पर, वर्शन की रेंज लगातार होती हैं, लेकिन वे एक-दूसरे से ओवरलैप भी हो सकती हैं.

उदाहरण के लिए, r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0, और r8-from-8.2.0, डायरेक्ट्री के नाम हैं. ये नाम, टारगेट किए गए R8 नियमों के सेट को दिखाते हैं. r8 डायरेक्ट्री में मौजूद नियमों का इस्तेमाल, R8 के किसी भी वर्शन में किया जा सकता है. r8-from-8.0.0-upto-8.2.0 डायरेक्ट्री में मौजूद नियमों का इस्तेमाल, R8 में 8.0.0 से लेकर 8.2.0 तक के वर्शन में किया जा सकता है. हालांकि, 8.2.0 वर्शन में इन नियमों का इस्तेमाल नहीं किया जा सकता.

Android Gradle प्लग इन, उस जानकारी का इस्तेमाल करके सभी नियमों को चुनता है जिनका इस्तेमाल R8 के मौजूदा वर्शन में किया जा सकता है. अगर किसी लाइब्रेरी में टारगेट किए गए R8 के नियमों के बारे में नहीं बताया गया है, तो Android Gradle प्लग इन, लेगसी जगहों (एएआर के लिए proguard.txt या जेएआर के लिए META-INF/proguard/<ProGuard-rule-files>) से नियम चुनेगा.