अपने ऐप्लिकेशन को जितना हो सके उतना छोटा और तेज़ बनाने के लिए, आपको isMinifyEnabled = true
की मदद से रिलीज़ बिल्ड को ऑप्टिमाइज़ और छोटा करना चाहिए.
ऐसा करने से, इस्तेमाल न होने वाले कोड को हटाने की सुविधा चालू हो जाती है. अस्पष्ट बनाने की सुविधा चालू हो जाती है, जिससे आपके ऐप्लिकेशन की क्लास और सदस्यों के नाम छोटे हो जाते हैं. ऑप्टिमाइज़ेशन, ऐप्लिकेशन के साइज़ को कम करने और उसकी परफ़ॉर्मेंस को बेहतर बनाने के लिए बेहतर कोड ऑप्टिमाइज़ेशन रणनीतियां लागू करता है. इस पेज में बताया गया है कि R8 आपके प्रोजेक्ट के लिए कंपाइल टाइम टास्क कैसे करता है. साथ ही, इन्हें अपनी पसंद के मुताबिक बनाने का तरीका भी बताया गया है.
Android Gradle प्लग इन 3.4.0 या इसके बाद के वर्शन का इस्तेमाल करके प्रोजेक्ट बनाने पर, प्लग इन अब कोड को कंपाइल करने के समय ऑप्टिमाइज़ करने के लिए ProGuard का इस्तेमाल नहीं करता. इसके बजाय, प्लगिन R8 कंपाइलर के साथ काम करके, कंपाइल-टाइम के इन टास्क को हैंडल करता है:
- कोड छोटा करना (या ट्री-शैकिंग): यह आपके ऐप्लिकेशन और उसकी लाइब्रेरी डिपेंडेंसी से, इस्तेमाल न होने वाली क्लास, फ़ील्ड, मेथड, और एट्रिब्यूट का पता लगाता है और उन्हें सुरक्षित तरीके से हटा देता है. इससे, 64 हज़ार रेफ़रंस की सीमा को पूरा करने में मदद मिलती है. उदाहरण के लिए, अगर किसी लाइब्रेरी डिपेंडेंसी के सिर्फ़ कुछ एपीआई का इस्तेमाल किया जाता है, तो लाइब्रेरी को छोटा करने से, लाइब्रेरी के उस कोड की पहचान की जा सकती है जिसका इस्तेमाल आपका ऐप्लिकेशन नहीं कर रहा है. साथ ही, आपके ऐप्लिकेशन से सिर्फ़ उस कोड को हटाया जा सकता है. ज़्यादा जानने के लिए, अपने कोड को छोटा करने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.
- संसाधन को छोटा करना: इससे आपके पैकेज किए गए ऐप्लिकेशन से, इस्तेमाल न किए गए संसाधन हट जाते हैं. इनमें, आपके ऐप्लिकेशन की लाइब्रेरी डिपेंडेंसी में मौजूद इस्तेमाल न किए गए संसाधन भी शामिल हैं. यह सुविधा, कोड छोटा करने की सुविधा के साथ काम करती है. इसकी मदद से, इस्तेमाल न किए गए कोड को हटाने के बाद, ऐसे सभी संसाधनों को भी सुरक्षित तरीके से हटाया जा सकता है जिनका रेफ़रंस अब नहीं दिया गया है. ज़्यादा जानने के लिए, अपने संसाधनों को छोटा करने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.
- ऑप्टिमाइज़ेशन: रनटाइम की परफ़ॉर्मेंस को बेहतर बनाने और ऐप्लिकेशन की DEX फ़ाइलों के साइज़ को और कम करने के लिए, आपके कोड की जांच करता है और उसे फिर से लिखता है. इससे कोड की रनटाइम परफ़ॉर्मेंस 30% तक बेहतर होती है. साथ ही, स्टार्टअप और फ़्रेम टाइमिंग में काफ़ी सुधार होता है. उदाहरण के लिए, अगर R8 को पता चलता है कि दिए गए if/else स्टेटमेंट के लिए
else {}
ब्रांच कभी नहीं ली गई, तो R8else {}
ब्रांच का कोड हटा देता है. ज़्यादा जानने के लिए, कोड ऑप्टिमाइज़ेशन सेक्शन पर जाएं. - अस्पष्ट बनाना (या आइडेंटिफ़ायर छोटा करना): इससे क्लास और सदस्यों के नाम को छोटा कर देता है, जिससे DEX फ़ाइल का साइज़ कम हो जाता है. ज़्यादा जानने के लिए, अपने कोड को अस्पष्ट बनाने का तरीका बताने वाले सेक्शन पर जाएं.
अपने ऐप्लिकेशन का रिलीज़ वर्शन बनाते समय, R8 को कॉन्फ़िगर किया जा सकता है, ताकि वह आपके लिए ऊपर बताए गए, कंपाइल के समय होने वाले टास्क को पूरा कर सके. ProGuard की नियम फ़ाइलों की मदद से, कुछ टास्क बंद किए जा सकते हैं या R8 के काम करने के तरीके में बदलाव किया जा सकता है. असल में, R8, ProGuard की सभी मौजूदा नियम फ़ाइलों के साथ काम करता है. इसलिए, R8 का इस्तेमाल करने के लिए Android Gradle प्लग इन को अपडेट करने पर, आपको अपने मौजूदा नियमों में बदलाव करने की ज़रूरत नहीं पड़ेगी.
छोटा करने, अस्पष्ट बनाने, और ऑप्टिमाइज़ेशन की सुविधा चालू करें
Android Studio 3.4 या Android Gradle प्लग इन 3.4.0 और उसके बाद के वर्शन का इस्तेमाल करने पर, R8 डिफ़ॉल्ट कंपाइलर होता है. यह आपके प्रोजेक्ट के Java बाइटकोड को DEX फ़ॉर्मैट में बदलता है, जो Android प्लैटफ़ॉर्म पर चलता है. हालांकि, Android Studio का इस्तेमाल करके नया प्रोजेक्ट बनाने पर, कोड को छोटा करना, कोड को समझने में मुश्किल बनाना, और कोड को ऑप्टिमाइज़ करना डिफ़ॉल्ट रूप से चालू नहीं होता. ऐसा इसलिए है, क्योंकि कंपाइल के समय किए जाने वाले ये ऑप्टिमाइज़ेशन, आपके प्रोजेक्ट के बिल्ड होने में लगने वाले समय को बढ़ा देते हैं. साथ ही, अगर कोड को रखने के लिए ज़रूरत के मुताबिक बदलाव नहीं किया जाता है, तो गड़बड़ियां हो सकती हैं.
इसलिए, अपने ऐप्लिकेशन के उस फ़ाइनल वर्शन को बनाते समय, कंपाइल के समय होने वाले इन टास्क को चालू करना सबसे अच्छा होता है जिसे पब्लिश करने से पहले टेस्ट किया जाता है. छोटा करने, गुप्त करने, और ऑप्टिमाइज़ करने की सुविधा चालू करने के लिए, प्रोजेक्ट-लेवल की बिल्ड स्क्रिप्ट में ये शामिल करें.
Kotlin
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" ) } } ... }
Groovy
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 के अतिरिक्त नियम वाली फ़ाइलें शामिल होती हैं. हालांकि, हमारा सुझाव है कि आप
|
लाइब्रेरी डिपेंडेंसी |
AAR लाइब्रेरी में:
JAR लाइब्रेरी में: इन जगहों के अलावा, Android Gradle प्लग इन 3.6 या इसके बाद के वर्शन में भी टारगेट किए गए शंर्क नियम काम करते हैं. |
अगर किसी एएआर या जेएआर लाइब्रेरी को उसके नियमों वाली फ़ाइल के साथ पब्लिश किया जाता है और आपने उस लाइब्रेरी को कंपाइल-टाइम डिपेंडेंसी के तौर पर शामिल किया है, तो आपके प्रोजेक्ट को कंपाइल करते समय R8 अपने-आप उन नियमों को लागू कर देता है. पारंपरिक ProGuard नियमों के अलावा, Android Gradle प्लग इन 3.6 या इसके बाद वाले वर्शन भी टारगेट किए गए श्रिंक नियमों के साथ काम करता है. ये ऐसे नियम हैं जो खास श्रिंकर (R8 या ProGuard) के साथ-साथ, श्रिंकर के खास वर्शन को टारगेट करते हैं. लाइब्रेरी के साथ पैकेज की गई नियम फ़ाइलों का इस्तेमाल तब करना फ़ायदेमंद होता है, जब लाइब्रेरी के सही तरीके से काम करने के लिए कुछ नियमों की ज़रूरत हो. इसका मतलब है कि लाइब्रेरी के डेवलपर ने आपके लिए समस्या हल करने के तरीके अपनाए हैं. हालांकि, आपको यह पता होना चाहिए कि नियम जोड़े जाते हैं. इसलिए, लाइब्रेरी डिपेंडेंसी में शामिल कुछ नियमों को हटाया नहीं जा सकता. साथ ही, इन नियमों से आपके ऐप्लिकेशन के दूसरे हिस्सों के कंपाइल होने पर असर पड़ सकता है. उदाहरण के लिए, अगर किसी लाइब्रेरी में कोड ऑप्टिमाइज़ेशन को बंद करने का नियम शामिल है, तो वह नियम आपके पूरे प्रोजेक्ट के लिए ऑप्टिमाइज़ेशन बंद कर देता है. |
Android ऐसेट पैकेज टूल 2 (AAPT2) | minifyEnabled true की मदद से अपना प्रोजेक्ट बनाने के बाद:
<module-dir>/build/intermediates/aapt_proguard_file/.../aapt_rules.txt
|
AAPT2, आपके ऐप्लिकेशन के मेनिफ़ेस्ट, लेआउट, और ऐप्लिकेशन के अन्य रिसॉर्स में दी गई क्लास के रेफ़रंस के आधार पर, Keep के नियम जनरेट करता है. उदाहरण के लिए, 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) के साथ-साथ, श्रिंकर के खास वर्शन को टारगेट करते हैं. इससे, लाइब्रेरी डेवलपर अपने नियमों को उन प्रोजेक्ट में बेहतर तरीके से काम करने के लिए तैयार कर सकते हैं जिनमें नए वर्शन का इस्तेमाल किया जाता है. साथ ही, मौजूदा नियमों का इस्तेमाल, पुराने वर्शन वाले प्रोजेक्ट में किया जा सकता है.
टारगेट किए गए, छोटा करने के नियम तय करने के लिए, लाइब्रेरी डेवलपर को उन्हें एएआर या जेएआर लाइब्रेरी में खास जगहों पर शामिल करना होगा, जैसा कि नीचे बताया गया है.
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>
इसका मतलब है कि टारगेट किए गए श्रिंक नियमों को META-INF/com.android.tools
JAR की डायरेक्ट्री या एएआर की META-INF/com.android.tools
डायरेक्ट्री में
classes.jar
में सेव किया जाता है.
उस डायरेक्ट्री में, 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 फ़ाइलों की सूची दिखती है:
Kotlin
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 तय करता है कि रनटाइम के दौरान उसकी ज़रूरत नहीं होती. इस प्रोसेस से आपके ऐप्लिकेशन का साइज़ काफ़ी छोटा हो सकता है. जैसे, आपके ऐप्लिकेशन में कई लाइब्रेरी डिपेंडेंसी शामिल हों, लेकिन उनके फ़ंक्शन का बहुत कम इस्तेमाल किया जाता हो.
आपके ऐप्लिकेशन के कोड को छोटा करने के लिए, R8 सबसे पहले आपके ऐप्लिकेशन के कोड के सभी एंट्री पॉइंट तय करता है. ऐसा, कॉन्फ़िगरेशन फ़ाइलों के सेट के आधार पर होता है. इन एंट्री पॉइंट में वे सभी क्लास शामिल होती हैं जिनका इस्तेमाल Android प्लैटफ़ॉर्म, आपके ऐप्लिकेशन की गतिविधियों या सेवाओं को खोलने के लिए कर सकता है. R8, हर एंट्री पॉइंट से शुरू करके आपके ऐप्लिकेशन के कोड की जांच करता है. इससे, उन सभी तरीकों, सदस्य वैरिएबल, और अन्य क्लास का ग्राफ़ बनाया जाता है जिन्हें आपका ऐप्लिकेशन रनटाइम के दौरान ऐक्सेस कर सकता है. उस ग्राफ़ से कनेक्ट नहीं होने वाले कोड को ऐसा कोड माना जाता है जिसे ऐक्सेस नहीं किया जा सकता. साथ ही, उसे ऐप्लिकेशन से हटाया जा सकता है.
पहली इमेज में, रनटाइम लाइब्रेरी डिपेंडेंसी वाला ऐप्लिकेशन दिखाया गया है. ऐप्लिकेशन के कोड की जांच करते समय, R8 यह तय करता है कि foo()
, faz()
, और bar()
के तरीकों तक MainActivity.class
एंट्री पॉइंट से पहुंचा जा सकता है. हालांकि, रनटाइम के दौरान आपके ऐप्लिकेशन में क्लास OkayApi.class
या उसके तरीके baz()
का कभी इस्तेमाल नहीं किया जाता. साथ ही, R8 आपके ऐप्लिकेशन को छोटा करते समय उस कोड को हटा देता है.
R8, प्रोजेक्ट की R8 कॉन्फ़िगरेशन फ़ाइलों में मौजूद -keep
नियमों के ज़रिए एंट्री पॉइंट तय करता है. इसका मतलब है कि नियम उन क्लास के बारे में बताते हैं जिन्हें R8 को, ऐप्लिकेशन का साइज़ छोटा करते समय खारिज नहीं करना चाहिए. R8 उन क्लास को आपके ऐप्लिकेशन का एंट्री पॉइंट मानता है. 'Android Gradle प्लग इन' और AAPT2, आपके लिए ज़्यादातर ऐप्लिकेशन प्रोजेक्ट के लिए ज़रूरी
बनावटी नियमों को अपने-आप जनरेट करते हैं. जैसे, आपके ऐप्लिकेशन की गतिविधियां, व्यू, और सेवाएं. हालांकि, अगर आपको इस डिफ़ॉल्ट व्यवहार को, डेटा को सेव रखने के अन्य नियमों के साथ पसंद के मुताबिक बनाना है, तो किस कोड को सेव रखना है, यह तय करने के तरीके के बारे में बताने वाला सेक्शन पढ़ें.
अगर आपको सिर्फ़ अपने ऐप्लिकेशन के रिसॉर्स का साइज़ कम करना है, तो अपने रिसॉर्स को छोटा करने के तरीके के बारे में बताने वाले सेक्शन पर जाएं.
ध्यान दें कि अगर किसी लाइब्रेरी प्रोजेक्ट को छोटा किया जाता है, तो उस लाइब्रेरी पर निर्भर ऐप्लिकेशन में, छोटी की गई लाइब्रेरी क्लास शामिल होती हैं. अगर लाइब्रेरी APK में क्लास मौजूद नहीं हैं, तो आपको लाइब्रेरी में कॉन्टेंट सेव रखने के नियमों में बदलाव करना पड़ सकता है. अगर किसी लाइब्रेरी को AAR फ़ॉर्मैट में बनाया और पब्लिश किया जा रहा है, तो AAR फ़ाइल में उन लोकल JAR फ़ाइलों को छोटा नहीं किया जाता जिन पर आपकी लाइब्रेरी निर्भर करती है.
यह तय करना कि कौनसा कोड रखना है
ज़्यादातर मामलों में, R8 के लिए सिर्फ़ इस्तेमाल न किए गए कोड को हटाने के लिए, ProGuard की डिफ़ॉल्ट नियम फ़ाइल (proguard-android-optimize.txt
) काफ़ी होती है. हालांकि, कुछ मामलों में R8 का सही तरीके से विश्लेषण करना मुश्किल होता है. साथ ही, ऐसा हो सकता है कि वह आपके ऐप्लिकेशन के लिए ज़रूरी कोड हटा दे. कोड को गलत तरीके से हटाने के कुछ उदाहरण यहां दिए गए हैं:
- जब आपका ऐप्लिकेशन, Java नेटिव इंटरफ़ेस (जेएनआई) से किसी तरीके को कॉल करता है
- जब आपका ऐप्लिकेशन रनटाइम के दौरान कोड देखता है (जैसे कि रिफ़्लेक्शन के साथ)
अपने ऐप्लिकेशन की जांच करने पर, गलत तरीके से हटाए गए कोड की वजह से होने वाली गड़बड़ियां पता चल सकती हैं. हालांकि, हटाए गए कोड की रिपोर्ट जनरेट करके यह भी देखा जा सकता है कि कौनसा कोड हटाया गया है.
गड़बड़ियों को ठीक करने और 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
इस डायरेक्ट्री के कॉन्टेंट की ज़िप फ़ाइल बनाएं:
cd app/build/intermediates/cmake/universal/release/obj
zip -r symbols.zip .
Google Play Console में, मैन्युअल तरीके से
symbols.zip
फ़ाइल अपलोड करें.
अपने संसाधनों को छोटा करना
संसाधन छोटा करने की सुविधा, सिर्फ़ कोड छोटा करने की सुविधा के साथ काम करती है. कोड श्रिन्कर इस्तेमाल नहीं किए गए सभी कोड हटा देने के बाद, रिसॉर्स श्रिंकर यह पता लगा सकता है कि ऐप्लिकेशन अब भी किन संसाधनों का इस्तेमाल कर रहा है. यह खास तौर पर तब ज़रूरी होता है, जब आपने कोड लाइब्रेरी जोड़ी हो और उनमें रिसॉर्स शामिल हों. आपको लाइब्रेरी का इस्तेमाल न किया गया कोड हटाना होगा, ताकि लाइब्रेरी के रिसॉर्स का रेफ़रंस न रहे. इससे रिसॉर्स को छोटा करने वाला टूल, रिसॉर्स को हटा सकता है.
संसाधन को छोटा करने की सुविधा चालू करने के लिए, अपनी बिल्ड स्क्रिप्ट में shrinkResources
प्रॉपर्टी को true
पर सेट करें. साथ ही, कोड को छोटा करने के लिए minifyEnabled
पर सेट करें. उदाहरण के लिए:
Kotlin
android { ... buildTypes { getByName("release") { isShrinkResources = true isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" ) } } }
Groovy
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_
प्रीफ़िक्स वाले सभी रिसॉर्स को 'इस्तेमाल किया गया' के तौर पर मार्क किया जाएगा.
Kotlin
val name = String.format("img_%1d", angle + 1) val res = resources.getIdentifier(name, "drawable", packageName)
Java
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
प्रॉपर्टी का इस्तेमाल करके उन भाषाओं की जानकारी दी जा सकती है. जिन भाषाओं के लिए संसाधन नहीं दिए गए हैं उन्हें हटा दिया जाता है.
यहां दिए गए स्निपेट में, भाषा के रिसॉर्स को सिर्फ़ अंग्रेज़ी और फ़्रेंच भाषा तक सीमित करने का तरीका बताया गया है:
Kotlin
android { defaultConfig { ... resourceConfigurations.addAll(listOf("en", "fr")) } }
Groovy
android { defaultConfig { ... resConfigs "en", "fr" } }
Android ऐप्लिकेशन बंडल फ़ॉर्मैट का इस्तेमाल करके ऐप्लिकेशन रिलीज़ करते समय, ऐप्लिकेशन इंस्टॉल करते समय डिफ़ॉल्ट रूप से सिर्फ़ उपयोगकर्ता के डिवाइस पर कॉन्फ़िगर की गई भाषाएं ही डाउनलोड की जाती हैं. इसी तरह, डाउनलोड में सिर्फ़ डिवाइस की स्क्रीन की सघनता और डिवाइस के एबीआई से मेल खाने वाली नेटिव लाइब्रेरी शामिल होती हैं. ज़्यादा जानकारी के लिए, Android ऐप्लिकेशन बंडल कॉन्फ़िगरेशन देखें.
अगस्त 2021 से पहले बनाए गए APKs के साथ रिलीज़ किए जा रहे लेगसी ऐप्लिकेशन के लिए, आपके पास यह तय करने का विकल्प होता है कि आपके APK में कौनसी स्क्रीन डेंसिटी या ABI रिसॉर्स शामिल किए जाएं. इसके लिए, एक से ज़्यादा APK बनाएं, जिनमें से हर एक अलग डिवाइस कॉन्फ़िगरेशन को टारगेट करता हो.
डुप्लीकेट संसाधनों को मर्ज करना
डिफ़ॉल्ट रूप से, Gradle, एक जैसे नाम वाले संसाधनों को भी मर्ज करता है. जैसे,
एक ही नाम वाली ड्रॉ करने लायक फ़ाइलें, जो अलग-अलग रिसॉर्स फ़ोल्डर में मौजूद हो सकती हैं. इस कार्रवाई को 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 में रिट्रेस कमांड-लाइन टूल उपलब्ध होता है. यह टूल, कमांड-लाइन टूल पैकेज के साथ मौजूद होता है.
अपने ऐप्लिकेशन के स्टैक ट्रेस को फिर से ट्रेस करने के लिए, आपको यह पक्का करना चाहिए कि बिल्ड में उतनी जानकारी मौजूद हो जितनी रीट्रेस करने के लिए ज़रूरी है. इसके लिए, अपने मॉड्यूल की 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
एट्रिब्यूट का इस्तेमाल करके, बिल्ड सिस्टम को उसे हटाने के लिए कहें. इसके बारे में किस संसाधन को बनाए रखना है, यह तय करने का तरीका सेक्शन में बताया गया है.