अपने ऐप्लिकेशन का साइज़ छोटा करें, उसे अस्पष्ट बनाएं, और उसे ऑप्टिमाइज़ करें

अपने ऐप्लिकेशन को जितना हो सके उतना छोटा और तेज़ बनाने के लिए, आपको isMinifyEnabled = true की मदद से अपने रिलीज़ बिल्ड को ऑप्टिमाइज़ और छोटा करना चाहिए.

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

Android Gradle प्लग इन 3.4.0 या इसके बाद के वर्शन का इस्तेमाल करके प्रोजेक्ट बनाने पर, प्लग इन अब कोड को कंपाइल करने के समय ऑप्टिमाइज़ करने के लिए ProGuard का इस्तेमाल नहीं करता. इसके बजाय, प्लग इन R8 कंपाइलर के साथ काम करता है, ताकि कंपाइल करने के समय ये टास्क पूरे किए जा सकें:

  • कोड छोटा करना (या ट्री-शैकिंग): यह आपके ऐप्लिकेशन और उसकी लाइब्रेरी डिपेंडेंसी से, इस्तेमाल न होने वाली क्लास, फ़ील्ड, मेथड, और एट्रिब्यूट का पता लगाता है और उन्हें सुरक्षित तरीके से हटा देता है. इससे, 64 हज़ार रेफ़रंस की सीमा को पूरा करने में मदद मिलती है. उदाहरण के लिए, अगर किसी लाइब्रेरी डिपेंडेंसी के सिर्फ़ कुछ एपीआई का इस्तेमाल किया जाता है, तो कोड छोटा करने की सुविधा से उस लाइब्रेरी कोड की पहचान की जा सकती है जिसका इस्तेमाल आपका ऐप्लिकेशन नहीं कर रहा है. साथ ही, आपके ऐप्लिकेशन से सिर्फ़ उस कोड को हटाया जा सकता है. ज़्यादा जानने के लिए, अपने कोड को छोटा करने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.
  • संसाधन को छोटा करना: इससे पैकेज किए गए आपके ऐप्लिकेशन से, इस्तेमाल न किए गए संसाधन हट जाते हैं. इनमें, आपके ऐप्लिकेशन की लाइब्रेरी डिपेंडेंसी में मौजूद, इस्तेमाल न किए गए संसाधन भी शामिल हैं. यह सुविधा, कोड छोटा करने की सुविधा के साथ काम करती है. इसकी मदद से, इस्तेमाल न किए गए कोड को हटाने के बाद, ऐसे सभी संसाधनों को भी सुरक्षित तरीके से हटाया जा सकता है जिनका रेफ़रंस अब नहीं दिया गया है. ज़्यादा जानने के लिए, अपने संसाधनों को छोटा करने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.
  • ऑप्टिमाइज़ेशन: रनटाइम की परफ़ॉर्मेंस को बेहतर बनाने और ऐप्लिकेशन की DEX फ़ाइलों के साइज़ को और कम करने के लिए, आपके कोड की जांच करता है और उसे फिर से लिखता है. इससे कोड की रनटाइम परफ़ॉर्मेंस में 30% तक का सुधार होता है. साथ ही, स्टार्टअप और फ़्रेम टाइमिंग में काफ़ी सुधार होता है. उदाहरण के लिए, अगर R8 को पता चलता है कि किसी दिए गए if/else स्टेटमेंट के लिए else {} शाखा कभी नहीं ली जाती है, तो R8 else {} शाखा के लिए कोड हटा देता है. ज़्यादा जानने के लिए, कोड ऑप्टिमाइज़ेशन के बारे में बताने वाले सेक्शन पर जाएं.
  • डेटा को छिपाना (या आइडेंटिफ़ायर को छोटा करना): इससे क्लास और सदस्यों के नाम छोटे हो जाते हैं. इस वजह से, DEX फ़ाइल का साइज़ कम हो जाता है. ज़्यादा जानने के लिए, अपने कोड को छिपाने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.

अपने ऐप्लिकेशन का रिलीज़ वर्शन बनाते समय, R8 को कॉन्फ़िगर किया जा सकता है, ताकि वह ऊपर बताए गए, कंपाइल के समय होने वाले टास्क को आपके लिए पूरा कर सके. ProGuard की नियम फ़ाइलों की मदद से, कुछ टास्क बंद किए जा सकते हैं या R8 के काम करने के तरीके में बदलाव किया जा सकता है. असल में, R8, ProGuard की सभी मौजूदा नियम फ़ाइलों के साथ काम करता है. इसलिए, R8 का इस्तेमाल करने के लिए Android Gradle प्लग इन को अपडेट करने पर, आपको अपने मौजूदा नियमों में बदलाव करने की ज़रूरत नहीं पड़ेगी.

कोड का साइज़ कम करने, उसे अस्पष्ट बनाने, और ऑप्टिमाइज़ करने की सुविधाएं चालू करना

Android Studio 3.4 या Android Gradle प्लग इन 3.4.0 और इसके बाद के वर्शन का इस्तेमाल करने पर, R8 डिफ़ॉल्ट कंपाइलर होता है. यह आपके प्रोजेक्ट के Java बाइटकोड को DEX फ़ॉर्मैट में बदलता है, जो Android प्लैटफ़ॉर्म पर चलता है. हालांकि, Android Studio का इस्तेमाल करके नया प्रोजेक्ट बनाने पर, कोड को छोटा करना, कोड को समझने में मुश्किल बनाना, और कोड को ऑप्टिमाइज़ करना डिफ़ॉल्ट रूप से चालू नहीं होता. ऐसा इसलिए है, क्योंकि कंपाइल के समय होने वाले ये ऑप्टिमाइज़ेशन, आपके प्रोजेक्ट के बिल्ड होने में लगने वाले समय को बढ़ा देते हैं. साथ ही, अगर कोड में से किस कोड को रखना है, यह तय करने के लिए ज़रूरत के मुताबिक बदलाव नहीं किए जाते हैं, तो गड़बड़ियां हो सकती हैं.

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

KotlinGroovy
android {
    buildTypes {
        getByName("release") {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `isDebuggable=false`.
            isMinifyEnabled = true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            isShrinkResources = true

            proguardFiles(
                // Includes the default ProGuard rules files that are packaged with
                // the Android Gradle plugin. To learn more, go to the section about
                // R8 configuration files.
                getDefaultProguardFile("proguard-android-optimize.txt"),

                // Includes a local, custom Proguard rules file
                "proguard-rules.pro"
            )
        }
    }
    ...
}
android {
    buildTypes {
        release {
            // Enables code shrinking, obfuscation, and optimization for only
            // your project's release build type. Make sure to use a build
            // variant with `debuggable false`.
            minifyEnabled true

            // Enables resource shrinking, which is performed by the
            // Android Gradle plugin.
            shrinkResources true

            // Includes the default ProGuard rules files that are packaged with
            // the Android Gradle plugin. To learn more, go to the section about
            // R8 configuration files.
            proguardFiles getDefaultProguardFile(
                    'proguard-android-optimize.txt'),
                    'proguard-rules.pro'
        }
    }
    ...
}

R8 कॉन्फ़िगरेशन फ़ाइलें

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

सोर्स जगह की जानकारी ब्यौरा
Android Studio <module-dir>/proguard-rules.pro Android Studio का इस्तेमाल करके नया मॉड्यूल बनाने पर, IDE उस मॉड्यूल की रूट डायरेक्ट्री में एक proguard-rules.pro फ़ाइल बनाता है.

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

Android Gradle प्लग इन कंपाइल करने के समय, Android Gradle प्लग-इन से जनरेट किया जाता है. Android Gradle प्लग इन, proguard-android-optimize.txt जनरेट करता है. इसमें ऐसे नियम शामिल होते हैं जो ज़्यादातर Android प्रोजेक्ट के लिए काम के होते हैं. साथ ही, यह @Keep* एनोटेशन चालू करता है.

Android Studio का इस्तेमाल करके नया मॉड्यूल बनाते समय, डिफ़ॉल्ट रूप से मॉड्यूल-लेवल की बिल्ड स्क्रिप्ट, आपके रिलीज़ बिल्ड में यह नियम फ़ाइल शामिल करती है.

ध्यान दें: Android Gradle प्लग इन में, पहले से तय ProGuard के अतिरिक्त नियम वाली फ़ाइलें शामिल होती हैं. हालांकि, हमारा सुझाव है कि आप proguard-android-optimize.txt का इस्तेमाल करें.

लाइब्रेरी डिपेंडेंसी

AAR लाइब्रेरी में:
proguard.txt

JAR लाइब्रेरी में:
META-INF/proguard/<ProGuard-rules-file>

इन जगहों के अलावा, Android Gradle प्लग इन 3.6 या इसके बाद के वर्शन में भी टारगेट किए गए शंर्क नियम काम करते हैं.

अगर किसी AAR या JAR लाइब्रेरी को उसके नियमों वाली फ़ाइल के साथ पब्लिश किया गया है और आपने उस लाइब्रेरी को कंपाइल के समय की डिपेंडेंसी के तौर पर शामिल किया है, तो R8 आपके प्रोजेक्ट को कंपाइल करते समय उन नियमों को अपने-आप लागू कर देता है.

ProGuard के सामान्य नियमों के अलावा, Android Gradle प्लग इन के 3.6 या इसके बाद के वर्शन में, टारगेट किए गए छोटे किए गए नियम भी काम करते हैं. ये ऐसे नियम हैं जो खास श्रिंकर (R8 या ProGuard) के साथ-साथ, श्रिंकर के खास वर्शन को टारगेट करते हैं.

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

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

Android Asset Package Tool 2 (AAPT2) minifyEnabled true की मदद से अपना प्रोजेक्ट बनाने के बाद: <module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt AAPT2, आपके ऐप्लिकेशन के मैनिफ़ेस्ट, लेआउट, और अन्य ऐप्लिकेशन संसाधनों में मौजूद क्लास के रेफ़रंस के आधार पर, 'रखें' नियम जनरेट करता है. उदाहरण के लिए, AAPT2 में, ऐप्लिकेशन के मेनिफ़ेस्ट में एंट्री पॉइंट के तौर पर रजिस्टर की गई हर गतिविधि के लिए, 'रखें' नियम शामिल होता है.
कस्टम कॉन्फ़िगरेशन फ़ाइलें Android Studio का इस्तेमाल करके नया मॉड्यूल बनाने पर, डिफ़ॉल्ट रूप से IDE, <module-dir>/proguard-rules.pro बनाता है, ताकि आप अपने नियम जोड़ सकें. अन्य कॉन्फ़िगरेशन शामिल किए जा सकते हैं. R8, उन्हें कंपाइल करने के समय लागू करता है.

minifyEnabled प्रॉपर्टी को true पर सेट करने पर, R8 ऊपर दिए गए सभी उपलब्ध सोर्स के नियमों को जोड़ता है. R8 से जुड़ी समस्या हल करते समय, यह बात याद रखना ज़रूरी है. ऐसा इसलिए, क्योंकि लाइब्रेरी डिपेंडेंसी जैसी अन्य कंपाइल-टाइम डिपेंडेंसी, R8 के काम करने के तरीके में ऐसे बदलाव कर सकती हैं जिनके बारे में आपको पता नहीं है.

प्रोजेक्ट बनाते समय R8 जिन सभी नियमों को लागू करता है उनकी पूरी रिपोर्ट आउटपुट करने के लिए, अपने मॉड्यूल की proguard-rules.pro फ़ाइल में ये शामिल करें:

// You can specify any path and filename.
-printconfiguration ~/tmp/full-r8-config.txt

टारगेट किए गए शॉर्ट वीडियो के लिए, वीडियो के साइज़ को छोटा करने के नियम

Android Gradle प्लग इन 3.6 या इसके बाद के वर्शन, लाइब्रेरी के उन नियमों के साथ काम करते हैं जो खास श्रिंकर (R8 या ProGuard) के साथ-साथ, श्रिंकर के खास वर्शन को टारगेट करते हैं. इससे, लाइब्रेरी डेवलपर अपने नियमों को उन प्रोजेक्ट में बेहतर तरीके से काम करने के लिए तैयार कर सकते हैं जिनमें नए वर्शन का इस्तेमाल किया जाता है. साथ ही, मौजूदा नियमों का इस्तेमाल, पुराने वर्शन वाले प्रोजेक्ट में किया जा सकता है.

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

In an AAR library:
    proguard.txt (legacy location)
    classes.jar
    └── META-INF
        └── com.android.tools (targeted shrink rules location)
            ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
            └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rules-file> (legacy location)
    └── com.android.tools (targeted shrink rules location)
        ├── r8-from-<X>-upto-<Y>/<R8-rules-file>
        └── proguard-from-<X>-upto-<Y>/<ProGuard-rules-file>

इसका मतलब है कि टारगेट किए गए सिंक किए गए नियम, JAR की META-INF/com.android.tools डायरेक्ट्री में या AAR की classes.jar में मौजूद META-INF/com.android.tools डायरेक्ट्री में सेव किए जाते हैं.

उस डायरेक्ट्री में, r8-from-<X>-upto-<Y> या proguard-from-<X>-upto-<Y> के फ़ॉर्मैट में नाम वाली कई डायरेक्ट्री हो सकती हैं. इससे यह पता चलता है कि डायरेक्ट्री में मौजूद नियम, किस श्रिंकर के किस वर्शन के लिए लिखे गए हैं. ध्यान दें कि -from-<X> और -upto-<Y> वाले हिस्से ज़रूरी नहीं हैं. <Y> वर्शन खास है. साथ ही, वर्शन की रेंज लगातार होनी चाहिए.

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

इस जानकारी के आधार पर, Android Gradle प्लग-इन 3.6 या इसके बाद के वर्शन, मैच होने वाली R8 डायरेक्ट्री से नियम चुनेंगे. अगर किसी लाइब्रेरी में, टारगेट किए गए स्माल किए गए वर्शन के नियमों के बारे में नहीं बताया गया है, तो Android Gradle प्लग इन, लेगसी जगहों (एएआर के लिए proguard.txt या JAR के लिए META-INF/proguard/<ProGuard-rules-file>) से नियम चुनेगा.

लाइब्रेरी डेवलपर अपनी लाइब्रेरी में, टारगेट किए गए छोटे किए गए नियम या ProGuard के लेगसी नियमों में से किसी एक को शामिल कर सकते हैं. अगर उन्हें 3.6 से पहले के Android Gradle प्लग इन या अन्य टूल के साथ काम करना है, तो वे दोनों तरह के नियम शामिल कर सकते हैं.

अन्य कॉन्फ़िगरेशन शामिल करना

Android Studio का इस्तेमाल करके नया प्रोजेक्ट या मॉड्यूल बनाने पर, IDE आपके लिए एक <module-dir>/proguard-rules.pro फ़ाइल बनाता है. इसमें अपने नियम शामिल किए जा सकते हैं. आपके पास, अन्य फ़ाइलों के अतिरिक्त नियमों को भी शामिल करने का विकल्प है. इसके लिए, उन्हें अपने मॉड्यूल की बिल्ड स्क्रिप्ट में proguardFiles प्रॉपर्टी में जोड़ें.

उदाहरण के लिए, हर बिल्ड वैरिएंट के लिए खास नियम जोड़े जा सकते हैं. इसके लिए, उससे जुड़े productFlavor ब्लॉक में एक और proguardFiles प्रॉपर्टी जोड़ें. यहां दी गई Gradle फ़ाइल, flavor2 प्रॉडक्ट फ़्लेवर में flavor2-rules.pro जोड़ती है. अब flavor2, ProGuard के तीनों नियमों का इस्तेमाल करता है, क्योंकि release के ब्लॉक में मौजूद नियम भी लागू होते हैं.

इसके अलावा, testProguardFiles प्रॉपर्टी जोड़ी जा सकती है. इससे, सिर्फ़ टेस्ट APK में शामिल ProGuard फ़ाइलों की सूची दिखती है:

KotlinGroovy
android {
    ...
    buildTypes {
        getByName("release") {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                "proguard-rules.pro"
            )
            testProguardFiles(
                // The proguard files listed here are included in the
                // test APK only.
                "test-proguard-rules.pro"
            )
        }
    }
    flavorDimensions.add("version")
    productFlavors {
        create("flavor1") {
            ...
        }
        create("flavor2") {
            proguardFile("flavor2-rules.pro")
        }
    }
}
android {
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                // List additional ProGuard rules for the given build type here. By default,
                // Android Studio creates and includes an empty rules file for you (located
                // at the root directory of each module).
                'proguard-rules.pro'
            testProguardFiles
                // The proguard files listed here are included in the
                // test APK only.
                'test-proguard-rules.pro'
        }
    }
    flavorDimensions "version"
    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

अपना कोड छोटा करना

minifyEnabled प्रॉपर्टी को true पर सेट करने पर, R8 की मदद से कोड छोटा करने की सुविधा डिफ़ॉल्ट रूप से चालू हो जाती है.

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

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

पहली इमेज में, रनटाइम लाइब्रेरी डिपेंडेंसी वाला ऐप्लिकेशन दिखाया गया है. ऐप्लिकेशन के कोड की जांच करते समय, R8 यह तय करता है कि foo(), faz(), और bar() तरीकों को MainActivity.class एंट्री पॉइंट से ऐक्सेस किया जा सकता है. हालांकि, रनटाइम के दौरान आपका ऐप्लिकेशन, क्लास OkayApi.class या उसके तरीके baz() का कभी इस्तेमाल नहीं करता. साथ ही, R8 आपके ऐप्लिकेशन को छोटा करते समय उस कोड को हटा देता है.

पहली इमेज. कंपाइल करने के समय, R8 आपके प्रोजेक्ट के 'रखें' नियमों के आधार पर एक ग्राफ़ बनाता है. इससे, ऐक्सेस न किए जा सकने वाले कोड का पता चलता है.

R8, प्रोजेक्ट की R8 कॉन्फ़िगरेशन फ़ाइलों में मौजूद -keep नियमों के ज़रिए एंट्री पॉइंट तय करता है. इसका मतलब है कि 'रखें' नियमों में उन क्लास के बारे में बताया जाता है जिन्हें R8 को आपके ऐप्लिकेशन को छोटा करते समय नहीं हटाना चाहिए. R8, उन क्लास को आपके ऐप्लिकेशन के संभावित एंट्री पॉइंट के तौर पर इस्तेमाल करता है. Android Gradle प्लग इन और AAPT2, 'रखें' नियम अपने-आप जनरेट करते हैं. ये नियम, आपके ज़्यादातर ऐप्लिकेशन प्रोजेक्ट के लिए ज़रूरी होते हैं. जैसे, आपके ऐप्लिकेशन की गतिविधियां, व्यू, और सेवाएं. हालांकि, अगर आपको इस डिफ़ॉल्ट व्यवहार को, डेटा को सेव रखने के अन्य नियमों के साथ पसंद के मुताबिक बनाना है, तो किस कोड को सेव रखना है, यह तय करने का तरीका बताने वाला सेक्शन पढ़ें.

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

ध्यान दें कि अगर किसी लाइब्रेरी प्रोजेक्ट को छोटा किया जाता है, तो उस लाइब्रेरी पर निर्भर ऐप्लिकेशन में, छोटी की गई लाइब्रेरी क्लास शामिल होती हैं. अगर लाइब्रेरी APK में क्लास मौजूद नहीं हैं, तो आपको लाइब्रेरी में कॉन्टेंट सेव रखने के नियमों में बदलाव करना पड़ सकता है. अगर किसी लाइब्रेरी को AAR फ़ॉर्मैट में बनाया और पब्लिश किया जा रहा है, तो AAR फ़ाइल में उन लोकल JAR फ़ाइलों को छोटा नहीं किया जाता जिन पर आपकी लाइब्रेरी निर्भर करती है.

यह तय करना कि कौनसा कोड रखना है

ज़्यादातर मामलों में, R8 के लिए सिर्फ़ इस्तेमाल न किए गए कोड को हटाने के लिए, ProGuard की डिफ़ॉल्ट नियम फ़ाइल (proguard-android-optimize.txt) काफ़ी होती है. हालांकि, कुछ मामलों में R8 का सही तरीके से विश्लेषण करना मुश्किल होता है. साथ ही, ऐसा हो सकता है कि वह आपके ऐप्लिकेशन के लिए ज़रूरी कोड को हटा दे. कोड को गलत तरीके से हटाने के कुछ उदाहरण यहां दिए गए हैं:

  • जब आपका ऐप्लिकेशन, Java Native Interface (JNI) से किसी तरीके को कॉल करता है
  • जब आपका ऐप्लिकेशन रनटाइम के दौरान कोड को देखता है (जैसे कि रिफ़्लेक्शन के साथ)

अपने ऐप्लिकेशन की जांच करने पर, गलत तरीके से हटाए गए कोड की वजह से होने वाली गड़बड़ियां पता चल सकती हैं. हालांकि, हटाए गए कोड की रिपोर्ट जनरेट करके यह भी देखा जा सकता है कि कौनसा कोड हटाया गया है.

गड़बड़ियां ठीक करने और R8 को कुछ कोड को बनाए रखने के लिए, ProGuard के नियमों वाली फ़ाइल में एक -keep लाइन जोड़ें. उदाहरण के लिए:

-keep public class MyClass

इसके अलावा, जिस कोड को बनाए रखना है उसमें @Keep एनोटेशन जोड़ा जा सकता है. किसी क्लास में @Keep जोड़ने पर, पूरी क्लास में कोई बदलाव नहीं होता. किसी मेथड या फ़ील्ड में इसे जोड़ने पर, मेथड/फ़ील्ड (और उसका नाम) के साथ-साथ क्लास का नाम भी पहले जैसा ही रहेगा. ध्यान दें कि यह एनोटेशन सिर्फ़ तब उपलब्ध होता है, जब AndroidX एनोटेशन लाइब्रेरी का इस्तेमाल किया जा रहा हो. साथ ही, जब Android Gradle प्लग इन के साथ पैकेज की गई ProGuard नियम फ़ाइल को शामिल किया गया हो. इस बारे में ज़्यादा जानने के लिए, ऐप्लिकेशन को छोटा करने की सुविधा चालू करने का तरीका बताने वाले सेक्शन पर जाएं.

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

नेटिव लाइब्रेरी हटाना

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

नेटिव क्रैश के लिए सहायता

Google Play Console, Android की ज़रूरी जानकारी में नेटिव क्रैश की रिपोर्ट करता है. कुछ ही चरणों में, अपने ऐप्लिकेशन के लिए नेटिव डीबग सिंबल फ़ाइल जनरेट और अपलोड की जा सकती है. यह फ़ाइल, Android Vitals में सिम्बॉलाइज़ किए गए नेटिव क्रैश स्टैक ट्रेस (इसमें क्लास और फ़ंक्शन के नाम शामिल होते हैं) को चालू करती है. इससे, आपको प्रोडक्शन में अपने ऐप्लिकेशन को डीबग करने में मदद मिलती है. आपके प्रोजेक्ट में इस्तेमाल किए गए Android Gradle प्लग इन के वर्शन और प्रोजेक्ट के बिल्ड आउटपुट के आधार पर, ये चरण अलग-अलग हो सकते हैं.

Android Gradle प्लग इन का 4.1 या इसके बाद का वर्शन

अगर आपके प्रोजेक्ट में Android ऐप्लिकेशन बंडल बनाया जाता है, तो इसमें नेटिव डीबग सिंबल वाली फ़ाइल को अपने-आप शामिल किया जा सकता है. इस फ़ाइल को रिलीज़ बिल्ड में शामिल करने के लिए, अपने ऐप्लिकेशन की build.gradle.kts फ़ाइल में यह सेटिंग जोड़ें:

android.buildTypes.release.ndk.debugSymbolLevel = { SYMBOL_TABLE | FULL }

इनमें से डीबग सिंबल का लेवल चुनें:

  • Play Console के सिंबल वाले स्टैक ट्रेस में फ़ंक्शन के नाम पाने के लिए, SYMBOL_TABLE का इस्तेमाल करें. इस लेवल पर टॉम्बस्टोन का इस्तेमाल किया जा सकता है.
  • Play Console के स्टैक ट्रेस में, फ़ंक्शन के नाम, फ़ाइलें, और लाइन नंबर देखने के लिए FULL का इस्तेमाल करें.

अगर आपके प्रोजेक्ट में APK बनाया जाता है, तो नेटिव डीबग सिंबल वाली फ़ाइल को अलग से जनरेट करने के लिए, पहले दिखाई गई build.gradle.kts बिल्ड सेटिंग का इस्तेमाल करें. Google Play Console में, नेटिव डीबग सिंबल वाली फ़ाइल को मैन्युअल तरीके से अपलोड करें. बिल्ड प्रोसेस का हिस्सा होने के तौर पर, 'Android Gradle प्लग इन', प्रोजेक्ट की यहां बताई गई जगह पर इस फ़ाइल का आउटपुट देता है:

app/build/outputs/native-debug-symbols/variant-name/native-debug-symbols.zip

'Android Gradle प्लग इन' 4.0 या इससे पहले का वर्शन (और अन्य बिल्ड सिस्टम)

बिल्ड प्रोसेस का हिस्सा होने के तौर पर, 'Android Gradle प्लग इन', प्रोजेक्ट डायरेक्ट्री में ज़्यादा जानकारी वाली लाइब्रेरी की एक कॉपी रखता है. इस डायरेक्ट्री का स्ट्रक्चर इस तरह का है:

app/build/intermediates/cmake/universal/release/obj/
├── armeabi-v7a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── arm64-v8a/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
├── x86/
│   ├── libgameengine.so
│   ├── libothercode.so
│   └── libvideocodec.so
└── x86_64/
    ├── libgameengine.so
    ├── libothercode.so
    └── libvideocodec.so
  1. इस डायरेक्ट्री के कॉन्टेंट की ज़िप फ़ाइल बनाएं:

    cd app/build/intermediates/cmake/universal/release/obj
    zip -r symbols.zip .
    
  2. Google Play Console में, मैन्युअल तरीके से symbols.zip फ़ाइल अपलोड करें.

अपने संसाधनों को छोटा करना

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

संसाधन को छोटा करने की सुविधा चालू करने के लिए, अपनी बिल्ड स्क्रिप्ट में shrinkResources प्रॉपर्टी को true पर सेट करें. साथ ही, कोड को छोटा करने के लिए minifyEnabled पर सेट करें. उदाहरण के लिए:

KotlinGroovy
android {
    ...
    buildTypes {
        getByName("release") {
            isShrinkResources = true
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android.txt"),
                "proguard-rules.pro"
            )
        }
    }
}
android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android.txt'),
                'proguard-rules.pro'
        }
    }
}

अगर आपने कोड को छोटा करने के लिए, minifyEnabled का इस्तेमाल करके अपना ऐप्लिकेशन पहले से नहीं बनाया है, तो shrinkResources को चालू करने से पहले ऐसा करें. ऐसा इसलिए, क्योंकि संसाधनों को हटाने से पहले, आपको अपनी proguard-rules.pro फ़ाइल में बदलाव करना पड़ सकता है, ताकि डाइनैमिक तौर पर बनाई गई या इस्तेमाल की गई क्लास या तरीकों को रखा जा सके.

यह तय करना कि कौनसे रिसॉर्स सेव रखने हैं

अगर आपको कुछ खास संसाधनों को सेव या हटाना है, तो अपने प्रोजेक्ट में <resources> टैग के साथ एक एक्सएमएल फ़ाइल बनाएं. साथ ही, tools:keep एट्रिब्यूट में सेव किए जाने वाले हर संसाधन और tools:discard एट्रिब्यूट में हटाए जाने वाले हर संसाधन की जानकारी दें. दोनों एट्रिब्यूट के लिए, संसाधन के नामों की सूची को कॉमा लगाकर अलग किया जा सकता है. तारे के निशान का इस्तेमाल वाइल्डकार्ड के तौर पर किया जा सकता है.

उदाहरण के लिए:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

इस फ़ाइल को अपने प्रोजेक्ट के संसाधनों में सेव करें. उदाहरण के लिए, res/raw/my.package.keep.xml पर. बिल्ड, इस फ़ाइल को आपके ऐप्लिकेशन में पैकेज नहीं करता.

ध्यान दें: पक्का करें कि आपने keep फ़ाइल के लिए कोई यूनीक नाम इस्तेमाल किया हो. जब अलग-अलग लाइब्रेरी को एक-दूसरे से लिंक किया जाता है, तो उनके 'डेटा को सेव रखने के नियम' आपस में मेल नहीं खाते. ऐसा न होने पर, अनदेखा किए गए नियमों या ज़रूरत के मुताबिक नहीं रखे गए संसाधनों से समस्याएं हो सकती हैं.

यह बताना कि किन संसाधनों को खारिज करना है, शायद बेवकूफी लग सकता है, क्योंकि उन्हें मिटाया जा सकता है. हालांकि, बिल्ड वैरिएंट का इस्तेमाल करते समय यह काम का हो सकता है. उदाहरण के लिए, अपने सभी संसाधनों को सामान्य प्रोजेक्ट डायरेक्ट्री में डाला जा सकता है. इसके बाद, हर बिल्ड वैरिएंट के लिए एक अलग my.package.build.variant.keep.xml फ़ाइल बनाई जा सकती है. ऐसा तब किया जा सकता है, जब आपको पता हो कि किसी संसाधन का इस्तेमाल कोड में किया गया है (इसलिए, उसे छोटा करने वाले टूल से नहीं हटाया गया है) और आपको यह भी पता हो कि इसका इस्तेमाल, दिए गए बिल्ड वैरिएंट के लिए नहीं किया जाएगा. ऐसा भी हो सकता है कि बिल्ड टूल ने किसी रिसॉर्स की ज़रूरत के हिसाब से गलत तरीके से पहचान की हो. ऐसा इसलिए हो सकता है, क्योंकि कंपाइलर, रिसॉर्स आईडी को इनलाइन जोड़ता है. इसके बाद, हो सकता है कि रिसॉर्स एनालाइज़र को कोड में मौजूद, असल तौर पर रेफ़र किए गए रिसॉर्स और पूरी संख्या वाली वैल्यू के बीच का अंतर न पता हो.

रेफ़रंस की सख्त जांच की सुविधा चालू करना

आम तौर पर, रिसोर्स को छोटा करने वाला टूल सटीक तरीके से यह पता लगा सकता है कि किसी रिसोर्स का इस्तेमाल किया गया है या नहीं. हालांकि, अगर आपका कोड Resources.getIdentifier() पर कॉल करता है (या आपकी कोई लाइब्रेरी ऐसा करती है—AppCompat लाइब्रेरी ऐसा करती है), तो इसका मतलब है कि आपका कोड डाइनैमिक तौर पर जनरेट की गई स्ट्रिंग के आधार पर, संसाधन के नाम खोज रहा है. ऐसा करने पर, रिसॉर्स को छोटा करने वाला टूल डिफ़ॉल्ट रूप से सुरक्षा के लिहाज़ से काम करता है. साथ ही, नाम के मैच होने वाले फ़ॉर्मैट वाले सभी रिसॉर्स को, इस्तेमाल किए जाने की संभावना वाले और हटाए जाने के लिए उपलब्ध नहीं के तौर पर मार्क करता है.

उदाहरण के लिए, नीचे दिए गए कोड की वजह से, img_ प्रीफ़िक्स वाले सभी संसाधनों को इस्तेमाल किया गया के तौर पर मार्क कर दिया जाता है.

KotlinJava
val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)
String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

रिसॉर्स को छोटा करने वाला टूल, आपके कोड में मौजूद सभी स्ट्रिंग कॉन्स्टेंट के साथ-साथ, अलग-अलग res/raw/ रिसॉर्स भी देखता है. साथ ही, file:///android_res/drawable//ic_plus_anim_016.png जैसे फ़ॉर्मैट में रिसॉर्स यूआरएल खोजता है. अगर इस तरह की स्ट्रिंग या ऐसी अन्य स्ट्रिंग मिलती हैं जिनका इस्तेमाल इस तरह के यूआरएल बनाने के लिए किया जा सकता है, तो उन्हें नहीं हटाया जाता.

ये सुरक्षित तरीके से छोटा करने वाले मोड के उदाहरण हैं. यह मोड डिफ़ॉल्ट रूप से चालू होता है. हालांकि, "पहले से सुरक्षित रहें" वाले इस तरीके को बंद किया जा सकता है. साथ ही, यह भी तय किया जा सकता है कि रिसोर्स को छोटा करने वाला टूल सिर्फ़ उन रिसोर्स को सेव रखे जिनका इस्तेमाल किया जा रहा है. ऐसा करने के लिए, keep.xml फ़ाइल में shrinkMode को strict पर सेट करें. इसके लिए, यह तरीका अपनाएं:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

अगर आपने स्ट्रिक्ट स्ट्रिंग मोड चालू किया है और आपका कोड, डाइनैमिक तौर पर जनरेट हुई स्ट्रिंग वाले संसाधनों का भी रेफ़रंस देता है, जैसा कि ऊपर दिखाया गया है, तो आपको tools:keep एट्रिब्यूट का इस्तेमाल करके, उन संसाधनों को मैन्युअल तौर पर रखना होगा.

इस्तेमाल नहीं किए गए वैकल्पिक रिसॉर्स हटाना

Gradle का संसाधन छोटा करने वाला टूल, सिर्फ़ उन संसाधनों को हटाता है जिनका रेफ़रंस आपके ऐप्लिकेशन कोड में नहीं दिया गया है. इसका मतलब है कि यह अलग-अलग डिवाइस कॉन्फ़िगरेशन के लिए, अलग-अलग संसाधन नहीं हटाएगा. ज़रूरत पड़ने पर, Android Gradle प्लग इन की resConfigs प्रॉपर्टी का इस्तेमाल करके, वैकल्पिक संसाधन फ़ाइलों को हटाया जा सकता है. ये फ़ाइलें आपके ऐप्लिकेशन के लिए ज़रूरी नहीं हैं.

उदाहरण के लिए, अगर किसी ऐसी लाइब्रेरी का इस्तेमाल किया जा रहा है जिसमें भाषा के संसाधन शामिल हैं, जैसे कि AppCompat या Google Play Services, तो आपके ऐप्लिकेशन में उन लाइब्रेरी के मैसेज के लिए, अनुवाद की गई सभी भाषा की स्ट्रिंग शामिल होती हैं. भले ही, आपके ऐप्लिकेशन के बाकी हिस्से का अनुवाद उन भाषाओं में किया गया हो या नहीं. अगर आपको सिर्फ़ उन भाषाओं को शामिल रखना है जिनमें आपका ऐप्लिकेशन आधिकारिक तौर पर काम करता है, तो resConfig प्रॉपर्टी का इस्तेमाल करके उन भाषाओं की जानकारी दी जा सकती है. जिन भाषाओं के लिए संसाधन नहीं दिए गए हैं उन्हें हटा दिया जाता है.

यहां दिए गए स्निपेट में, भाषा के रिसॉर्स को सिर्फ़ अंग्रेज़ी और फ़्रेंच में सीमित करने का तरीका बताया गया है:

KotlinGroovy
android {
    defaultConfig {
        ...
        resourceConfigurations.addAll(listOf("en", "fr"))
    }
}
android {
    defaultConfig {
        ...
        resConfigs "en", "fr"
    }
}

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

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

डुप्लीकेट संसाधनों को मर्ज करना

डिफ़ॉल्ट रूप से, Gradle एक जैसे नाम वाले संसाधनों को भी मर्ज करता है. जैसे, एक ही नाम वाले drawable, जो अलग-अलग संसाधन फ़ोल्डर में हो सकते हैं. इस व्यवहार को shrinkResources प्रॉपर्टी से कंट्रोल नहीं किया जाता और इसे बंद नहीं किया जा सकता. ऐसा इसलिए ज़रूरी है, ताकि जब आपके कोड में खोजे जा रहे नाम से कई संसाधन मैच करते हों, तो गड़बड़ियों से बचा जा सके.

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

Gradle, डुप्लीकेट संसाधनों को यहां खोजता है:

  • मुख्य सोर्स सेट से जुड़े मुख्य संसाधन, आम तौर पर src/main/res/ में मौजूद होते हैं.
  • बिल्ड टाइप और बिल्ड फ़्लेवर से मिले वैरिएंट ओवरले.
  • लाइब्रेरी प्रोजेक्ट की डिपेंडेंसी.

Gradle, डुप्लीकेट रिसॉर्स को इस क्रम में मर्ज करता है:

डिपेंडेंसी → मुख्य → बिल्ड फ़्लेवर → बिल्ड टाइप

उदाहरण के लिए, अगर आपके मुख्य संसाधनों और किसी बिल्ड फ़्लेवर, दोनों में डुप्लीकेट संसाधन दिखता है, तो Gradle, बिल्ड फ़्लेवर में मौजूद संसाधन को चुनता है.

अगर एक ही सोर्स सेट में एक जैसे रिसॉर्स दिखते हैं, तो Gradle उन्हें मर्ज नहीं कर सकता और रिसॉर्स मर्ज करने से जुड़ी गड़बड़ी का मैसेज दिखाता है. ऐसा तब हो सकता है, जब आपने अपनी build.gradle.kts फ़ाइल की sourceSet प्रॉपर्टी में एक से ज़्यादा सोर्स सेट तय किए हों. उदाहरण के लिए, अगर src/main/res/ और src/main/res2/, दोनों में एक जैसे संसाधन हैं.

अपने कोड को बदलना

कोड को अस्पष्ट बनाने का मकसद, आपके ऐप्लिकेशन के क्लास, मेथड, और फ़ील्ड के नामों को छोटा करके, ऐप्लिकेशन का साइज़ कम करना है. R8 का इस्तेमाल करके कोड को बदलने का उदाहरण यहां दिया गया है:

androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
    android.content.Context mContext -> a
    int mListItemLayout -> O
    int mViewSpacingRight -> l
    android.widget.Button mButtonNeutral -> w
    int mMultiChoiceItemLayout -> M
    boolean mShowTitle -> P
    int mViewSpacingLeft -> j
    int mButtonPanelSideLayout -> K

कोड को उलझाने की सुविधा से, आपके ऐप्लिकेशन का कोड नहीं हटता. हालांकि, जिन ऐप्लिकेशन में कई क्लास, मेथड, और फ़ील्ड को इंडेक्स करने वाली DEX फ़ाइलें होती हैं उनका साइज़ काफ़ी कम हो सकता है. हालांकि, कोड को धुंधला करने की प्रोसेस में, आपके कोड के अलग-अलग हिस्सों के नाम बदल दिए जाते हैं. इसलिए, कुछ टास्क के लिए, आपको अतिरिक्त टूल की ज़रूरत पड़ती है. जैसे, स्टैक ट्रेस की जांच करना. कोड को बदलने के बाद, स्टैक ट्रेस को समझने के लिए, बदले गए स्टैक ट्रेस को डिकोड करने का तरीका बताने वाला सेक्शन पढ़ें.

इसके अलावा, अगर आपका कोड आपके ऐप्लिकेशन के तरीकों और कक्षाओं के लिए, अनुमानित नाम रखने पर निर्भर करता है, तो आपको उन हस्ताक्षरों को एंट्री पॉइंट के तौर पर इस्तेमाल करना चाहिए. साथ ही, उनके लिए नियम तय करने चाहिए. इस बारे में कोड को किस तरह से कस्टमाइज़ करें सेक्शन में बताया गया है. 'रखें' नियम, R8 को यह बताते हैं कि वह कोड को आपके ऐप्लिकेशन के फ़ाइनल DEX में न सिर्फ़ रखें, बल्कि उसका मूल नाम भी बनाए रखें.

उलझाने वाले स्टैक ट्रेस को डिकोड करना

R8 आपके कोड को अस्पष्ट करने के बाद, स्टैक ट्रेस को समझना मुश्किल हो जाता है. ऐसा इसलिए होता है, क्योंकि हो सकता है कि क्लास और तरीकों के नाम बदल दिए गए हों. ओरिजनल स्टैक ट्रेस पाने के लिए, आपको स्टैक ट्रेस को फिर से ट्रैक करना होगा.

कोड ऑप्टिमाइज़ेशन

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

  • अगर आपका कोड किसी दिए गए if/else स्टेटमेंट के लिए कभी भी else {} शाखा को नहीं लेता है, तो हो सकता है कि R8, else {} शाखा के लिए कोड हटा दे.
  • अगर आपका कोड किसी मेथड को सिर्फ़ कुछ जगहों पर कॉल करता है, तो हो सकता है कि R8 उस मेथड को हटा दे और उसे कॉल करने वाली कुछ जगहों पर इनलाइन कर दे.
  • अगर R8 यह पता लगाता है कि किसी क्लास में सिर्फ़ एक यूनीक सबक्लास है और क्लास को खुद इंस्टैंशिएट नहीं किया गया है (उदाहरण के लिए, ऐसी एब्स्ट्रैक्ट बेस क्लास जिसका इस्तेमाल सिर्फ़ एक कॉन्क्रीट लागू करने वाली क्लास करती है), तो R8 इन दोनों क्लास को आपस में जोड़ सकता है और ऐप्लिकेशन से एक क्लास को हटा सकता है.
  • ज़्यादा जानने के लिए, जैक व्हार्टन की लिखी R8 ऑप्टिमाइज़ेशन ब्लॉग पोस्ट पढ़ें.

R8 में, अलग-अलग ऑप्टिमाइज़ेशन को बंद या चालू करने या किसी ऑप्टिमाइज़ेशन के व्यवहार में बदलाव करने की अनुमति नहीं है. असल में, R8 उन सभी ProGuard नियमों को अनदेखा करता है जो डिफ़ॉल्ट ऑप्टिमाइज़ेशन में बदलाव करने की कोशिश करते हैं. जैसे, -optimizations और -optimizationpasses. यह पाबंदी इसलिए ज़रूरी है, क्योंकि R8 को लगातार बेहतर बनाया जा रहा है. इसलिए, ऑप्टिमाइज़ेशन के लिए स्टैंडर्ड व्यवहार बनाए रखने से, Android Studio टीम को आपकी समस्याओं को आसानी से हल करने में मदद मिलती है.

ध्यान दें कि ऑप्टिमाइज़ेशन चालू करने से, आपके ऐप्लिकेशन के स्टैक ट्रेस बदल जाएंगे. उदाहरण के लिए, इनलाइन करने से स्टैक फ़्रेम हट जाएंगे. ओरिजनल स्टैक ट्रेस पाने का तरीका जानने के लिए, फिर से ट्रैक करने वाला सेक्शन देखें.

रनटाइम परफ़ॉर्मेंस पर असर

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

अगर R8 चालू है, तो स्टार्टअप की परफ़ॉर्मेंस को और बेहतर बनाने के लिए, आपको स्टार्टअप प्रोफ़ाइलें भी बनानी चाहिए.

बेहतर ऑप्टिमाइज़ेशन की सुविधा चालू करना

R8 में अतिरिक्त ऑप्टिमाइज़ेशन का एक सेट शामिल होता है, जिसे "फ़ुल मोड" कहा जाता है. इसकी वजह से, यह ProGuard से अलग तरीके से काम करता है. ये ऑप्टिमाइज़ेशन, Android Gradle प्लग इन के 8.0.0 वर्शन से डिफ़ॉल्ट रूप से चालू होते हैं.

अपने प्रोजेक्ट की gradle.properties फ़ाइल में ये चीज़ें शामिल करके, इन अतिरिक्त ऑप्टिमाइज़ेशन को बंद किया जा सकता है:

android.enableR8.fullMode=false

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

हालांकि, "फ़ुल मोड" का इस्तेमाल करते समय, R8 यह अनुमान नहीं लगाता. अगर R8 यह दावा करता है कि आपका कोड रनटाइम के दौरान कभी भी क्लास का इस्तेमाल नहीं करता, तो वह आपके ऐप्लिकेशन के फ़ाइनल DEX से क्लास को हटा देता है. इसका मतलब है कि अगर आपको क्लास और उसके स्टैटिक शुरू करने वाले को बनाए रखना है, तो इसके लिए आपको अपनी नियम फ़ाइल में 'रखें' नियम शामिल करना होगा.

अगर आपको R8 के "फ़ुल मोड" का इस्तेमाल करते समय कोई समस्या आती है, तो इसके समाधान के लिए R8 के बारे में अक्सर पूछे जाने वाले सवालों का पेज देखें. अगर आपको समस्या हल करने में समस्या आ रही है, तो कृपया गड़बड़ी की शिकायत करें.

स्टैक ट्रेस को फिर से ट्रैक करना

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

ओरिजनल स्टैक ट्रेस को वापस पाने के लिए, R8 में retrace कमांड-लाइन टूल उपलब्ध है. यह टूल, कमांड-लाइन टूल पैकेज के साथ बंडल किया गया है.

अपने ऐप्लिकेशन के स्टैक ट्रेस को फिर से ट्रैक करने के लिए, आपको यह पक्का करना होगा कि बिल्ड में ज़रूरत के मुताबिक जानकारी हो, ताकि उसे फिर से ट्रैक किया जा सके. इसके लिए, अपने मॉड्यूल की proguard-rules.pro फ़ाइल में ये नियम जोड़ें:

-keepattributes LineNumberTable,SourceFile
-renamesourcefileattribute SourceFile

LineNumberTable एट्रिब्यूट, तरीकों में पोज़िशन की जानकारी को बनाए रखता है, ताकि उन पोज़िशन को स्टैक ट्रेस में प्रिंट किया जा सके. SourceFile एट्रिब्यूट से यह पक्का होता है कि सभी संभावित रनटाइम, पोज़िशन की जानकारी को प्रिंट करते हैं. -renamesourcefileattribute डायरेक्टिव, स्टैक ट्रैस में सोर्स फ़ाइल का नाम सिर्फ़ SourceFile पर सेट करता है. फिर से ट्रैक करते समय, सोर्स फ़ाइल के असली नाम की ज़रूरत नहीं होती, क्योंकि मैपिंग फ़ाइल में सोर्स फ़ाइल मौजूद होती है.

R8 हर बार चलने पर एक mapping.txt फ़ाइल बनाता है. इसमें स्टैक ट्रेस को ओरिजनल स्टैक ट्रेस पर वापस मैप करने के लिए ज़रूरी जानकारी होती है. Android Studio, फ़ाइल को <module-name>/build/outputs/mapping/<build-type>/ डायरेक्ट्री में सेव करता है.

Google Play पर अपने ऐप्लिकेशन को पब्लिश करते समय, अपने ऐप्लिकेशन के हर वर्शन के लिए mapping.txt फ़ाइल अपलोड की जा सकती है. Android ऐप्लिकेशन बंडल का इस्तेमाल करके पब्लिश करते समय, यह फ़ाइल ऐप्लिकेशन बंडल के कॉन्टेंट के तौर पर अपने-आप शामिल हो जाती है. इसके बाद, Google Play, उपयोगकर्ताओं की बताई गई समस्याओं से आने वाले स्टैक ट्रेस को फिर से ट्रैक करेगा, ताकि आप Play Console में उनकी समीक्षा कर सकें. ज़्यादा जानकारी के लिए, क्रैश स्टैक ट्रेस को डिकोड करने के तरीके के बारे में सहायता केंद्र का लेख पढ़ें.

R8 से जुड़ी समस्या हल करना

इस सेक्शन में, R8 का इस्तेमाल करके, छोटा करने, छिपाने, और ऑप्टिमाइज़ करने की सुविधा चालू करते समय, समस्याओं को हल करने के कुछ तरीके बताए गए हैं. अगर आपको यहां अपनी समस्या का समाधान नहीं मिलता है, तो R8 के बारे में अक्सर पूछे जाने वाले सवालों का पेज और ProGuard से जुड़ी समस्या हल करने के बारे में गाइड भी पढ़ें.

हटाए गए (या रखे गए) कोड की रिपोर्ट जनरेट करना

R8 से जुड़ी कुछ समस्याओं को हल करने में आपकी मदद करने के लिए, उस सभी कोड की रिपोर्ट देखना मददगार हो सकता है जिसे R8 ने आपके ऐप्लिकेशन से हटाया है. जिस मॉड्यूल के लिए आपको यह रिपोर्ट जनरेट करनी है उसके लिए, अपनी कस्टम नियम फ़ाइल में -printusage <output-dir>/usage.txt जोड़ें. R8 को चालू करने और अपना ऐप्लिकेशन बनाने पर, R8 आपके दिए गए पाथ और फ़ाइल के नाम के साथ एक रिपोर्ट दिखाता है. हटाए गए कोड की रिपोर्ट कुछ इस तरह दिखती है:

androidx.drawerlayout.R$attr
androidx.vectordrawable.R
androidx.appcompat.app.AppCompatDelegateImpl
    public void setSupportActionBar(androidx.appcompat.widget.Toolbar)
    public boolean hasWindowFeature(int)
    public void setHandleNativeActionModesEnabled(boolean)
    android.view.ViewGroup getSubDecor()
    public void setLocalNightMode(int)
    final androidx.appcompat.app.AppCompatDelegateImpl$AutoNightModeManager getAutoNightModeManager()
    public final androidx.appcompat.app.ActionBarDrawerToggle$Delegate getDrawerToggleDelegate()
    private static final boolean DEBUG
    private static final java.lang.String KEY_LOCAL_NIGHT_MODE
    static final java.lang.String EXCEPTION_HANDLER_MESSAGE_SUFFIX
...

अगर आपको उन एंट्री पॉइंट की रिपोर्ट देखनी है जिन्हें R8 आपके प्रोजेक्ट के 'रखें' नियमों से तय करता है , तो अपनी कस्टम नियम फ़ाइल में -printseeds <output-dir>/seeds.txt शामिल करें. R8 को चालू करने और अपना ऐप्लिकेशन बनाने पर, R8 आपके दिए गए पाथ और फ़ाइल के नाम के साथ एक रिपोर्ट दिखाता है. सेव किए गए एंट्री पॉइंट की रिपोर्ट इस तरह दिखती है:

com.example.myapplication.MainActivity
androidx.appcompat.R$layout: int abc_action_menu_item_layout
androidx.appcompat.R$attr: int activityChooserViewStyle
androidx.appcompat.R$styleable: int MenuItem_android_id
androidx.appcompat.R$styleable: int[] CoordinatorLayout_Layout
androidx.lifecycle.FullLifecycleObserverAdapter
...

इस्तेमाल नहीं किए जाने वाले रिसॉर्स को हटाने की प्रोसेस से जुड़ी समस्या हल करना

संसाधनों को छोटा करने पर, बिल्ड विंडो में, ऐप्लिकेशन से हटाए गए संसाधनों की खास जानकारी दिखती है. Gradle से ज़्यादा जानकारी वाला टेक्स्ट आउटपुट देखने के लिए, आपको पहले विंडो की बाईं ओर मौजूद व्यू टॉगल करें पर क्लिक करना होगा. उदाहरण के लिए:

:android:shrinkDebugResources
Removed unused resources: Resource data reduced from 2570KB to 1711KB: Removed 33%
:android:validateDebugSigning

Gradle, <module-name>/build/outputs/mapping/release/ में resources.txt नाम की गड़बड़ी की जानकारी देने वाली फ़ाइल भी बनाता है. यह वही फ़ोल्डर है जिसमें ProGuard की आउटपुट फ़ाइलें होती हैं. इस फ़ाइल में यह जानकारी शामिल होती है कि कौनसे संसाधन, दूसरे संसाधनों का रेफ़रंस देते हैं और किन संसाधनों का इस्तेमाल किया जाता है या उन्हें हटाया जाता है.

उदाहरण के लिए, यह पता करने के लिए कि @drawable/ic_plus_anim_016 अब भी आपके ऐप्लिकेशन में क्यों है, resources.txt फ़ाइल खोलें और उस फ़ाइल का नाम खोजें. आपको ऐसा लग सकता है कि इसका रेफ़रंस किसी दूसरे संसाधन से लिया गया है, जैसा कि यहां बताया गया है:

16:25:48.005 [QUIET] [system.out] @drawable/add_schedule_fab_icon_anim : reachable=true
16:25:48.009 [QUIET] [system.out]     @drawable/ic_plus_anim_016

अब आपको यह जानना होगा कि @drawable/add_schedule_fab_icon_anim को ऐक्सेस क्यों किया जा सकता है. ऊपर की ओर खोजने पर, आपको पता चलेगा कि यह संसाधन "ऐसे संसाधन जो रूट से ऐक्सेस किए जा सकते हैं:" में शामिल है. इसका मतलब है कि add_schedule_fab_icon_anim का कोड रेफ़रंस है. इसका मतलब है कि ऐक्सेस किए जा सकने वाले कोड में उसका R.drawable आईडी मिला है.

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

10:32:50.590 [QUIET] [system.out] Marking drawable:ic_plus_anim_016:2130837506
    used because it format-string matches string pool constant ic_plus_anim_%1$d.

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